commit ecd1605a23ef54d2a4a69e1a361172d350e03b3b
Author: Luigi Scarso <luigi.scarso@gmail.com>
Date:   Wed Sep 5 21:57:30 2018 +0000

    luatex: correct  automake file for pplib; replaced cweb files with c files.
    
    git-svn-id: svn://tug.org/texlive/trunk@48594 c570f23f-e606-0410-a88d-b1316a301751

---
 texk/web2c/luatexdir/am/luapplib.am               |   86 
 texk/web2c/luatexdir/dvi/dvigen.c                 | 1558 -------
 texk/web2c/luatexdir/dvi/dvigen.w                 | 1258 +++++
 texk/web2c/luatexdir/font/dofont.c                |  141 
 texk/web2c/luatexdir/font/dofont.w                |  145 
 texk/web2c/luatexdir/font/luafont.c               | 2420 -----------
 texk/web2c/luatexdir/font/luafont.w               | 2426 +++++++++++
 texk/web2c/luatexdir/font/mapfile.c               |  770 ---
 texk/web2c/luatexdir/font/mapfile.w               |  715 +++
 texk/web2c/luatexdir/font/pkin.c                  |  424 -
 texk/web2c/luatexdir/font/pkin.w                  |  426 +
 texk/web2c/luatexdir/font/sfnt.c                  |  392 -
 texk/web2c/luatexdir/font/sfnt.w                  |  462 ++
 texk/web2c/luatexdir/font/subfont.c               |  277 -
 texk/web2c/luatexdir/font/subfont.w               |  265 +
 texk/web2c/luatexdir/font/texfont.c               | 1950 --------
 texk/web2c/luatexdir/font/texfont.w               | 1972 +++++++++
 texk/web2c/luatexdir/font/tfmofm.c                | 1186 -----
 texk/web2c/luatexdir/font/tfmofm.w                | 1119 +++++
 texk/web2c/luatexdir/font/tounicode.c             |  624 --
 texk/web2c/luatexdir/font/tounicode.w             |  612 ++
 texk/web2c/luatexdir/font/tt_glyf.c               |  569 --
 texk/web2c/luatexdir/font/tt_glyf.w               |  695 +++
 texk/web2c/luatexdir/font/tt_table.c              |  404 -
 texk/web2c/luatexdir/font/tt_table.w              |  460 ++
 texk/web2c/luatexdir/font/vfovf.c                 | 1444 ------
 texk/web2c/luatexdir/font/vfovf.w                 | 1480 ++++++
 texk/web2c/luatexdir/font/vfpacket.c              |  445 --
 texk/web2c/luatexdir/font/vfpacket.w              |  424 +
 texk/web2c/luatexdir/font/writecff.c              | 3156 --------------
 texk/web2c/luatexdir/font/writecff.w              | 3381 +++++++++++++++
 texk/web2c/luatexdir/font/writeenc.c              |  164 
 texk/web2c/luatexdir/font/writeenc.w              |  162 
 texk/web2c/luatexdir/font/writefont.c             | 1069 ----
 texk/web2c/luatexdir/font/writefont.w             | 1116 +++++
 texk/web2c/luatexdir/font/writet1.c               | 1695 -------
 texk/web2c/luatexdir/font/writet1.w               | 1741 ++++++++
 texk/web2c/luatexdir/font/writet3.c               |  356 -
 texk/web2c/luatexdir/font/writet3.w               |  371 +
 texk/web2c/luatexdir/font/writettf.c              | 1573 -------
 texk/web2c/luatexdir/font/writettf.w              | 1797 ++++++++
 texk/web2c/luatexdir/font/writetype0.c            |  130 
 texk/web2c/luatexdir/font/writetype0.w            |  136 
 texk/web2c/luatexdir/font/writetype2.c            |  356 -
 texk/web2c/luatexdir/font/writetype2.w            |  432 +
 texk/web2c/luatexdir/image/pdftoepdf.c            | 1049 ----
 texk/web2c/luatexdir/image/pdftoepdf.w            |  972 ++++
 texk/web2c/luatexdir/image/writeimg.c             |  799 ---
 texk/web2c/luatexdir/image/writeimg.w             |  782 +++
 texk/web2c/luatexdir/image/writejbig2.c           |  861 ---
 texk/web2c/luatexdir/image/writejbig2.w           |  852 +++
 texk/web2c/luatexdir/image/writejp2.c             |  302 -
 texk/web2c/luatexdir/image/writejp2.w             |  297 +
 texk/web2c/luatexdir/image/writejpg.c             |  609 --
 texk/web2c/luatexdir/image/writejpg.w             |  593 ++
 texk/web2c/luatexdir/image/writepng.c             |  645 --
 texk/web2c/luatexdir/image/writepng.w             |  686 +++
 texk/web2c/luatexdir/lang/hnjalloc.c              |   65 
 texk/web2c/luatexdir/lang/hnjalloc.w              |   69 
 texk/web2c/luatexdir/lang/hyphen.c                |  788 ---
 texk/web2c/luatexdir/lang/hyphen.w                |  875 ++++
 texk/web2c/luatexdir/lang/texlang.c               | 1359 ------
 texk/web2c/luatexdir/lang/texlang.w               | 1213 +++++
 texk/web2c/luatexdir/lua/helpers.c                |   29 
 texk/web2c/luatexdir/lua/helpers.w                |   26 
 texk/web2c/luatexdir/lua/llfslibext.c             |  171 
 texk/web2c/luatexdir/lua/lpdfelib.c               | 1666 -------
 texk/web2c/luatexdir/lua/lpdfscannerlib.c         | 1110 -----
 texk/web2c/luatexdir/lua/luainit.c                | 1172 -----
 texk/web2c/luatexdir/lua/luainit.w                | 1166 +++++
 texk/web2c/luatexdir/lua/luanode.c                |  461 --
 texk/web2c/luatexdir/lua/luanode.w                |  451 ++
 texk/web2c/luatexdir/lua/luastuff.c               |  759 ---
 texk/web2c/luatexdir/lua/luastuff.w               |  654 +++
 texk/web2c/luatexdir/lua/luatoken.c               |  585 --
 texk/web2c/luatexdir/lua/luatoken.w               |  424 +
 texk/web2c/luatexdir/lua/mplibstuff.c             |  115 
 texk/web2c/luatexdir/lua/mplibstuff.w             |   93 
 texk/web2c/luatexdir/lua/texluac.c                |  532 --
 texk/web2c/luatexdir/lua/texluac.w                |  495 ++
 texk/web2c/luatexdir/lua/texluajitc.c             |  650 --
 texk/web2c/luatexdir/lua/texluajitc.w             |  575 ++
 texk/web2c/luatexdir/luasocket/src/_ftp.lua       |  329 +
 texk/web2c/luatexdir/luasocket/src/_ftp_lua.c     |  749 +++
 texk/web2c/luatexdir/luasocket/src/_headers.lua   |  106 
 texk/web2c/luatexdir/luasocket/src/_headers_lua.c |  301 +
 texk/web2c/luatexdir/luasocket/src/_smtp.lua      |  256 +
 texk/web2c/luatexdir/luasocket/src/_smtp_lua.c    |  504 ++
 texk/web2c/luatexdir/luasocket/src/_tp.lua        |  134 
 texk/web2c/luatexdir/luasocket/src/_tp_lua.c      |  244 +
 texk/web2c/luatexdir/luasocket/src/_url.lua       |  309 +
 texk/web2c/luatexdir/luasocket/src/_url_lua.c     |  554 ++
 texk/web2c/luatexdir/pdf/pdfaction.c              |  176 
 texk/web2c/luatexdir/pdf/pdfaction.w              |  176 
 texk/web2c/luatexdir/pdf/pdfannot.c               |   93 
 texk/web2c/luatexdir/pdf/pdfannot.w               |   94 
 texk/web2c/luatexdir/pdf/pdfcolorstack.c          |  357 -
 texk/web2c/luatexdir/pdf/pdfcolorstack.w          |  358 +
 texk/web2c/luatexdir/pdf/pdfdest.c                |  401 -
 texk/web2c/luatexdir/pdf/pdfdest.w                |  390 +
 texk/web2c/luatexdir/pdf/pdffont.c                |  214 
 texk/web2c/luatexdir/pdf/pdffont.w                |  195 
 texk/web2c/luatexdir/pdf/pdfgen.c                 | 2516 -----------
 texk/web2c/luatexdir/pdf/pdfgen.w                 | 2520 +++++++++++
 texk/web2c/luatexdir/pdf/pdfglyph.c               |  289 -
 texk/web2c/luatexdir/pdf/pdfglyph.w               |  254 +
 texk/web2c/luatexdir/pdf/pdfimage.c               |  156 
 texk/web2c/luatexdir/pdf/pdfimage.w               |  150 
 texk/web2c/luatexdir/pdf/pdflink.c                |  165 
 texk/web2c/luatexdir/pdf/pdflink.w                |  164 
 texk/web2c/luatexdir/pdf/pdflistout.c             | 1072 ----
 texk/web2c/luatexdir/pdf/pdflistout.w             | 1078 ++++
 texk/web2c/luatexdir/pdf/pdfliteral.c             |  216 
 texk/web2c/luatexdir/pdf/pdfliteral.w             |  201 
 texk/web2c/luatexdir/pdf/pdfobj.c                 |  231 -
 texk/web2c/luatexdir/pdf/pdfobj.w                 |  221 +
 texk/web2c/luatexdir/pdf/pdfoutline.c             |  259 -
 texk/web2c/luatexdir/pdf/pdfoutline.w             |  251 +
 texk/web2c/luatexdir/pdf/pdfpage.c                |  290 -
 texk/web2c/luatexdir/pdf/pdfpage.w                |  274 +
 texk/web2c/luatexdir/pdf/pdfpagetree.c            |  246 -
 texk/web2c/luatexdir/pdf/pdfpagetree.w            |  230 +
 texk/web2c/luatexdir/pdf/pdfrule.c                |  100 
 texk/web2c/luatexdir/pdf/pdfrule.w                |   90 
 texk/web2c/luatexdir/pdf/pdfsaverestore.c         |   76 
 texk/web2c/luatexdir/pdf/pdfsaverestore.w         |   81 
 texk/web2c/luatexdir/pdf/pdfsetmatrix.c           |  230 -
 texk/web2c/luatexdir/pdf/pdfsetmatrix.w           |  228 +
 texk/web2c/luatexdir/pdf/pdfshipout.c             |  290 -
 texk/web2c/luatexdir/pdf/pdfshipout.w             |  313 +
 texk/web2c/luatexdir/pdf/pdftables.c              |  284 -
 texk/web2c/luatexdir/pdf/pdftables.w              |  278 +
 texk/web2c/luatexdir/pdf/pdfthread.c              |  276 -
 texk/web2c/luatexdir/pdf/pdfthread.w              |  275 +
 texk/web2c/luatexdir/pdf/pdfxform.c               |  132 
 texk/web2c/luatexdir/pdf/pdfxform.w               |  131 
 texk/web2c/luatexdir/tex/align.c                  | 1297 -----
 texk/web2c/luatexdir/tex/align.w                  | 1144 +++++
 texk/web2c/luatexdir/tex/arithmetic.c             |  815 ---
 texk/web2c/luatexdir/tex/arithmetic.w             |  735 +++
 texk/web2c/luatexdir/tex/backend.c                |  123 
 texk/web2c/luatexdir/tex/backend.h                |   54 
 texk/web2c/luatexdir/tex/buildpage.c              | 1164 -----
 texk/web2c/luatexdir/tex/buildpage.w              | 1013 ++++
 texk/web2c/luatexdir/tex/commands.c               |  932 ----
 texk/web2c/luatexdir/tex/commands.w               |  894 ++++
 texk/web2c/luatexdir/tex/conditional.c            |  568 --
 texk/web2c/luatexdir/tex/conditional.w            |  552 ++
 texk/web2c/luatexdir/tex/directions.c             |  196 
 texk/web2c/luatexdir/tex/directions.w             |  223 +
 texk/web2c/luatexdir/tex/dumpdata.c               |  539 --
 texk/web2c/luatexdir/tex/dumpdata.w               |  526 ++
 texk/web2c/luatexdir/tex/equivalents.c            | 1220 -----
 texk/web2c/luatexdir/tex/equivalents.w            | 1168 +++++
 texk/web2c/luatexdir/tex/errors.c                 | 1032 ----
 texk/web2c/luatexdir/tex/errors.w                 |  972 ++++
 texk/web2c/luatexdir/tex/expand.c                 |  967 ----
 texk/web2c/luatexdir/tex/expand.w                 |  829 +++
 texk/web2c/luatexdir/tex/extensions.c             | 1329 ------
 texk/web2c/luatexdir/tex/extensions.w             | 1211 +++++
 texk/web2c/luatexdir/tex/filename.c               |  379 -
 texk/web2c/luatexdir/tex/filename.w               |  361 +
 texk/web2c/luatexdir/tex/inputstack.c             |  954 ----
 texk/web2c/luatexdir/tex/inputstack.w             |  822 +++
 texk/web2c/luatexdir/tex/linebreak.c              | 2520 -----------
 texk/web2c/luatexdir/tex/linebreak.w              | 2164 +++++++++
 texk/web2c/luatexdir/tex/mainbody.c               |  769 ---
 texk/web2c/luatexdir/tex/mainbody.w               |  708 +++
 texk/web2c/luatexdir/tex/maincontrol.c            | 4205 -------------------
 texk/web2c/luatexdir/tex/maincontrol.w            | 3670 ++++++++++++++++
 texk/web2c/luatexdir/tex/mathcodes.c              |  360 -
 texk/web2c/luatexdir/tex/mathcodes.w              |  367 +
 texk/web2c/luatexdir/tex/memoryword.c             |   29 
 texk/web2c/luatexdir/tex/memoryword.w             |   55 
 texk/web2c/luatexdir/tex/mlist.c                  | 4781 ----------------------
 texk/web2c/luatexdir/tex/mlist.w                  | 4386 ++++++++++++++++++++
 texk/web2c/luatexdir/tex/nesting.c                |  429 -
 texk/web2c/luatexdir/tex/nesting.w                |  436 ++
 texk/web2c/luatexdir/tex/packaging.c              | 2142 ---------
 texk/web2c/luatexdir/tex/packaging.w              | 2170 +++++++++
 texk/web2c/luatexdir/tex/postlinebreak.c          |  601 --
 texk/web2c/luatexdir/tex/postlinebreak.w          |  583 ++
 texk/web2c/luatexdir/tex/primitive.c              |  785 ---
 texk/web2c/luatexdir/tex/primitive.w              |  664 +++
 texk/web2c/luatexdir/tex/printing.c               | 1281 -----
 texk/web2c/luatexdir/tex/printing.w               | 1135 +++++
 texk/web2c/luatexdir/tex/scanning.c               | 2743 ------------
 texk/web2c/luatexdir/tex/scanning.w               | 2621 ++++++++++++
 texk/web2c/luatexdir/tex/stringpool.c             |  375 -
 texk/web2c/luatexdir/tex/stringpool.w             |  353 +
 texk/web2c/luatexdir/tex/texdeffont.c             |  219 -
 texk/web2c/luatexdir/tex/texdeffont.w             |  188 
 texk/web2c/luatexdir/tex/texfileio.c              | 1509 ------
 texk/web2c/luatexdir/tex/texfileio.w              | 1389 ++++++
 texk/web2c/luatexdir/tex/texmath.c                | 2723 ------------
 texk/web2c/luatexdir/tex/texmath.w                | 2592 +++++++++++
 texk/web2c/luatexdir/tex/texnodes.c               | 4770 ---------------------
 texk/web2c/luatexdir/tex/texnodes.w               | 3910 +++++++++++++++++
 texk/web2c/luatexdir/tex/textcodes.c              |  530 --
 texk/web2c/luatexdir/tex/textcodes.w              |  490 ++
 texk/web2c/luatexdir/tex/textoken.c               | 3916 ------------------
 texk/web2c/luatexdir/tex/textoken.w               | 3541 ++++++++++++++++
 texk/web2c/luatexdir/utils/avlstuff.c             |   63 
 texk/web2c/luatexdir/utils/avlstuff.w             |   61 
 texk/web2c/luatexdir/utils/managed-sa.c           |  298 -
 texk/web2c/luatexdir/utils/managed-sa.w           |  306 +
 texk/web2c/luatexdir/utils/unistring.c            |  203 
 texk/web2c/luatexdir/utils/unistring.w            |  210 
 texk/web2c/luatexdir/utils/utils.c                |  512 --
 texk/web2c/luatexdir/utils/utils.w                |  499 ++
 210 files changed, 86375 insertions(+), 90146 deletions(-)

--- texlive-bin.orig/texk/web2c/luatexdir/am/luapplib.am
+++ /dev/null
@@ -1,86 +0,0 @@
-## texk/web2c/luatexdir/am/luapplib.am: Makefile fragment for libluapplib.
-##
-## Copyright (C) 2018 Luigi Scarso <tex-live@tug.org>
-## You may freely use, modify and/or distribute this file.
-
-## luapplib
-##
-EXTRA_LIBRARIES += libluapplib.a liblua53pplib.a libluajitpplib.a
-
-libluapplib_a_DEPENDENCIES = $(ZLIB_DEPEND)
-liblua53pplib_a_DEPENDENCIES = $(ZLIB_DEPEND)
-libluajitpplib_a_DEPENDENCIES = $(ZLIB_DEPEND)
-
-$(libluapplib_a_OBJECTS): $(LUA_DEPEND)
-$(liblua53pplib_a_OBJECTS): $(LUA_DEPEND)
-$(libluajitpplib_a_OBJECTS): $(LUAJIT_DEPEND)
-
-## replace -I../../libs/zlib/include $(ZLIB_INCLUDES)
-
-libluapplib_a_CPPFLAGS = \
-	-I$(top_srcdir)/luatexdir/luapplib -I$(top_srcdir)/luatexdir/luapplib/util $(ZLIB_INCLUDES) $(LUA_INCLUDES)
-
-liblua53pplib_a_CPPFLAGS = \
-	-I$(top_srcdir)/luatexdir/luapplib -I$(top_srcdir)/luatexdir/luapplib/util $(ZLIB_INCLUDES) $(LUA_LUA53_INCLUDES)
-
-libluajitpplib_a_CPPFLAGS = \
-	-I$(top_srcdir)/luatexdir/luapplib -I$(top_srcdir)/luatexdir/luapplib/util $(ZLIB_INCLUDES) $(LUAJIT_INCLUDES)
-
-libluapplib_a_CFLAGS = # $(WARNING_CFLAGS)
-libluajitpplib_a_CFLAGS = # $(WARNING_CFLAGS)
-
-nodist_libluapplib_a_SOURCES = $(libluapplib_sources)
-nodist_liblua53pplib_a_SOURCES = $(libluapplib_sources)
-nodist_libluajitpplib_a_SOURCES = $(libluapplib_sources)
-
-libluapplib_sources = \
-	luatexdir/luapplib/ppapi.h \
-	luatexdir/luapplib/pparray.c \
-	luatexdir/luapplib/pparray.h \
-	luatexdir/luapplib/ppconf.h \
-	luatexdir/luapplib/ppcrypt.c \
-	luatexdir/luapplib/ppcrypt.h \
-	luatexdir/luapplib/ppdict.c \
-	luatexdir/luapplib/ppdict.h \
-	luatexdir/luapplib/ppfilter.h \
-	luatexdir/luapplib/ppheap.c \
-	luatexdir/luapplib/ppheap.h \
-	luatexdir/luapplib/pplib.h \
-	luatexdir/luapplib/ppload.c \
-	luatexdir/luapplib/ppload.h \
-	luatexdir/luapplib/ppstream.c \
-	luatexdir/luapplib/ppstream.h \
-	luatexdir/luapplib/ppxref.c \
-	luatexdir/luapplib/ppxref.h \
-	luatexdir/luapplib/util/utilbasexx.c \
-	luatexdir/luapplib/util/utilbasexx.h \
-	luatexdir/luapplib/util/utilcrypt.c \
-	luatexdir/luapplib/util/utilcrypt.h \
-	luatexdir/luapplib/util/utilcryptdef.h \
-	luatexdir/luapplib/util/utildecl.h \
-	luatexdir/luapplib/util/utilflate.c \
-	luatexdir/luapplib/util/utilflate.h \
-	luatexdir/luapplib/util/utilfpred.c \
-	luatexdir/luapplib/util/utilfpred.h \
-	luatexdir/luapplib/util/utiliof.c \
-	luatexdir/luapplib/util/utiliof.h \
-	luatexdir/luapplib/util/utillog.c \
-	luatexdir/luapplib/util/utillog.h \
-	luatexdir/luapplib/util/utillzw.c \
-	luatexdir/luapplib/util/utillzw.h \
-	luatexdir/luapplib/util/utilmd5.c \
-	luatexdir/luapplib/util/utilmd5.h \
-	luatexdir/luapplib/util/utilmem.c \
-	luatexdir/luapplib/util/utilmem.h \
-	luatexdir/luapplib/util/utilnumber.c \
-	luatexdir/luapplib/util/utilnumber.h \
-	luatexdir/luapplib/util/utilplat.h \
-	luatexdir/luapplib/util/utilsha.c \
-	luatexdir/luapplib/util/utilsha.h \
-	luatexdir/luapplib/zlib/zconf.h \
-	luatexdir/luapplib/zlib/zlib.h 
-
-
-
-liblua53pplib_sources = $(libluapplib_sources)
-libluajitpplib_sources = $(libluapplib_sources)
--- texlive-bin.orig/texk/web2c/luatexdir/dvi/dvigen.c
+++ /dev/null
@@ -1,1558 +0,0 @@
-/*
-
-dvigen.w
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#undef write_dvi
-
-/*tex This is the current mode: */
-
-#define mode cur_list.mode_field
-
-/*tex
-
-The most important output produced by a run of \TeX\ is the ``device
-independent'' (\.{DVI}) file that specifies where characters and rules are to
-appear on printed pages. The form of these files was designed by David R. Fuchs
-in 1979. Almost any reasonable typesetting device can be driven by a program that
-takes \.{DVI} files as input, and dozens of such \.{DVI}-to-whatever programs
-have been written. Thus, it is possible to print the output of \TeX\ on many
-different kinds of equipment, using \TeX\ as a device-independent ``front end.''
-
-A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a series of
-commands in a machine-like language. The first byte of each command is the
-operation code, and this code is followed by zero or more bytes that provide
-parameters to the command. The parameters themselves may consist of several
-consecutive bytes; for example, the `|set_rule|' command has two parameters, each
-of which is four bytes long. Parameters are usually regarded as nonnegative
-integers; but four-byte-long parameters, and shorter parameters that denote
-distances, can be either positive or negative. Such parameters are given in two's
-complement notation. For example, a two-byte-long distance parameter has a value
-between $-2^{15}$ and $2^{15}-1$. As in \.{TFM} files, numbers that occupy more
-than one byte position appear in BigEndian order.
-
-A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one or more
-``pages,'' followed by a ``postamble.'' The preamble is simply a |pre| command,
-with its parameters that define the dimensions used in the file; this must come
-first. Each ``page'' consists of a |bop| command, followed by any number of other
-commands that tell where characters are to be placed on a physical page, followed
-by an |eop| command. The pages appear in the order that \TeX\ generated them. If
-we ignore |nop| commands and \\{fnt\_def} commands (which are allowed between any
-two commands in the file), each |eop| command is immediately followed by a |bop|
-command, or by a |post| command; in the latter case, there are no more pages in
-the file, and the remaining bytes form the postamble. Further details about the
-postamble will be explained later.
-
-Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
-quantities that give the location number of some other byte in the file; the
-first byte is number~0, then comes number~1, and so on. For example, one of the
-parameters of a |bop| command points to the previous |bop|; this makes it
-feasible to read the pages in backwards order, in case the results are being
-directed to a device that stacks its output face up. Suppose the preamble of a
-\.{DVI} file occupies bytes 0 to 99. Now if the first page occupies bytes 100 to
-999, say, and if the second page occupies bytes 1000 to 1999, then the |bop| that
-starts in byte 1000 points to 100 and the |bop| that starts in byte 2000 points
-to 1000. (The very first |bop|, i.e., the one starting in byte 100, has a pointer
-of~$-1$.)
-
-The \.{DVI} format is intended to be both compact and easily interpreted by a
-machine. Compactness is achieved by making most of the information implicit
-instead of explicit. When a \.{DVI}-reading program reads the commands for a
-page, it keeps track of several quantities: (a)~The current font |f| is an
-integer; this value is changed only by \\{fnt} and \\{fnt\_num} commands. (b)~The
-current position on the page is given by two numbers called the horizontal and
-vertical coordinates, |h| and |v|. Both coordinates are zero at the upper left
-corner of the page; moving to the right corresponds to increasing the horizontal
-coordinate, and moving down corresponds to increasing the vertical coordinate.
-Thus, the coordinates are essentially Cartesian, except that vertical directions
-are flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)~The current
-spacing amounts are given by four numbers |w|, |x|, |y|, and |z|, where |w|
-and~|x| are used for horizontal spacing and where |y| and~|z| are used for
-vertical spacing. (d)~There is a stack containing |(h,v,w,x,y,z)| values; the
-\.{DVI} commands |push| and |pop| are used to change the current level of
-operation. Note that the current font~|f| is not pushed and popped; the stack
-contains only information about positioning.
-
-The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up to
-32 bits, including the sign. Since they represent physical distances, there is a
-small unit of measurement such that increasing |h| by~1 means moving a certain
-tiny distance to the right. The actual unit of measurement is variable, as
-explained below; \TeX\ sets things up so that its \.{DVI} output is in sp units,
-i.e., scaled points, in agreement with all the |scaled| dimensions in \TeX's data
-structures.
-
-Here is a list of all the commands that may appear in a \.{DVI} file. Each
-command is specified by its symbolic name (e.g., |bop|), its opcode byte (e.g.,
-139), and its parameters (if any). The parameters are followed by a bracketed
-number telling how many bytes they occupy; for example, `|p[4]|' means that
-parameter |p| is four bytes long.
-
-\startitemize
-
-\startitem
-    |set_char_0| 0. Typeset character number~0 from font~|f| such that the
-    reference point of the character is at |(h,v)|. Then increase |h| by the
-    width of that character. Note that a character may have zero or negative
-    width, so one cannot be sure that |h| will advance after this command; but
-    |h| usually does increase.
-\stopitem
-
-\startitem
-    \\{set\_char\_1} through \\{set\_char\_127} (opcodes 1 to 127).
-    Do the operations of |set_char_0|; but use the character whose number
-    matches the opcode, instead of character~0.
-\stopitem
-
-\startitem
-    |set1| 128 |c[1]|. Same as |set_char_0|, except that character number~|c| is
-    typeset. \TeX82 uses this command for characters in the range |128<=c<256|.
-\stopitem
-
-\startitem
-    |set2| 129 |c[2]|. Same as |set1|, except that |c|~is two bytes long, so it
-    is in the range |0<=c<65536|. \TeX82 never uses this command, but it should
-    come in handy for extensions of \TeX\ that deal with oriental languages.
-\stopitem
-
-\startitem
-    |set3| 130 |c[3]|. Same as |set1|, except that |c|~is three bytes long, so it
-    can be as large as $2^{24}-1$. Not even the Chinese language has this many
-    characters, but this command might prove useful in some yet unforeseen
-    extension.
-\stopitem
-
-\startitem
-    |set4| 131 |c[4]|. Same as |set1|, except that |c|~is four bytes long.
-    Imagine that.
-\stopitem
-
-\startitem
-    |set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle of height~|a|
-    and width~|b|, with its bottom left corner at |(h,v)|. Then set |h:=h+b|. If
-    either |a<=0| or |b<=0|, nothing should be typeset. Note that if |b<0|, the
-    value of |h| will decrease even though nothing else happens. See below for
-    details about how to typeset rules so that consistency with \MF\ is
-    guaranteed.
-\stopitem
-
-\startitem
-    |put1| 133 |c[1]|. Typeset character number~|c| from font~|f| such that the
-    reference point of the character is at |(h,v)|. (The `put' commands are
-    exactly like the `set' commands, except that they simply put out a character
-    or a rule without moving the reference point afterwards.)
-\stopitem
-
-\startitem
-    |put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
-\stopitem
-
-\startitem
-    |put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
-\stopitem
-
-\startitem
-    |put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
-\stopitem
-
-\startitem
-    |put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that |h| is not
-    changed.
-\stopitem
-
-\startitem
-    |nop| 138. No operation, do nothing. Any number of |nop|'s may occur between
-    \.{DVI} commands, but a |nop| cannot be inserted between a command and its
-    parameters or between two parameters.
-\stopitem
-
-\startitem
-    |bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning of a page:
-    Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set the current
-    font |f| to an undefined value. The ten $c_i$ parameters hold the values of
-    \.{\\count0} $\ldots$ \.{\\count9} in \TeX\ at the time \.{\\shipout} was
-    invoked for this page; they can be used to identify pages, if a user wants to
-    print only part of a \.{DVI} file. The parameter |p| points to the previous
-    |bop| in the file; the first |bop| has $p=-1$.
-\stopitem
-
-\startitem
-    |eop| 140. End of page: Print what you have read since the previous |bop|. At
-    this point the stack should be empty. (The \.{DVI}-reading programs that
-    drive most output devices will have kept a buffer of the material that
-    appears on the page that has just ended. This material is largely, but not
-    entirely, in order by |v| coordinate and (for fixed |v|) by |h|~coordinate;
-    so it usually needs to be sorted into some order that is appropriate for the
-    device in question.)
-\stopitem
-
-\startitem
-    |push| 141. Push the current values of |(h,v,w,x,y,z)| onto the top of the
-    stack; do not change any of these values. Note that |f| is not pushed.
-\stopitem
-
-\startitem
-    |pop| 142. Pop the top six values off of the stack and assign them
-    respectively to |(h,v,w,x,y,z)|. The number of pops should never exceed the
-    number of pushes, since it would be highly embarrassing if the stack were
-    empty at the time of a |pop| command.
-
-\startitem
-    |right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units. The parameter
-    is a signed number in two's complement notation, |-128<=b<128|; if |b<0|, the
-    reference point moves left.
-\stopitem
-
-\startitem
-    |right2| 144 |b[2]|. Same as |right1|, except that |b| is a two-byte quantity
-    in the range |-32768<=b<32768|.
-\stopitem
-
-\startitem
-    |right3| 145 |b[3]|. Same as |right1|, except that |b| is a three-byte
-    quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
-\stopitem
-
-\startitem
-    |right4| 146 |b[4]|. Same as |right1|, except that |b| is a four-byte
-    quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
-\stopitem
-
-\startitem
-    |w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck, this
-    parameterless command will usually suffice, because the same kind of motion
-    will occur several times in succession; the following commands explain how
-    |w| gets particular values.
-\stopitem
-
-\startitem
-    |w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a signed
-    quantity in two's complement notation, |-128<=b<128|. This command changes
-    the current |w|~spacing and moves right by |b|.
-\stopitem
-
-\startitem
-    |w2| 149 |b[2]|. Same as |w1|, but |b| is two bytes long, |-32768<=b<32768|.
-\stopitem
-
-\startitem
-    |w3| 150 |b[3]|. Same as |w1|, but |b| is three bytes long,
-    |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
-\stopitem
-
-\startitem
-    |w4| 151 |b[4]|. Same as |w1|, but |b| is four bytes long,
-    |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
-\stopitem
-
-\startitem
-    |x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|' commands are
-    like the `|w|' commands except that they involve |x| instead of |w|.
-\stopitem
-
-\startitem
-    |x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a signed
-    quantity in two's complement notation, |-128<=b<128|. This command changes
-    the current |x|~spacing and moves right by |b|.
-\stopitem
-
-\startitem
-    |x2| 154 |b[2]|. Same as |x1|, but |b| is two bytes long, |-32768<=b<32768|.
-\stopitem
-
-\startitem
-    |x3| 155 |b[3]|. Same as |x1|, but |b| is three bytes long,
-    |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
-\stopitem
-
-\startitem
-    |x4| 156 |b[4]|. Same as |x1|, but |b| is four bytes long,
-    |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
-\stopitem
-
-\startitem
-    |down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units. The parameter is
-    a signed number in two's complement notation, |-128<=a<128|; if |a<0|, the
-    reference point moves up.
-\stopitem
-
-\startitem
-    |down2| 158 |a[2]|. Same as |down1|, except that |a| is a two-byte quantity
-    in the range |-32768<=a<32768|.
-\stopitem
-
-\startitem
-    |down3| 159 |a[3]|. Same as |down1|, except that |a| is a three-byte quantity
-    in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
-\stopitem
-
-\startitem
-    |down4| 160 |a[4]|. Same as |down1|, except that |a| is a four-byte quantity
-    in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
-\stopitem
-
-\startitem
-    |y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck, this
-    parameterless command will usually suffice, because the same kind of motion
-    will occur several times in succession; the following commands explain how
-    |y| gets particular values.
-\stopitem
-
-\startitem
-    |y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a signed
-    quantity in two's complement notation, |-128<=a<128|. This command changes
-    the current |y|~spacing and moves down by |a|.
-\stopitem
-
-\startitem
-    |y2| 163 |a[2]|. Same as |y1|, but |a| is two bytes long, |-32768<=a<32768|.
-\stopitem
-
-\startitem
-    |y3| 164 |a[3]|. Same as |y1|, but |a| is three bytes long,
-    |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
-\stopitem
-
-\startitem
-    |y4| 165 |a[4]|. Same as |y1|, but |a| is four bytes long,
-    |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
-\stopitem
-
-\startitem
-    |z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands are
-    like the `|y|' commands except that they involve |z| instead of |y|.
-\stopitem
-
-\startitem
-    |z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a signed
-    quantity in two's complement notation, |-128<=a<128|. This command changes
-    the current |z|~spacing and moves down by |a|.
-\stopitem
-
-\startitem
-    |z2| 168 |a[2]|. Same as |z1|, but |a| is two bytes long, |-32768<=a<32768|.
-\stopitem
-
-\startitem
-    |z3| 169 |a[3]|. Same as |z1|, but |a| is three bytes long,
-    |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
-\stopitem
-
-\startitem
-    |z4| 170 |a[4]|. Same as |z1|, but |a| is four bytes long,
-    |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
-\stopitem
-
-\startitem
-    |fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been defined by a
-    \\{fnt\_def} instruction, as explained below.
-\stopitem
-
-\startitem
-    \\{fnt\_num\_1} through \\{fnt\_num\_63} (opcodes 172 to 234). Set |f:=1|,
-    \dots, \hbox{|f:=63|}, respectively.
-\stopitem
-
-\startitem
-    |fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font numbers in
-    the range |64<=k<256|.
-\stopitem
-
-\startitem
-    |fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two bytes long, so it
-    is in the range |0<=k<65536|. \TeX82 never generates this command, but large
-    font numbers may prove useful for specifications of color or texture, or they
-    may be used for special fonts that have fixed numbers in some external coding
-    scheme.
-\stopitem
-
-\startitem
-    |fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three bytes long, so it
-    can be as large as $2^{24}-1$.
-\stopitem
-
-\startitem
-    |fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four bytes long; this
-    is for the really big font numbers (and for the negative ones).
-\stopitem
-
-\startitem
-    |xxx1| 239 |k[1]| |x[k]|. This command is undefined in general; it functions
-    as a $(k+2)$-byte |nop| unless special \.{DVI}-reading programs are being
-    used. \TeX82 generates |xxx1| when a short enough \.{\\special} appears,
-    setting |k| to the number of bytes being sent. It is recommended that |x| be
-    a string having the form of a keyword followed by possible parameters
-    relevant to that keyword.
-\stopitem
-
-\startitem
-    |xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
-\stopitem
-
-\startitem
-    |xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
-\stopitem
-
-\startitem
-    |xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously large.
-    \TeX82 uses |xxx4| when sending a string of length 256 or more.
-\stopitem
-
-\startitem
-    |fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. Define
-    font |k|, where |0<=k<256|; font definitions will be explained shortly.
-\stopitem
-
-\startitem
-    |fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. Define
-    font |k|, where |0<=k<65536|.
-\stopitem
-
-\startitem
-    |fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. Define
-    font |k|, where |0<=k<@t$2^{24}$@>|.
-\stopitem
-
-\startitem
-    |fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. Define
-    font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
-\stopitem
-
-\startitem
-    |pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|. Beginning of the
-    preamble; this must come at the very beginning of the file. Parameters |i|,
-    |num|, |den|, |mag|, |k|, and |x| are explained below.
-\stopitem
-
-\startitem
-    |post| 248. Beginning of the postamble, see below.
-\stopitem
-
-\startitem
-    |post_post| 249. Ending of the postamble, see below.
-\stopitem
-
-\startitem
-    Commands 250--255 are undefined at the present time.
-\stopitem
-
-*/
-
-#define set_char_0  0           /* typeset character 0 and move right */
-#define set1  128               /* typeset a character and move right */
-#define set_rule  132           /* typeset a rule and move right */
-#define put1    133             /* typeset a character without moving */
-#define put_rule  137           /* typeset a rule */
-#define nop  138                /* no operation */
-#define bop  139                /* beginning of page */
-#define eop  140                /* ending of page */
-#define push  141               /* save the current positions */
-#define pop  142                /* restore previous positions */
-#define right1    143           /* move right */
-#define right4    146           /* move right, 4 bytes */
-#define w0  147                 /* move right by |w| */
-#define w1  148                 /* move right and set |w| */
-#define x0  152                 /* move right by |x| */
-#define x1  153                 /* move right and set |x| */
-#define down1  157              /* move down */
-#define down4  160              /* move down, 4 bytes */
-#define y0  161                 /* move down by |y| */
-#define y1  162                 /* move down and set |y| */
-#define z0  166                 /* move down by |z| */
-#define z1  167                 /* move down and set |z| */
-#define fnt_num_0  171          /* set current font to 0 */
-#define fnt1  235               /* set current font */
-#define xxx1  239               /* extension to \.{DVI} primitives */
-#define xxx4  242               /* potentially long extension to \.{DVI} primitives */
-#define fnt_def1  243           /* define the meaning of a font number */
-#define pre  247                /* preamble */
-#define post  248               /* postamble beginning */
-#define post_post  249          /* postamble ending */
-
-/*tex
-
-The preamble contains basic information about the file as a whole. As stated
-above, there are six parameters: $$\hbox{|i[1]| |num[4]| |den[4]|
-|mag[4]| |k[1]| |x[k]|.}$$ The |i| byte identifies \.{DVI} format;
-currently this byte is always set to~2. (The value |i=3| is currently used for an
-extended format that allows a mixture of right-to-left and left-to-right
-typesetting. Some day we will set |i=4|, when \.{DVI} format makes another
-incompatible change---perhaps in the year 2048.)
-
-The next two parameters, |num| and |den|, are positive integers that define the
-units of measurement; they are the numerator and denominator of a fraction by
-which all dimensions in the \.{DVI} file could be multiplied in order to get
-lengths in units of $10^{-7}$ meters. Since $\rm 7227{pt} = 254{cm}$, and since
-\TeX\ works with scaled points where there are $2^{16}$ sp in a point, \TeX\ sets
-$|num|/|den|=(254\cdot10^5)/(7227\cdot2^{16})=25400000/473628672$.
-
-The |mag| parameter is what \TeX\ calls \.{\\mag}, i.e., 1000 times the desired
-magnification. The actual fraction by which dimensions are multiplied is
-therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\ source document does
-not call for any `\.{true}' dimensions, and if you change it only by specifying a
-different \.{\\mag} setting, the \.{DVI} file that \TeX\ creates will be
-completely unchanged except for the value of |mag| in the preamble and postamble.
-(Fancy \.{DVI}-reading programs allow users to override the |mag|~setting when a
-\.{DVI} file is being printed.)
-
-Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
-interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
-
-The next macro identifies the kind of \.{DVI} files described here.
-
-*/
-
-#define id_byte 2
-
-/*tex
-
-Font definitions for a given font number |k| contain further parameters
-$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$ The four-byte value |c|
-is the check sum that \TeX\ found in the \.{TFM} file for this font; |c| should
-match the check sum of the font found by programs that read this \.{DVI} file.
-
-Parameter |s| contains a fixed-point scale factor that is applied to the
-character widths in font |k|; font dimensions in \.{TFM} files and other font
-files are relative to this quantity, which is called the ``at size'' elsewhere in
-this documentation. The value of |s| is always positive and less than $2^{27}$.
-It is given in the same units as the other \.{DVI} dimensions, i.e., in sp when
-\TeX82 has made the file. Parameter |d| is similar to |s|; it is the ``design
-size,'' and (like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be used
-at $|mag|\cdot s/1000d$ times its normal size.
-
-The remaining part of a font definition gives the external name of the font,
-which is an ASCII string of length |a+l|. The number |a| is the length of the
-``area'' or directory, and |l| is the length of the font name itself; the
-standard local system font area is supposed to be used when |a=0|. The |n| field
-contains the area in its first |a| bytes.
-
-Font definitions must appear before the first use of a particular font number.
-Once font |k| is defined, it must not be defined again; however, we
-shall see below that font definitions appear in the postamble as well as
-in the pages, so in this sense each font number is defined exactly twice,
-if at all. Like |nop| commands, font definitions can
-appear before the first |bop|, or between an |eop| and a |bop|.
-
-Sometimes it is desirable to make horizontal or vertical rules line up precisely
-with certain features in characters of a font. It is possible to guarantee the
-correct matching between \.{DVI} output and the characters generated by \MF\ by
-adhering to the following principles: (1)~The \MF\ characters should be
-positioned so that a bottom edge or left edge that is supposed to line up with
-the bottom or left edge of a rule appears at the reference point, i.e., in row~0
-and column~0 of the \MF\ raster. This ensures that the position of the rule will
-not be rounded differently when the pixel size is not a perfect multiple of the
-units of measurement in the \.{DVI} file. (2)~A typeset rule of height $a>0$ and
-width $b>0$ should be equivalent to a \MF-generated character having black pixels
-in precisely those raster positions whose \MF\ coordinates satisfy
-|0<=x<@t$\alpha$@>b| and |0<=y<@t$\alpha$@>a|, where $\alpha$ is the number of
-pixels per \.{DVI} unit.
-
-The last page in a \.{DVI} file is followed by `|post|'; this command introduces
-the postamble, which summarizes important facts that \TeX\ has accumulated about
-the file, making it possible to print subsets of the data with reasonable
-efficiency. The postamble has the form
-
-$$\vbox{\halign{\hbox{#\hfil}\cr
-  |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
-  $\langle\,$font definitions$\,\rangle$\cr
-  |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
-
-Here |p| is a pointer to the final |bop| in the file. The next three parameters,
-|num|, |den|, and |mag|, are duplicates of the quantities that appeared in the
-preamble.
-
-Parameters |l| and |u| give respectively the height-plus-depth of the tallest
-page and the width of the widest page, in the same units as other dimensions of
-the file. These numbers might be used by a \.{DVI}-reading program to position
-individual ``pages'' on large sheets of film or paper; however, the standard
-convention for output on normal size paper is to position each page so that the
-upper left-hand corner is exactly one inch from the left and the top. Experience
-has shown that it is unwise to design \.{DVI}-to-printer software that attempts
-cleverly to center the output; a fixed position of the upper left corner is
-easiest for users to understand and to work with. Therefore |l| and~|u| are often
-ignored. Parameter |s| is the maximum stack depth (i.e., the largest excess of
-
-|push| commands over |pop| commands) needed to process this file. Then comes |t|,
-the total number of pages (|bop| commands) present.
-
-The postamble continues with font definitions, which are any number of
-\\{fnt\_def} commands as described above, possibly interspersed with |nop|
-commands. Each font number that is used in the \.{DVI} file must be defined
-exactly twice: Once before it is first selected by a \\{fnt} command, and once in
-the postamble.
-
-The last part of the postamble, following the |post_post| byte that signifies the
-end of the font definitions, contains |q|, a pointer to the |post| command that
-started the postamble. An identification byte, |i|, comes next; this currently
-equals~2, as in the preamble.
-
-The |i| byte is followed by four or more bytes that are all equal to the decimal
-number 223 (i.e., '337 in octal). \TeX\ puts out four to seven of these trailing
-bytes, until the total length of the file is a multiple of four bytes, since this
-works out best on machines that pack four bytes per word; but any number of 223's
-is allowed, as long as there are at least four of them. In effect, 223 is a sort
-of signature that is added at the very end.
-
-This curious way to finish off a \.{DVI} file makes it feasible for
-\.{DVI}-reading programs to find the postamble first, on most computers, even
-though \TeX\ wants to write the postamble last. Most operating systems permit
-random access to individual words or bytes of a file, so the \.{DVI} reader can
-start at the end and skip backwards over the 223's until finding the
-identification byte. Then it can back up four bytes, read |q|, and move to byte
-|q| of the file. This byte should, of course, contain the value 248 (|post|); now
-the postamble can be read, so the \.{DVI} reader can discover all the information
-needed for typesetting the pages. Note that it is also possible to skip through
-the \.{DVI} file at reasonably high speed to locate a particular page, if that
-proves desirable. This saves a lot of time, since \.{DVI} files used in
-production jobs tend to be large.
-
-Unfortunately, however, standard \PASCAL\ does not include the ability to access
-a random position in a file, or even to determine the length of a file. Almost
-all systems nowadays provide the necessary capabilities, so \.{DVI} format has
-been designed to work most efficiently with modern operating systems. But if
-\.{DVI} files have to be processed under the restrictions of standard \PASCAL,
-one can simply read them from front to back, since the necessary header
-information is present in the preamble and in the font definitions. (The |l| and
-|u| and |s| and |t| parameters, which appear only in the postamble, are
-``frills'' that are handy but not absolutely necessary.)
-
-After considering \TeX's eyes and stomach, we come now to the bowels.
-
-The |ship_out| procedure is given a pointer to a box; its mission is to describe
-that box in \.{DVI} form, outputting a ``page'' to |dvi_file|. The \.{DVI}
-coordinates $(h,v)=(0,0)$ should correspond to the upper left corner of the box
-being shipped.
-
-Since boxes can be inside of boxes inside of boxes, the main work of |ship_out|
-is done by two mutually recursive routines, |hlist_out| and |vlist_out|, which
-traverse the hlists and vlists inside of horizontal and vertical boxes.
-
-As individual pages are being processed, we need to accumulate information about
-the entire set of pages, since such statistics must be reported in the postamble.
-The global variables |total_pages|, |max_v|, |max_h|, |max_push|, and |last_bop|
-are used to record this information.
-
-The variable |doing_leaders| is |true| while leaders are being output. The
-variable |dead_cycles| contains the number of times an output routine has been
-initiated since the last |ship_out|.
-
-A few additional global variables are also defined here for use in |vlist_out|
-and |hlist_out|. They could have been local variables, but that would waste stack
-space when boxes are deeply nested, since the values of these variables are not
-needed during recursive calls.
-
-*/
-
-/* Some global variables are defined in |backend| module. */
-
-static int max_push = 0;  /* deepest nesting of |push| commands encountered so far */
-static int last_bop = -1; /* location of previous |bop| in the \.{DVI} output */
-static int oval, ocmd;    /* used by |out_cmd| for generating |set|, |fnt| and |fnt_def| commands */
-pointer g;                /* current glue specification */
-
-/*tex
-
-The \.{DVI} bytes are output to a buffer instead of being written directly to the
-output file. This makes it possible to reduce the overhead of subroutine calls,
-thereby measurably speeding up the computation, since output of \.{DVI} bytes is
-part of \TeX's inner loop. And it has another advantage as well, since we can
-change instructions in the buffer in order to make the output more compact. For
-example, a `|down2|' command can be changed to a `|y2|', thereby making a
-subsequent `|y0|' command possible, saving two bytes.
-
-The output buffer is divided into two parts of equal size; the bytes found in
-|dvi_buf[0..half_buf-1]| constitute the first half, and those in
-|dvi_buf[half_buf..dvi_buf_size-1]| constitute the second. The global variable
-|dvi_ptr| points to the position that will receive the next output byte. When
-|dvi_ptr| reaches |dvi_limit|, which is always equal to one of the two values
-|half_buf| or |dvi_buf_size|, the half buffer that is about to be invaded next is
-sent to the output and |dvi_limit| is changed to its other value. Thus, there is
-always at least a half buffer's worth of information present, except at the very
-beginning of the job.
-
-Bytes of the \.{DVI} file are numbered sequentially starting with 0; the next
-byte to be generated will be number |dvi_offset+dvi_ptr|. A byte is present in
-the buffer only if its number is |>=dvi_gone|.
-
-Some systems may find it more efficient to make |dvi_buf| a |packed| array, since
-output of four bytes at once may be facilitated.
-
-Initially the buffer is all in one piece; we will output half of it only after it
-first fills up.
-
-*/
-
-int dvi_buf_size = 800;     /* size of the output buffer; must be a multiple of 8 */
-eight_bits *dvi_buf;        /* buffer for \.{DVI} output */
-static int half_buf = 0;    /* half of |dvi_buf_size| */
-static int dvi_limit = 0;   /* end of the current half buffer */
-static int dvi_ptr = 0;     /* the next available buffer address */
-static int dvi_offset = 0;  /* |dvi_buf_size| times the number of times the output buffer has been fully emptied */
-static int dvi_gone = 0;    /* the number of bytes already output to |dvi_file| */
-
-/*
-To put a byte in the buffer without paying the cost of invoking a procedure
-each time, we use the macro |dvi_out|.
-*/
-
-#define dvi_out(A) do {                 \
-	dvi_buf[dvi_ptr++]=(eight_bits)(A);	\
-    if (dvi_ptr==dvi_limit) dvi_swap(); \
-} while (0)
-
-#define dvi_set(A,B) do {                       \
-    oval=A; ocmd=set1; out_cmd(); dvi.h += (B); \
-} while (0)
-
-#define dvi_put(A)  do {          \
-    oval=A; ocmd=put1; out_cmd(); \
-} while (0)
-
-/*
-The |vinfo| fields in the entries of the down stack or the right stack
-have six possible settings: |y_here| or |z_here| mean that the \.{DVI}
-command refers to |y| or |z|, respectively (or to |w| or |x|, in the
-case of horizontal motion); |yz_OK| means that the \.{DVI} command is
-\\{down} (or \\{right}) but can be changed to either |y| or |z| (or
-to either |w| or |x|); |y_OK| means that it is \\{down} and can be changed
-to |y| but not |z|; |z_OK| is similar; and |d_fixed| means it must stay
-\\{down}.
-
-The four settings |yz_OK|, |y_OK|, |z_OK|, |d_fixed| would not need to
-be distinguished from each other if we were simply solving the
-digit-subscripting problem mentioned above. But in \TeX's case there is
-a complication because of the nested structure of |push| and |pop|
-commands. Suppose we add parentheses to the digit-subscripting problem,
-redefining hits so that $\delta_y\ldots \delta_y$ is a hit if all $y$'s between
-the $\delta$'s are enclosed in properly nested parentheses, and if the
-parenthesis level of the right-hand $\delta_y$ is deeper than or equal to
-that of the left-hand one. Thus, `(' and `)' correspond to `|push|'
-and `|pop|'. Now if we want to assign a subscript to the final 1 in the
-sequence
-$$2_y\,7_d\,1_d\,(\,8_z\,2_y\,8_z\,)\,1$$
-we cannot change the previous $1_d$ to $1_y$, since that would invalidate
-the $2_y\ldots2_y$ hit. But we can change it to $1_z$, scoring a hit
-since the intervening $8_z$'s are enclosed in parentheses.
-*/
-
-typedef enum {
-    y_here = 1,                 /* |vinfo| when the movement entry points to a |y| command */
-    z_here = 2,                 /* |vinfo| when the movement entry points to a |z| command */
-    yz_OK = 3,                  /* |vinfo| corresponding to an unconstrained \\{down} command */
-    y_OK = 4,                   /* |vinfo| corresponding to a \\{down} that can't become a |z| */
-    z_OK = 5,                   /* |vinfo| corresponding to a \\{down} that can't become a |y| */
-    d_fixed = 6,                /* |vinfo| corresponding to a \\{down} that can't change */
-} movement_codes;
-
-/* As we search through the stack, we are in one of three states,
-   |y_seen|, |z_seen|, or |none_seen|, depending on whether we have
-   encountered |y_here| or |z_here| nodes. These states are encoded as
-   multiples of 6, so that they can be added to the |info| fields for quick
-   decision-making. */
-
-#  define none_seen 0           /* no |y_here| or |z_here| nodes have been encountered yet */
-#  define y_seen 6              /* we have seen |y_here| but not |z_here| */
-#  define z_seen 12             /* we have seen |z_here| but not |y_here| */
-
-void movement(scaled w, eight_bits o);
-
-/*
-extern void prune_movements(int l);
-*/
-
-/*
-The actual distances by which we want to move might be computed as the
-sum of several separate movements. For example, there might be several
-glue nodes in succession, or we might want to move right by the width of
-some box plus some amount of glue. More importantly, the baselineskip
-distances are computed in terms of glue together with the depth and
-height of adjacent boxes, and we want the \.{DVI} file to lump these
-three quantities together into a single motion.
-
-Therefore, \TeX\ maintains two pairs of global variables: |dvi.h| and |dvi.v|
-are the |h| and |v| coordinates corresponding to the commands actually
-output to the \.{DVI} file, while |cur.h| and |cur.v| are the coordinates
-corresponding to the current state of the output routines. Coordinate
-changes will accumulate in |cur.h| and |cur.v| without being reflected
-in the output, until such a change becomes necessary or desirable; we
-can call the |movement| procedure whenever we want to make |dvi.h=pos.h|
-or |dvi.v=pos.v|.
-
-The current font reflected in the \.{DVI} output is called |dvi_f|;
-there is no need for a `\\{cur\_f}' variable.
-
-The depth of nesting of |hlist_out| and |vlist_out| is called |cur_s|;
-this is essentially the depth of |push| commands in the \.{DVI} output.
-*/
-
-/*tex A \.{DVI} position in page coordinates, in sync with DVI file: */
-
-static scaledpos dvi;
-
-#  define synch_h(p) do {            \
-    if (p.h != dvi.h) {              \
-      movement(p.h - dvi.h, right1); \
-      dvi.h = p.h;                   \
-    }                                \
-  } while (0)
-
-#  define synch_v(p) do {            \
-    if (p.v != dvi.v) {              \
-      movement(dvi.v - p.v, down1);  \
-      dvi.v = p.v;                   \
-    }                                \
-  } while (0)
-
-#  define synch_dvi_with_pos(p) do {synch_h(p); synch_v(p); } while (0)
-
-/*tex
-
-The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling
-|write_dvi(a,b)|. For best results, this procedure should be optimized to run as
-fast as possible on each particular system, since it is part of \TeX's inner
-loop. It is safe to assume that |a| and |b+1| will both be multiples of 4 when
-|write_dvi(a,b)| is called; therefore it is possible on many machines to use
-efficient methods to pack four bytes per word and to output an array of words
-with one system call.
-
-*/
-
-static void write_dvi(int a, int b)
-{
-    int k;
-    for (k = a; k <= b; k++)
-        fputc(dvi_buf[k], static_pdf->file);
-}
-
-/*tex This outputs half of the buffer: */
-
-static void dvi_swap(void)
-{
-    if (dvi_limit == dvi_buf_size) {
-        write_dvi(0, half_buf - 1);
-        dvi_limit = half_buf;
-        dvi_offset = dvi_offset + dvi_buf_size;
-        dvi_ptr = 0;
-    } else {
-        write_dvi(half_buf, dvi_buf_size - 1);
-        dvi_limit = dvi_buf_size;
-    }
-    dvi_gone = dvi_gone + half_buf;
-}
-
-/*tex
-
-The |dvi_four| procedure outputs four bytes in two's complement notation, without
-risking arithmetic overflow.
-
-*/
-
-static void dvi_four(int x)
-{
-    if (x >= 0) {
-        dvi_out(x / 0100000000);
-    } else {
-        x = x + 010000000000;
-        x = x + 010000000000;
-        dvi_out((x / 0100000000) + 128);
-    }
-    x = x % 0100000000;
-    dvi_out(x / 0200000);
-    x = x % 0200000;
-    dvi_out(x / 0400);
-    dvi_out(x % 0400);
-}
-
-/*tex
-
-A mild optimization of the output is performed by the |dvi_pop| routine, which
-issues a |pop| unless it is possible to cancel a `|push| |pop|' pair. The
-parameter to |dvi_pop| is the byte address following the old |push| that matches
-the new |pop|.
-
-*/
-
-static void dvi_push(void)
-{
-    dvi_out(push);
-}
-
-static void dvi_pop(int l)
-{
-    if ((l == dvi_offset + dvi_ptr) && (dvi_ptr > 0))
-        decr(dvi_ptr);
-    else
-        dvi_out(pop);
-}
-
-/*tex
-
-Here's a procedure that outputs a font definition. $\Omega$ allows more than 256
-different fonts per job, so the right font definition command must be selected.
-
-*/
-
-static void out_cmd(void)
-{
-    if ((oval < 0x100) && (oval >= 0)) {
-        if ((ocmd != set1) || (oval > 127)) {
-            if ((ocmd == fnt1) && (oval < 64))
-                oval += fnt_num_0;
-            else
-                dvi_out(ocmd);
-        }
-    } else {
-        if ((oval < 0x10000) && (oval >= 0)) {
-            dvi_out(ocmd + 1);
-        } else {
-            if ((oval < 0x1000000) && (oval >= 0)) {
-                dvi_out(ocmd + 2);
-            } else {
-                dvi_out(ocmd + 3);
-                if (oval >= 0) {
-                    dvi_out(oval / 0x1000000);
-                } else {
-                    oval += 0x40000000;
-                    oval += 0x40000000;
-                    dvi_out((oval / 0x1000000) + 128);
-                    oval = oval % 0x1000000;
-                }
-                dvi_out(oval / 0x10000);
-                oval = oval % 0x10000;
-            }
-            dvi_out(oval / 0x10000);
-            oval = oval % 0x10000;
-        }
-        dvi_out(oval / 0x100);
-        oval = oval % 0x100;
-    }
-    dvi_out(oval);
-}
-
-static void dvi_font_def(internal_font_number f)
-{
-    char *fa;
-    oval = f - 1;
-    ocmd = fnt_def1;
-    out_cmd();
-    dvi_out(font_check_0(f));
-    dvi_out(font_check_1(f));
-    dvi_out(font_check_2(f));
-    dvi_out(font_check_3(f));
-    dvi_four(font_size(f));
-    dvi_four(font_dsize(f));
-    dvi_out(0);                 /* |font_area(f)| is unused */
-    dvi_out(strlen(font_name(f)));
-    /* Output the font name whose internal number is |f| */
-    fa = font_name(f);
-    while (*fa != '\0') {
-        dvi_out(*fa++);
-    }
-}
-
-/*tex
-
-Versions of \TeX\ intended for small computers might well choose to omit the
-ideas in the next few parts of this program, since it is not really necessary to
-optimize the \.{DVI} code by making use of the |w0|, |x0|, |y0|, and |z0|
-commands. Furthermore, the algorithm that we are about to describe does not
-pretend to give an optimum reduction in the length of the \.{DVI} code; after
-all, speed is more important than compactness. But the method is surprisingly
-effective, and it takes comparatively little time.
-
-We can best understand the basic idea by first considering a simpler problem that
-has the same essential characteristics. Given a sequence of digits, say
-$3\,1\,4\,1\,5\,9\,2\,6\,5\,3\,5\,8\,9$, we want to assign subscripts $d$, $y$,
-or $z$ to each digit so as to maximize the number of ``$y$-hits'' and
-``$z$-hits''; a $y$-hit is an instance of two appearances of the same digit with
-the subscript $y$, where no $y$'s intervene between the two appearances, and a
-$z$-hit is defined similarly. For example, the sequence above could be decorated
-with subscripts as follows:
-$$3_z\,1_y\,4_d\,1_y\,5_y\,9_d\,2_d\,6_d\,5_y\,3_z\,5_y\,8_d\,9_d.$$ There are
-three $y$-hits ($1_y\ldots1_y$ and $5_y\ldots5_y\ldots5_y$) and one $z$-hit
-($3_z\ldots3_z$); there are no $d$-hits, since the two appearances of $9_d$ have
-$d$'s between them, but we don't count $d$-hits so it doesn't matter how many
-there are. These subscripts are analogous to the \.{DVI} commands called
-\\{down}, $y$, and $z$, and the digits are analogous to different amounts of
-vertical motion; a $y$-hit or $z$-hit corresponds to the opportunity to use the
-one-byte commands |y0| or |z0| in a \.{DVI} file.
-
-\TeX's method of assigning subscripts works like this: Append a new digit, say
-$\delta$, to the right of the sequence. Now look back through the sequence until
-one of the following things happens: (a)~You see $\delta_y$ or $\delta_z$, and
-this was the first time you encountered a $y$ or $z$ subscript, respectively.
-Then assign $y$ or $z$ to the new $\delta$; you have scored a hit. (b)~You see
-$\delta_d$, and no $y$ subscripts have been encountered so far during this
-search. Then change the previous $\delta_d$ to $\delta_y$ (this corresponds to
-changing a command in the output buffer), and assign $y$ to the new $\delta$;
-it's another hit. (c)~You see $\delta_d$, and a $y$ subscript has been seen but
-not a $z$. Change the previous $\delta_d$ to $\delta_z$ and assign $z$ to the new
-$\delta$. (d)~You encounter both $y$ and $z$ subscripts before encountering a
-suitable $\delta$, or you scan all the way to the front of the sequence. Assign
-$d$ to the new $\delta$; this assignment may be changed later.
-
-The subscripts $3_z\,1_y\,4_d\ldots\,$ in the example above were, in fact,
-produced by this procedure, as the reader can verify. (Go ahead and try it.)
-
-In order to implement such an idea, \TeX\ maintains a stack of pointers to the
-\\{down}, $y$, and $z$ commands that have been generated for the current page.
-And there is a similar stack for \\{right}, |w|, and |x| commands. These stacks
-are called the down stack and right stack, and their top elements are maintained
-in the variables |down_ptr| and |right_ptr|.
-
-Each entry in these stacks contains four fields: The |width| field is the amount
-of motion down or to the right; the |location| field is the byte number of the
-\.{DVI} command in question (including the appropriate |dvi_offset|); the |vlink|
-field points to the next item below this one on the stack; and the |vinfo| field
-encodes the options for possible change in the \.{DVI} command.
-
-*/
-
-/*tex The \.{DVI} byte number for a movement command: */
-
-#define location(A) varmem[(A)+1].cint
-
-/*tex The heads of the down and right stacks: */
-
-static halfword down_ptr = null;
-static halfword right_ptr = null;
-
-/*tex
-
-Here is a subroutine that produces a \.{DVI} command for some specified downward
-or rightward motion. It has two parameters: |w| is the amount of motion, and |o|
-is either |down1| or |right1|. We use the fact that the command codes have
-convenient arithmetic properties: |y1-down1=w1-right1| and |z1-down1=x1-right1|.
-
-*/
-
-void movement(scaled w, eight_bits o)
-{
-    small_number mstate;        /* have we seen a |y| or |z|? */
-    halfword p, q;              /* current and top nodes on the stack */
-    int k;                      /* index into |dvi_buf|, modulo |dvi_buf_size| */
-    /*tex something todo? */
-    if (false) {
-        /*tex new node for the top of the stack */
-        q = new_node(movement_node, 0);
-        width(q) = w;
-        location(q) = dvi_offset + dvi_ptr;
-        if (o == down1) {
-            vlink(q) = down_ptr;
-            down_ptr = q;
-        } else {
-            vlink(q) = right_ptr;
-            right_ptr = q;
-        }
-        /*tex
-
-            Look at the other stack entries until deciding what sort of \.{DVI}
-            command to generate; |goto found| if node |p| is a ``hit''.
-        */
-        p = vlink(q);
-        mstate = none_seen;
-        while (p != null) {
-            if (width(p) == w) {
-                /*
-                    Consider a node with matching width;|goto found| if it's a
-                    hit. We might find a valid hit in a |y| or |z| byte that is
-                    already gone from the buffer. But we can't change bytes that
-                    are gone forever; ``the moving finger writes, $\ldots\,\,$.''
-                */
-                switch (mstate + vinfo(p)) {
-                    case none_seen + yz_OK:
-                    case none_seen + y_OK:
-                    case z_seen + yz_OK:
-                    case z_seen + y_OK:
-                        if (location(p) < dvi_gone) {
-                            goto NOT_FOUND;
-                        } else {
-                            /* Change buffered instruction to |y| or |w| and |goto found| */
-                            k = location(p) - dvi_offset;
-                            if (k < 0)
-                                k = k + dvi_buf_size;
-                            dvi_buf[k] = (eight_bits) (dvi_buf[k] + y1 - down1);
-                            vinfo(p) = y_here;
-                            goto FOUND;
-                        }
-                        break;
-                    case none_seen + z_OK:
-                    case y_seen + yz_OK:
-                    case y_seen + z_OK:
-                        if (location(p) < dvi_gone) {
-                            goto NOT_FOUND;
-                        } else {
-                            /* Change buffered instruction to |z| or |x| and |goto found| */
-                            k = location(p) - dvi_offset;
-                            if (k < 0)
-                                k = k + dvi_buf_size;
-                            dvi_buf[k] = (eight_bits) (dvi_buf[k] + z1 - down1);
-                            vinfo(p) = z_here;
-                            goto FOUND;
-                        }
-                        break;
-                    case none_seen + y_here:
-                    case none_seen + z_here:
-                    case y_seen + z_here:
-                    case z_seen + y_here:
-                        goto FOUND;
-                        break;
-                    default:
-                        break;
-                }
-            } else {
-                switch (mstate + vinfo(p)) {
-                    case none_seen + y_here:
-                        mstate = y_seen;
-                        break;
-                    case none_seen + z_here:
-                        mstate = z_seen;
-                        break;
-                    case y_seen + z_here:
-                    case z_seen + y_here:
-                        goto NOT_FOUND;
-                        break;
-                    default:
-                        break;
-                }
-            }
-            p = vlink(p);
-        }
-    }
-  NOT_FOUND:
-    /*tex
-        Generate a |down| or |right| command for |w| and |return|:
-    */
-    if (abs(w) >= 040000000) {
-        /*tex |down4| or |right4| */
-        dvi_out(o + 3);
-        dvi_four(w);
-        return;
-    }
-    if (abs(w) >= 0100000) {
-        /*tex |down3| or |right3| */
-        dvi_out(o + 2);
-        if (w < 0)
-            w = w + 0100000000;
-        dvi_out(w / 0200000);
-        w = w % 0200000;
-        goto TWO;
-    }
-    if (abs(w) >= 0200) {
-        /*tex |down2| or |right2| */
-        dvi_out(o + 1);
-        if (w < 0)
-            w = w + 0200000;
-        goto TWO;
-    }
-    /*tex |down1| or |right1| */
-    dvi_out(o);
-    if (w < 0)
-        w = w + 0400;
-    goto ONE;
-  TWO:
-    dvi_out(w / 0400);
-  ONE:
-    dvi_out(w % 0400);
-    return;
-  FOUND:
-    /*tex
-
-        Generate a |y0| or |z0| command in order to reuse a previous appearance
-        of~|w|.
-
-        The program below removes movement nodes that are introduced after a
-        |push|, before it outputs the corresponding |pop|.
-
-        When the |movement| procedure gets to the label |found|, the value of
-        |vinfo(p)| will be either |y_here| or |z_here|. If it is, say, |y_here|,
-        the procedure generates a |y0| command (or a |w0| command), and marks all
-        |vinfo| fields between |q| and |p| so that |y| is not OK in that range.
-
-     */
-    vinfo(q) = vinfo(p);
-    if (vinfo(q) == y_here) {
-        /*tex |y0| or |w0| */
-        dvi_out(o + y0 - down1);
-        while (vlink(q) != p) {
-            q = vlink(q);
-            switch (vinfo(q)) {
-            case yz_OK:
-                vinfo(q) = z_OK;
-                break;
-            case y_OK:
-                vinfo(q) = d_fixed;
-                break;
-            default:
-                break;
-            }
-        }
-    } else {
-        /*tex |z0| or |x0| */
-        dvi_out(o + z0 - down1);
-        while (vlink(q) != p) {
-            q = vlink(q);
-            switch (vinfo(q)) {
-            case yz_OK:
-                vinfo(q) = y_OK;
-                break;
-            case z_OK:
-                vinfo(q) = d_fixed;
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-/*tex
-
-In case you are wondering when all the movement nodes are removed from \TeX's
-memory, the answer is that they are recycled just before |hlist_out| and
-|vlist_out| finish outputting a box. This restores the down and right stacks to
-the state they were in before the box was output, except that some |vinfo|'s may
-have become more restrictive.
-
-Here we delete movement nodes with |location>=l|:
-
-*/
-
-static void prune_movements(int l)
-{
-    pointer p;
-    while (down_ptr != null) {
-        if (location(down_ptr) < l)
-            break;
-        p = down_ptr;
-        down_ptr = vlink(p);
-        flush_node(p);
-    }
-    while (right_ptr != null) {
-        if (location(right_ptr) < l)
-            return;
-        p = right_ptr;
-        right_ptr = vlink(p);
-        flush_node(p);
-    }
-}
-
-/*tex
-
-When |hlist_out| is called, its duty is to output the box represented by the
-|hlist_node| pointed to by |temp_ptr|. The reference point of that box has
-coordinates |(cur.h,cur.v)|.
-
-Similarly, when |vlist_out| is called, its duty is to output the box represented
-by the |vlist_node| pointed to by |temp_ptr|. The reference point of that box has
-coordinates |(cur.h,cur.v)|.
-
-The recursive procedures |hlist_out| and |vlist_out| each have a local variable
-|save_dvi| to hold the value of |dvi| just before entering a new level of
-recursion. In effect, the value of |save_dvi| on \TeX's run-time stack
-corresponds to the values of |h| and |v| that a \.{DVI}-reading program will push
-onto its coordinate stack.
-
-*/
-
-void dvi_place_rule(PDF pdf, halfword q, scaledpos size)
-{
-    synch_dvi_with_pos(pdf->posstruct->pos);
-    if ((subtype(q) >= box_rule) && (subtype(q) <= user_rule)) {
-        /*tex place nothing, only take space */
-        if (textdir_is_L(pdf->posstruct->dir))
-            dvi.h += size.h;
-    } else {
-        /*tex normal_rule or >= 100 being a leader rule */
-        if (textdir_is_L(pdf->posstruct->dir)) {
-            /*tex movement optimization for |dir_*L*| */
-            dvi_out(set_rule);
-            dvi.h += size.h;
-        } else
-            dvi_out(put_rule);
-    }
-    dvi_four(size.v);
-    dvi_four(size.h);
-}
-
-void dvi_place_glyph(PDF pdf, internal_font_number f, int c, int ex)
-{
-    scaled_whd ci;
-    synch_dvi_with_pos(pdf->posstruct->pos);
-    if (f != pdf->f_cur) {
-        /*tex Change font |f_cur| to |f| */
-        if (!font_used(f)) {
-            dvi_font_def(f);
-            set_font_used(f, true);
-        }
-        oval = f - 1;
-        ocmd = fnt1;
-        out_cmd();
-        pdf->f_cur = f;
-    }
-    if (textdir_is_L(pdf->posstruct->dir)) {
-        ci = get_charinfo_whd(f, c);
-        /*tex movement optimization for |dir_*L*| */
-        dvi_set(c, ci.wd);
-    } else {
-        dvi_put(c);
-    }
-}
-
-void dvi_special(PDF pdf, halfword p)
-{
-    /*tex holds print |selector| */
-    int old_setting;
-    /*tex index into |cur_string| */
-    unsigned k;
-    synch_dvi_with_pos(pdf->posstruct->pos);
-    old_setting = selector;
-    selector = new_string;
-    show_token_list(token_link(write_tokens(p)), null, -1);
-    selector = old_setting;
-    if (cur_length < 256) {
-        dvi_out(xxx1);
-        dvi_out(cur_length);
-    } else {
-        dvi_out(xxx4);
-        dvi_four((int) cur_length);
-    }
-    for (k = 0; k < cur_length; k++) {
-        dvi_out(cur_string[k]);
-    }
-    /*tex erase the string */
-    cur_length = 0;
-}
-
-/*tex
-
-Here's an example of how these conventions are used. Whenever it is time to ship
-out a box of stuff, we shall use the macro |ensure_dvi_open|.
-
-*/
-
-void dvi_write_header(PDF pdf)
-{
-    unsigned l;
-    /*tex index into |str_pool| */
-    unsigned s;
-    /*tex saved |selector| setting */
-    int old_setting;
-    if (half_buf == 0) {
-        half_buf = dvi_buf_size / 2;
-        dvi_limit = dvi_buf_size;
-    }
-    dvi_out(pre);
-    /*tex output the preamble */
-    dvi_out(id_byte);
-    dvi_four(25400000);
-    /*tex conversion ratio for sp */
-    dvi_four(473628672);
-    prepare_mag();
-    /*tex magnification factor is frozen */
-    dvi_four(mag_par);
-    if (output_comment) {
-        l = (unsigned) strlen(output_comment);
-        dvi_out(l);
-        for (s = 0; s < l; s++) {
-            dvi_out(output_comment[s]);
-        }
-    } else {
-        /*tex the default code is unchanged */
-        old_setting = selector;
-        selector = new_string;
-        tprint(" LuaTeX output ");
-        print_int(year_par);
-        print_char('.');
-        print_two(month_par);
-        print_char('.');
-        print_two(day_par);
-        print_char(':');
-        print_two(time_par / 60);
-        print_two(time_par % 60);
-        selector = old_setting;
-        dvi_out(cur_length);
-        for (s = 0; s < cur_length; s++)
-            dvi_out(cur_string[s]);
-        cur_length = 0;
-    }
-}
-
-void dvi_begin_page(PDF pdf)
-{
-    int k;
-    /*tex location of the current |bop| */
-    int page_loc;
-    ensure_output_state(pdf, ST_HEADER_WRITTEN);
-    /*tex Initialize variables as |ship_out| begins */
-    page_loc = dvi_offset + dvi_ptr;
-    dvi_out(bop);
-    for (k = 0; k <= 9; k++)
-        dvi_four(count(k));
-    dvi_four(last_bop);
-    last_bop = page_loc;
-}
-
-void dvi_end_page(PDF pdf)
-{
-    (void) pdf;
-    dvi_out(eop);
-}
-
-/*tex
-
-At the end of the program, we must finish things off by writing the post\-amble.
-If |total_pages=0|, the \.{DVI} file was never opened. If |total_pages>=65536|,
-the \.{DVI} file will lie. And if |max_push>=65536|, the user deserves whatever
-chaos might ensue.
-
-*/
-
-void dvi_open_file(PDF pdf) {
-    ensure_output_file_open(pdf, ".dvi");
-}
-
-void dvi_finish_file(PDF pdf, int fatal_error)
-{
-    int k;
-    int callback_id = callback_defined(stop_run_callback);
-    if (fatal_error) {
-        print_err(" ==> Fatal error occurred, bad output DVI file produced!");
-    }
-    while (cur_s > -1) {
-        if (cur_s > 0) {
-            dvi_out(pop);
-        } else {
-            dvi_out(eop);
-            incr(total_pages);
-        }
-        decr(cur_s);
-    }
-    if (total_pages == 0) {
-        if (callback_id == 0) {
-            tprint_nl("No pages of output.");
-            print_ln();
-        } else if (callback_id > 0) {
-            run_callback(callback_id, "->");
-        }
-    } else {
-        /*tex beginning of the postamble */
-        dvi_out(post);
-        dvi_four(last_bop);
-        last_bop = dvi_offset + dvi_ptr - 5;
-        /*tex |post| location */
-        dvi_four(25400000);
-        /*tex conversion ratio for sp */
-        dvi_four(473628672);
-        prepare_mag();
-        /*tex magnification factor */
-        dvi_four(mag_par);
-        dvi_four(max_v);
-        dvi_four(max_h);
-        dvi_out(max_push / 256);
-        dvi_out(max_push % 256);
-        dvi_out((total_pages / 256) % 256);
-        dvi_out(total_pages % 256);
-        /*tex Output the font definitions for all fonts that were used */
-        k = max_font_id();
-        while (k > 0) {
-            if (font_used(k)) {
-                dvi_font_def(k);
-            }
-            decr(k);
-        }
-        dvi_out(post_post);
-        dvi_four(last_bop);
-        dvi_out(id_byte);
-        /*tex the number of 223's */
-#ifndef IPC
-        k = 4 + ((dvi_buf_size - dvi_ptr) % 4);
-#else
-        k = 7 - ((3 + dvi_offset + dvi_ptr) % 4);
-#endif
-        while (k > 0) {
-            dvi_out(223);
-            decr(k);
-        }
-        /*tex
-            Here is how we clean out the buffer when \TeX\ is all through;
-            |dvi_ptr| will be a multiple of~4.
-        */
-        if (dvi_limit == half_buf)
-            write_dvi(half_buf, dvi_buf_size - 1);
-        if (dvi_ptr > 0)
-            write_dvi(0, dvi_ptr - 1);
-        if (callback_id == 0) {
-            tprint_nl("Output written on ");
-            tprint(pdf->file_name);
-            tprint(" (");
-            print_int(total_pages);
-            tprint(" page");
-            if (total_pages != 1)
-                print_char('s');
-            tprint(", ");
-            print_int(dvi_offset + dvi_ptr);
-            tprint(" bytes).");
-        } else if (callback_id > 0) {
-            run_callback(callback_id, "->");
-        }
-        close_file(pdf->file);
-    }
-}
-
-void dvi_push_list(PDF pdf, scaledpos *saved_pos, int *saved_loc)
-{
-    if (cur_s > max_push) {
-        max_push = cur_s;
-    }
-    if (cur_s > 0) {
-        dvi_push();
-        *saved_pos = dvi;
-    }
-    *saved_loc = dvi_offset + dvi_ptr;
-}
-
-void dvi_pop_list(PDF pdf, scaledpos *saved_pos, int *saved_loc)
-{
-    prune_movements(*saved_loc);
-    if (cur_s > 0) {
-        dvi_pop(*saved_loc);
-        dvi = *saved_pos;
-    }
-}
-
-void dvi_set_reference_point(PDF pdf, posstructure *refpoint)
-{
-    refpoint->pos.h = one_true_inch;
-    refpoint->pos.v = pdf->page_size.v - one_true_inch;
-    dvi = refpoint->pos;
-}
-
-int dvi_get_status_ptr(PDF pdf)
-{
-    return dvi_ptr;
-}
-
-int dvi_get_status_gone(PDF pdf)
-{
-    return dvi_gone;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/dvi/dvigen.w
@@ -0,0 +1,1258 @@
+% dvigen.w
+%
+% Copyright 2009-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/>.
+
+\def\MF{MetaFont}
+\def\MP{MetaPost}
+\def\PASCAL{Pascal}
+\def\[#1]{#1}
+\pdfoutput=1
+\pdfmapline{cmtex10 < cmtex10.pfb}
+\pdfmapfile{pdftex.map}
+
+\title{: generation of DVI output}
+
+@ Initial identification of this file, and the needed headers.
+@c
+#include "ptexlib.h"
+
+@ Here is the start of the actual C file.
+@c
+#undef write_dvi
+
+/* todo: move macros to api */
+
+#define mode cur_list.mode_field        /* current mode */
+
+@ The most important output produced by a run of \TeX\ is the ``device
+independent'' (\.{DVI}) file that specifies where characters and rules
+are to appear on printed pages. The form of these files was designed by
+David R. Fuchs in 1979. Almost any reasonable typesetting device can be
+@^Fuchs, David Raymond@>
+@:DVI_files}{\.{DVI} files@>
+driven by a program that takes \.{DVI} files as input, and dozens of such
+\.{DVI}-to-whatever programs have been written. Thus, it is possible to
+print the output of \TeX\ on many different kinds of equipment, using \TeX\
+as a device-independent ``front end.''
+
+A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
+series of commands in a machine-like language. The first byte of each command
+is the operation code, and this code is followed by zero or more bytes
+that provide parameters to the command. The parameters themselves may consist
+of several consecutive bytes; for example, the `|set_rule|' command has two
+parameters, each of which is four bytes long. Parameters are usually
+regarded as nonnegative integers; but four-byte-long parameters,
+and shorter parameters that denote distances, can be
+either positive or negative. Such parameters are given in two's complement
+notation. For example, a two-byte-long distance parameter has a value between
+$-2^{15}$ and $2^{15}-1$. As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order.
+
+A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
+or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that define the dimensions used in the
+file; this must come first.  Each ``page'' consists of a |bop| command,
+followed by any number of other commands that tell where characters are to
+be placed on a physical page, followed by an |eop| command. The pages
+appear in the order that \TeX\ generated them. If we ignore |nop| commands
+and \\{fnt\_def} commands (which are allowed between any two commands in
+the file), each |eop| command is immediately followed by a |bop| command,
+or by a |post| command; in the latter case, there are no more pages in the
+file, and the remaining bytes form the postamble.  Further details about
+the postamble will be explained later.
+
+Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first byte is number~0, then comes number~1, and so on. For example,
+one of the parameters of a |bop| command points to the previous |bop|;
+this makes it feasible to read the pages in backwards order, in case the
+results are being directed to a device that stacks its output face up.
+Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
+first page occupies bytes 100 to 999, say, and if the second
+page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
+points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
+very first |bop|, i.e., the one starting in byte 100, has a pointer of~$-1$.)
+
+@ The \.{DVI} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+implicit instead of explicit. When a \.{DVI}-reading program reads the
+commands for a page, it keeps track of several quantities: (a)~The current
+font |f| is an integer; this value is changed only
+by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
+is given by two numbers called the horizontal and vertical coordinates,
+|h| and |v|. Both coordinates are zero at the upper left corner of the page;
+moving to the right corresponds to increasing the horizontal coordinate, and
+moving down corresponds to increasing the vertical coordinate. Thus, the
+coordinates are essentially Cartesian, except that vertical directions are
+flipped; the Cartesian version of |(h,v)| would be |(h,-v)|.  (c)~The
+current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
+where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
+are used for vertical spacing. (d)~There is a stack containing
+|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
+change the current level of operation. Note that the current font~|f| is
+not pushed and popped; the stack contains only information about
+positioning.
+
+The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
+to 32 bits, including the sign. Since they represent physical distances,
+there is a small unit of measurement such that increasing |h| by~1 means
+moving a certain tiny distance to the right. The actual unit of
+measurement is variable, as explained below; \TeX\ sets things up so that
+its \.{DVI} output is in sp units, i.e., scaled points, in agreement with
+all the |scaled| dimensions in \TeX's data structures.
+
+@ Here is a list of all the commands that may appear in a \.{DVI} file. Each
+command is specified by its symbolic name (e.g., |bop|), its opcode byte
+(e.g., 139), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|p[4]|' means that parameter |p| is four bytes long.
+
+\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
+such that the reference point of the character is at |(h,v)|. Then
+increase |h| by the width of that character. Note that a character may
+have zero or negative width, so one cannot be sure that |h| will advance
+after this command; but |h| usually does increase.
+
+\yskip\hang\\{set\_char\_1} through \\{set\_char\_127} (opcodes 1 to 127).
+Do the operations of |set_char_0|; but use the character whose number
+matches the opcode, instead of character~0.
+
+\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
+number~|c| is typeset. \TeX82 uses this command for characters in the
+range |128<=c<256|.
+
+\yskip\hang|@!set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
+bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
+command, but it should come in handy for extensions of \TeX\ that deal
+with oriental languages.
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+\yskip\hang|@!set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
+bytes long, so it can be as large as $2^{24}-1$. Not even the Chinese
+language has this many characters, but this command might prove useful
+in some yet unforeseen extension.
+
+\yskip\hang|@!set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
+bytes long. Imagine that.
+
+\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
+of height~|a| and width~|b|, with its bottom left corner at |(h,v)|. Then
+set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
+that if |b<0|, the value of |h| will decrease even though nothing else happens.
+See below for details about how to typeset rules so that consistency with
+\MF\ is guaranteed.
+
+\yskip\hang|@!put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
+such that the reference point of the character is at |(h,v)|. (The `put'
+commands are exactly like the `set' commands, except that they simply put out a
+character or a rule without moving the reference point afterwards.)
+
+\yskip\hang|@!put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
+
+\yskip\hang|@!put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
+
+\yskip\hang|@!put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
+
+\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
+|h| is not changed.
+
+\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
+may occur between \.{DVI} commands, but a |nop| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
+of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
+the current font |f| to an undefined value.  The ten $c_i$ parameters hold
+the values of \.{\\count0} $\ldots$ \.{\\count9} in \TeX\ at the time
+\.{\\shipout} was invoked for this page; they can be used to identify
+pages, if a user wants to print only part of a \.{DVI} file. The parameter
+|p| points to the previous |bop| in the file; the first
+|bop| has $p=-1$.
+
+\yskip\hang|eop| 140.  End of page: Print what you have read since the
+previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
+programs that drive most output devices will have kept a buffer of the
+material that appears on the page that has just ended. This material is
+largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
+|h|~coordinate; so it usually needs to be sorted into some order that is
+appropriate for the device in question.)
+
+\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
+top of the stack; do not change any of these values. Note that |f| is
+not pushed.
+
+\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
+them respectively to |(h,v,w,x,y,z)|. The number of pops should never
+exceed the number of pushes, since it would be highly embarrassing if the
+stack were empty at the time of a |pop| command.
+
+\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
+The parameter is a signed number in two's complement notation, |-128<=b<128|;
+if |b<0|, the reference point moves left.
+
+\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
+two-byte quantity in the range |-32768<=b<32768|.
+
+\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
+three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
+four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|w| gets particular values.
+
+\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |w|~spacing and moves right by |b|.
+
+\yskip\hang|@!w2| 149 |b[2]|. Same as |w1|, but |b| is two bytes long,
+|-32768<=b<32768|.
+
+\yskip\hang|@!w3| 150 |b[3]|. Same as |w1|, but |b| is three bytes long,
+|@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|@!w4| 151 |b[4]|. Same as |w1|, but |b| is four bytes long,
+|@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
+commands are like the `|w|' commands except that they involve |x| instead
+of |w|.
+
+\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |x|~spacing and moves right by |b|.
+
+\yskip\hang|@!x2| 154 |b[2]|. Same as |x1|, but |b| is two bytes long,
+|-32768<=b<32768|.
+
+\yskip\hang|@!x3| 155 |b[3]|. Same as |x1|, but |b| is three bytes long,
+|@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|@!x4| 156 |b[4]|. Same as |x1|, but |b| is four bytes long,
+|@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
+The parameter is a signed number in two's complement notation, |-128<=a<128|;
+if |a<0|, the reference point moves up.
+
+\yskip\hang|@!down2| 158 |a[2]|. Same as |down1|, except that |a| is a
+two-byte quantity in the range |-32768<=a<32768|.
+
+\yskip\hang|@!down3| 159 |a[3]|. Same as |down1|, except that |a| is a
+three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|@!down4| 160 |a[4]|. Same as |down1|, except that |a| is a
+four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|y| gets particular values.
+
+\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |y|~spacing and moves down by |a|.
+
+\yskip\hang|@!y2| 163 |a[2]|. Same as |y1|, but |a| is two bytes long,
+|-32768<=a<32768|.
+
+\yskip\hang|@!y3| 164 |a[3]|. Same as |y1|, but |a| is three bytes long,
+|@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|@!y4| 165 |a[4]|. Same as |y1|, but |a| is four bytes long,
+|@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
+are like the `|y|' commands except that they involve |z| instead of |y|.
+
+\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |z|~spacing and moves down by |a|.
+
+\yskip\hang|@!z2| 168 |a[2]|. Same as |z1|, but |a| is two bytes long,
+|-32768<=a<32768|.
+
+\yskip\hang|@!z3| 169 |a[3]|. Same as |z1|, but |a| is three bytes long,
+|@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|@!z4| 170 |a[4]|. Same as |z1|, but |a| is four bytes long,
+|@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
+defined by a \\{fnt\_def} instruction, as explained below.
+
+\yskip\hang\\{fnt\_num\_1} through \\{fnt\_num\_63} (opcodes 172 to 234). Set
+|f:=1|, \dots, \hbox{|f:=63|}, respectively.
+
+\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
+numbers in the range |64<=k<256|.
+
+\yskip\hang|@!fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
+bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
+command, but large font numbers may prove useful for specifications of
+color or texture, or they may be used for special fonts that have fixed
+numbers in some external coding scheme.
+
+\yskip\hang|@!fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|@!fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
+bytes long; this is for the really big font numbers (and for the negative ones).
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
+programs are being used. \TeX82 generates |xxx1| when a short enough
+\.{\\special} appears, setting |k| to the number of bytes being sent. It
+is recommended that |x| be a string having the form of a keyword followed
+by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|@!xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
+large. \TeX82 uses |xxx4| when sending a string of length 256 or more.
+
+\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<256|; font definitions will be explained shortly.
+
+\yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<65536|.
+
+\yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
+
+\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@c
+#define set_char_0  0           /* typeset character 0 and move right */
+#define set1  128               /* typeset a character and move right */
+#define set_rule  132           /* typeset a rule and move right */
+#define put1    133             /* typeset a character without moving */
+#define put_rule  137           /* typeset a rule */
+#define nop  138                /* no operation */
+#define bop  139                /* beginning of page */
+#define eop  140                /* ending of page */
+#define push  141               /* save the current positions */
+#define pop  142                /* restore previous positions */
+#define right1    143           /* move right */
+#define right4    146           /* move right, 4 bytes */
+#define w0  147                 /* move right by |w| */
+#define w1  148                 /* move right and set |w| */
+#define x0  152                 /* move right by |x| */
+#define x1  153                 /* move right and set |x| */
+#define down1  157              /* move down */
+#define down4  160              /* move down, 4 bytes */
+#define y0  161                 /* move down by |y| */
+#define y1  162                 /* move down and set |y| */
+#define z0  166                 /* move down by |z| */
+#define z1  167                 /* move down and set |z| */
+#define fnt_num_0  171          /* set current font to 0 */
+#define fnt1  235               /* set current font */
+#define xxx1  239               /* extension to \.{DVI} primitives */
+#define xxx4  242               /* potentially long extension to \.{DVI} primitives */
+#define fnt_def1  243           /* define the meaning of a font number */
+#define pre  247                /* preamble */
+#define post  248               /* postamble beginning */
+#define post_post  249          /* postamble ending */
+
+@ The preamble contains basic information about the file as a whole. As
+stated above, there are six parameters:
+$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
+The |i| byte identifies \.{DVI} format; currently this byte is always set
+to~2. (The value |i=3| is currently used for an extended format that
+allows a mixture of right-to-left and left-to-right typesetting.
+Some day we will set |i=4|, when \.{DVI} format makes another
+incompatible change---perhaps in the year 2048.)
+
+The next two parameters, |num| and |den|, are positive integers that define
+the units of measurement; they are the numerator and denominator of a
+fraction by which all dimensions in the \.{DVI} file could be multiplied
+in order to get lengths in units of $10^{-7}$ meters. Since $\rm 7227{pt} =
+254{cm}$, and since \TeX\ works with scaled points where there are $2^{16}$
+sp in a point, \TeX\ sets
+$|num|/|den|=(254\cdot10^5)/(7227\cdot2^{16})=25400000/473628672$.
+@^sp@>
+
+The |mag| parameter is what \TeX\ calls \.{\\mag}, i.e., 1000 times the
+desired magnification. The actual fraction by which dimensions are
+multiplied is therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\
+source document does not call for any `\.{true}' dimensions, and if you
+change it only by specifying a different \.{\\mag} setting, the \.{DVI}
+file that \TeX\ creates will be completely unchanged except for the value
+of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow
+users to override the |mag|~setting when a \.{DVI} file is being printed.)
+
+Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
+interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
+
+
+@c
+#define id_byte 2               /* identifies the kind of \.{DVI} files described here */
+
+@ Font definitions for a given font number |k| contain further parameters
+$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
+The four-byte value |c| is the check sum that \TeX\ found in the \.{TFM}
+file for this font; |c| should match the check sum of the font found by
+programs that read this \.{DVI} file.
+@^check sum@>
+
+Parameter |s| contains a fixed-point scale factor that is applied to
+the character widths in font |k|; font dimensions in \.{TFM} files and
+other font files are relative to this quantity, which is called the
+``at size'' elsewhere in this documentation. The value of |s| is
+always positive and less than $2^{27}$. It is given in the same units
+as the other \.{DVI} dimensions, i.e., in sp when \TeX82 has made the
+file.  Parameter |d| is similar to |s|; it is the ``design size,'' and
+(like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be used
+at $|mag|\cdot s/1000d$ times its normal size.
+
+The remaining part of a font definition gives the external name of the font,
+which is an ASCII string of length |a+l|. The number |a| is the length
+of the ``area'' or directory, and |l| is the length of the font name itself;
+the standard local system font area is supposed to be used when |a=0|.
+The |n| field contains the area in its first |a| bytes.
+
+Font definitions must appear before the first use of a particular font number.
+Once font |k| is defined, it must not be defined again; however, we
+shall see below that font definitions appear in the postamble as well as
+in the pages, so in this sense each font number is defined exactly twice,
+if at all. Like |nop| commands, font definitions can
+appear before the first |bop|, or between an |eop| and a |bop|.
+
+@ Sometimes it is desirable to make horizontal or vertical rules line up
+precisely with certain features in characters of a font. It is possible to
+guarantee the correct matching between \.{DVI} output and the characters
+generated by \MF\ by adhering to the following principles: (1)~The \MF\
+characters should be positioned so that a bottom edge or left edge that is
+supposed to line up with the bottom or left edge of a rule appears at the
+reference point, i.e., in row~0 and column~0 of the \MF\ raster. This
+ensures that the position of the rule will not be rounded differently when
+the pixel size is not a perfect multiple of the units of measurement in
+the \.{DVI} file. (2)~A typeset rule of height $a>0$ and width $b>0$
+should be equivalent to a \MF-generated character having black pixels in
+precisely those raster positions whose \MF\ coordinates satisfy
+|0<=x<@t$\alpha$@>b| and |0<=y<@t$\alpha$@>a|, where $\alpha$ is the number
+of pixels per \.{DVI} unit.
+@:METAFONT}{\MF@>
+@^alignment of rules with characters@>
+@^rules aligning with characters@>
+
+@ The last page in a \.{DVI} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \TeX\ has
+accumulated about the file, making it possible to print subsets of the data
+with reasonable efficiency. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+  |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
+  $\langle\,$font definitions$\,\rangle$\cr
+  |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the final |bop| in the file. The next three
+parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
+appeared in the preamble.
+
+Parameters |l| and |u| give respectively the height-plus-depth of the tallest
+page and the width of the widest page, in the same units as other dimensions
+of the file. These numbers might be used by a \.{DVI}-reading program to
+position individual ``pages'' on large sheets of film or paper; however,
+the standard convention for output on normal size paper is to position each
+page so that the upper left-hand corner is exactly one inch from the left
+and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
+software that attempts cleverly to center the output; a fixed position of
+the upper left corner is easiest for users to understand and to work with.
+Therefore |l| and~|u| are often ignored.
+
+Parameter |s| is the maximum stack depth (i.e., the largest excess of
+|push| commands over |pop| commands) needed to process this file. Then
+comes |t|, the total number of pages (|bop| commands) present.
+
+The postamble continues with font definitions, which are any number of
+\\{fnt\_def} commands as described above, possibly interspersed with |nop|
+commands. Each font number that is used in the \.{DVI} file must be defined
+exactly twice: Once before it is first selected by a \\{fnt} command, and once
+in the postamble.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the font definitions, contains |q|, a pointer to the
+|post| command that started the postamble.  An identification byte, |i|,
+comes next; this currently equals~2, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., '337 in octal). \TeX\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{DVI} file makes it feasible for
+\.{DVI}-reading programs to find the postamble first, on most computers,
+even though \TeX\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{DVI} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{DVI} reader can discover all the information needed for typesetting the
+pages. Note that it is also possible to skip through the \.{DVI} file at
+reasonably high speed to locate a particular page, if that proves
+desirable. This saves a lot of time, since \.{DVI} files used in production
+jobs tend to be large.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{DVI} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back, since the necessary
+header information is present in the preamble and in the font definitions.
+(The |l| and |u| and |s| and |t| parameters, which appear only in the
+postamble, are ``frills'' that are handy but not absolutely necessary.)
+
+
+@* \[32] Shipping pages out.
+After considering \TeX's eyes and stomach, we come now to the bowels.
+@^bowels@>
+
+The |ship_out| procedure is given a pointer to a box; its mission is
+to describe that box in \.{DVI} form, outputting a ``page'' to |dvi_file|.
+The \.{DVI} coordinates $(h,v)=(0,0)$ should correspond to the upper left
+corner of the box being shipped.
+
+Since boxes can be inside of boxes inside of boxes, the main work of
+|ship_out| is done by two mutually recursive routines, |hlist_out|
+and |vlist_out|, which traverse the hlists and vlists inside of horizontal
+and vertical boxes.
+
+As individual pages are being processed, we need to accumulate
+information about the entire set of pages, since such statistics must be
+reported in the postamble. The global variables |total_pages|, |max_v|,
+|max_h|, |max_push|, and |last_bop| are used to record this information.
+
+The variable |doing_leaders| is |true| while leaders are being output.
+The variable |dead_cycles| contains the number of times an output routine
+has been initiated since the last |ship_out|.
+
+A few additional global variables are also defined here for use in
+|vlist_out| and |hlist_out|. They could have been local variables, but
+that would waste stack space when boxes are deeply nested, since the
+values of these variables are not needed during recursive calls.
+@^recursion@>
+
+@c
+int total_pages = 0;            /* the number of pages that have been shipped out */
+scaled max_v = 0;               /* maximum height-plus-depth of pages shipped so far */
+scaled max_h = 0;               /* maximum width of pages shipped so far */
+int max_push = 0;               /* deepest nesting of |push| commands encountered so far */
+int last_bop = -1;              /* location of previous |bop| in the \.{DVI} output */
+int dead_cycles = 0;            /* recent outputs that didn't ship anything out */
+boolean doing_leaders = false;  /* are we inside a leader box? */
+int oval, ocmd;                 /* used by |out_cmd| for generating |set|, |fnt| and |fnt_def| commands */
+pointer g;                      /* current glue specification */
+int lq, lr;                     /* quantities used in calculations for leaders */
+int cur_s = -1;                 /* current depth of output box nesting, initially $-1$ */
+
+@ The \.{DVI} bytes are output to a buffer instead of being written directly
+to the output file. This makes it possible to reduce the overhead of
+subroutine calls, thereby measurably speeding up the computation, since
+output of \.{DVI} bytes is part of \TeX's inner loop. And it has another
+advantage as well, since we can change instructions in the buffer in order to
+make the output more compact. For example, a `|down2|' command can be
+changed to a `|y2|', thereby making a subsequent `|y0|' command possible,
+saving two bytes.
+
+The output buffer is divided into two parts of equal size; the bytes found
+in |dvi_buf[0..half_buf-1]| constitute the first half, and those in
+|dvi_buf[half_buf..dvi_buf_size-1]| constitute the second. The global
+variable |dvi_ptr| points to the position that will receive the next
+output byte. When |dvi_ptr| reaches |dvi_limit|, which is always equal
+to one of the two values |half_buf| or |dvi_buf_size|, the half buffer that
+is about to be invaded next is sent to the output and |dvi_limit| is
+changed to its other value. Thus, there is always at least a half buffer's
+worth of information present, except at the very beginning of the job.
+
+Bytes of the \.{DVI} file are numbered sequentially starting with 0;
+the next byte to be generated will be number |dvi_offset+dvi_ptr|.
+A byte is present in the buffer only if its number is |>=dvi_gone|.
+
+Some systems may find it more efficient to make |dvi_buf| a |packed|
+array, since output of four bytes at once may be facilitated.
+@^system dependencies@>
+
+
+@ Initially the buffer is all in one piece; we will output half of it only
+after it first fills up.
+
+@c
+int dvi_buf_size = 800;         /* size of the output buffer; must be a multiple of 8 */
+eight_bits *dvi_buf;            /* buffer for \.{DVI} output */
+dvi_index half_buf = 0;         /* half of |dvi_buf_size| */
+dvi_index dvi_limit = 0;        /* end of the current half buffer */
+dvi_index dvi_ptr = 0;          /* the next available buffer address */
+int dvi_offset = 0;             /* |dvi_buf_size| times the number of times the output buffer has been fully emptied */
+int dvi_gone = 0;               /* the number of bytes already output to |dvi_file| */
+
+@ The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling
+|write_dvi(a,b)|. For best results, this procedure should be optimized to
+run as fast as possible on each particular system, since it is part of
+\TeX's inner loop. It is safe to assume that |a| and |b+1| will both be
+multiples of 4 when |write_dvi(a,b)| is called; therefore it is possible on
+many machines to use efficient methods to pack four bytes per word and to
+output an array of words with one system call.
+@^system dependencies@>
+@^inner loop@>
+@^defecation@>
+
+@c
+static void write_dvi(dvi_index a, dvi_index b)
+{
+    dvi_index k;
+    for (k = a; k <= b; k++)
+        fputc(dvi_buf[k], static_pdf->file);
+}
+
+/* outputs half of the buffer */
+void dvi_swap(void)
+{
+    if (dvi_limit == dvi_buf_size) {
+        write_dvi(0, half_buf - 1);
+        dvi_limit = half_buf;
+        dvi_offset = dvi_offset + dvi_buf_size;
+        dvi_ptr = 0;
+    } else {
+        write_dvi(half_buf, dvi_buf_size - 1);
+        dvi_limit = dvi_buf_size;
+    }
+    dvi_gone = dvi_gone + half_buf;
+}
+
+@ The |dvi_four| procedure outputs four bytes in two's complement notation,
+without risking arithmetic overflow.
+
+@c
+void dvi_four(int x)
+{
+    if (x >= 0) {
+        dvi_out(x / 0100000000);
+    } else {
+        x = x + 010000000000;
+        x = x + 010000000000;
+        dvi_out((x / 0100000000) + 128);
+    }
+    x = x % 0100000000;
+    dvi_out(x / 0200000);
+    x = x % 0200000;
+    dvi_out(x / 0400);
+    dvi_out(x % 0400);
+}
+
+@
+A mild optimization of the output is performed by the |dvi_pop|
+routine, which issues a |pop| unless it is possible to cancel a
+`|push| |pop|' pair. The parameter to |dvi_pop| is the byte address
+following the old |push| that matches the new |pop|.
+
+
+@c
+void dvi_push(void)
+{
+    dvi_out(push);
+}
+
+void dvi_pop(int l)
+{
+    if ((l == dvi_offset + dvi_ptr) && (dvi_ptr > 0))
+        decr(dvi_ptr);
+    else
+        dvi_out(pop);
+}
+
+@ Here's a procedure that outputs a font definition. $\Omega$ allows
+more than 256 different fonts per job, so the right font definition
+command must be selected.
+
+@c
+void out_cmd(void)
+{
+    if ((oval < 0x100) && (oval >= 0)) {
+        if ((ocmd != set1) || (oval > 127)) {
+            if ((ocmd == fnt1) && (oval < 64))
+                oval += fnt_num_0;
+            else
+                dvi_out(ocmd);
+        }
+    } else {
+        if ((oval < 0x10000) && (oval >= 0)) {
+            dvi_out(ocmd + 1);
+        } else {
+            if ((oval < 0x1000000) && (oval >= 0)) {
+                dvi_out(ocmd + 2);
+            } else {
+                dvi_out(ocmd + 3);
+                if (oval >= 0) {
+                    dvi_out(oval / 0x1000000);
+                } else {
+                    oval += 0x40000000;
+                    oval += 0x40000000;
+                    dvi_out((oval / 0x1000000) + 128);
+                    oval = oval % 0x1000000;
+                }
+                dvi_out(oval / 0x10000);
+                oval = oval % 0x10000;
+            }
+            dvi_out(oval / 0x10000);
+            oval = oval % 0x10000;
+        }
+        dvi_out(oval / 0x100);
+        oval = oval % 0x100;
+    }
+    dvi_out(oval);
+}
+
+void dvi_font_def(internal_font_number f)
+{
+    char *fa;
+    oval = f - 1;
+    ocmd = fnt_def1;
+    out_cmd();
+    dvi_out(font_check_0(f));
+    dvi_out(font_check_1(f));
+    dvi_out(font_check_2(f));
+    dvi_out(font_check_3(f));
+    dvi_four(font_size(f));
+    dvi_four(font_dsize(f));
+    dvi_out(0);                 /* |font_area(f)| is unused */
+    dvi_out(strlen(font_name(f)));
+    /* Output the font name whose internal number is |f| */
+    fa = font_name(f);
+    while (*fa != '\0') {
+        dvi_out(*fa++);
+    }
+}
+
+@ Versions of \TeX\ intended for small computers might well choose to omit
+the ideas in the next few parts of this program, since it is not really
+necessary to optimize the \.{DVI} code by making use of the |w0|, |x0|,
+|y0|, and |z0| commands. Furthermore, the algorithm that we are about to
+describe does not pretend to give an optimum reduction in the length
+of the \.{DVI} code; after all, speed is more important than compactness.
+But the method is surprisingly effective, and it takes comparatively little
+time.
+
+We can best understand the basic idea by first considering a simpler problem
+that has the same essential characteristics. Given a sequence of digits,
+say $3\,1\,4\,1\,5\,9\,2\,6\,5\,3\,5\,8\,9$, we want to assign subscripts
+$d$, $y$, or $z$ to each digit so as to maximize the number of ``$y$-hits''
+and ``$z$-hits''; a $y$-hit is an instance of two appearances of the same
+digit with the subscript $y$, where no $y$'s intervene between the two
+appearances, and a $z$-hit is defined similarly. For example, the sequence
+above could be decorated with subscripts as follows:
+$$3_z\,1_y\,4_d\,1_y\,5_y\,9_d\,2_d\,6_d\,5_y\,3_z\,5_y\,8_d\,9_d.$$
+There are three $y$-hits ($1_y\ldots1_y$ and $5_y\ldots5_y\ldots5_y$) and
+one $z$-hit ($3_z\ldots3_z$); there are no $d$-hits, since the two appearances
+of $9_d$ have $d$'s between them, but we don't count $d$-hits so it doesn't
+matter how many there are. These subscripts are analogous to the \.{DVI}
+commands called \\{down}, $y$, and $z$, and the digits are analogous to
+different amounts of vertical motion; a $y$-hit or $z$-hit corresponds to
+the opportunity to use the one-byte commands |y0| or |z0| in a \.{DVI} file.
+
+\TeX's method of assigning subscripts works like this: Append a new digit,
+say $\delta$, to the right of the sequence. Now look back through the
+sequence until one of the following things happens: (a)~You see
+$\delta_y$ or $\delta_z$, and this was the first time you encountered a
+$y$ or $z$ subscript, respectively.  Then assign $y$ or $z$ to the new
+$\delta$; you have scored a hit. (b)~You see $\delta_d$, and no $y$
+subscripts have been encountered so far during this search.  Then change
+the previous $\delta_d$ to $\delta_y$ (this corresponds to changing a
+command in the output buffer), and assign $y$ to the new $\delta$; it's
+another hit.  (c)~You see $\delta_d$, and a $y$ subscript has been seen
+but not a $z$.  Change the previous $\delta_d$ to $\delta_z$ and assign
+$z$ to the new $\delta$. (d)~You encounter both $y$ and $z$ subscripts
+before encountering a suitable $\delta$, or you scan all the way to the
+front of the sequence. Assign $d$ to the new $\delta$; this assignment may
+be changed later.
+
+The subscripts $3_z\,1_y\,4_d\ldots\,$ in the example above were, in fact,
+produced by this procedure, as the reader can verify. (Go ahead and try it.)
+
+@ In order to implement such an idea, \TeX\ maintains a stack of pointers
+to the \\{down}, $y$, and $z$ commands that have been generated for the
+current page. And there is a similar stack for \\{right}, |w|, and |x|
+commands. These stacks are called the down stack and right stack, and their
+top elements are maintained in the variables |down_ptr| and |right_ptr|.
+
+Each entry in these stacks contains four fields: The |width| field is
+the amount of motion down or to the right; the |location| field is the
+byte number of the \.{DVI} command in question (including the appropriate
+|dvi_offset|); the |vlink| field points to the next item below this one
+on the stack; and the |vinfo| field encodes the options for possible change
+in the \.{DVI} command.
+
+@c
+#define location(A) varmem[(A)+1].cint  /* \.{DVI} byte number for a movement command */
+
+halfword down_ptr = null, right_ptr = null;     /* heads of the down and right stacks */
+
+@ Here is a subroutine that produces a \.{DVI} command for some specified
+downward or rightward motion. It has two parameters: |w| is the amount
+of motion, and |o| is either |down1| or |right1|. We use the fact that
+the command codes have convenient arithmetic properties: |y1-down1=w1-right1|
+and |z1-down1=x1-right1|.
+
+@c
+void movement(scaled w, eight_bits o)
+{
+    small_number mstate;        /* have we seen a |y| or |z|? */
+    halfword p, q;              /* current and top nodes on the stack */
+    int k;                      /* index into |dvi_buf|, modulo |dvi_buf_size| */
+    if (false) {                /* TODO: HUH? */
+        q = new_node(movement_node, 0); /* new node for the top of the stack */
+        width(q) = w;
+        location(q) = dvi_offset + dvi_ptr;
+        if (o == down1) {
+            vlink(q) = down_ptr;
+            down_ptr = q;
+        } else {
+            vlink(q) = right_ptr;
+            right_ptr = q;
+        }
+        /* Look at the other stack entries until deciding what sort of \.{DVI} command
+           to generate; |goto found| if node |p| is a ``hit'' */
+        p = vlink(q);
+        mstate = none_seen;
+        while (p != null) {
+            if (width(p) == w) {
+                /* Consider a node with matching width;|goto found| if it's a hit */
+                /* We might find a valid hit in a |y| or |z| byte that is already gone
+                   from the buffer. But we can't change bytes that are gone forever; ``the
+                   moving finger writes, $\ldots\,\,$.'' */
+
+                switch (mstate + vinfo(p)) {
+                case none_seen + yz_OK:
+                case none_seen + y_OK:
+                case z_seen + yz_OK:
+                case z_seen + y_OK:
+                    if (location(p) < dvi_gone) {
+                        goto NOT_FOUND;
+                    } else {
+                        /* Change buffered instruction to |y| or |w| and |goto found| */
+                        k = location(p) - dvi_offset;
+                        if (k < 0)
+                            k = k + dvi_buf_size;
+                        dvi_buf[k] = (eight_bits) (dvi_buf[k] + y1 - down1);
+                        vinfo(p) = y_here;
+                        goto FOUND;
+                    }
+                    break;
+                case none_seen + z_OK:
+                case y_seen + yz_OK:
+                case y_seen + z_OK:
+                    if (location(p) < dvi_gone) {
+                        goto NOT_FOUND;
+                    } else {
+                        /* Change buffered instruction to |z| or |x| and |goto found| */
+                        k = location(p) - dvi_offset;
+                        if (k < 0)
+                            k = k + dvi_buf_size;
+                        dvi_buf[k] = (eight_bits) (dvi_buf[k] + z1 - down1);
+                        vinfo(p) = z_here;
+                        goto FOUND;
+                    }
+                    break;
+                case none_seen + y_here:
+                case none_seen + z_here:
+                case y_seen + z_here:
+                case z_seen + y_here:
+                    goto FOUND;
+                    break;
+                default:
+                    break;
+                }
+            } else {
+                switch (mstate + vinfo(p)) {
+                case none_seen + y_here:
+                    mstate = y_seen;
+                    break;
+                case none_seen + z_here:
+                    mstate = z_seen;
+                    break;
+                case y_seen + z_here:
+                case z_seen + y_here:
+                    goto NOT_FOUND;
+                    break;
+                default:
+                    break;
+                }
+            }
+            p = vlink(p);
+        }
+    }
+  NOT_FOUND:
+    /* Generate a |down| or |right| command for |w| and |return| */
+    if (abs(w) >= 040000000) {
+        dvi_out(o + 3);         /* |down4| or |right4| */
+        dvi_four(w);
+        return;
+    }
+    if (abs(w) >= 0100000) {
+        dvi_out(o + 2);         /* |down3| or |right3| */
+        if (w < 0)
+            w = w + 0100000000;
+        dvi_out(w / 0200000);
+        w = w % 0200000;
+        goto TWO;
+    }
+    if (abs(w) >= 0200) {
+        dvi_out(o + 1);         /* |down2| or |right2| */
+        if (w < 0)
+            w = w + 0200000;
+        goto TWO;
+    }
+    dvi_out(o);                 /* |down1| or |right1| */
+    if (w < 0)
+        w = w + 0400;
+    goto ONE;
+  TWO:
+    dvi_out(w / 0400);
+  ONE:
+    dvi_out(w % 0400);
+    return;
+  FOUND:
+    /* Generate a |y0| or |z0| command in order to reuse a previous appearance of~|w| */
+    /* The program below removes movement nodes that are introduced after a |push|,
+       before it outputs the corresponding |pop|. */
+    /*
+       When the |movement| procedure gets to the label |found|, the value of
+       |vinfo(p)| will be either |y_here| or |z_here|. If it is, say, |y_here|,
+       the procedure generates a |y0| command (or a |w0| command), and marks
+       all |vinfo| fields between |q| and |p| so that |y| is not OK in that range.
+     */
+    vinfo(q) = vinfo(p);
+    if (vinfo(q) == y_here) {
+        dvi_out(o + y0 - down1);        /* |y0| or |w0| */
+        while (vlink(q) != p) {
+            q = vlink(q);
+            switch (vinfo(q)) {
+            case yz_OK:
+                vinfo(q) = z_OK;
+                break;
+            case y_OK:
+                vinfo(q) = d_fixed;
+                break;
+            default:
+                break;
+            }
+        }
+    } else {
+        dvi_out(o + z0 - down1);        /* |z0| or |x0| */
+        while (vlink(q) != p) {
+            q = vlink(q);
+            switch (vinfo(q)) {
+            case yz_OK:
+                vinfo(q) = y_OK;
+                break;
+            case z_OK:
+                vinfo(q) = d_fixed;
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+@ In case you are wondering when all the movement nodes are removed from
+\TeX's memory, the answer is that they are recycled just before
+|hlist_out| and |vlist_out| finish outputting a box. This restores the
+down and right stacks to the state they were in before the box was output,
+except that some |vinfo|'s may have become more restrictive.
+
+
+@c
+/* delete movement nodes with |location>=l| */
+void prune_movements(int l)
+{
+    pointer p;                  /* node being deleted */
+    while (down_ptr != null) {
+        if (location(down_ptr) < l)
+            break;
+        p = down_ptr;
+        down_ptr = vlink(p);
+        flush_node(p);
+    }
+    while (right_ptr != null) {
+        if (location(right_ptr) < l)
+            return;
+        p = right_ptr;
+        right_ptr = vlink(p);
+        flush_node(p);
+    }
+}
+
+scaledpos dvi;                  /* a \.{DVI} position in page coordinates, in sync with DVI file */
+
+@ When |hlist_out| is called, its duty is to output the box represented
+by the |hlist_node| pointed to by |temp_ptr|. The reference point of that
+box has coordinates |(cur.h,cur.v)|.
+
+Similarly, when |vlist_out| is called, its duty is to output the box represented
+by the |vlist_node| pointed to by |temp_ptr|. The reference point of that
+box has coordinates |(cur.h,cur.v)|.
+@^recursion@>
+
+@ The recursive procedures |hlist_out| and |vlist_out| each have a local variable
+|save_dvi| to hold the value of |dvi| just before
+entering a new level of recursion.  In effect, the value of |save_dvi|
+on \TeX's run-time stack corresponds to the values of |h| and |v|
+that a \.{DVI}-reading program will push onto its coordinate stack.
+
+@c
+void dvi_place_rule(PDF pdf, halfword q, scaledpos size)
+{
+    synch_dvi_with_pos(pdf->posstruct->pos);
+    if ((subtype(q) >= box_rule) && (subtype(q) <= user_rule)) {
+        /* place nothing, only take space */
+        if (textdir_is_L(pdf->posstruct->dir))
+            dvi.h += size.h;
+    } else {
+        /* normal_rule or >= 100 being a leader rule */
+        if (textdir_is_L(pdf->posstruct->dir)) {
+            dvi_out(set_rule);      /* movement optimization for |dir_*L*| */
+            dvi.h += size.h;
+        } else
+            dvi_out(put_rule);
+    }
+    dvi_four(size.v);
+    dvi_four(size.h);
+}
+
+void dvi_place_glyph(PDF pdf, internal_font_number f, int c, int ex)
+{
+    /* TODO: do something on ex, select font (if possible) */
+    scaled_whd ci;
+    synch_dvi_with_pos(pdf->posstruct->pos);
+    if (f != pdf->f_cur) {
+        /* Change font |f_cur| to |f| */
+        if (!font_used(f)) {
+            dvi_font_def(f);
+            set_font_used(f, true);
+        }
+        oval = f - 1;
+        ocmd = fnt1;
+        out_cmd();
+        pdf->f_cur = f;
+    }
+    if (textdir_is_L(pdf->posstruct->dir)) {
+        ci = get_charinfo_whd(f, c);
+        dvi_set(c, ci.wd);      /* movement optimization for |dir_*L*| */
+    } else
+        dvi_put(c);
+}
+
+void dvi_special(PDF pdf, halfword p)
+{
+    int old_setting;            /* holds print |selector| */
+    unsigned k;                 /* index into |cur_string| */
+    synch_dvi_with_pos(pdf->posstruct->pos);
+    old_setting = selector;
+    selector = new_string;
+    show_token_list(token_link(write_tokens(p)), null, -1);
+    selector = old_setting;
+    if (cur_length < 256) {
+        dvi_out(xxx1);
+        dvi_out(cur_length);
+    } else {
+        dvi_out(xxx4);
+        dvi_four((int) cur_length);
+    }
+    for (k = 0; k < cur_length; k++)
+        dvi_out(cur_string[k]);
+    cur_length = 0;             /* erase the string */
+}
+
+@ Here's an example of how these conventions are used. Whenever it is time to
+ship out a box of stuff, we shall use the macro |ensure_dvi_open|.
+
+@c
+void ensure_dvi_header_written(PDF pdf)
+{
+    unsigned l;
+    unsigned s;                 /* index into |str_pool| */
+    int old_setting;            /* saved |selector| setting */
+    assert(output_mode_used == OMODE_DVI);
+    assert(pdf->o_state == ST_FILE_OPEN);
+
+    if (half_buf == 0) {
+        half_buf = dvi_buf_size / 2;
+        dvi_limit = dvi_buf_size;
+    }
+
+    dvi_out(pre);
+    dvi_out(id_byte);           /* output the preamble */
+    dvi_four(25400000);
+    dvi_four(473628672);        /* conversion ratio for sp */
+    prepare_mag();
+    dvi_four(mag_par);          /* magnification factor is frozen */
+    if (output_comment) {
+        l = (unsigned) strlen(output_comment);
+        dvi_out(l);
+        for (s = 0; s < l; s++)
+            dvi_out(output_comment[s]);
+    } else {                    /* the default code is unchanged */
+        old_setting = selector;
+        selector = new_string;
+        tprint(" LuaTeX output ");
+        print_int(year_par);
+        print_char('.');
+        print_two(month_par);
+        print_char('.');
+        print_two(day_par);
+        print_char(':');
+        print_two(time_par / 60);
+        print_two(time_par % 60);
+        selector = old_setting;
+        dvi_out(cur_length);
+        for (s = 0; s < cur_length; s++)
+            dvi_out(cur_string[s]);
+        cur_length = 0;
+    }
+}
+
+void dvi_begin_page(PDF pdf)
+{
+    int k;
+    int page_loc;               /* location of the current |bop| */
+    ensure_output_state(pdf, ST_HEADER_WRITTEN);
+    /* Initialize variables as |ship_out| begins */
+    page_loc = dvi_offset + dvi_ptr;
+    dvi_out(bop);
+    for (k = 0; k <= 9; k++)
+        dvi_four(count(k));
+    dvi_four(last_bop);
+    last_bop = page_loc;
+}
+
+void dvi_end_page(PDF pdf)
+{
+    (void) pdf;
+    dvi_out(eop);
+}
+
+@ At the end of the program, we must finish things off by writing the
+post\-amble. If |total_pages=0|, the \.{DVI} file was never opened.
+If |total_pages>=65536|, the \.{DVI} file will lie. And if
+|max_push>=65536|, the user deserves whatever chaos might ensue.
+
+@c
+void finish_dvi_file(PDF pdf, int version, int revision)
+{
+    int k;
+    int callback_id = callback_defined(stop_run_callback);
+    (void) version;
+    (void) revision;
+    while (cur_s > -1) {
+        if (cur_s > 0) {
+            dvi_out(pop);
+        } else {
+            dvi_out(eop);
+            incr(total_pages);
+        }
+        decr(cur_s);
+    }
+    if (total_pages == 0) {
+        if (callback_id == 0) {
+            tprint_nl("No pages of output.");
+            print_ln();
+        } else if (callback_id > 0) {
+            run_callback(callback_id, "->");
+        }
+    } else {
+        dvi_out(post);          /* beginning of the postamble */
+        dvi_four(last_bop);
+        last_bop = dvi_offset + dvi_ptr - 5;    /* |post| location */
+        dvi_four(25400000);
+        dvi_four(473628672);    /* conversion ratio for sp */
+        prepare_mag();
+        dvi_four(mag_par);      /* magnification factor */
+        dvi_four(max_v);
+        dvi_four(max_h);
+        dvi_out(max_push / 256);
+        dvi_out(max_push % 256);
+        dvi_out((total_pages / 256) % 256);
+        dvi_out(total_pages % 256);
+        /* Output the font definitions for all fonts that were used */
+        k = max_font_id();
+        while (k > 0) {
+            if (font_used(k)) {
+                dvi_font_def(k);
+            }
+            decr(k);
+        }
+
+        dvi_out(post_post);
+        dvi_four(last_bop);
+        dvi_out(id_byte);
+#ifndef IPC
+        k = 4 + ((dvi_buf_size - dvi_ptr) % 4); /* the number of 223's */
+#else
+        k = 7 - ((3 + dvi_offset + dvi_ptr) % 4);       /* the number of 223's */
+#endif
+
+        while (k > 0) {
+            dvi_out(223);
+            decr(k);
+        }
+        /* Empty the last bytes out of |dvi_buf| */
+        /* Here is how we clean out the buffer when \TeX\ is all through; |dvi_ptr|
+           will be a multiple of~4. */
+        if (dvi_limit == half_buf)
+            write_dvi(half_buf, dvi_buf_size - 1);
+        if (dvi_ptr > 0)
+            write_dvi(0, dvi_ptr - 1);
+
+        if (callback_id == 0) {
+            tprint_nl("Output written on ");
+            tprint(pdf->file_name);
+            tprint(" (");
+            print_int(total_pages);
+            tprint(" page");
+            if (total_pages != 1)
+                print_char('s');
+            tprint(", ");
+            print_int(dvi_offset + dvi_ptr);
+            tprint(" bytes).");
+        } else if (callback_id > 0) {
+            run_callback(callback_id, "->");
+        }
+        close_file(pdf->file);
+    }
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/dofont.w
@@ -0,0 +1,145 @@
+% dofont.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"
+
+@ a bit more interfacing is needed for proper error reporting
+
+@c
+static char *font_error_message(pointer u, char *nom, scaled s)
+{
+    char *str = xmalloc(256);
+    char *c = makecstring(cs_text(u));
+    const char *extra = "metric data not found or bad";
+    if (s >= 0) {
+        snprintf(str, 255, "Font \\%s=%s at %gpt not loadable: %s", c, nom,
+                 (double) s / 65536, extra);
+    } else if (s != -1000) {
+        snprintf(str, 255, "Font \\%s=%s scaled %d not loadable: %s", c, nom,
+                 (int) (-s), extra);
+    } else {
+        snprintf(str, 255, "Font \\%s=%s not loadable: %s", c, nom, extra);
+    }
+    free(c);
+    return str;
+}
+
+static int do_define_font(int f, const char *cnom, scaled s, int natural_dir)
+{
+
+    boolean res;                /* was the callback successful? */
+    int callback_id;
+    char *cnam;
+    int r, t;
+    res = 0;
+
+    callback_id = callback_defined(define_font_callback);
+    if (callback_id > 0) {
+        cnam = xstrdup(cnom);
+        callback_id = run_and_save_callback(callback_id, "Sdd->", cnam, s, f);
+        free(cnam);
+        if (callback_id > 0) {  /* success */
+            luaL_checkstack(Luas, 1, "out of stack space");
+            lua_rawgeti(Luas, LUA_REGISTRYINDEX, callback_id);
+            t = lua_type(Luas, -1);
+            if (t == LUA_TTABLE) {
+                res = font_from_lua(Luas, f);
+                destroy_saved_callback(callback_id);
+                /* |lua_pop(Luas, 1);| *//* done by |font_from_lua| */
+            } else if (t == LUA_TNUMBER) {
+                r = (int) lua_tointeger(Luas, -1);
+                destroy_saved_callback(callback_id);
+                delete_font(f);
+                lua_pop(Luas, 1);
+                return r;
+            } else {
+                lua_pop(Luas, 1);
+                delete_font(f);
+                return 0;
+            }
+        }
+    } else if (callback_id == 0) {
+        res = read_tfm_info(f, cnom, s);
+        if (res) {
+            set_hyphen_char(f, default_hyphen_char_par);
+            set_skew_char(f, default_skew_char_par);
+        }
+    }
+    if (font_name(f) && strlen(font_name(f)) > 255) {
+        /* the font name has to fit in the dvi file's single byte storage */
+        /* no need to test area, as we are never using it */
+        res = 0;
+    }
+    if (res) {
+        if (font_type(f) != virtual_font_type) {        /* implies lua */
+            do_vf(f);
+            set_font_natural_dir(f, natural_dir);
+        }
+        return f;
+    } else {
+        delete_font(f);
+        return 0;
+    }
+
+}
+
+int read_font_info(pointer u, char *cnom, scaled s, int natural_dir)
+{
+    int f;
+    char *msg;
+
+    f = new_font();
+    if ((f = do_define_font(f, cnom, s, natural_dir))) {
+        return f;
+    } else {
+        const char *help[] =
+            { "I wasn't able to read the size data for this font,",
+            "so I will ignore the font specification.",
+            "[Wizards can fix TFM files using TFtoPL/PLtoTF.]",
+            "You might try inserting a different font spec;",
+            "e.g., type `I\\font<same font id>=<substitute font name>'.",
+            NULL
+        };
+        if (suppress_fontnotfound_error_par == 0) {
+            msg = font_error_message(u, cnom, s);
+            tex_error(msg, help);
+            free(msg);
+        }
+        return 0;
+    }
+}
+
+@ TODO This function is a placeholder. There can easily appears holes in
+   the |font_tables| array, and we could attempt to reuse those
+
+@c
+int find_font_id(const char *nom, scaled s)
+{
+    int f;
+    f = new_font();
+    if ((f = do_define_font(f, nom, s, -1))) {
+        return f;
+    } else {
+        return 0;
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/dofont.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-/*tex
-
-    A bit more interfacing is needed for proper error reporting.
-
-*/
-
-static char *font_error_message(pointer u, char *nom, scaled s)
-{
-    char *str = xmalloc(256);
-    char *c = makecstring(cs_text(u));
-    const char *extra = "metric data not found or bad";
-    if (s >= 0) {
-        snprintf(str, 255, "Font \\%s=%s at %gpt not loadable: %s", c, nom, (double) s / 65536, extra);
-    } else if (s != -1000) {
-        snprintf(str, 255, "Font \\%s=%s scaled %d not loadable: %s", c, nom, (int) (-s), extra);
-    } else {
-        snprintf(str, 255, "Font \\%s=%s not loadable: %s", c, nom, extra);
-    }
-    free(c);
-    return str;
-}
-
-static int do_define_font(int f, const char *cnom, scaled s, int natural_dir)
-{
-    boolean res = 0;
-    char *cnam;
-    int r, t;
-    int callback_id = callback_defined(define_font_callback);
-    if (callback_id > 0) {
-        cnam = xstrdup(cnom);
-        callback_id = run_and_save_callback(callback_id, "Sdd->", cnam, s, f);
-        free(cnam);
-        if (callback_id > 0) {
-            /*tex Success. */
-            luaL_checkstack(Luas, 1, "out of stack space");
-            lua_rawgeti(Luas, LUA_REGISTRYINDEX, callback_id);
-            t = lua_type(Luas, -1);
-            if (t == LUA_TTABLE) {
-                res = font_from_lua(Luas, f);
-                destroy_saved_callback(callback_id);
-            } else if (t == LUA_TNUMBER) {
-                r = (int) lua_tointeger(Luas, -1);
-                destroy_saved_callback(callback_id);
-                delete_font(f);
-                lua_pop(Luas, 1);
-                return r;
-            } else {
-                lua_pop(Luas, 1);
-                delete_font(f);
-                return 0;
-            }
-        }
-    } else if (callback_id == 0) {
-        res = read_tfm_info(f, cnom, s);
-        if (res) {
-            set_hyphen_char(f, default_hyphen_char_par);
-            set_skew_char(f, default_skew_char_par);
-        }
-    }
-    if (font_name(f) && strlen(font_name(f)) > 255) {
-        /*tex
-
-            The font name has to fit in the dvi file's single byte storage. There
-            is no need to test area, as we are never using it.
-        */
-        res = 0;
-    }
-    if (res) {
-        if (font_type(f) != virtual_font_type) {
-            /*tex This implies \LUA. */
-            do_vf(f);
-            set_font_natural_dir(f, natural_dir);
-        }
-        return f;
-    } else {
-        delete_font(f);
-        return 0;
-    }
-
-}
-
-int read_font_info(pointer u, char *cnom, scaled s, int natural_dir)
-{
-    int f;
-    char *msg;
-
-    f = new_font();
-    if ((f = do_define_font(f, cnom, s, natural_dir))) {
-        return f;
-    } else {
-        const char *help[] = {
-            "I wasn't able to read the size data for this font,",
-            "so I will ignore the font specification.",
-            "[Wizards can fix TFM files using TFtoPL/PLtoTF.]",
-            "You might try inserting a different font spec;",
-            "e.g., type `I\\font<same font id>=<substitute font name>'.",
-            NULL
-        };
-        if (suppress_fontnotfound_error_par == 0) {
-            msg = font_error_message(u, cnom, s);
-            tex_error(msg, help);
-            free(msg);
-        }
-        return 0;
-    }
-}
-
-int find_font_id(const char *nom, scaled s)
-{
-    int f;
-    f = new_font();
-    if ((f = do_define_font(f, nom, s, -1))) {
-        return f;
-    } else {
-        return 0;
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/luafont.w
@@ -0,0 +1,2426 @@
+% 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,streamprovider,font_streamprovider(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 (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 ts;
+    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;
+                } 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, pdf)) {
+                    size_t len;
+                    l += 5;
+                    ts = lua_rawlen(L, -2);
+                    lua_rawgeti(L, -2, 2);
+                    if (ts == 3) {
+                        if (lua_type(L,-1) == LUA_TSTRING) {
+                            /* no need to do something */
+                        } else if (lua_type(L,-1) == LUA_TNUMBER) {
+                            /* no need to do something */
+                        } else {
+                            normal_error("vf command","invalid packet pdf literal category");
+                        }
+                        lua_rawgeti(L, -3, 3);
+                    }
+                    if (lua_type(L,-1) == LUA_TSTRING) {
+                        (void) lua_tolstring(L, -1, &len);
+                        if (len > 0) {
+                            l = (int) (l + 5 + (int) len);
+                        }
+                    } else {
+                        normal_error("vf command","invalid packet pdf literal");
+                    }
+                    lua_pop(L, ts == 3 ? 2 : 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);
+                        if (len > 0) {
+                            l = (int) (l + 5 + (int) len);
+                        }
+                    } else {
+                        normal_error("vf command","invalid packet special");
+                    }
+                    lua_pop(L, 1);
+                } else {
+                    normal_error("vf command","unknown packet command");
+                }
+            } else {
+                normal_error("vf command","no packet command");
+            }
+            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 sf = 0;
+    int ts = 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)) {
+                    /* we could be sparse but no real reason */
+                    cmd = packet_nop_code;
+                    lua_rawgeti(L, -2, 2);
+                    n = (int) lua_roundnumber(L, -1);
+                    if (n == 0) {
+                        sf = f;
+                    } else {
+                        sf = (n > max_f ? l_fonts[1] : l_fonts[n]);
+                    }
+                    lua_rawgeti(L, -3, 3);
+                    n = (int) lua_roundnumber(L, -1);
+                    lua_pop(L, 2);
+                    append_packet(packet_font_code);
+                    do_store_four(sf);
+                    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, pdf)) {
+                    cmd = packet_pdf_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) lua_roundnumber(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) lua_roundnumber(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) lua_roundnumber(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) lua_roundnumber(L, -1);
+                        do_store_four(sp_to_dvi(n, atsize));
+                        lua_rawgeti(L, -3, 3);
+                        n = (int) lua_roundnumber(L, -1);
+                        do_store_four(sp_to_dvi(n, atsize));
+                        lua_pop(L, 2);
+                        break;
+                    case packet_pdf_code:
+                        ts = (int) lua_rawlen(L, -2);
+                        lua_rawgeti(L, -2, 2);
+                        if (ts == 3) {
+                            /* mode on stack */
+                            s = lua_tostring(L, -1);
+                            if (lua_type(L, -1) == LUA_TSTRING) {
+                                /* <pdf> <mode> <direct|page|text|raw|origin> */
+                                if (lua_key_eq(s, mode)) {
+                                    cmd = packet_pdf_mode;
+                                    lua_rawgeti(L, -3, 3);
+                                    /* mode on stack */
+                                    s = lua_tostring(L, -1);
+                                }
+                            } else {
+                                /* <pdf> <direct|page|text|raw|origin> <string> */
+                            }
+                            if (lua_type(L, -1) == LUA_TSTRING) {
+                                if (lua_key_eq(s, direct)) {
+                                    n = direct_always;
+                                } else if (lua_key_eq(s, page)) {
+                                    n = direct_page;
+                                } else if (lua_key_eq(s, text)) {
+                                    n = direct_text;
+                                } else if (lua_key_eq(s, font)) {
+                                    n = direct_font;
+                                } else if (lua_key_eq(s, raw)) {
+                                    n = direct_raw;
+                                } else if (lua_key_eq(s, origin)) {
+                                    n = set_origin;
+                                } else {
+                                 /* normal_warning("vf command","invalid pdf literal type"); */
+                                    n = set_origin ;
+                                }
+                            } else {
+                                n = (int) lua_roundnumber(L, -1);
+                                if (n < set_origin || n >= scan_special) {
+                                    n = set_origin ;
+                                }
+                            }
+                            if (cmd == packet_pdf_code) {
+                                lua_rawgeti(L, -3, 3);
+                                /* string on stack */
+                            }
+                        } else {
+                            n = set_origin;
+                        }
+                        append_packet(cmd);
+                        do_store_four(n);
+                        if (cmd == packet_pdf_code) {
+                            s = luaL_checklstring(L, -1, &l);
+                            do_store_four(l);
+                            if (l > 0) {
+                                m = (int) l;
+                                while (m > 0) {
+                                    n = *s++;
+                                    m--;
+                                    append_packet(n);
+                                }
+                            }
+                        }
+                        lua_pop(L,ts == 3 ? 2 : 1);
+                        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 table");
+            /* 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 = lua_numeric_field_by_index(L,lua_key_index(streamprovider), 0);
+    set_font_streamprovider(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 at index %i in lua-loaded font '%s' (1)",i,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' (2)", 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);
+                    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, 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;
+}
+
+int characters_from_lua(lua_State * L, int f)
+{
+    int i, n, t, lt;
+    int *l_fonts = NULL;
+    int s_top;
+    const char *ss;
+    boolean no_math = false;
+    /* speedup */
+    no_math = n_boolean_field(L, lua_key_index(nomath), 0);
+    /* type */
+    i = n_enum_field(L, lua_key_index(type), font_type(f), font_type_strings);
+    set_font_type(f, i);
+    /* fonts */
+    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' (3)", 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' (4)", font_name(f));
+    } else {
+        l_fonts = xmalloc(3 * sizeof(int));
+        l_fonts[0] = 0;
+        l_fonts[1] = f;
+        l_fonts[2] = 0;
+    }
+    /* characters */
+    lua_key_rawgeti(characters);
+    if (lua_istable(L, -1)) {
+        /* find the array size values */
+        int num = 0;            /* number of charinfo's to add */
+        int todo = 0;
+        int bc = font_bc(f);
+        int ec = font_ec(f);
+        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)) {
+                        todo++;
+                        if (! quick_char_exists(f,i)) {
+                            num++;
+                            if (i > ec)
+                                ec = i;
+                            if (bc < 0)
+                                bc = i;
+                            if (bc >= 0 && i < bc)
+                                bc = i;
+                        }
+                    }
+                }
+            }
+            lua_pop(L, 1);
+        }
+        if (todo > 0) {
+            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) {
+                        if (quick_char_exists(f,i)) {
+                            charinfo *co = char_info(f, i);
+                            set_charinfo_name(co, NULL);
+                            set_charinfo_tounicode(co, NULL);
+                            set_charinfo_packets(co, NULL);
+                            set_charinfo_ligatures(co, NULL);
+                            set_charinfo_kerns(co, NULL);
+                            set_charinfo_vert_variants(co, NULL);
+                            set_charinfo_hor_variants(co, NULL);
+                        }
+                        font_char_from_lua(L, f, i, l_fonts, !no_math);
+                    }
+                }
+                lua_pop(L, 1);
+            }
+            lua_pop(L, 1);
+        }
+    }
+    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;
+    }
+    if (fix_node_lists) {
+        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;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/luafont.c
+++ /dev/null
@@ -1,2420 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-#define noVERBOSE
-
-/*tex
-
-    Todo: make these 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,
-};
-
-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])) {
-                /*tex 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 {
-                    /*tex The 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);
-}
-
-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) {
-        /*tex Fetch the table from the registry if it was saved there by |font_from_lua|. */
-        lua_rawgeti(L, LUA_REGISTRYINDEX, font_cache_id(f));
-        /*tex Font dimenensions 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,streamprovider,font_streamprovider(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,squeeze,font_squeeze(f));
-    dump_intfield(L,mode,font_mode(f));
-    dump_intfield(L,width,font_width(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));
-    /*tex 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 (pdf_font_attr(f) != 0) {
-        char *s = makecstring(pdf_font_attr(f));
-        dump_stringfield(L,attributes,s);
-        free(s);
-    }
-    /*tex Parameters: */
-    write_lua_parameters(L, f);
-    write_lua_math_parameters(L, f);
-    /*tex Characters: */
-    lua_push_string_by_name(L,characters);
-    lua_createtable(L, font_tables[f]->charinfo_size, 0);
-    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) {
-        /*tex Renew the cache. */
-        int r;
-        lua_pushvalue(L, -1);
-        r = luaL_ref(L, LUA_REGISTRYINDEX);
-        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) { \
-        /*tex Keep the table on stack. */ \
-    } else{ \
-        lua_pop(L, 1); \
-    }
-
-#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));       \
-}
-
-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;
-    /*tex Fetch the string pointer: */
-    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);
-    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;
-    /*tex Fetch the string pointer: */
-    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);
-    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;
-    /*tex Fetch the string pointer: */
-    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);
-    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)
-{
-    /*tex Fetch the string pointer: */
-    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);
-    lua_rawget(L, -2);
-    return lua_tostring(L,-1);
-}
-
-static int n_some_field(lua_State * L, int name_index)
-{
-    /*tex Fetch the string pointer: */
-    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);
-    lua_rawget(L, -2);
-    return lua_type(L,-1);
-}
-
-static int count_char_packet_bytes(lua_State * L)
-{
-    register int i;
-    register int ts;
-    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;
-                } 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) || lua_key_eq(s, lua)) {
-                    l += 5;
-                } else if (lua_key_eq(s, scale)) {
-                    l += sizeof(float) + 1;
-                } else if (lua_key_eq(s, pdf)) {
-                    size_t len;
-                    l += 5;
-                    ts = lua_rawlen(L, -2);
-                    lua_rawgeti(L, -2, 2);
-                    if (ts == 3) {
-                        if (lua_type(L,-1) == LUA_TSTRING) {
-                            /*tex There is no need to do something. */
-                        } else if (lua_type(L,-1) == LUA_TNUMBER) {
-                            /*tex There is no need to do something. */
-                        } else {
-                            normal_error("vf command","invalid packet pdf literal category");
-                        }
-                        lua_rawgeti(L, -3, 3);
-                    }
-                    if (lua_type(L,-1) == LUA_TSTRING) {
-                        (void) lua_tolstring(L, -1, &len);
-                        if (len > 0) {
-                            l = (int) (l + 5 + (int) len);
-                        }
-                    } else {
-                        normal_error("vf command","invalid packet pdf literal");
-                    }
-                    lua_pop(L, ts == 3 ? 2 : 1);
-                } else if (lua_key_eq(s, special)) {
-                    size_t len;
-                    lua_rawgeti(L, -2, 2);
-                    if (lua_type(L,-1) == LUA_TSTRING) {
-                        (void) lua_tolstring(L, -1, &len);
-                        if (len > 0) {
-                            l = (int) (l + 5 + (int) len);
-                        }
-                    } else {
-                        normal_error("vf command","invalid packet special");
-                    }
-                    lua_pop(L, 1);
-                } else {
-                    normal_error("vf command","unknown packet command");
-                }
-            } else {
-                normal_error("vf command","no packet command");
-            }
-            /*tex Pop the command name: */
-            lua_pop(L, 1);
-        }
-        /*tex Pop this item: */
-        lua_pop(L, 1);
-    }
-    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);
-}
-
-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 sf = 0;
-    int ts = 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)) {
-            /*tex 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)) {
-                    /*tex we could be sparse but no real reason */
-                    cmd = packet_nop_code;
-                    lua_rawgeti(L, -2, 2);
-                    n = (int) lua_roundnumber(L, -1);
-                    if (n == 0) {
-                        sf = f;
-                    } else {
-                        sf = (n > max_f ? l_fonts[1] : l_fonts[n]);
-                    }
-                    lua_rawgeti(L, -3, 3);
-                    n = (int) lua_roundnumber(L, -1);
-                    lua_pop(L, 2);
-                    append_packet(packet_font_code);
-                    do_store_four(sf);
-                    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, pdf)) {
-                    cmd = packet_pdf_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) lua_roundnumber(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,-1));
-                        do_store_four(n);
-                        lua_pop(L, 1);
-                        break;
-                    case packet_char_code:
-                        append_packet(cmd);
-                        lua_rawgeti(L, -2, 2);
-                        n = (int) lua_roundnumber(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) lua_roundnumber(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) lua_roundnumber(L, -1);
-                        do_store_four(sp_to_dvi(n, atsize));
-                        lua_rawgeti(L, -3, 3);
-                        n = (int) lua_roundnumber(L, -1);
-                        do_store_four(sp_to_dvi(n, atsize));
-                        lua_pop(L, 2);
-                        break;
-                    case packet_pdf_code:
-                        ts = (int) lua_rawlen(L, -2);
-                        lua_rawgeti(L, -2, 2);
-                        if (ts == 3) {
-                            /*tex mode on stack */
-                            s = lua_tostring(L, -1);
-                            if (lua_type(L, -1) == LUA_TSTRING) {
-                                /*tex |<pdf> <mode> <direct|page|text|raw|origin>| */
-                                if (lua_key_eq(s, mode)) {
-                                    cmd = packet_pdf_mode;
-                                    lua_rawgeti(L, -3, 3);
-                                    /*tex mode on stack */
-                                    s = lua_tostring(L, -1);
-                                }
-                            } else {
-                                /*tex |<pdf> <direct|page|text|raw|origin> <string>| */
-                            }
-                            if (lua_type(L, -1) == LUA_TSTRING) {
-                                if (lua_key_eq(s, direct)) {
-                                    n = direct_always;
-                                } else if (lua_key_eq(s, page)) {
-                                    n = direct_page;
-                                } else if (lua_key_eq(s, text)) {
-                                    n = direct_text;
-                                } else if (lua_key_eq(s, font)) {
-                                    n = direct_font;
-                                } else if (lua_key_eq(s, raw)) {
-                                    n = direct_raw;
-                                } else if (lua_key_eq(s, origin)) {
-                                    n = set_origin;
-                                } else {
-                                    n = set_origin ;
-                                }
-                            } else {
-                                n = (int) lua_roundnumber(L, -1);
-                                if (n < set_origin || n >= scan_special) {
-                                    n = set_origin ;
-                                }
-                            }
-                            if (cmd == packet_pdf_code) {
-                                /*tex string on stack */
-                                lua_rawgeti(L, -3, 3);
-                            }
-                        } else {
-                            n = set_origin;
-                        }
-                        append_packet(cmd);
-                        do_store_four(n);
-                        if (cmd == packet_pdf_code) {
-                            s = luaL_checklstring(L, -1, &l);
-                            do_store_four(l);
-                            if (l > 0) {
-                                m = (int) l;
-                                while (m > 0) {
-                                    n = *s++;
-                                    m--;
-                                    append_packet(n);
-                                }
-                            }
-                        }
-                        lua_pop(L,ts == 3 ? 2 : 1);
-                        break;
-                    case packet_special_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_lua_code:
-                        append_packet(cmd);
-                        lua_rawgeti(L, -2, 2);
-                        n = luaL_ref(L, LUA_REGISTRYINDEX);
-                        do_store_four(n);
-                        break;
-                    case packet_image_code:
-                        append_packet(cmd);
-                        lua_rawgeti(L, -2, 2);
-                        if (lua_istable(L, -1)) {
-                            lua_getglobal(L, "img");
-                            lua_pushstring(L, "new");
-                            lua_gettable(L, -2);
-                            lua_insert(L, -3);
-                            lua_pop(L, 1);
-                            lua_call(L, 1, 1);
-                        }
-                        luaL_checkudata(L, -1, TYPE_IMG);
-                        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");
-                }
-            }
-            /*tex Command code: */
-            lua_pop(L, 1);
-        } else {
-            normal_error("vf command","commands has to be a table");
-        }
-        /*tex Command table: */
-        lua_pop(L, 1);
-    }
-    append_packet(packet_end_code);
-    set_charinfo_packets(co, cpackets);
-    return;
-}
-
-static void read_lua_cidinfo(lua_State * L, int f)
-{
-    int i;
-    char *s;
-    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");
-        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);
-}
-
-static void read_lua_parameters(lua_State * L, int f)
-{
-    int i, n, t;
-    const char *s;
-    lua_key_rawgeti(parameters);
-    if (lua_istable(L, -1)) {
-        /*tex The number of parameters is the |max(IntegerKeys(L)),7)| */
-        n = 7;
-        lua_pushnil(L);
-        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);
-        }
-        if (n > 7)
-            set_font_params(f, n);
-        /*tex 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);
-                set_font_param(f, i, n);
-            }
-            lua_pop(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);
-                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);
-
-}
-
-static void read_lua_math_parameters(lua_State * L, int f)
-{
-    int i = 0, n = 0, t;
-    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);
-        }
-        set_font_oldmath(f,false);
-    } else {
-        set_font_oldmath(f,true);
-    }
-    lua_pop(L, 1);
-}
-
-#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);
-}
-
-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;
-    /*tex The number of ligature table items: */
-    int nl = 0;
-    /*tex The number of kern table items: */
-    int nk = 0;
-    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);
-        /*tex |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(11);
-                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);
-        }
-        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);
-            /*tex
-                Here is a complete example:
-
-                \starttyping
-                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  } },
-                }
-                \stoptyping
-
-            */
-            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);
-        }
-        /*tex end of |has_math| */
-        count_hash_items(L, kerns, nk);
-        if (nk > 0) {
-            /*tex The kerns table is still on stack. */
-            ckerns = xcalloc((unsigned) (nk + 1), sizeof(kerninfo));
-            ctr = 0;
-            /*tex Traverse the hash. */
-            lua_pushnil(L);
-            while (lua_next(L, -2) != 0) {
-                k = non_boundarychar;
-                lt = lua_type(L,-2);
-                if (lt == LUA_TNUMBER) {
-                    /*tex Adjacent char: */
-                    k = (int) lua_tointeger(L, -2);
-                    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);
-                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);
-            }
-            /*tex A 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);
-        }
-        /*tex Packet commands. */
-        lua_key_rawgeti(commands);
-        if (lua_istable(L, -1)) {
-            lua_pushnil(L);
-            if (lua_next(L, -2) != 0) {
-                lua_pop(L, 2);
-                read_char_packets(L, (int *) l_fonts, co, f, atsize);
-            }
-        }
-        lua_pop(L, 1);
-        /*tex The ligatures. */
-        count_hash_items(L, ligatures, nl);
-        if (nl > 0) {
-            /*tex The ligatures table still on stack. */
-            cligs = xcalloc((unsigned) (nl + 1), sizeof(liginfo));
-            ctr = 0;
-            /*tex Traverse the hash. */
-            lua_pushnil(L);
-            while (lua_next(L, -2) != 0) {
-                k = non_boundarychar;
-                lt = lua_type(L,-2);
-                if (lt == LUA_TNUMBER) {
-                    /*tex Adjacent char: */
-                    k = (int) lua_tointeger(L, -2);
-                    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)) {
-                    /*tex Ligature: */
-                    r = lua_numeric_field_by_index(L, lua_key_index(char), -1);
-                }
-                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);
-                }
-                /*tex The iterator value: */
-                lua_pop(L, 1);
-            }
-            /*tex A 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);
-            }
-            /*tex The ligatures table. */
-            lua_pop(L, 1);
-        }
-    }
-}
-
-/*tex
-
-    The caller has to fix the state of the lua stack when there is an error!
-
-*/
-
-int font_from_lua(lua_State * L, int f)
-{
-    int i, n, r, t, lt;
-    int s_top;
-    int bc;
-    int ec;
-    char *s;
-    const char *ss;
-    int *l_fonts = NULL;
-    int save_ref ;
-    boolean no_math = false;
-    /*tex Will we save a cache of the \LUA\ table? */
-    save_ref = 1;
-    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;
-    /*tex |n_string_field| leaves a value on stack. */
-    lua_pop(L,1);
-    /*tex 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 = lua_numeric_field_by_index(L,lua_key_index(streamprovider), 0);
-    set_font_streamprovider(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(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(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(squeeze), 1000);
-    if (i < FONT_SQUEEZE_MIN)
-        i = FONT_SQUEEZE_MIN;
-    if (i > FONT_SQUEEZE_MAX)
-        i = FONT_SQUEEZE_MAX;
-    set_font_squeeze(f, i);
-    i = lua_numeric_field_by_index(L,lua_key_index(width), 0);
-    if (i < FONT_WIDTH_MIN)
-        i = FONT_WIDTH_MIN;
-    if (i > FONT_WIDTH_MAX)
-        i = FONT_WIDTH_MAX;
-    set_font_width(f, i);
-    i = lua_numeric_field_by_index(L,lua_key_index(mode), 0);
-    if (i < FONT_MODE_MIN)
-        i = FONT_MODE_MIN;
-    if (i > FONT_MODE_MAX)
-        i = FONT_MODE_MAX;
-    set_font_mode(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);
-    }
-    /*tex Now fetch the base fonts, if needed. */
-    count_hash_items(L, fonts, n);
-    if (n > 0) {
-        /*tex The 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;
-                    }
-                    /*tex Pop id and entry. */
-                    lua_pop(L, 2);
-                    continue;
-                }
-                /*tex Pop id. */
-                lua_pop(L, 1);
-            };
-            ss = NULL;
-            if (lua_istable(L, -1)) {
-                ss = n_string_field(L, lua_key_index(name));
-                /*tex The string is anchored. */
-                lua_pop(L,1);
-            }
-            if (ss != NULL) {
-                t = lua_numeric_field_by_index(L, lua_key_index(size), -1000);
-                /*tex 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 at index %i in lua-loaded font '%s' (1)",i,font_name(f));
-            }
-            /*tex Pop the list entry. */
-            lua_pop(L, 1);
-        }
-        /*tex Pop the font table. */
-        lua_pop(L, 1);
-    } else if (font_type(f) == virtual_font_type) {
-        /*tex
-            We no longer do this check but instead create an entry. This permits
-            (valid) tricks.
-        */
-        /*
-        formatted_error("font","invalid local fonts in lua-loaded font '%s' (2)", font_name(f));
-        */
-    } else {
-        l_fonts = xmalloc(3 * sizeof(int));
-        l_fonts[0] = 0;
-        l_fonts[1] = f;
-        l_fonts[2] = 0;
-    }
-    /*tex The 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);
-    /*tex The characters. */
-    lua_key_rawgeti(characters);
-    if (lua_istable(L, -1)) {
-        /*tex Find the array size values; |num| holds the number of characters to add. */
-        int num = 0;
-        ec = 0;
-        bc = -1;
-        /*tex The first key: */
-        lua_pushnil(L);
-        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) {
-            int fstep;
-            font_malloc_charinfo(f, num);
-            set_font_bc(f, bc);
-            set_font_ec(f, ec);
-            /*tex The first key: */
-            lua_pushnil(L);
-            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);
-            /*tex
-
-                Handle font expansion last: the |copy_font| routine is called eventually,
-                and that needs to know |bc| and |ec|. We permits virtual fonts to use
-                expansion as one can always turn it off.
-
-            */
-            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);
-                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, fstretch, fshrink, fstep);
-            }
-
-        } else {
-            formatted_warning("font","lua-loaded font '%d' with name '%s' has no characters", f, font_name(f));
-        }
-        if (save_ref > 0) {
-            /*tex This pops the table. */
-            r = luaL_ref(L, LUA_REGISTRYINDEX);
-            set_font_cache_id(f, r);
-        } else {
-            lua_pop(L, 1);
-            set_font_cache_id(f, save_ref);
-        }
-    } else {
-        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;
-}
-
-int characters_from_lua(lua_State * L, int f)
-{
-    int i, n, t, lt;
-    int *l_fonts = NULL;
-    int s_top;
-    const char *ss;
-    boolean no_math = false;
-    /*tex Speedup: */
-    no_math = n_boolean_field(L, lua_key_index(nomath), 0);
-    /*tex Type: */
-    i = n_enum_field(L, lua_key_index(type), font_type(f), font_type_strings);
-    set_font_type(f, i);
-    /*tex Fonts: */
-    count_hash_items(L, fonts, n);
-    if (n > 0) {
-        /*tex The 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;
-                    }
-                    /*tex Pop id and entry. */
-                    lua_pop(L, 2);
-                    continue;
-                }
-                /*tex Pop id. */
-                lua_pop(L, 1);
-            };
-            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);
-                /*tex 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' (3)", font_name(f));
-            }
-            /*tex Pop list entry. */
-            lua_pop(L, 1);
-        }
-        /*tex Pop font table. */
-        lua_pop(L, 1);
-    } else if (font_type(f) == virtual_font_type) {
-        formatted_error("font","invalid local fonts in lua-loaded font '%s' (4)", font_name(f));
-    } else {
-        l_fonts = xmalloc(3 * sizeof(int));
-        l_fonts[0] = 0;
-        l_fonts[1] = f;
-        l_fonts[2] = 0;
-    }
-    /*tex The characters. */
-    lua_key_rawgeti(characters);
-    if (lua_istable(L, -1)) {
-        /*tex Find the array size values; |num| has the amount. */
-        int num = 0;
-        int todo = 0;
-        int bc = font_bc(f);
-        int ec = font_ec(f);
-        /*tex First key: */
-        lua_pushnil(L);
-        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)) {
-                        todo++;
-                        if (! quick_char_exists(f,i)) {
-                            num++;
-                            if (i > ec)
-                                ec = i;
-                            if (bc < 0)
-                                bc = i;
-                            if (bc >= 0 && i < bc)
-                                bc = i;
-                        }
-                    }
-                }
-            }
-            lua_pop(L, 1);
-        }
-        if (todo > 0) {
-            font_malloc_charinfo(f, num);
-            set_font_bc(f, bc);
-            set_font_ec(f, ec);
-            /*tex First key: */
-            lua_pushnil(L);
-            while (lua_next(L, -2) != 0) {
-                lt = lua_type(L,-2);
-                if (lt == LUA_TNUMBER) {
-                    i = (int) lua_tointeger(L, -2);
-                    if (i >= 0) {
-                        if (quick_char_exists(f,i)) {
-                            charinfo *co = char_info(f, i);
-                            set_charinfo_name(co, NULL);
-                            set_charinfo_tounicode(co, NULL);
-                            set_charinfo_packets(co, NULL);
-                            set_charinfo_ligatures(co, NULL);
-                            set_charinfo_kerns(co, NULL);
-                            set_charinfo_vert_variants(co, NULL);
-                            set_charinfo_hor_variants(co, NULL);
-                        }
-                        font_char_from_lua(L, f, i, l_fonts, !no_math);
-                    }
-                }
-                lua_pop(L, 1);
-            }
-            lua_pop(L, 1);
-        }
-    }
-    if (l_fonts != NULL)
-        free(l_fonts);
-    return true;
-}
-
-/*tex Ligaturing starts here */
-
-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);
-        /*tex
-            Below might not be correct in contrived border case. but we use it
-            only for debugging.
-        */
-        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);
-        }
-        /*tex
-            Maybe 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). This has a very low priority, so low that it might
-            never happen.
-        */
-        /*tex 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 */
-        }
-        /*tex Right side: */
-        if (keep_right) {
-            halfword new_second = copy_node(fwd);
-            /*tex This is correct, because we {\em know} |lig_ptr| points to {\em 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);
-            /*tex This works because we {\em know} |lig_ptr| points to {\em one} node. */
-            couple_nodes(lig_ptr(newgl), fwd);
-            if (next != null) {
-                couple_nodes(newgl, next);
-            }
-        }
-        /*tex Check and return. */
-        *frst = cur;
-        return 1;
-    }
-    return 0;
-}
-
-/*tex
-
-    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|.
-
-*/
-
-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);
-        /*tex There is 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) {
-        /*tex A glyph followed by \unknown */
-        if (type(cur) == glyph_node) {
-            halfword fwd = vlink(cur);
-            if (fwd == null) {
-                /*tex The last character of a paragraph. */
-                if (right == null)
-                    break;
-                /*tex |par| prohibits the use of |couple_nodes| here. */
-                try_couple_nodes(cur, right);
-                right = null;
-                continue;
-            }
-            if (type(fwd) == glyph_node) {
-                /*tex a glyph followed by a glyph */
-                if (font(cur) != font(fwd))
-                    break;
-                if (try_ligature(&cur, fwd))
-                    continue;
-            } else if (type(fwd) == disc_node) {
-                /*tex a glyph followed by a disc */
-                halfword pre = vlink_pre_break(fwd);
-                halfword nob = vlink_no_break(fwd);
-                halfword next, tail;
-                liginfo lig;
-                /*tex Check on: |a{b?}{?}{?}| and |a+b=>B| : |{B?}{?}{a?}| */
-                /*tex 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))) {
-                    /*tex 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);
-                    /*tex Now ligature the |pre_break|. */
-                    nesting_prepend(pre_break(fwd), copy_node(cur));
-                    /*tex As we have removed cur, we need to start again. */
-                    cur = prev;
-                }
-                /*tex 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)) {
-                    /*tex Move |cur| from before |disc| to |no_break| part. */
-                    halfword prev = alink(cur);
-                    uncouple_node(cur);
-                    couple_nodes(prev, fwd);
-                    /*tex We {\em know} it's empty. */
-                    couple_nodes(no_break(fwd), cur);
-                    /*tex Now copy |cur| the |pre_break|. */
-                    nesting_prepend(pre_break(fwd), copy_node(cur));
-                    /*tex Move next from after disc to |no_break| part. */
-                    tail = vlink(next);
-                    uncouple_node(next);
-                    try_couple_nodes(fwd, tail);
-                    /*tex We {\em know} this works. */
-                    couple_nodes(cur, next);
-                    /*tex Make sure the list is correct. */
-                    tlink(no_break(fwd)) = next;
-                    /*tex Now copy next to the |post_break|. */
-                    nesting_append(post_break(fwd), copy_node(next));
-                    /*tex As we have removed cur, we need to start again. */
-                    cur = prev;
-                }
-                /*tex 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) {
-                    /*tex Shame, didn't need it. */
-                    flush_node(right);
-                    /*tex No need to reset |right|, we're going to leave the loop anyway. */
-                }
-                break;
-            } else {
-                /*tex Is something unknown. */
-                if (right == null)
-                    break;
-                couple_nodes(cur, right);
-                couple_nodes(right, fwd);
-                right = null;
-                continue;
-            }
-            /*tex A discretionary followed by \unknown */
-        } else if (type(cur) == disc_node) {
-            /*tex 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)))) {
-                        /*tex Building an |init_disc| followed by a |select_disc|: |{a-}{b}{AB} {-}{}{} c| */
-  		        halfword last1 = vlink(next), tail;
-                        uncouple_node(next);
-                        try_couple_nodes(fwd, last1);
-                        /*tex |{a-}{b}{AB} {-}{c}{}| */
-                        nesting_append(post_break(fwd), copy_node(next));
-                        /*tex |{a-}{b}{AB} {-}{c}{-}| */
-                        if (vlink_no_break(cur) != null) {
-                            nesting_prepend(no_break(fwd), copy_node(vlink_pre_break(fwd)));
-                        }
-                        /*tex |{a-}{b}{AB} {b-}{c}{-}| */
-                        if (vlink_post_break(cur) != null)
-                            nesting_prepend_list(pre_break(fwd), copy_node_list(vlink_post_break(cur)));
-                        /*tex |{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)));
-                        }
-                        /*tex |{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);
-                        /*tex |{a-}{BC}{ABC} {b-}{c}{AB-}| */
-                        tail = tlink_post_break(cur);
-                        nesting_append(post_break(cur), next);
-                        handle_lig_nest(post_break(cur), tail);
-                        /*tex Set the subtypes: */
-                        subtype(cur) = init_disc;
-                        subtype(fwd) = select_disc;
-                    }
-                }
-            }
-
-        } else {
-            /*tex We have glyph nor disc. */
-            return cur;
-        }
-        /*tex Goto the next node, where |\par| allows |vlink(cur)| to be NULL. */
-        cur = vlink(cur);
-    }
-    return cur;
-}
-
-/*tex The return value is the new tail, head should be a dummy: */
-
-halfword handle_ligaturing(halfword head, halfword tail)
-{
-    /*tex A trick to allow explicit |node==null| tests. */
-    halfword save_tail1 = null;
-    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) {
-        prev = tail;
-    }
-    if (tail != null) {
-        try_couple_nodes(prev, save_tail1);
-    }
-    return prev;
-}
-
-
-/*tex Kerning starts here: */
-
-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);
-            /*tex 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);
-            /*tex 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));
-            }
-            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 = 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;
-}
-
-/*tex The ligaturing and kerning \LUA\ interface: */
-
-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_settop(Luas, top);
-        return tail;
-    }
-    nodelist_to_lua(Luas, head);
-    nodelist_to_lua(Luas, tail);
-    if ((i=lua_pcall(Luas, 2, 0, 0)) != 0) {
-        formatted_warning("ligkern","error: %s",lua_tostring(Luas, -1));
-        lua_settop(Luas, top);
-        luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        return tail;
-    }
-    if (fix_node_lists) {
-        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;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/font/mapfile.c
+++ /dev/null
@@ -1,770 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <math.h>
-#include <kpathsea/c-auto.h>
-#include <kpathsea/c-memstr.h>
-#include <string.h>
-
-#define FM_BUF_SIZE 1024
-
-static FILE *fm_file;
-
-static unsigned char *fm_buffer = NULL;
-static int fm_size = 0;
-static int fm_curbyte = 0;
-
-#define fm_open(a)        (fm_file = fopen((char *)(a), FOPEN_RBIN_MODE))
-#define fm_read_file()    readbinfile(fm_file,&fm_buffer,&fm_size)
-#define fm_close()        xfclose(fm_file, cur_file_name)
-#define fm_getchar()      fm_buffer[fm_curbyte++]
-#define fm_eof()          (fm_curbyte>fm_size)
-#define is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
-
-typedef enum { FM_DUPIGNORE, FM_REPLACE, FM_DELETE } updatemode;
-
-typedef struct mitem {
-    /*tex |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
-    updatemode mode;
-    /*tex map file or map line */
-    maptype type;
-    /*tex pointer to map file name or map line */
-    char *line;
-    /*tex line number in map file */
-    int lineno;
-} mapitem;
-
-mapitem *mitem = NULL;
-
-#define read_field(r, q, buf) do { \
-    q = buf;                       \
-    while (*r != ' ' && *r != '<' && *r != '"' && *r != '\0') \
-        *q++ = *r++;               \
-    *q = '\0';                     \
-    skip_char(r, ' ');             \
-} while (0)
-
-#define set_field(F) do {     \
-    if (q > buf)              \
-        fm->F = xstrdup(buf); \
-    if (*r == '\0')           \
-        goto done;            \
-} while (0)
-
-fm_entry *new_fm_entry(void)
-{
-    fm_entry *fm;
-    fm = xtalloc(1, fm_entry);
-    fm->tfm_name = NULL;
-    fm->ps_name = NULL;
-    fm->fd_flags = FD_FLAGS_NOT_SET_IN_MAPLINE;
-    fm->ff_name = NULL;
-    fm->encname = NULL;
-    fm->type = 0;
-    fm->slant = 0;
-    fm->extend = 1000;
-    unset_slantset(fm);
-    unset_extendset(fm);
-    unset_inuse(fm);
-    return fm;
-}
-
-void delete_fm_entry(fm_entry * fm)
-{
-    xfree(fm->tfm_name);
-    xfree(fm->ps_name);
-    xfree(fm->ff_name);
-    xfree(fm);
-}
-
-static ff_entry *new_ff_entry(void)
-{
-    ff_entry *ff;
-    ff = xtalloc(1, ff_entry);
-    ff->ff_name = NULL;
-    ff->ff_path = NULL;
-    return ff;
-}
-
-static void delete_ff_entry(ff_entry * ff)
-{
-    xfree(ff->ff_name);
-    xfree(ff->ff_path);
-    xfree(ff);
-}
-
-static struct avl_table *tfm_tree = NULL;
-static struct avl_table *ff_tree = NULL;
-static struct avl_table *encname_tree = NULL;
-
-/*tex
-
-    We sort |fm_entry| into |tfm_tree| by |tfm_name|:
-
-*/
-
-static int comp_fm_entry_tfm(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return strcmp(((const fm_entry *) pa)->tfm_name, ((const fm_entry *) pb)->tfm_name);
-}
-
-/* We sort |ff_entry| into |ff_tree| by |ff_name|: */
-
-static int comp_ff_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return strcmp(((const ff_entry *) pa)->ff_name, ((const ff_entry *) pb)->ff_name);
-}
-
-static void create_avl_trees(void)
-{
-    tfm_tree = avl_create(comp_fm_entry_tfm, NULL, &avl_xallocator);
-    ff_tree = avl_create(comp_ff_entry, NULL, &avl_xallocator);
-    encname_tree = avl_create(comp_string_entry, NULL, &avl_xallocator);
-}
-
-int avl_do_entry(fm_entry * fm, int mode)
-{
-    fm_entry *p;
-    void *a;
-    void **aa;
-    int delete_new = 0;
-    if (tfm_tree == NULL)
-        create_avl_trees();
-    p = (fm_entry *) avl_find(tfm_tree, fm);
-    if (p != NULL) {
-        switch (mode) {
-            case FM_DUPIGNORE:
-                formatted_warning("map file", "entry for '%s' already exists, duplicates ignored", fm->tfm_name);
-                delete_new = 1;
-                break;
-            case FM_REPLACE:
-            case FM_DELETE:
-                if (is_inuse(p)) {
-                    formatted_warning("map file", "entry for '%s' has been used, replace/delete not allowed", fm->tfm_name);
-                    delete_new = 1;
-                } else {
-                    a = avl_delete(tfm_tree, p);
-                    assert(a != NULL);
-                    delete_fm_entry(p);
-                }
-                break;
-            default:
-                formatted_error("map file", "something bad happened",0);
-        }
-    }
-    if ((mode == FM_DUPIGNORE || mode == FM_REPLACE) && delete_new == 0) {
-        aa = avl_probe(tfm_tree, fm);
-        if (aa == NULL) {
-            /*tex Is this a problem? */
-        }
-    } else
-        delete_new = 1;
-    return delete_new;
-}
-
-/*tex
-
-    Add the encoding name to an AVL tree. This has nothing to do with |writeenc.c|.
-
-*/
-
-static char *add_encname(char *s)
-{
-    char *p;
-    void **aa;
-    if ((p = (char *) avl_find(encname_tree, s)) == NULL) {
-        /*tex The encoding name has not yet been registered. */
-        p = xstrdup(s);
-        aa = avl_probe(encname_tree, p);
-        if (aa == NULL) {
-            /*tex Is this a problem? */
-        }
-    }
-    return p;
-}
-
-/*tex
-
-    A consistency check for map entry, with warn flag.
-
-*/
-
-static int check_fm_entry(fm_entry * fm, boolean warn)
-{
-    int a = 0;
-    if (is_fontfile(fm) && !is_included(fm)) {
-        if (warn)
-            formatted_warning("map file",
-                "ambiguous entry for '%s': font file present but not included, "
-                "will be treated as font file not present", fm->tfm_name);
-        xfree(fm->ff_name);
-        /*tex Do not set variable |a| as this entry will be still accepted. */
-    }
-    /*tex If both ps_name and font file are missing, drop this entry. */
-    if (fm->ps_name == NULL && !is_fontfile(fm)) {
-        if (warn)
-            formatted_warning("map file", "invalid entry for '%s': both ps_name and font file missing", fm->tfm_name);
-        a += 1;
-    }
-    /*tex \TRUETYPE\ fonts cannot be reencoded without subsetting. */
-    if (is_truetype(fm) && is_reencoded(fm) && !is_subsetted(fm)) {
-        if (warn)
-            formatted_warning("map file", "invalid entry for '%s': only subsetted TrueType font can be reencoded", fm->tfm_name);
-        a += 2;
-    }
-    /*tex The value of |SlantFont| and |ExtendFont| must be reasonable. */
-    if (fm->slant < FONT_SLANT_MIN || fm->slant > FONT_SLANT_MAX) {
-        if (warn)
-            formatted_warning("map file", "invalid entry for '%s': value '%g' is to large for SlantFont",
-                 fm->tfm_name, fm->slant / 1000.0);
-        a += 8;
-    }
-    if (fm->extend < FONT_EXTEND_MIN || fm->extend > FONT_EXTEND_MAX) {
-        if (warn)
-            formatted_warning("map file", "invalid entry for '%s': value '%g' is too large for ExtendFont",
-                 fm->tfm_name, fm->extend / 1000.0);
-        a += 16;
-    }
-    return a;
-}
-
-/*tex
-
-    Returns the font number if s is one of the 14 std. font names, -1 otherwise.
-    A bit speed trimmed. Using these base fonts is oldfashioned and doesn't
-    happen in a decent \LUATEX\ produced file.
-
-*/
-
-int check_std_t1font(char *s)
-{
-    static const char *std_t1font_names[] = {
-        "Courier",               /* 0:7 */
-        "Courier-Bold",          /* 1:12 */
-        "Courier-Oblique",       /* 2:15 */
-        "Courier-BoldOblique",   /* 3:19 */
-        "Helvetica",             /* 4:9 */
-        "Helvetica-Bold",        /* 5:14 */
-        "Helvetica-Oblique",     /* 6:17 */
-        "Helvetica-BoldOblique", /* 7:21 */
-        "Symbol",                /* 8:6 */
-        "Times-Roman",           /* 9:11 */
-        "Times-Bold",            /* 10:10 */
-        "Times-Italic",          /* 11:12 */
-        "Times-BoldItalic",      /* 12:16 */
-        "ZapfDingbats"           /* 13:12 */
-    };
-    static const int index[] = {
-        -1, -1, -1, -1, -1, -1, 8,  0, -1,  4, 10,
-         9, -1, -1,  5,  2, 12, 6, -1,  3, -1,  7
-    };
-    size_t n;
-    int k = -1;
-    n = strlen(s);
-    if (n > 21)
-        return -1;
-    if (n == 12) {
-        /*tex three names have length 12 */
-        switch (*s) {
-            case 'C':
-                /*tex Courier-Bold */
-                k = 1;
-                break;
-            case 'T':
-                /*tex Times-Italic */
-                k = 11;
-                break;
-            case 'Z':
-                /*tex ZapfDingbats */
-                k = 13;
-                break;
-            default:
-                return -1;
-        }
-    } else
-        k = index[n];
-    if (k > -1 && !strcmp(std_t1font_names[k], s))
-        return k;
-    return -1;
-}
-
-static void fm_scan_line(void)
-{
-    int a, b, c, j, u = 0, v = 0;
-    char cc;
-    float d;
-    fm_entry *fm;
-    char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
-    char *p, *q, *s;
-    char *r = NULL;
-    switch (mitem->type) {
-        case MAPFILE:
-            p = fm_line;
-            while (!fm_eof()) {
-                if (fm_curbyte == fm_size) {
-                    fm_curbyte++;
-                    cc = 10;
-                } else {
-                    cc = (char) fm_getchar();
-                }
-                append_char_to_buf(cc, p, fm_line, FM_BUF_SIZE);
-                if (cc == 10)
-                    break;
-            }
-            *(--p) = '\0';
-            r = fm_line;
-            break;
-        case MAPLINE:
-            /*tex Work on a string from |makecstring|. */
-            r = mitem->line;
-            break;
-        default:
-            assert(0);
-    }
-    if (*r == '\0' || is_cfg_comment(*r))
-        return;
-    fm = new_fm_entry();
-    read_field(r, q, buf);
-    set_field(tfm_name);
-    if (!isdigit((unsigned char)*r)) {
-        /*tex The 2nd field |ps_name| may not start with a digit. */
-        read_field(r, q, buf);
-        set_field(ps_name);
-    }
-    if (isdigit((unsigned char)*r)) {
-        /*tex Is the font descriptor |/Flags| given? */
-        for (s = r; isdigit((unsigned char)*s); s++);
-        if (*s == ' ' || *s == '"' || *s == '<' || *s == '\0') {
-            /*tex not e.g.\ |8r.enc| */
-            fm->fd_flags = atoi(r);
-            while (isdigit((unsigned char)*r))
-                r++;
-        }
-    }
-    /*tex Loop through specials, encoding, font file:*/
-    while (1) {
-        skip_char(r, ' ');
-        switch (*r) {
-            case '\0':
-                goto done;
-            case '"':
-                /*tex The pening quote. */
-                r++;
-                u = v = 0;
-                do {
-                    skip_char(r, ' ');
-                    if (sscanf(r, "%f %n", &d, &j) > 0) {
-                        /*tex Jump behind number, eat also blanks, if any. */
-                        s = r + j;
-                        if (*(s - 1) == 'E' || *(s - 1) == 'e') {
-                            /*tex  e.g.\ |0.5ExtendFont|: |%f = 0.5E| */
-                            s--;
-                        }
-                        if (str_prefix(s, "SlantFont")) {
-                            /*tex Correct rounding also for negative numbers. */
-                            d *= (float) 1000.0;
-                            fm->slant = (int) (d > 0 ? d + 0.5 : d - 0.5);
-                            set_slantset(fm);
-                            r = s + strlen("SlantFont");
-                        } else if (str_prefix(s, "ExtendFont")) {
-                            d *= (float) 1000.0;
-                            fm->extend = (int) (d > 0 ? d + 0.5 : d - 0.5);
-                            set_extendset(fm);
-                            r = s + strlen("ExtendFont");
-                        } else {
-                            /*tex unknown name, jump over it */
-                            for (r = s; *r != ' ' && *r != '"' && *r != '\0'; r++);
-                            c = *r;
-                            *r = '\0';
-                            formatted_warning("map file", "invalid entry for '%s': unknown name '%s' ignored", fm->tfm_name, s);
-                            *r = (char) c;
-                        }
-                    } else
-                        for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
-                }
-                while (*r == ' ');
-                if (*r == '"') {
-                    /*tex The closing quote. */
-                    r++;
-                } else {
-                    formatted_warning("map file", "invalid entry for '%s': closing quote missing", fm->tfm_name);
-                    goto bad_line;
-                }
-                break;
-            case 'P':
-                /*tex Handle cases for sub fonts like |PidEid=3,1| */
-                formatted_warning("map file", "invalid entry for '%s': subfonts are not supported", fm->tfm_name);
-                goto bad_line;
-                break;
-            default:
-                /*tex Encoding or font file specification. */
-                a = b = 0;
-                if (*r == '<') {
-                    a = *r++;
-                    if (*r == '<' || *r == '[')
-                        b = *r++;
-                }
-                read_field(r, q, buf);
-                /*tex Encoding, Formats: |8r.enc| or |<8r.enc| or |<[8r.enc| */
-                if (strlen(buf) > 4 && strcasecmp(strend(buf) - 4, ".enc") == 0) {
-                    /*tex |u|, |v| used if intervening blank: |<< foo| */
-                    fm->encname = add_encname(buf);
-                    u = v = 0;
-                } else if (strlen(buf) > 0) {
-                    /*tex
-
-                        We get the file name given where possible formats are:
-
-                        \starttabulate[|||]
-                        \NC subsetting    \NC \tpe {<cmr10.pfa}  \NC \NR
-                        \NC no subsetting \NC \tpe {<<cmr10.pfa} \NC \NR
-                        \NC no embedding  \NC \tpe {cmr10.pfa}   \NC \NR
-                        \stoptabulate
-
-                    */
-                    if (a == '<' || u == '<') {
-                        set_included(fm);
-                        if ((a == '<' && b == 0) || (a == 0 && v == 0)) {
-                            set_subsetted(fm);
-                        } else {
-                            /*tex
-
-                                Otherwise |b| equals |<| or |[| which means that we have no subsetting.
-                            */
-                        }
-                    }
-                    set_field(ff_name);
-                    u = v = 0;
-                } else {
-                    u = a;
-                    v = b;
-                }
-        }
-    }
-  done:
-    if (fm->ps_name != NULL && (check_std_t1font(fm->ps_name) >= 0))
-        set_std_t1font(fm);
-    if (is_fontfile(fm) && strlen(fm_fontfile(fm)) > 3) {
-        if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttf") == 0)
-            set_truetype(fm);
-        else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttc") == 0)
-            set_truetype(fm);
-        else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".otf") == 0)
-            set_opentype(fm);
-        else
-            set_type1(fm);
-    } else {
-        /*tex Assume a builtin font is \TYPEONE\: */
-        set_type1(fm);
-    }
-    if (check_fm_entry(fm, true) != 0)
-        goto bad_line;
-    /*tex
-
-       Until here the map line has been completely scanned without errors; |fm|
-       points to a valid, freshly filled-out |fm_entry| structure. Now follows
-       the actual work of registering or deleting.
-
-    */
-    if (avl_do_entry(fm, mitem->mode) == 0)
-        return;
-  bad_line:
-    delete_fm_entry(fm);
-}
-
-static void fm_read_info(void)
-{
-    int callback_id;
-    int file_opened = 0;
-
-    if (tfm_tree == NULL)
-        create_avl_trees();
-    if (mitem->line == NULL) {
-        /*tex There is nothing to do. */
-        return;
-    }
-    mitem->lineno = 1;
-    switch (mitem->type) {
-    case MAPFILE:
-        xfree(fm_buffer);
-        fm_curbyte = 0;
-        fm_size = 0;
-        cur_file_name = luatex_find_file(mitem->line, find_map_file_callback);
-        if (cur_file_name) {
-            callback_id = callback_defined(read_map_file_callback);
-            if (callback_id > 0) {
-                if (run_callback(callback_id, "S->bSd", cur_file_name,
-                                 &file_opened, &fm_buffer, &fm_size)) {
-                    if (file_opened) {
-                        if (fm_size > 0) {
-                            report_start_file(filetype_map,cur_file_name);
-                            while (!fm_eof()) {
-                                fm_scan_line();
-                                mitem->lineno++;
-                            }
-                            report_stop_file(filetype_map);
-                            fm_file = NULL;
-                        }
-                    } else {
-                        formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
-                    }
-                } else {
-                    formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
-                }
-            } else {
-                if (!fm_open(cur_file_name)) {
-                    formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
-                } else {
-                    fm_read_file();
-                    report_start_file(filetype_map,cur_file_name);
-                    while (!fm_eof()) {
-                        fm_scan_line();
-                        mitem->lineno++;
-                    }
-                    fm_close();
-                    report_stop_file(filetype_map);
-                    fm_file = NULL;
-                }
-            }
-            cur_file_name = NULL;
-        }
-        break;
-    case MAPLINE:
-        cur_file_name = NULL;
-        fm_scan_line();
-        break;
-    default:
-        assert(0);
-    }
-    /*tex Done with this line: */
-    mitem->line = NULL;
-    cur_file_name = NULL;
-    return;
-}
-
-fm_entry *getfontmap(char *tfm_name)
-{
-    fm_entry *fm;
-    fm_entry tmp;
-    if (tfm_name == NULL) {
-        /*tex Wide \LUA\ loaded fonts may not have a name. */
-        return NULL;
-    }
-    if (tfm_tree == NULL) {
-        /*tex Only read the default map file. */
-        fm_read_info();
-    }
-    /*tex Look up the name. */
-    tmp.tfm_name = tfm_name;
-    fm = (fm_entry *) avl_find(tfm_tree, &tmp);
-    if (fm == NULL)
-        return NULL;
-    set_inuse(fm);
-    return fm;
-}
-
-/*tex
-
-    Process map file given by its name or map line contents. Items not beginning
-    with [+-=] flush default map file, if it has not yet been read. Leading
-    blanks and blanks immediately following [+-=] are ignored.
-
-*/
-
-void process_map_item(char *s, int type)
-{
-    char *p;
-    int mode;
-    if (*s == ' ') {
-        /*tex Ignore leading blanks: */
-        s++;
-    }
-    switch (*s) {
-        case '+':
-            /* +mapfile.map, +mapline: insert an entry, if it is not duplicate */
-            mode = FM_DUPIGNORE;
-            s++;
-            break;
-        case '=':
-            /* =mapfile.map, =mapline: try to replace an existing entry */
-            mode = FM_REPLACE;
-            s++;
-            break;
-        case '-':
-            /* -mapfile.map, -mapline: try to delete an entry */
-            mode = FM_DELETE;
-            s++;
-            break;
-        default:
-            /* like +, but also: flush the default map file name */
-            mode = FM_DUPIGNORE;
-            mitem->line = NULL;
-    }
-    if (*s == ' ') {
-        /*tex Ignore a blank after |[+-=]| */
-        s++;
-    }
-    /*tex The map item starts here. */
-    p = s;
-    switch (type) {
-        case MAPFILE:
-            /*tex Remove blank at the end. */
-            while (*p != '\0' && *p != ' ')
-                p++;
-            *p = '\0';
-            break;
-        case MAPLINE:
-            /*tex A blank at end is allowed. */
-            break;
-        default:
-            assert(0);
-    }
-    if (mitem->line != NULL) {
-        /*tex Read default map file first */
-        fm_read_info();
-    }
-    if (*s != '\0') {
-        /*tex Only if real item to process. */
-        mitem->mode = mode;
-        mitem->type = type;
-        mitem->line = s;
-        fm_read_info();
-    }
-}
-
-void pdfmapfile(int t)
-{
-    char *s = tokenlist_to_cstring(t, true, NULL);
-    process_map_item(s, MAPFILE);
-    free(s);
-}
-
-void pdfmapline(int t)
-{
-    char *s = tokenlist_to_cstring(t, true, NULL);
-    process_map_item(s, MAPLINE);
-    free(s);
-}
-
-void pdf_init_map_file(const char *map_name)
-{
-    assert(mitem == NULL);
-    mitem = xtalloc(1, mapitem);
-    mitem->mode = FM_DUPIGNORE;
-    mitem->type = MAPFILE;
-    mitem->line = xstrdup(map_name);
-}
-
-/*tex
-
-    An early check whether a font file exists. Search tree |ff_tree| is used in
-    1st instance, as it may be faster than the |kpse_find_file|, and
-    |kpse_find_file| is called only once per font file name plus expansion
-    parameter. This might help keeping speed, if many \PDF\ pages with same fonts
-    are to be embedded (not that we deal with that fragile approach any longer in
-    \LUATEX).
-
-    The |ff_tree| contains only font files, which are actually needed, so this tree
-    typically is much smaller than the tfm_tree.
-
-*/
-
-ff_entry *check_ff_exist(char *ff_name, boolean is_tt)
-{
-    ff_entry *ff;
-    ff_entry tmp;
-    void **aa;
-    int callback_id;
-    char *filepath = NULL;
-    tmp.ff_name = ff_name;
-    ff = (ff_entry *) avl_find(ff_tree, &tmp);
-    if (ff == NULL) {
-        /*tex The name is not yet in the database. */
-        ff = new_ff_entry();
-        ff->ff_name = xstrdup(ff_name);
-        if (is_tt) {
-            callback_id = callback_defined(find_truetype_file_callback);
-            if (callback_id > 0) {
-                run_callback(callback_id, "S->S", ff_name, &filepath);
-                if (filepath && strlen(filepath) == 0)
-                    filepath = NULL;
-                ff->ff_path = filepath;
-            } else {
-                ff->ff_path = kpse_find_file(ff_name, kpse_truetype_format, 0);
-            }
-        } else {
-            callback_id = callback_defined(find_type1_file_callback);
-            if (callback_id > 0) {
-                run_callback(callback_id, "S->S", ff_name, &filepath);
-                if (filepath && strlen(filepath) == 0)
-                    filepath = NULL;
-                ff->ff_path = filepath;
-            } else {
-                ff->ff_path = kpse_find_file(ff_name, kpse_type1_format, 0);
-            }
-        }
-        aa = avl_probe(ff_tree, ff);
-        if (aa == NULL) {
-            /*tex Is this a problem? */
-        }
-    }
-    return ff;
-}
-
-int is_subsetable(fm_entry * fm)
-{
-    assert(is_included(fm));
-    return is_subsetted(fm);
-}
-
-/*tex Cleaning up: */
-
-static void destroy_fm_entry_tfm(void *pa, void *pb)
-{
-    fm_entry *fm;
-    (void) pb;
-    fm = (fm_entry *) pa;
-    delete_fm_entry(fm);
-}
-
-static void destroy_ff_entry(void *pa, void *pb)
-{
-    ff_entry *ff;
-    (void) pb;
-    ff = (ff_entry *) pa;
-    delete_ff_entry(ff);
-}
-
-void fm_free(void)
-{
-    if (tfm_tree != NULL) {
-        avl_destroy(tfm_tree, destroy_fm_entry_tfm);
-        tfm_tree = NULL;
-    }
-    if (ff_tree != NULL) {
-        avl_destroy(ff_tree, destroy_ff_entry);
-        ff_tree = NULL;
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/mapfile.w
@@ -0,0 +1,715 @@
+% mapfile.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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 <math.h>
+#include <kpathsea/c-auto.h>
+#include <kpathsea/c-memstr.h>
+#include <string.h>
+
+#define FM_BUF_SIZE     1024
+
+static FILE *fm_file;
+
+static unsigned char *fm_buffer = NULL;
+static int fm_size = 0;
+static int fm_curbyte = 0;
+
+#define fm_open(a)      (fm_file = fopen((char *)(a), FOPEN_RBIN_MODE))
+#define fm_read_file()  readbinfile(fm_file,&fm_buffer,&fm_size)
+#define fm_close()      xfclose(fm_file, cur_file_name)
+#define fm_getchar()    fm_buffer[fm_curbyte++]
+#define fm_eof()        (fm_curbyte>fm_size)
+#define is_cfg_comment(c) \
+    (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
+
+typedef enum { FM_DUPIGNORE, FM_REPLACE, FM_DELETE } updatemode;
+
+typedef struct mitem {
+    updatemode mode;            /* FM_DUPIGNORE or FM_REPLACE or FM_DELETE */
+    maptype type;               /* map file or map line */
+    char *line;                 /* pointer to map file name or map line */
+    int lineno;                 /* line number in map file */
+} mapitem;
+mapitem *mitem = NULL;
+
+#define read_field(r, q, buf) do {  \
+    q = buf;                        \
+    while (*r != ' ' && *r != '<' && *r != '"' && *r != '\0') \
+        *q++ = *r++;                \
+    *q = '\0';                      \
+    skip_char(r, ' ');                  \
+} while (0)
+
+#define set_field(F) do {           \
+    if (q > buf)                    \
+        fm->F = xstrdup(buf);       \
+    if (*r == '\0')                 \
+        goto done;                  \
+} while (0)
+
+fm_entry *new_fm_entry(void)
+{
+    fm_entry *fm;
+    fm = xtalloc(1, fm_entry);
+    fm->tfm_name = NULL;
+    fm->ps_name = NULL;
+    fm->fd_flags = FD_FLAGS_NOT_SET_IN_MAPLINE;
+    fm->ff_name = NULL;
+    fm->encname = NULL;
+    fm->type = 0;
+    fm->slant = 0;
+    fm->extend = 1000;
+    unset_slantset(fm);
+    unset_extendset(fm);
+    unset_inuse(fm);
+    return fm;
+}
+
+void delete_fm_entry(fm_entry * fm)
+{
+    xfree(fm->tfm_name);
+    xfree(fm->ps_name);
+    xfree(fm->ff_name);
+    xfree(fm);
+}
+
+static ff_entry *new_ff_entry(void)
+{
+    ff_entry *ff;
+    ff = xtalloc(1, ff_entry);
+    ff->ff_name = NULL;
+    ff->ff_path = NULL;
+    return ff;
+}
+
+static void delete_ff_entry(ff_entry * ff)
+{
+    xfree(ff->ff_name);
+    xfree(ff->ff_path);
+    xfree(ff);
+}
+
+/**********************************************************************/
+
+static struct avl_table *tfm_tree = NULL;
+static struct avl_table *ff_tree = NULL;
+static struct avl_table *encname_tree = NULL;
+
+/* AVL sort fm_entry into tfm_tree by tfm_name */
+
+static int comp_fm_entry_tfm(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return strcmp(((const fm_entry *) pa)->tfm_name,
+                  ((const fm_entry *) pb)->tfm_name);
+}
+
+/* AVL sort ff_entry into ff_tree by ff_name */
+
+static int comp_ff_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return strcmp(((const ff_entry *) pa)->ff_name,
+                  ((const ff_entry *) pb)->ff_name);
+}
+
+static void create_avl_trees(void)
+{
+    assert(tfm_tree == NULL);
+    tfm_tree = avl_create(comp_fm_entry_tfm, NULL, &avl_xallocator);
+    assert(tfm_tree != NULL);
+    assert(ff_tree == NULL);
+    ff_tree = avl_create(comp_ff_entry, NULL, &avl_xallocator);
+    assert(ff_tree != NULL);
+    assert(encname_tree == NULL);
+    encname_tree = avl_create(comp_string_entry, NULL, &avl_xallocator);
+    assert(encname_tree != NULL);
+}
+
+int avl_do_entry(fm_entry * fm, int mode)
+{
+    fm_entry *p;
+    void *a;
+    void **aa;
+    int delete_new = 0;
+    if (tfm_tree == NULL)
+        create_avl_trees();
+    p = (fm_entry *) avl_find(tfm_tree, fm);
+    if (p != NULL) {
+        switch (mode) {
+        case FM_DUPIGNORE:
+            formatted_warning("map file", "entry for '%s' already exists, duplicates ignored", fm->tfm_name);
+            delete_new = 1;
+            break;
+        case FM_REPLACE:
+        case FM_DELETE:
+            if (is_inuse(p)) {
+                formatted_warning("map file", "entry for '%s' has been used, replace/delete not allowed", fm->tfm_name);
+                delete_new = 1;
+            } else {
+                a = avl_delete(tfm_tree, p);
+                assert(a != NULL);
+                delete_fm_entry(p);
+            }
+            break;
+        default:
+            assert(0);
+        }
+    }
+    if ((mode == FM_DUPIGNORE || mode == FM_REPLACE) && delete_new == 0) {
+        aa = avl_probe(tfm_tree, fm);
+        assert(aa != NULL);
+    } else
+        delete_new = 1;
+    return delete_new;
+}
+
+/* add the encoding name to an AVL tree. this has nothing to do with writeenc.c */
+
+static char *add_encname(char *s)
+{
+    char *p;
+    void **aa;
+    assert(s != NULL);
+    assert(encname_tree != NULL);
+    if ((p = (char *) avl_find(encname_tree, s)) == NULL) {     /* encoding name not yet registered */
+        p = xstrdup(s);
+        aa = avl_probe(encname_tree, p);
+        assert(aa != NULL);
+    }
+    return p;
+}
+
+/**********************************************************************/
+/* consistency check for map entry, with warn flag */
+
+static int check_fm_entry(fm_entry * fm, boolean warn)
+{
+    int a = 0;
+    assert(fm != NULL);
+
+    if (is_fontfile(fm) && !is_included(fm)) {
+        if (warn)
+            formatted_warning("map file",
+                 "ambiguous entry for '%s': font file present but not included, "
+                 "will be treated as font file not present", fm->tfm_name);
+        xfree(fm->ff_name);
+        /* do not set variable |a| as this entry will be still accepted */
+    }
+
+    /* if both ps_name and font file are missing, drop this entry */
+    if (fm->ps_name == NULL && !is_fontfile(fm)) {
+        if (warn)
+            formatted_warning("map file", "invalid entry for '%s': both ps_name and font file missing", fm->tfm_name);
+        a += 1;
+    }
+
+    /* TrueType fonts cannot be reencoded without subsetting */
+    if (is_truetype(fm) && is_reencoded(fm) && !is_subsetted(fm)) {
+        if (warn)
+            formatted_warning("map file", "invalid entry for '%s': only subsetted TrueType font can be reencoded", fm->tfm_name);
+        a += 2;
+    }
+
+    /* the value of SlantFont and ExtendFont must be reasonable */
+    if (fm->slant < FONT_SLANT_MIN || fm->slant > FONT_SLANT_MAX) {
+        if (warn)
+            formatted_warning("map file", "invalid entry for '%s': value '%g' is to large for SlantFont",
+                 fm->tfm_name, fm->slant / 1000.0);
+        a += 8;
+    }
+    if (fm->extend < FONT_EXTEND_MIN || fm->extend > FONT_EXTEND_MAX) {
+        if (warn)
+            formatted_warning("map file", "invalid entry for '%s': value '%g' is too large for ExtendFont",
+                 fm->tfm_name, fm->extend / 1000.0);
+        a += 16;
+    }
+
+    return a;
+}
+
+/**********************************************************************/
+/* returns the font number if s is one of the 14 std. font names, -1 otherwise; speed-trimmed. */
+
+int check_std_t1font(char *s)
+{
+    static const char *std_t1font_names[] = {
+        "Courier",              /* 0:7 */
+        "Courier-Bold",         /* 1:12 */
+        "Courier-Oblique",      /* 2:15 */
+        "Courier-BoldOblique",  /* 3:19 */
+        "Helvetica",            /* 4:9 */
+        "Helvetica-Bold",       /* 5:14 */
+        "Helvetica-Oblique",    /* 6:17 */
+        "Helvetica-BoldOblique",        /* 7:21 */
+        "Symbol",               /* 8:6 */
+        "Times-Roman",          /* 9:11 */
+        "Times-Bold",           /* 10:10 */
+        "Times-Italic",         /* 11:12 */
+        "Times-BoldItalic",     /* 12:16 */
+        "ZapfDingbats"          /* 13:12 */
+    };
+    static const int index[] =
+        { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6, -1,
+        3, -1, 7
+    };
+    size_t n;
+    int k = -1;
+    assert(s != NULL);
+    n = strlen(s);
+    if (n > 21)
+        return -1;
+    if (n == 12) {              /* three names have length 12 */
+        switch (*s) {
+        case 'C':
+            k = 1;              /* Courier-Bold */
+            break;
+        case 'T':
+            k = 11;             /* Times-Italic */
+            break;
+        case 'Z':
+            k = 13;             /* ZapfDingbats */
+            break;
+        default:
+            return -1;
+        }
+    } else
+        k = index[n];
+    if (k > -1 && !strcmp(std_t1font_names[k], s))
+        return k;
+    return -1;
+}
+
+/**********************************************************************/
+
+static void fm_scan_line(void)
+{
+    int a, b, c, j, u = 0, v = 0;
+    char cc;
+    float d;
+    fm_entry *fm;
+    char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
+    char *p, *q, *s;
+    char *r = NULL;
+    switch (mitem->type) {
+    case MAPFILE:
+        p = fm_line;
+        while (!fm_eof()) {
+            if (fm_curbyte == fm_size) {
+                fm_curbyte++;
+                cc = 10;
+            } else {
+                cc = (char) fm_getchar();
+            }
+            append_char_to_buf(cc, p, fm_line, FM_BUF_SIZE);
+            if (cc == 10)
+                break;
+        }
+        *(--p) = '\0';
+        r = fm_line;
+        break;
+    case MAPLINE:
+        r = mitem->line;        /* work on string from makecstring() */
+        break;
+    default:
+        assert(0);
+    }
+    if (*r == '\0' || is_cfg_comment(*r))
+        return;
+    fm = new_fm_entry();
+    read_field(r, q, buf);
+    set_field(tfm_name);
+    if (!isdigit((unsigned char)*r)) {         /* 2nd field ps_name may not start with a digit */
+        read_field(r, q, buf);
+        set_field(ps_name);
+    }
+    if (isdigit((unsigned char)*r)) {          /* font descriptor /Flags given? */
+        for (s = r; isdigit((unsigned char)*s); s++);
+        if (*s == ' ' || *s == '"' || *s == '<' || *s == '\0') {        /* not e. g. 8r.enc */
+            fm->fd_flags = atoi(r);
+            while (isdigit((unsigned char)*r))
+                r++;
+        }
+    }
+    while (1) {                 /* loop through "specials", encoding, font file */
+        skip_char(r, ' ');
+        switch (*r) {
+        case '\0':
+            goto done;
+        case '"':              /* opening quote */
+            r++;
+            u = v = 0;
+            do {
+                skip_char(r, ' ');
+                if (sscanf(r, "%f %n", &d, &j) > 0) {
+                    s = r + j;  /* jump behind number, eat also blanks, if any */
+                    if (*(s - 1) == 'E' || *(s - 1) == 'e')
+                        s--;    /* e. g. 0.5ExtendFont: %f = 0.5E */
+                    if (str_prefix(s, "SlantFont")) {
+                        d *= (float) 1000.0;    /* correct rounding also for neg. numbers */
+                        fm->slant = (int) (d > 0 ? d + 0.5 : d - 0.5);
+                        set_slantset(fm);
+                        r = s + strlen("SlantFont");
+                    } else if (str_prefix(s, "ExtendFont")) {
+                        d *= (float) 1000.0;
+                        fm->extend = (int) (d > 0 ? d + 0.5 : d - 0.5);
+                        set_extendset(fm);
+                        r = s + strlen("ExtendFont");
+                    } else {    /* unknown name */
+                        for (r = s; *r != ' ' && *r != '"' && *r != '\0'; r++); /* jump over name */
+                        c = *r; /* remember char for temporary end of string */
+                        *r = '\0';
+                        formatted_warning("map file", "invalid entry for '%s': unknown name '%s' ignored", fm->tfm_name, s);
+                        *r = (char) c;
+                    }
+                } else
+                    for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
+            }
+            while (*r == ' ');
+            if (*r == '"')      /* closing quote */
+                r++;
+            else {
+                formatted_warning("map file", "invalid entry for '%s': closing quote missing", fm->tfm_name);
+                goto bad_line;
+            }
+            break;
+        case 'P':              /* handle cases for sub fonts like 'PidEid=3,1' */
+            formatted_warning("map file", "invalid entry for '%s': subfonts are not supported", fm->tfm_name);
+            goto bad_line;
+            break;
+        default:               /* encoding or font file specification */
+            a = b = 0;
+            if (*r == '<') {
+                a = *r++;
+                if (*r == '<' || *r == '[')
+                    b = *r++;
+            }
+            read_field(r, q, buf);
+            /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
+            if (strlen(buf) > 4 && strcasecmp(strend(buf) - 4, ".enc") == 0) {
+                fm->encname = add_encname(buf);
+                u = v = 0;      /* u, v used if intervening blank: "<< foo" */
+            } else if (strlen(buf) > 0) {       /* file name given */
+                /* font file, formats:
+                 * subsetting:    '<cmr10.pfa'
+                 * no subsetting: '<<cmr10.pfa'
+                 * no embedding:  'cmr10.pfa'
+                 */
+                if (a == '<' || u == '<') {
+                    set_included(fm);
+                    if ((a == '<' && b == 0) || (a == 0 && v == 0))
+                        set_subsetted(fm);
+                    /* otherwise b == '<' (or '[') => no subsetting */
+                }
+                set_field(ff_name);
+                u = v = 0;
+            } else {
+                u = a;
+                v = b;
+            }
+        }
+    }
+  done:
+    if (fm->ps_name != NULL && (check_std_t1font(fm->ps_name) >= 0))
+        set_std_t1font(fm);
+    if (is_fontfile(fm) && strlen(fm_fontfile(fm)) > 3) {
+        if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttf") == 0)
+            set_truetype(fm);
+        else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttc") == 0)
+            set_truetype(fm);
+        else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".otf") == 0)
+            set_opentype(fm);
+        else
+            set_type1(fm);
+    } else
+        set_type1(fm);          /* assume a builtin font is Type1 */
+    if (check_fm_entry(fm, true) != 0)
+        goto bad_line;
+    /*
+       Until here the map line has been completely scanned without errors;
+       fm points to a valid, freshly filled-out fm_entry structure.
+       Now follows the actual work of registering/deleting.
+     */
+    if (avl_do_entry(fm, mitem->mode) == 0)
+        return;
+  bad_line:
+    delete_fm_entry(fm);
+}
+
+/**********************************************************************/
+
+static void fm_read_info(void)
+{
+    int callback_id;
+    int file_opened = 0;
+
+    if (tfm_tree == NULL)
+        create_avl_trees();
+    if (mitem->line == NULL)    /* nothing to do */
+        return;
+    mitem->lineno = 1;
+    switch (mitem->type) {
+    case MAPFILE:
+        xfree(fm_buffer);
+        fm_curbyte = 0;
+        fm_size = 0;
+        cur_file_name = luatex_find_file(mitem->line, find_map_file_callback);
+        if (cur_file_name) {
+            callback_id = callback_defined(read_map_file_callback);
+            if (callback_id > 0) {
+                if (run_callback(callback_id, "S->bSd", cur_file_name,
+                                 &file_opened, &fm_buffer, &fm_size)) {
+                    if (file_opened) {
+                        if (fm_size > 0) {
+                            report_start_file(filetype_map,cur_file_name);
+                            while (!fm_eof()) {
+                                fm_scan_line();
+                                mitem->lineno++;
+                            }
+                            report_stop_file(filetype_map);
+                            fm_file = NULL;
+                        }
+                    } else {
+                        formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
+                    }
+                } else {
+                    formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
+                }
+            } else {
+                if (!fm_open(cur_file_name)) {
+                    formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
+                } else {
+                    fm_read_file();
+                    report_start_file(filetype_map,cur_file_name);
+                    while (!fm_eof()) {
+                        fm_scan_line();
+                        mitem->lineno++;
+                    }
+                    fm_close();
+                    report_stop_file(filetype_map);
+                    fm_file = NULL;
+                }
+            }
+            cur_file_name = NULL;
+        }
+        break;
+    case MAPLINE:
+        cur_file_name = NULL;
+        fm_scan_line();
+        break;
+    default:
+        assert(0);
+    }
+    mitem->line = NULL;         /* done with this line */
+    cur_file_name = NULL;
+    return;
+}
+
+/**********************************************************************/
+
+fm_entry *getfontmap(char *tfm_name)
+{
+    fm_entry *fm;
+    fm_entry tmp;
+    if (tfm_name == NULL)       /* wide, lua loaded fonts may not have a name */
+        return NULL;
+    if (tfm_tree == NULL)
+        fm_read_info();         /* only to read default map file */
+    tmp.tfm_name = tfm_name;    /* Look up for tfmname */
+    fm = (fm_entry *) avl_find(tfm_tree, &tmp);
+    if (fm == NULL)
+        return NULL;
+    set_inuse(fm);
+    return fm;
+}
+
+/**********************************************************************/
+/*
+ * Process map file given by its name or map line contents. Items not
+ * beginning with [+-=] flush default map file, if it has not yet been
+ * read. Leading blanks and blanks immediately following [+-=] are
+ * ignored.
+ */
+
+void process_map_item(char *s, int type)
+{
+    char *p;
+    int mode;
+    if (*s == ' ')
+        s++;                    /* ignore leading blank */
+    switch (*s) {
+    case '+':                  /* +mapfile.map, +mapline */
+        mode = FM_DUPIGNORE;    /* insert entry, if it is not duplicate */
+        s++;
+        break;
+    case '=':                  /* =mapfile.map, =mapline */
+        mode = FM_REPLACE;      /* try to replace earlier entry */
+        s++;
+        break;
+    case '-':                  /* -mapfile.map, -mapline */
+        mode = FM_DELETE;       /* try to delete entry */
+        s++;
+        break;
+    default:
+        mode = FM_DUPIGNORE;    /* like +, but also: */
+        mitem->line = NULL;     /* flush default map file name */
+    }
+    if (*s == ' ')
+        s++;                    /* ignore blank after [+-=] */
+    p = s;                      /* map item starts here */
+    switch (type) {
+    case MAPFILE:              /* remove blank at end */
+        while (*p != '\0' && *p != ' ')
+            p++;
+        *p = '\0';
+        break;
+    case MAPLINE:              /* blank at end allowed */
+        break;
+    default:
+        assert(0);
+    }
+    if (mitem->line != NULL)    /* read default map file first */
+        fm_read_info();
+    if (*s != '\0') {           /* only if real item to process */
+        mitem->mode = mode;
+        mitem->type = type;
+        mitem->line = s;
+        fm_read_info();
+    }
+}
+
+void pdfmapfile(int t)
+{
+    char *s = tokenlist_to_cstring(t, true, NULL);
+    process_map_item(s, MAPFILE);
+    free(s);
+}
+
+void pdfmapline(int t)
+{
+    char *s = tokenlist_to_cstring(t, true, NULL);
+    process_map_item(s, MAPLINE);
+    free(s);
+}
+
+void pdf_init_map_file(const char *map_name)
+{
+    assert(mitem == NULL);
+    mitem = xtalloc(1, mapitem);
+    mitem->mode = FM_DUPIGNORE;
+    mitem->type = MAPFILE;
+    mitem->line = xstrdup(map_name);
+}
+
+/**********************************************************************/
+/*
+ * Early check whether a font file exists. Search tree ff_tree is used
+ * in 1st instance, as it may be faster than the kpse_find_file(), and
+ * kpse_find_file() is called only once per font file name + expansion
+ * parameter. This might help keeping speed, if many PDF pages with
+ * same fonts are to be embedded.
+ *
+ * The ff_tree contains only font files, which are actually needed,
+ * so this tree typically is much smaller than the tfm_tree.
+ */
+
+ff_entry *check_ff_exist(char *ff_name, boolean is_tt)
+{
+    ff_entry *ff;
+    ff_entry tmp;
+    void **aa;
+    int callback_id;
+    char *filepath = NULL;
+
+    assert(ff_name != NULL);
+    tmp.ff_name = ff_name;
+    ff = (ff_entry *) avl_find(ff_tree, &tmp);
+    if (ff == NULL) {           /* not yet in database */
+        ff = new_ff_entry();
+        ff->ff_name = xstrdup(ff_name);
+        if (is_tt) {
+            callback_id = callback_defined(find_truetype_file_callback);
+            if (callback_id > 0) {
+                run_callback(callback_id, "S->S", ff_name, &filepath);
+                if (filepath && strlen(filepath) == 0)
+                    filepath = NULL;
+                ff->ff_path = filepath;
+            } else {
+                ff->ff_path = kpse_find_file(ff_name, kpse_truetype_format, 0);
+            }
+        } else {
+            callback_id = callback_defined(find_type1_file_callback);
+            if (callback_id > 0) {
+                run_callback(callback_id, "S->S", ff_name, &filepath);
+                if (filepath && strlen(filepath) == 0)
+                    filepath = NULL;
+                ff->ff_path = filepath;
+            } else {
+                ff->ff_path = kpse_find_file(ff_name, kpse_type1_format, 0);
+            }
+        }
+        aa = avl_probe(ff_tree, ff);
+        assert(aa != NULL);
+    }
+    return ff;
+}
+
+/**********************************************************************/
+
+int is_subsetable(fm_entry * fm)
+{
+    assert(is_included(fm));
+    return is_subsetted(fm);
+}
+
+/**********************************************************************/
+/* cleaning up... */
+
+static void destroy_fm_entry_tfm(void *pa, void *pb)
+{
+    fm_entry *fm;
+    (void) pb;
+    fm = (fm_entry *) pa;
+    delete_fm_entry(fm);
+}
+
+static void destroy_ff_entry(void *pa, void *pb)
+{
+    ff_entry *ff;
+    (void) pb;
+    ff = (ff_entry *) pa;
+    delete_ff_entry(ff);
+}
+
+void fm_free(void)
+{
+    if (tfm_tree != NULL) {
+        avl_destroy(tfm_tree, destroy_fm_entry_tfm);
+        tfm_tree = NULL;
+    }
+    if (ff_tree != NULL) {
+        avl_destroy(ff_tree, destroy_ff_entry);
+        ff_tree = NULL;
+    }
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/pkin.w
@@ -0,0 +1,426 @@
+% pkin.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% Copyright 2006-2008 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/>.
+
+@
+NAME
+
+pkin.c - implementation of readchar()
+
+DESCRIPTION
+
+This implementation of readchar() uses parts of the program dvips
+written by Tomas Rokicki--the inventor of the pkformat--(loadfont.c,
+download.c and unpack.c). Dvips in turn is derived from pktype.
+Pktype(TeX) is described in debt in ``The PKtype processor'',
+which is available as pktype.weave as part of the METAFONTware.
+What was needed to implement readchar() is rearranged in pkfile.c to
+get more modularity in the style of MODULA2.
+
+BUGFIXES
+
+May 1997: Eric Delaunay <delaunay@@lix.polytechnique.fr> reports a
+problem with huge fonts (greater than 1008 DPI). The code for
+handling PK characters in `extended format' was wrongly derived
+from dvips. Made some minor improvements regarding error handling.
+
+REDESIGN
+
+Piet Tutelaers <rcpt@@urc.tue.nl>
+
+Modified for use with pdftex by Han The Thanh <thanh@@fi.muni.cz>.
+
+@c
+
+
+#include "ptexlib.h"
+
+typedef short shalfword;
+
+@
+Now we have some routines to get stuff from the pk file.  pkbyte returns
+the next byte from the pk file.
+
+@c
+static shalfword pkbyte(void)
+{
+    register shalfword i;
+    i = t3_getchar();
+    if (t3_eof())
+        normal_error("type 3","unexpected EOF in pk file");
+    return (i);
+}
+
+static int pkduo(void)
+{
+    register int i;
+
+    i = pkbyte();
+    if (i > 127)
+        i -= 256;
+    i = i * 256 + pkbyte();
+    return (i);
+}
+
+static int pktrio(void)
+{
+    register int i;
+
+    i = pkbyte();
+    if (i > 127)
+        i -= 256;
+    i = i * 256 + pkbyte();
+    i = i * 256 + pkbyte();
+    return (i);
+}
+
+static int pkquad(void)
+{
+    register int i;
+
+    i = pkbyte();
+    if (i > 127)
+        i -= 256;
+    i = i * 256 + pkbyte();
+    i = i * 256 + pkbyte();
+    i = i * 256 + pkbyte();
+    return (i);
+}
+
+
+
+@  The next part is devoted to unpacking the character data.
+
+@  We need procedures to get a nybble, bit, and packed word from the
+packed data structure.
+
+@c
+static halfword inputbyte, flagbyte;
+static halfword bitweight;
+static halfword dynf;
+static halfword repeatcount;
+
+static shalfword getnyb(void)
+{
+    halfword temp;
+    if (bitweight == 0) {
+        bitweight = 16;
+        inputbyte = pkbyte();
+        temp = inputbyte >> 4;
+    } else {
+        bitweight = 0;
+        temp = inputbyte & 15;
+    }
+    return (shalfword) (temp);
+}
+
+static boolean getbit(void)
+{
+    bitweight >>= 1;
+    if (bitweight == 0) {
+        inputbyte = pkbyte();
+        bitweight = 128;
+    }
+    return (inputbyte & bitweight);
+}
+
+static halfword(*realfunc) (void);
+long pk_remainder;
+static halfword handlehuge(halfword i, halfword k);
+
+static halfword pkpackednum(void)
+{
+    register halfword i, j;
+    i = getnyb();
+    if (i == 0) {
+        do {
+            j = getnyb();
+            i++;
+        } while (!(j != 0));
+        if (i > 3) {
+/*
+    Damn, we got a huge count!  We {\it fake} it by giving an artificially
+    large repeat count.
+ */
+            return (handlehuge(i, j));
+        } else {
+            while (i > 0) {
+                j = j * 16 + getnyb();
+                i--;
+            }
+            return (j - 15 + (13 - dynf) * 16 + dynf);
+        }
+    } else if (i <= dynf)
+        return (i);
+    else if (i < 14)
+        return ((i - dynf - 1) * 16 + getnyb() + dynf + 1);
+    else {
+        if (i == 14)
+            repeatcount = pkpackednum();
+        else
+            repeatcount = 1;
+#ifdef DEBUG
+        printf("[%d]", (int) repeatcount);
+#endif
+        return ((*realfunc) ());
+    }
+}
+
+static halfword rest(void)
+{
+    halfword i;
+
+    if (pk_remainder < 0) {
+        pk_remainder = -pk_remainder;
+        return (0);
+    } else if (pk_remainder > 0) {
+        if (pk_remainder > 4000) {
+            pk_remainder = 4000 - pk_remainder;
+            return (4000);
+        } else {
+            i = (halfword) pk_remainder;
+            pk_remainder = 0;
+            realfunc = pkpackednum;
+            return (i);
+        }
+    } else {
+        normal_error("type 3","pk issue that shouldn't happen");
+        return 0;
+     /*NOTREACHED*/}
+}
+
+static halfword handlehuge(halfword i, halfword k)
+{
+    register long j = k;
+
+    while (i) {
+        j = (j << 4L) + getnyb();
+        i--;
+    }
+    pk_remainder = j - 15 + (13 - dynf) * 16 + dynf;
+    realfunc = rest;
+    return (rest());
+}
+
+
+@  And now we have our unpacking routine.
+
+@c
+static halfword gpower[17] = { 0, 1, 3, 7, 15, 31, 63, 127,
+    255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535
+};
+
+static void unpack(chardesc * cd)
+{
+    register int i, j;
+    register halfword word, wordweight;
+    halfword *raster;
+    shalfword rowsleft;
+    boolean turnon;
+    shalfword hbit;
+    halfword count;
+    shalfword wordwidth;
+
+    wordwidth = (shalfword) ((cd->cwidth + 15) / 16);
+    i = (int) (2 * cd->cheight * (long) wordwidth);
+    if (i <= 0)
+        i = 2;
+    if (i > cd->rastersize) {
+        xfree(cd->raster);
+        cd->rastersize = i;
+        cd->raster = xtalloc((unsigned) cd->rastersize, halfword);
+    }
+    raster = cd->raster;
+    realfunc = pkpackednum;
+    dynf = flagbyte / 16;
+    turnon = flagbyte & 8;
+    if (dynf == 14) {
+        bitweight = 0;
+        for (i = 1; i <= cd->cheight; i++) {
+            word = 0;
+            wordweight = 32768;
+            for (j = 1; j <= cd->cwidth; j++) {
+                if (getbit())
+                    word += wordweight;
+                wordweight >>= 1;
+                if (wordweight == 0) {
+                    *raster++ = word;
+                    word = 0;
+                    wordweight = 32768;
+                }
+            }
+            if (wordweight != 32768)
+                *raster++ = word;
+        }
+    } else {
+        rowsleft = (shalfword) cd->cheight;
+        hbit = (shalfword) cd->cwidth;
+        repeatcount = 0;
+        wordweight = 16;
+        word = 0;
+        bitweight = 0;
+        while (rowsleft > 0) {
+            count = (*realfunc) ();
+            while (count != 0) {
+                if ((count < wordweight) && (count < hbit)) {
+                    if (turnon)
+                        word += gpower[wordweight] - gpower[wordweight - count];
+                    hbit = (shalfword) (hbit - count);
+                    wordweight -= count;
+                    count = 0;
+                } else if ((count >= hbit) && (hbit <= wordweight)) {
+                    if (turnon)
+                        word += gpower[wordweight] - gpower[wordweight - hbit];
+                    *raster++ = word;
+                    for (i = 1; i <= repeatcount; i++) {
+                        for (j = 1; j <= wordwidth; j++) {
+                            *raster = *(raster - wordwidth);
+                            raster++;
+                        }
+                    }
+                    rowsleft = (shalfword) (rowsleft - repeatcount - 1);
+                    repeatcount = 0;
+                    word = 0;
+                    wordweight = 16;
+                    count -= hbit;
+                    hbit = (shalfword) cd->cwidth;
+                } else {
+                    if (turnon)
+                        word += gpower[wordweight];
+                    *raster++ = word;
+                    word = 0;
+                    count -= wordweight;
+                    hbit = (shalfword) (hbit - wordweight);
+                    wordweight = 16;
+                }
+            }
+            turnon = !turnon;
+        }
+        if ((rowsleft != 0) || ((int) hbit != cd->cwidth))
+            normal_error("type 3","error while unpacking, more bits than required");
+    }
+}
+
+@
+|readchar()|: the main routine
+check pk preamble if necessary,
+Reads the character definition of character `c' into `cd' if available,
+return FALSE (0) otherwise.
+
+@c
+int readchar(boolean check_preamble, chardesc * cd)
+{
+    register shalfword i;
+    register int k;
+    register int length = 0;
+
+/*
+    Check the preamble of the pkfile
+ */
+    if (check_preamble) {
+        if (pkbyte() != 247)
+            normal_error("type 3","bad pk file, expected pre");
+        if (pkbyte() != 89)
+            normal_error("type 3","bad version of pk file");
+        for (i = pkbyte(); i > 0; i--)  /* creator of pkfile */
+            (void) pkbyte();
+        (void) pkquad();        /* design size */
+        k = pkquad();           /* checksum    */
+        k = pkquad();           /* hppp        */
+        k = pkquad();           /* vppp   */
+    }
+/*
+    Now we skip to the desired character definition
+ */
+    while ((flagbyte = pkbyte()) != 245) {
+        if (flagbyte < 240) {
+            switch (flagbyte & 7) {
+            case 0:
+            case 1:
+            case 2:
+            case 3:
+                length = (flagbyte & 7) * 256 + pkbyte() - 3;
+                cd->charcode = pkbyte();
+                (void) pktrio();        /* TFMwidth */
+                cd->xescape = pkbyte(); /* pixel width */
+                cd->cwidth = pkbyte();
+                cd->cheight = pkbyte();
+                cd->xoff = pkbyte();
+                cd->yoff = pkbyte();
+                if (cd->xoff > 127)
+                    cd->xoff -= 256;
+                if (cd->yoff > 127)
+                    cd->yoff -= 256;
+                break;
+            case 4:
+            case 5:
+            case 6:
+                length = (int) ((flagbyte & 3) * 65536L + pkbyte() * 256L);
+                length = (int) (length + pkbyte() - 4L);
+                cd->charcode = pkbyte();
+                (void) pktrio();        /* TFMwidth */
+                cd->xescape = pkduo();  /* pixelwidth */
+                cd->cwidth = pkduo();
+                cd->cheight = pkduo();
+                cd->xoff = pkduo();
+                cd->yoff = pkduo();
+                break;
+            case 7:
+                length = (int) (pkquad() - 9L);
+                cd->charcode = pkquad();
+                (void) pkquad();        /* TFMwidth */
+                cd->xescape = pkquad(); /* pixelwidth */
+                k = pkquad();
+                cd->cwidth = pkquad();
+                cd->cheight = pkquad();
+                cd->xoff = pkquad();
+                cd->yoff = pkquad();
+            }
+            if (length <= 0)
+                formatted_error("type 3","pk packet length '%i' too small", (int) length);
+            unpack(cd);
+            return 1;
+        } else {
+            k = 0;
+            switch (flagbyte) {
+            case 243:
+                k = pkbyte();
+                if (k > 127)
+                    k -= 256;
+            case 242:
+                k = k * 256 + pkbyte();
+            case 241:
+                k = k * 256 + pkbyte();
+            case 240:
+                k = k * 256 + pkbyte();
+                while (k-- > 0)
+                    i = pkbyte();
+                break;
+            case 244:
+                k = pkquad();
+                break;
+            case 246:
+                break;
+            default:
+                formatted_error("type 3","unexpected pk command '%i'", (int) flagbyte);
+            }
+        }
+    }
+    return 0;                   /* character not found */
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/pkin.c
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-Copyright 2006-2008 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/>.
-
-*/
-
-/*tex
-
-This implementation of readchar() uses parts of the program dvips written by
-Tomas Rokicki--the inventor of the pkformat--(loadfont.c, download.c and
-unpack.c). Dvips in turn is derived from pktype. Pktype(TeX) is described in debt
-in ``The PKtype processor'', which is available as pktype.weave as part of the
-METAFONTware. What was needed to implement readchar() is rearranged in pkfile.c
-to get more modularity in the style of MODULA2.
-
-May 1997: Eric Delaunay <delaunay@lix.polytechnique.fr> reports a problem with
-huge fonts (greater than 1008 DPI). The code for handling PK characters in
-`extended format' was wrongly derived from dvips. Made some minor improvements
-regarding error handling.
-
-At some point Piet Tutelaers <rcpt@urc.tue.nl> redesigned the code and later Han
-The Thanh <thanh@fi.muni.cz> again modified the code to suite \PDFTEX. Of course
-in \LUATEX\ again we adapted the code.
-
-*/
-
-#include "ptexlib.h"
-
-typedef short shalfword;
-
-/*tex
-
-Now we have some routines to get stuff from the \PK\ file. |pkbyte| returns the
-next byte from the \PK\ file.
-
-*/
-
-static shalfword pkbyte(void)
-{
-    register shalfword i;
-    i = t3_getchar();
-    if (t3_eof())
-        normal_error("type 3","unexpected EOF in pk file");
-    return (i);
-}
-
-static int pkduo(void)
-{
-    register int i;
-    i = pkbyte();
-    if (i > 127)
-        i -= 256;
-    i = i * 256 + pkbyte();
-    return (i);
-}
-
-static int pktrio(void)
-{
-    register int i;
-    i = pkbyte();
-    if (i > 127)
-        i -= 256;
-    i = i * 256 + pkbyte();
-    i = i * 256 + pkbyte();
-    return (i);
-}
-
-static int pkquad(void)
-{
-    register int i;
-    i = pkbyte();
-    if (i > 127)
-        i -= 256;
-    i = i * 256 + pkbyte();
-    i = i * 256 + pkbyte();
-    i = i * 256 + pkbyte();
-    return (i);
-}
-
-/*tex
-
-    The next part is devoted to unpacking the character data. We need procedures
-    to get a nybble, bit, and packed word from the packed data structure.
-
-*/
-
-static halfword inputbyte, flagbyte;
-static halfword bitweight;
-static halfword dynf;
-static halfword repeatcount;
-
-static shalfword getnyb(void)
-{
-    halfword temp;
-    if (bitweight == 0) {
-        bitweight = 16;
-        inputbyte = pkbyte();
-        temp = inputbyte >> 4;
-    } else {
-        bitweight = 0;
-        temp = inputbyte & 15;
-    }
-    return (shalfword) (temp);
-}
-
-static boolean getbit(void)
-{
-    bitweight >>= 1;
-    if (bitweight == 0) {
-        inputbyte = pkbyte();
-        bitweight = 128;
-    }
-    return (inputbyte & bitweight);
-}
-
-static halfword(*realfunc) (void);
-long pk_remainder;
-static halfword handlehuge(halfword i, halfword k);
-
-static halfword pkpackednum(void)
-{
-    register halfword i, j;
-    i = getnyb();
-    if (i == 0) {
-        do {
-            j = getnyb();
-            i++;
-        } while (!(j != 0));
-        if (i > 3) {
-            /*tex
-                Hm, we got a huge count! We {\em fake} it by giving an
-                artificially large repeat count.
-            */
-            return (handlehuge(i, j));
-        } else {
-            while (i > 0) {
-                j = j * 16 + getnyb();
-                i--;
-            }
-            return (j - 15 + (13 - dynf) * 16 + dynf);
-        }
-    } else if (i <= dynf)
-        return (i);
-    else if (i < 14)
-        return ((i - dynf - 1) * 16 + getnyb() + dynf + 1);
-    else {
-        if (i == 14)
-            repeatcount = pkpackednum();
-        else
-            repeatcount = 1;
-        return ((*realfunc) ());
-    }
-}
-
-static halfword rest(void)
-{
-    halfword i;
-    if (pk_remainder < 0) {
-        pk_remainder = -pk_remainder;
-        return (0);
-    } else if (pk_remainder > 0) {
-        if (pk_remainder > 4000) {
-            pk_remainder = 4000 - pk_remainder;
-            return (4000);
-        } else {
-            i = (halfword) pk_remainder;
-            pk_remainder = 0;
-            realfunc = pkpackednum;
-            return (i);
-        }
-    } else {
-        normal_error("type 3","pk issue that shouldn't happen");
-        return 0;
-    }
-}
-
-static halfword handlehuge(halfword i, halfword k)
-{
-    register long j = k;
-    while (i) {
-        j = (j << 4L) + getnyb();
-        i--;
-    }
-    pk_remainder = j - 15 + (13 - dynf) * 16 + dynf;
-    realfunc = rest;
-    return (rest());
-}
-
-
-/*tex
-
-    And now we have our unpacking routine.
-
-*/
-
-static halfword gpower[17] = {
-        0,   1,    3,    7,   15,   31,    63,   127,
-      255, 511, 1023, 2047, 4095, 8191, 16383, 32767,
-    65535
-};
-
-static void unpack(chardesc * cd)
-{
-    register int i, j;
-    register halfword word, wordweight;
-    halfword *raster;
-    shalfword rowsleft;
-    boolean turnon;
-    shalfword hbit;
-    halfword count;
-    shalfword wordwidth;
-    wordwidth = (shalfword) ((cd->cwidth + 15) / 16);
-    i = (int) (2 * cd->cheight * (long) wordwidth);
-    if (i <= 0)
-        i = 2;
-    if (i > cd->rastersize) {
-        xfree(cd->raster);
-        cd->rastersize = i;
-        cd->raster = xtalloc((unsigned) cd->rastersize, halfword);
-    }
-    raster = cd->raster;
-    realfunc = pkpackednum;
-    dynf = flagbyte / 16;
-    turnon = flagbyte & 8;
-    if (dynf == 14) {
-        bitweight = 0;
-        for (i = 1; i <= cd->cheight; i++) {
-            word = 0;
-            wordweight = 32768;
-            for (j = 1; j <= cd->cwidth; j++) {
-                if (getbit())
-                    word += wordweight;
-                wordweight >>= 1;
-                if (wordweight == 0) {
-                    *raster++ = word;
-                    word = 0;
-                    wordweight = 32768;
-                }
-            }
-            if (wordweight != 32768)
-                *raster++ = word;
-        }
-    } else {
-        rowsleft = (shalfword) cd->cheight;
-        hbit = (shalfword) cd->cwidth;
-        repeatcount = 0;
-        wordweight = 16;
-        word = 0;
-        bitweight = 0;
-        while (rowsleft > 0) {
-            count = (*realfunc) ();
-            while (count != 0) {
-                if ((count < wordweight) && (count < hbit)) {
-                    if (turnon)
-                        word += gpower[wordweight] - gpower[wordweight - count];
-                    hbit = (shalfword) (hbit - count);
-                    wordweight -= count;
-                    count = 0;
-                } else if ((count >= hbit) && (hbit <= wordweight)) {
-                    if (turnon)
-                        word += gpower[wordweight] - gpower[wordweight - hbit];
-                    *raster++ = word;
-                    for (i = 1; i <= repeatcount; i++) {
-                        for (j = 1; j <= wordwidth; j++) {
-                            *raster = *(raster - wordwidth);
-                            raster++;
-                        }
-                    }
-                    rowsleft = (shalfword) (rowsleft - repeatcount - 1);
-                    repeatcount = 0;
-                    word = 0;
-                    wordweight = 16;
-                    count -= hbit;
-                    hbit = (shalfword) cd->cwidth;
-                } else {
-                    if (turnon)
-                        word += gpower[wordweight];
-                    *raster++ = word;
-                    word = 0;
-                    count -= wordweight;
-                    hbit = (shalfword) (hbit - wordweight);
-                    wordweight = 16;
-                }
-            }
-            turnon = !turnon;
-        }
-        if ((rowsleft != 0) || ((int) hbit != cd->cwidth))
-            normal_error("type 3","error while unpacking, more bits than required");
-    }
-}
-
-/*tex
-
-    The main routine check pk preamble if necessary. It reads the character
-    definition of character |c| into |cd| if available or return |FALSE|
-    otherwise.
-
-*/
-
-int readchar(boolean check_preamble, chardesc * cd)
-{
-    register shalfword i;
-    register int k;
-    register int length = 0;
-    /*tex Check the preamble of the \PK\ file. */
-    if (check_preamble) {
-        if (pkbyte() != 247)
-            normal_error("type 3","bad pk file, expected pre");
-        if (pkbyte() != 89)
-            normal_error("type 3","bad version of pk file");
-        /*tex The creator of the file: */
-        for (i = pkbyte(); i > 0; i--)
-            (void) pkbyte();
-        /*tex The design size: */
-        (void) pkquad();
-        /*tex The checksum: */
-        k = pkquad();
-        /*tex The hppp: */
-        k = pkquad();
-        /*tex The vppp: */
-        k = pkquad();
-    }
-    /*tex
-        We also skip to the desired character definition.
-    */
-    while ((flagbyte = pkbyte()) != 245) {
-        if (flagbyte < 240) {
-            switch (flagbyte & 7) {
-                case 0:
-                case 1:
-                case 2:
-                case 3:
-                    length = (flagbyte & 7) * 256 + pkbyte() - 3;
-                    cd->charcode = pkbyte();
-                    /*tex The \TFM\ width: */
-                    (void) pktrio();
-                    /*tex the pixel width: */
-                    cd->xescape = pkbyte();
-                    cd->cwidth = pkbyte();
-                    cd->cheight = pkbyte();
-                    cd->xoff = pkbyte();
-                    cd->yoff = pkbyte();
-                    if (cd->xoff > 127)
-                        cd->xoff -= 256;
-                    if (cd->yoff > 127)
-                        cd->yoff -= 256;
-                    break;
-                case 4:
-                case 5:
-                case 6:
-                    length = (int) ((flagbyte & 3) * 65536L + pkbyte() * 256L);
-                    length = (int) (length + pkbyte() - 4L);
-                    cd->charcode = pkbyte();
-                    /*tex The \TFM\ width: */
-                    (void) pktrio();
-                    /*tex the pixel width: */
-                    cd->xescape = pkduo();
-                    cd->cwidth = pkduo();
-                    cd->cheight = pkduo();
-                    cd->xoff = pkduo();
-                    cd->yoff = pkduo();
-                    break;
-                case 7:
-                    length = (int) (pkquad() - 9L);
-                    cd->charcode = pkquad();
-                    /*tex The \TFM\ width: */
-                    (void) pkquad();
-                    /*tex the pixel width: */
-                    cd->xescape = pkquad();
-                    k = pkquad();
-                    cd->cwidth = pkquad();
-                    cd->cheight = pkquad();
-                    cd->xoff = pkquad();
-                    cd->yoff = pkquad();
-            }
-            if (length <= 0)
-                formatted_error("type 3","pk packet length '%i' too small", (int) length);
-            unpack(cd);
-            return 1;
-        } else {
-            k = 0;
-            switch (flagbyte) {
-                case 243:
-                    k = pkbyte();
-                    if (k > 127)
-                        k -= 256;
-                case 242:
-                    k = k * 256 + pkbyte();
-                case 241:
-                    k = k * 256 + pkbyte();
-                case 240:
-                    k = k * 256 + pkbyte();
-                    while (k-- > 0)
-                        i = pkbyte();
-                    break;
-                case 244:
-                    k = pkquad();
-                    break;
-                case 246:
-                    break;
-                default:
-                    formatted_error("type 3","unexpected pk command '%i'", (int) flagbyte);
-            }
-        }
-    }
-    /*tex Character not found: */
-    return 0;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/sfnt.w
@@ -0,0 +1,462 @@
+% sfnt.w
+%
+% Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata,
+% the dvipdfmx project team <dvipdfmx@@project.ktug.or.kr>
+% Copyright 2006-2008 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/>.
+
+@ Based on dvipdfmx-0.13.2c
+@c
+
+
+#include "ptexlib.h"
+
+#if  HAVE_CONFIG_H
+#  include <w2c/config.h>
+#endif                          /* |HAVE_CONFIG_H_| */
+
+#include <string.h>
+
+#include "font/sfnt.h"
+
+@ type:
+
+ `true' (0x74727565): TrueType (Mac)
+
+ `typ1' (0x74797031) (Mac): PostScript font housed in a sfnt wrapper
+
+ 0x00010000: TrueType (Win)/OpenType
+
+ `OTTO': PostScript CFF font with OpenType wrapper
+
+ `ttcf': TrueType Collection
+
+@c
+#define SFNT_TRUETYPE   0x00010000UL
+#define SFNT_MAC_TRUE 0x74727565UL
+#define SFNT_OPENTYPE   0x00010000UL
+#define SFNT_POSTSCRIPT 0x4f54544fUL
+#define SFNT_TTC        0x74746366UL
+
+sfnt *sfnt_open(unsigned char *buff, int buflen)
+{
+    sfnt *sfont;
+    ULONG type;
+
+    sfont = xmalloc(sizeof(sfnt));
+    sfont->loc = 0;
+    sfont->buffer = buff;
+    sfont->buflen = buflen;
+
+    type = sfnt_get_ulong(sfont);
+
+    if (type == SFNT_TRUETYPE || type == SFNT_MAC_TRUE) {
+        sfont->type = SFNT_TYPE_TRUETYPE;
+    } else if (type == SFNT_OPENTYPE) {
+        sfont->type = SFNT_TYPE_OPENTYPE;
+    } else if (type == SFNT_POSTSCRIPT) {
+        sfont->type = SFNT_TYPE_POSTSCRIPT;
+    } else if (type == SFNT_TTC) {
+        sfont->type = SFNT_TYPE_TTC;
+    }
+
+    sfont->loc = 0;
+    sfont->directory = NULL;
+    return sfont;
+}
+
+static void release_directory(struct sfnt_table_directory *td)
+{
+    long i;
+
+    if (td) {
+        if (td->tables) {
+            for (i = 0; i < td->num_tables; i++) {
+                if (td->tables[i].data)
+                    RELEASE(td->tables[i].data);
+            }
+            RELEASE(td->tables);
+        }
+        if (td->flags)
+            RELEASE(td->flags);
+        RELEASE(td);
+    }
+
+    return;
+}
+
+void sfnt_close(sfnt * sfont)
+{
+
+    if (sfont) {
+        if (sfont->directory)
+            release_directory(sfont->directory);
+        RELEASE(sfont);
+    }
+
+    return;
+}
+
+int put_big_endian(void *s, LONG q, int n)
+{
+    int i;
+    char *p;
+
+    p = (char *) s;
+    for (i = n - 1; i >= 0; i--) {
+        p[i] = (char) (q & 0xff);
+        q >>= 8;
+    }
+
+    return n;
+}
+
+@ Convert four-byte number to big endianess in a machine independent way.
+
+@c
+static void convert_tag(char *tag, unsigned long u_tag)
+{
+    int i;
+
+    for (i = 3; i >= 0; i--) {
+        tag[i] = (char) (u_tag % 256);
+        u_tag /= 256;
+    }
+
+    return;
+}
+
+
+@ Computes the max power of 2 <= n
+
+@c
+static unsigned max2floor(unsigned n)
+{
+    int val = 1;
+
+    while (n > 1) {
+        n /= 2;
+        val *= 2;
+    }
+
+    return (unsigned) val;
+}
+
+
+@ Computes the log2 of the max power of 2 <= n
+
+@c
+static unsigned log2floor(unsigned n)
+{
+    unsigned val = 0;
+
+    while (n > 1) {
+        n /= 2;
+        val++;
+    }
+
+    return val;
+}
+
+@ @c
+static ULONG sfnt_calc_checksum(void *data, ULONG length)
+{
+    ULONG chksum = 0;
+    BYTE *p, *endptr;
+    ULONG count = 0;
+
+    p = (BYTE *) data;
+    endptr = p + length;
+    while (p < endptr) {
+        chksum = chksum + (ULONG) (p[0] << (8 * (3 - count)));
+        count = ((count + 1) & 3);
+        p++;
+    }
+
+    return chksum;
+}
+
+@ @c
+static int find_table_index(struct sfnt_table_directory *td, const char *tag)
+{
+    int idx;
+
+    if (!td)
+        return -1;
+
+    for (idx = 0; idx < td->num_tables; idx++) {
+        if (!memcmp(td->tables[idx].tag, tag, 4))
+            return idx;
+    }
+
+    return -1;
+}
+
+@ @c
+void sfnt_set_table(sfnt * sfont, const char *tag, void *data, ULONG length)
+{
+    struct sfnt_table_directory *td;
+    int idx;
+
+    ASSERT(sfont);
+
+    td = sfont->directory;
+    idx = find_table_index(td, tag);
+
+    if (idx < 0) {
+        idx = td->num_tables;
+        td->num_tables++;
+        td->tables = RENEW(td->tables, td->num_tables, struct sfnt_table);
+        memcpy(td->tables[idx].tag, tag, 4);
+    }
+
+    td->tables[idx].check_sum = sfnt_calc_checksum(data, length);
+    td->tables[idx].offset = 0L;
+    td->tables[idx].length = length;
+    td->tables[idx].data = data;
+
+    return;
+}
+
+@ @c
+ULONG sfnt_find_table_len(sfnt * sfont, const char *tag)
+{
+    ULONG length;
+    struct sfnt_table_directory *td;
+    int idx;
+
+    ASSERT(sfont && tag);
+
+    td = sfont->directory;
+    idx = find_table_index(td, tag);
+    if (idx < 0)
+        length = 0;
+    else {
+        length = td->tables[idx].length;
+    }
+
+    return length;
+}
+
+@ @c
+ULONG sfnt_find_table_pos(sfnt * sfont, const char *tag)
+{
+    ULONG offset;
+    struct sfnt_table_directory *td;
+    int idx;
+
+    ASSERT(sfont && tag);
+
+    td = sfont->directory;
+    idx = find_table_index(td, tag);
+    if (idx < 0)
+        offset = 0;
+    else {
+        offset = td->tables[idx].offset;
+    }
+
+    return offset;
+}
+
+@ @c
+ULONG sfnt_locate_table(sfnt * sfont, const char *tag)
+{
+    ULONG offset;
+
+    ASSERT(sfont && tag);
+
+    offset = sfnt_find_table_pos(sfont, tag);
+    if (offset == 0)
+        normal_error("ttf","sfnt table not found");
+
+    sfnt_seek_set(sfont, (long) offset);
+
+    return offset;
+}
+
+@ @c
+int sfnt_read_table_directory(sfnt * sfont, ULONG offset)
+{
+    struct sfnt_table_directory *td;
+    unsigned long i, u_tag;
+
+    ASSERT(sfont);
+
+    if (sfont->directory)
+        release_directory(sfont->directory);
+    sfont->directory = td = NEW(1, struct sfnt_table_directory);
+
+    ASSERT(sfont->buffer);
+    sfnt_seek_set(sfont, (long) offset);
+
+    td->version = sfnt_get_ulong(sfont);
+    td->num_tables = sfnt_get_ushort(sfont);
+    td->search_range = sfnt_get_ushort(sfont);
+    td->entry_selector = sfnt_get_ushort(sfont);
+    td->range_shift = sfnt_get_ushort(sfont);
+
+    td->flags = NEW(td->num_tables, char);
+    td->tables = NEW(td->num_tables, struct sfnt_table);
+
+    for (i = 0; i < td->num_tables; i++) {
+        u_tag = sfnt_get_ulong(sfont);
+
+        convert_tag(td->tables[i].tag, u_tag);
+        td->tables[i].check_sum = sfnt_get_ulong(sfont);
+        td->tables[i].offset = sfnt_get_ulong(sfont);
+        td->tables[i].length = sfnt_get_ulong(sfont);
+        td->tables[i].data = NULL;
+
+        td->flags[i] = 0;
+    }
+
+    td->num_kept_tables = 0;
+
+    return 0;
+}
+
+@ @c
+int sfnt_require_table(sfnt * sfont, const char *tag, int must_exist)
+{
+    struct sfnt_table_directory *td;
+    int idx;
+
+    ASSERT(sfont && sfont->directory);
+
+    td = sfont->directory;
+    idx = find_table_index(td, tag);
+    if (idx < 0) {
+        if (must_exist)
+            return -1;
+    } else {
+        td->flags[idx] |= SFNT_TABLE_REQUIRED;
+        td->num_kept_tables++;
+    }
+
+    return 0;
+}
+
+
+
+@ All tables begin on four byte boundries, and pad any remaining space
+  between tables with zeros
+
+  Entries in the Table Directory must be sorted in ascending order by tag
+
+  The head table contains checksum of the whole font file.
+  To compute:  first set it to 0, sum the entire font as ULONG,
+  then store 0xB1B0AFBA - sum.
+
+@c
+#  include "font/luatexfont.h"
+#  undef MIN
+#  define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#  define STREAM_COMPRESS
+
+static unsigned char wbuf[1024], padbytes[4] = { 0, 0, 0, 0 };
+
+pdf_obj *sfnt_create_FontFile_stream(sfnt * sfont)
+{
+    pdf_obj *stream;
+    struct sfnt_table_directory *td;
+    long offset, nb_read, length;
+    int i, sr;
+    char *p;
+
+    ASSERT(sfont && sfont->directory);
+
+    stream = pdf_new_stream(STREAM_COMPRESS);
+
+    td = sfont->directory;
+
+    /* Header */
+    p = (char *) wbuf;
+    p += sfnt_put_ulong(p, (LONG) td->version);
+    p += sfnt_put_ushort(p, td->num_kept_tables);
+    sr = (int) (max2floor(td->num_kept_tables) * 16);
+    p += sfnt_put_ushort(p, sr);
+    p += sfnt_put_ushort(p, log2floor(td->num_kept_tables));
+    p += sfnt_put_ushort(p, td->num_kept_tables * 16 - sr);
+
+    pdf_add_stream(stream, wbuf, 12);
+
+    /*
+     Compute start of actual tables (after headers).
+     */
+    offset = 12 + 16 * td->num_kept_tables;
+    for (i = 0; i < td->num_tables; i++) {
+        /* This table must exist in FontFile */
+        if (td->flags[i] & SFNT_TABLE_REQUIRED) {
+            if ((offset % 4) != 0) {
+                offset += 4 - (offset % 4);
+            }
+
+            p = (char *) wbuf;
+            memcpy(p, td->tables[i].tag, 4);
+            p += 4;
+            p += sfnt_put_ulong(p, (LONG) td->tables[i].check_sum);
+            p += sfnt_put_ulong(p, offset);
+            p += sfnt_put_ulong(p, (LONG) td->tables[i].length);
+            pdf_add_stream(stream, wbuf, 16);
+
+            offset = (long) (offset + (long) td->tables[i].length);
+        }
+    }
+
+    offset = 12 + 16 * td->num_kept_tables;
+    for (i = 0; i < td->num_tables; i++) {
+        if (td->flags[i] & SFNT_TABLE_REQUIRED) {
+            if ((offset % 4) != 0) {
+                length = 4 - (offset % 4);
+                pdf_add_stream(stream, padbytes, length);
+                offset += length;
+            }
+            if (!td->tables[i].data) {
+                if (!sfont->buffer)
+                {
+                    pdf_release_obj(stream);
+                    normal_error("ttf","file not opened or already closed");
+                    return NULL;
+                }
+
+                length = (long) td->tables[i].length;
+                sfnt_seek_set(sfont, (long) td->tables[i].offset);
+                while (length > 0) {
+                    nb_read = sfnt_read(wbuf, (int) MIN(length, 1024), sfont);
+                    if (nb_read < 0) {
+                        pdf_release_obj(stream);
+                        normal_error("ttf","reading file failed");
+                        return NULL;
+                    } else if (nb_read > 0) {
+                        pdf_add_stream(stream, wbuf, nb_read);
+                    }
+                    length -= nb_read;
+                }
+            } else {
+                pdf_add_stream(stream,
+                               (unsigned char *) td->tables[i].data,
+                               (long) td->tables[i].length);
+                RELEASE(td->tables[i].data);
+                td->tables[i].data = NULL;
+            }
+            /* Set offset for next table */
+            offset = (long) (offset + (long) td->tables[i].length);
+        }
+    }
+
+    return stream;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/sfnt.c
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
-
-Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata,
-the dvipdfmx project team <dvipdfmx@@project.ktug.or.kr>
-Copyright 2006-2008 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/>.
-
-*/
-
-/*tex
-
-    The code below is originally based on |dvipdfmx-0.13.2c|:
-
-*/
-
-#include "ptexlib.h"
-
-#if HAVE_CONFIG_H
-#  include <w2c/config.h>
-#endif
-
-#include <string.h>
-
-#include "font/sfnt.h"
-
-/*tex
-
-    Types:
-
-    \starttabulate[||||]
-    \NC \type {true} \NC 0x74727565 \NC (Mac) TrueType  \NC \NR
-    \NC \type {typ1| \NC 0x74797031 \NC (Mac) PostScript font housed in a sfnt wrapper \NC \NR
-    \NC              \NC 0x00010000 \NC TrueType (Win)/OpenType \NC \NR
-    \NC \type {OTTO} \NC            \NC PostScript CFF font with OpenType wrapper \NC \NR
-    \NC \type {ttcf} \NC            \NC TrueType Collection \NC \NR
-    \stoptabulate
-
-*/
-
-#define SFNT_TRUETYPE   0x00010000UL
-#define SFNT_MAC_TRUE   0x74727565UL
-#define SFNT_OPENTYPE   0x00010000UL
-#define SFNT_POSTSCRIPT 0x4f54544fUL
-#define SFNT_TTC        0x74746366UL
-
-sfnt *sfnt_open(unsigned char *buff, int buflen)
-{
-    sfnt *sfont;
-    ULONG type;
-    sfont = xmalloc(sizeof(sfnt));
-    sfont->loc = 0;
-    sfont->buffer = buff;
-    sfont->buflen = buflen;
-    type = sfnt_get_ulong(sfont);
-    if (type == SFNT_TRUETYPE || type == SFNT_MAC_TRUE) {
-        sfont->type = SFNT_TYPE_TRUETYPE;
-    } else if (type == SFNT_OPENTYPE) {
-        sfont->type = SFNT_TYPE_OPENTYPE;
-    } else if (type == SFNT_POSTSCRIPT) {
-        sfont->type = SFNT_TYPE_POSTSCRIPT;
-    } else if (type == SFNT_TTC) {
-        sfont->type = SFNT_TYPE_TTC;
-    }
-    sfont->loc = 0;
-    sfont->directory = NULL;
-    return sfont;
-}
-
-static void release_directory(struct sfnt_table_directory *td)
-{
-    long i;
-    if (td) {
-        if (td->tables) {
-            for (i = 0; i < td->num_tables; i++) {
-                if (td->tables[i].data)
-                    RELEASE(td->tables[i].data);
-            }
-            RELEASE(td->tables);
-        }
-        if (td->flags)
-            RELEASE(td->flags);
-        RELEASE(td);
-    }
-    return;
-}
-
-void sfnt_close(sfnt * sfont)
-{
-    if (sfont) {
-        if (sfont->directory)
-            release_directory(sfont->directory);
-        RELEASE(sfont);
-    }
-    return;
-}
-
-int put_big_endian(void *s, LONG q, int n)
-{
-    int i;
-    char *p;
-    p = (char *) s;
-    for (i = n - 1; i >= 0; i--) {
-        p[i] = (char) (q & 0xff);
-        q >>= 8;
-    }
-    return n;
-}
-
-/*tex
-
-    Convert four-byte number to big endianess in a machine independent way.
-
-*/
-
-static void convert_tag(char *tag, unsigned long u_tag)
-{
-    int i;
-    for (i = 3; i >= 0; i--) {
-        tag[i] = (char) (u_tag % 256);
-        u_tag /= 256;
-    }
-    return;
-}
-
-/*tex
-
-    Computes the max power of $|2 <= n$:
-
-*/
-
-static unsigned max2floor(unsigned n)
-{
-    int val = 1;
-    while (n > 1) {
-        n /= 2;
-        val *= 2;
-    }
-    return (unsigned) val;
-}
-
-/*tex
-
-    Computes the log2 of the max power of $2 <= n$
-
-*/
-
-static unsigned log2floor(unsigned n)
-{
-    unsigned val = 0;
-    while (n > 1) {
-        n /= 2;
-        val++;
-    }
-    return val;
-}
-
-static ULONG sfnt_calc_checksum(void *data, ULONG length)
-{
-    ULONG chksum = 0;
-    BYTE *p, *endptr;
-    ULONG count = 0;
-    p = (BYTE *) data;
-    endptr = p + length;
-    while (p < endptr) {
-        chksum = chksum + (ULONG) (p[0] << (8 * (3 - count)));
-        count = ((count + 1) & 3);
-        p++;
-    }
-    return chksum;
-}
-
-static int find_table_index(struct sfnt_table_directory *td, const char *tag)
-{
-    int idx;
-    if (!td)
-        return -1;
-    for (idx = 0; idx < td->num_tables; idx++) {
-        if (!memcmp(td->tables[idx].tag, tag, 4))
-            return idx;
-    }
-    return -1;
-}
-
-void sfnt_set_table(sfnt * sfont, const char *tag, void *data, ULONG length)
-{
-    struct sfnt_table_directory *td;
-    int idx;
-    td = sfont->directory;
-    idx = find_table_index(td, tag);
-    if (idx < 0) {
-        idx = td->num_tables;
-        td->num_tables++;
-        td->tables = RENEW(td->tables, td->num_tables, struct sfnt_table);
-        memcpy(td->tables[idx].tag, tag, 4);
-    }
-    td->tables[idx].check_sum = sfnt_calc_checksum(data, length);
-    td->tables[idx].offset = 0L;
-    td->tables[idx].length = length;
-    td->tables[idx].data = data;
-    return;
-}
-
-ULONG sfnt_find_table_len(sfnt * sfont, const char *tag)
-{
-    ULONG length;
-    struct sfnt_table_directory *td;
-    int idx;
-    td = sfont->directory;
-    idx = find_table_index(td, tag);
-    if (idx < 0)
-        length = 0;
-    else {
-        length = td->tables[idx].length;
-    }
-    return length;
-}
-
-ULONG sfnt_find_table_pos(sfnt * sfont, const char *tag)
-{
-    ULONG offset;
-    struct sfnt_table_directory *td;
-    int idx;
-    td = sfont->directory;
-    idx = find_table_index(td, tag);
-    if (idx < 0)
-        offset = 0;
-    else {
-        offset = td->tables[idx].offset;
-    }
-    return offset;
-}
-
-ULONG sfnt_locate_table(sfnt * sfont, const char *tag)
-{
-    ULONG offset;
-    offset = sfnt_find_table_pos(sfont, tag);
-    if (offset == 0)
-        normal_error("ttf","sfnt table not found");
-    sfnt_seek_set(sfont, (long) offset);
-    return offset;
-}
-
-int sfnt_read_table_directory(sfnt * sfont, ULONG offset)
-{
-    struct sfnt_table_directory *td;
-    unsigned long i, u_tag;
-    if (sfont->directory)
-        release_directory(sfont->directory);
-    sfont->directory = td = NEW(1, struct sfnt_table_directory);
-    sfnt_seek_set(sfont, (long) offset);
-    td->version = sfnt_get_ulong(sfont);
-    td->num_tables = sfnt_get_ushort(sfont);
-    td->search_range = sfnt_get_ushort(sfont);
-    td->entry_selector = sfnt_get_ushort(sfont);
-    td->range_shift = sfnt_get_ushort(sfont);
-    td->flags = NEW(td->num_tables, char);
-    td->tables = NEW(td->num_tables, struct sfnt_table);
-    for (i = 0; i < td->num_tables; i++) {
-        u_tag = sfnt_get_ulong(sfont);
-        convert_tag(td->tables[i].tag, u_tag);
-        td->tables[i].check_sum = sfnt_get_ulong(sfont);
-        td->tables[i].offset = sfnt_get_ulong(sfont);
-        td->tables[i].length = sfnt_get_ulong(sfont);
-        td->tables[i].data = NULL;
-        td->flags[i] = 0;
-    }
-    td->num_kept_tables = 0;
-    return 0;
-}
-
-int sfnt_require_table(sfnt * sfont, const char *tag, int must_exist)
-{
-    struct sfnt_table_directory *td;
-    int idx;
-    td = sfont->directory;
-    idx = find_table_index(td, tag);
-    if (idx < 0) {
-        if (must_exist)
-            return -1;
-    } else {
-        td->flags[idx] |= SFNT_TABLE_REQUIRED;
-        td->num_kept_tables++;
-    }
-    return 0;
-}
-
-/*tex
-
-    All tables begin on four byte boundries, and pad any remaining space between
-    tables with zeros. Entries in the Table Directory must be sorted in ascending
-    order by tag The head table contains checksum of the whole font file. To
-    compute: first set it to 0, sum the entire font as ULONG, then store
-    0xB1B0AFBA - sum.
-
-*/
-
-#  include "font/luatexfont.h"
-#  undef MIN
-#  define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#  define STREAM_COMPRESS
-
-static unsigned char wbuf[1024], padbytes[4] = { 0, 0, 0, 0 };
-
-pdf_obj *sfnt_create_FontFile_stream(sfnt * sfont)
-{
-    pdf_obj *stream;
-    struct sfnt_table_directory *td;
-    long offset, nb_read, length;
-    int i, sr;
-    char *p;
-    stream = pdf_new_stream(STREAM_COMPRESS);
-    td = sfont->directory;
-    p = (char *) wbuf;
-    p += sfnt_put_ulong(p, (LONG) td->version);
-    p += sfnt_put_ushort(p, td->num_kept_tables);
-    sr = (int) (max2floor(td->num_kept_tables) * 16);
-    p += sfnt_put_ushort(p, sr);
-    p += sfnt_put_ushort(p, log2floor(td->num_kept_tables));
-    p += sfnt_put_ushort(p, td->num_kept_tables * 16 - sr);
-    pdf_add_stream(stream, wbuf, 12);
-    /*tex Compute start of actual tables (after headers). */
-    offset = 12 + 16 * td->num_kept_tables;
-    for (i = 0; i < td->num_tables; i++) {
-        /*tex This table must exist in |FontFile|: */
-        if (td->flags[i] & SFNT_TABLE_REQUIRED) {
-            if ((offset % 4) != 0) {
-                offset += 4 - (offset % 4);
-            }
-            p = (char *) wbuf;
-            memcpy(p, td->tables[i].tag, 4);
-            p += 4;
-            p += sfnt_put_ulong(p, (LONG) td->tables[i].check_sum);
-            p += sfnt_put_ulong(p, offset);
-            p += sfnt_put_ulong(p, (LONG) td->tables[i].length);
-            pdf_add_stream(stream, wbuf, 16);
-            offset = (long) (offset + (long) td->tables[i].length);
-        }
-    }
-    offset = 12 + 16 * td->num_kept_tables;
-    for (i = 0; i < td->num_tables; i++) {
-        if (td->flags[i] & SFNT_TABLE_REQUIRED) {
-            if ((offset % 4) != 0) {
-                length = 4 - (offset % 4);
-                pdf_add_stream(stream, padbytes, length);
-                offset += length;
-            }
-            if (!td->tables[i].data) {
-                if (!sfont->buffer) {
-                    pdf_release_obj(stream);
-                    normal_error("ttf","file not opened or already closed");
-                    return NULL;
-                }
-                length = (long) td->tables[i].length;
-                sfnt_seek_set(sfont, (long) td->tables[i].offset);
-                while (length > 0) {
-                    nb_read = sfnt_read(wbuf, (int) MIN(length, 1024), sfont);
-                    if (nb_read < 0) {
-                        pdf_release_obj(stream);
-                        normal_error("ttf","reading file failed");
-                        return NULL;
-                    } else if (nb_read > 0) {
-                        pdf_add_stream(stream, wbuf, nb_read);
-                    }
-                    length -= nb_read;
-                }
-            } else {
-                pdf_add_stream(stream,
-                               (unsigned char *) td->tables[i].data,
-                               (long) td->tables[i].length);
-                RELEASE(td->tables[i].data);
-                td->tables[i].data = NULL;
-            }
-            /*tex Set offset for next table. */
-            offset = (long) (offset + (long) td->tables[i].length);
-        }
-    }
-    return stream;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/subfont.w
@@ -0,0 +1,265 @@
+% subfont.w
+%
+% Copyright 2005-2006 Han The Thanh <thanh@@pdftex.org>
+% Copyright 2006-2008 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 <string.h>
+
+@ @c
+static struct avl_table *sfd_tree = NULL;
+
+static unsigned char *sfd_buffer = NULL;
+static int sfd_size = 0;
+static int sfd_curbyte = 0;
+
+#define SFD_BUF_SIZE    SMALL_BUF_SIZE
+
+#define sfd_close()     xfclose(sfd_file, cur_file_name)
+#define sfd_open(a)      (sfd_file = fopen((char *)(a), FOPEN_RBIN_MODE))
+
+#define sfd_read_file() readbinfile(sfd_file,&sfd_buffer,&sfd_size)
+#define sfd_getchar()   sfd_buffer[sfd_curbyte++]
+#define sfd_eof()      (sfd_curbyte>=sfd_size)
+
+
+static FILE *sfd_file;
+static char sfd_line[SFD_BUF_SIZE];
+
+static subfont_entry *new_subfont_entry(void)
+{
+    int i;
+    subfont_entry *subfont;
+    subfont = xtalloc(1, subfont_entry);
+    subfont->infix = NULL;
+    for (i = 0; i < 256; ++i)
+        subfont->charcodes[i] = -1;     /* unassigned */
+    subfont->next = NULL;
+    return subfont;
+}
+
+static sfd_entry *new_sfd_entry(void)
+{
+    sfd_entry *sfd;
+    sfd = xtalloc(1, sfd_entry);
+    sfd->name = NULL;
+    sfd->subfont = NULL;
+    return sfd;
+}
+
+static void destroy_sfd_entry(void *pa, void *pb)
+{
+    subfont_entry *p, *q;
+    sfd_entry *sfd;
+    sfd = (sfd_entry *) pa;
+    (void) pb;
+    p = sfd->subfont;
+    while (p != NULL) {
+        q = p->next;
+        xfree(p->infix);
+        xfree(p);
+        p = q;
+    }
+    xfree(sfd->name);
+}
+
+static int comp_sfd_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return strcmp(((const sfd_entry *) pa)->name,
+                  ((const sfd_entry *) pb)->name);
+}
+
+void sfd_free(void)
+{
+    if (sfd_tree != NULL)
+        avl_destroy(sfd_tree, destroy_sfd_entry);
+}
+
+static void sfd_getline(boolean expect_eof)
+{
+    char *p;
+    char c;
+  restart:
+    if (sfd_eof()) {
+        if (expect_eof) {
+            if (*sfd_line == '#')
+                *sfd_line = 10;
+            return;
+        } else
+            normal_error("sub font","unexpected end of file");
+    }
+    p = sfd_line;
+    do {
+        c = (char) sfd_getchar();
+        append_char_to_buf(c, p, sfd_line, SFD_BUF_SIZE);
+    } while (c != 10 && !sfd_eof());
+    append_eol(p, sfd_line, SFD_BUF_SIZE);
+    if (p - sfd_line < 2 || *sfd_line == '#')
+        goto restart;
+}
+
+@ @c
+static sfd_entry *read_sfd(char *sfd_name)
+{
+    void **aa;
+    sfd_entry *sfd, tmp_sfd;
+    subfont_entry *sf;
+
+    char buf[SMALL_BUF_SIZE], *p;
+    long int i, j, k;
+    int n;
+    int callback_id = 0;
+    int file_opened = 0;
+    /* check whether this sfd has been read */
+    tmp_sfd.name = sfd_name;
+    if (sfd_tree == NULL) {
+        sfd_tree = avl_create(comp_sfd_entry, NULL, &avl_xallocator);
+        assert(sfd_tree != NULL);
+    }
+    sfd = (sfd_entry *) avl_find(sfd_tree, &tmp_sfd);
+    if (sfd != NULL)
+        return sfd;
+    xfree(sfd_buffer);
+    sfd_curbyte = 0;
+    sfd_size = 0;
+
+    cur_file_name = luatex_find_file(sfd_name, find_sfd_file_callback);
+    if (cur_file_name) {
+        callback_id = callback_defined(read_sfd_file_callback);
+        if (callback_id > 0) {
+            if (!(run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &sfd_buffer, &sfd_size) &&
+                  file_opened && sfd_size > 0)) {
+                formatted_warning("ttf font","cannot open SFD file for reading '%s'", cur_file_name);
+                cur_file_name = NULL;
+                return NULL;
+            }
+        } else {
+            if (!sfd_open(cur_file_name)) {
+                formatted_warning("ttf font", "cannot open SFD file for reading '%s'", cur_file_name);
+                cur_file_name = NULL;
+                return NULL;
+            }
+            sfd_read_file();
+            sfd_close();
+        }
+    }
+    tex_printf("{");
+    tex_printf("%s", cur_file_name);
+    sfd = new_sfd_entry();
+    sfd->name = xstrdup(sfd_name);
+    while (!sfd_eof()) {
+        sfd_getline(true);
+        if (*sfd_line == 10)    /* empty line indicating eof */
+            break;
+        sf = new_subfont_entry();
+        sf->next = sfd->subfont;
+        sfd->subfont = sf;
+        sscanf(sfd_line, "%s %n", buf, &n);
+        sf->infix = xstrdup(buf);
+        p = sfd_line + n;       /* skip to the next word */
+        k = 0;
+      read_ranges:
+        for (;;) {
+            if (*p == '\\') {   /* continue on next line */
+                sfd_getline(false);
+                p = sfd_line;
+                goto read_ranges;
+            } else if (*p == 0) /* end of subfont */
+                break;
+            if (sscanf(p, " %li %n", &i, &n) == 0)
+                formatted_error("sub font","invalid token: %s", p);
+            p += n;
+            if (*p == ':') {    /* offset */
+                k = i;
+                p++;
+            } else if (*p == '_') {     /* range */
+                if (sscanf(p + 1, " %li %n", &j, &n) == 0)
+                    formatted_error("sub font","invalid token: %s", p);
+                if (i > j || k + (j - i) > 255)
+                    formatted_error("sub font","invalid range: %s", p);
+                while (i <= j)
+                    sf->charcodes[k++] = i++;
+                p += n + 1;
+            } else              /* codepoint */
+                sf->charcodes[k++] = i;
+        }
+    }
+    tex_printf("}");
+    aa = avl_probe(sfd_tree, sfd);
+    assert(aa != NULL);
+    return sfd;
+}
+
+@ @c
+boolean handle_subfont_fm(fm_entry * fm, int mode)
+{
+    size_t l;
+    char *p, *q, *r;
+    sfd_entry *sfd;
+    subfont_entry *sf;
+    fm_entry *fm2;
+    char buf[SMALL_BUF_SIZE];
+    assert(fm->tfm_name != NULL);
+    p = fm->tfm_name;
+    q = strchr(p, '@@');         /* search for the first '@@' */
+    if (q == NULL)
+        return false;
+    r = strchr(q + 1, '@@');     /* search for the second '@@' */
+    if (r == NULL)
+        return false;
+    if (q <= p || r <= q + 1    /* prefix or sfd name is empty */
+        || r - p != (int) strlen(p) - 1)        /* or the second '@@' is not the last char yet */
+        return false;
+    l = (size_t) (r - (q + 1)); /* length of sfd name */
+    strncpy(buf, q + 1, l);
+    buf[l] = 0;
+    check_buf(strlen(buf) + 4, SMALL_BUF_SIZE);
+    strcat(buf, ".sfd");
+    sfd = read_sfd(buf);
+    if (sfd == NULL)
+        return false;
+    /* at this point we know fm is a subfont */
+    set_subfont(fm);
+    xfree(fm->ps_name);
+    /* set default values for PidEid */
+    if (fm->pid == -1) {
+        fm->pid = 3;
+        fm->eid = 1;
+    }
+    l = (size_t) (q - p);       /* length of base tfm name (prefix) */
+    for (sf = sfd->subfont; sf != NULL; sf = sf->next) {
+        strncpy(buf, p, l);
+        buf[l] = 0;
+        strcat(buf, sf->infix);
+        fm2 = new_fm_entry();
+        fm2->tfm_name = xstrdup(buf);
+        fm2->ff_name = xstrdup(fm->ff_name);
+        fm2->type = fm->type;
+        fm2->pid = fm->pid;
+        fm2->eid = fm->eid;
+        fm2->subfont = sf;
+        if (avl_do_entry(fm2, mode) != 0)       /* try to insert the entry */
+            delete_fm_entry(fm2);       /* delete it if failed */
+    }
+    delete_fm_entry(fm);
+    return true;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/subfont.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
-
-Copyright 2005-2006 Han The Thanh <thanh@pdftex.org>
-Copyright 2006-2008 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <string.h>
-
-static struct avl_table *sfd_tree = NULL;
-
-static unsigned char *sfd_buffer = NULL;
-static int sfd_size = 0;
-static int sfd_curbyte = 0;
-
-#define SFD_BUF_SIZE    SMALL_BUF_SIZE
-
-#define sfd_close()     xfclose(sfd_file, cur_file_name)
-#define sfd_open(a)     (sfd_file = fopen((char *)(a), FOPEN_RBIN_MODE))
-
-#define sfd_read_file() readbinfile(sfd_file,&sfd_buffer,&sfd_size)
-#define sfd_getchar()   sfd_buffer[sfd_curbyte++]
-#define sfd_eof()       (sfd_curbyte>=sfd_size)
-
-static FILE *sfd_file;
-static char sfd_line[SFD_BUF_SIZE];
-
-static subfont_entry *new_subfont_entry(void)
-{
-    int i;
-    subfont_entry *subfont;
-    subfont = xtalloc(1, subfont_entry);
-    subfont->infix = NULL;
-    for (i = 0; i < 256; ++i) {
-        /*tex Unassigned: */
-        subfont->charcodes[i] = -1;
-    }
-    subfont->next = NULL;
-    return subfont;
-}
-
-static sfd_entry *new_sfd_entry(void)
-{
-    sfd_entry *sfd;
-    sfd = xtalloc(1, sfd_entry);
-    sfd->name = NULL;
-    sfd->subfont = NULL;
-    return sfd;
-}
-
-static void destroy_sfd_entry(void *pa, void *pb)
-{
-    subfont_entry *p, *q;
-    sfd_entry *sfd;
-    sfd = (sfd_entry *) pa;
-    (void) pb;
-    p = sfd->subfont;
-    while (p != NULL) {
-        q = p->next;
-        xfree(p->infix);
-        xfree(p);
-        p = q;
-    }
-    xfree(sfd->name);
-}
-
-static int comp_sfd_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return strcmp(((const sfd_entry *) pa)->name, ((const sfd_entry *) pb)->name);
-}
-
-void sfd_free(void)
-{
-    if (sfd_tree != NULL)
-        avl_destroy(sfd_tree, destroy_sfd_entry);
-}
-
-static void sfd_getline(boolean expect_eof)
-{
-    char *p;
-    char c;
-  restart:
-    if (sfd_eof()) {
-        if (expect_eof) {
-            if (*sfd_line == '#')
-                *sfd_line = 10;
-            return;
-        } else
-            normal_error("sub font","unexpected end of file");
-    }
-    p = sfd_line;
-    do {
-        c = (char) sfd_getchar();
-        append_char_to_buf(c, p, sfd_line, SFD_BUF_SIZE);
-    } while (c != 10 && !sfd_eof());
-    append_eol(p, sfd_line, SFD_BUF_SIZE);
-    if (p - sfd_line < 2 || *sfd_line == '#')
-        goto restart;
-}
-
-static sfd_entry *read_sfd(char *sfd_name)
-{
-    void **aa;
-    sfd_entry *sfd, tmp_sfd;
-    subfont_entry *sf;
-    char buf[SMALL_BUF_SIZE], *p;
-    long int i, j, k;
-    int n;
-    int callback_id = 0;
-    int file_opened = 0;
-    /*tex Check whether this |sfd| has been read: */
-    tmp_sfd.name = sfd_name;
-    if (sfd_tree == NULL) {
-        sfd_tree = avl_create(comp_sfd_entry, NULL, &avl_xallocator);
-        assert(sfd_tree != NULL);
-    }
-    sfd = (sfd_entry *) avl_find(sfd_tree, &tmp_sfd);
-    if (sfd != NULL)
-        return sfd;
-    xfree(sfd_buffer);
-    sfd_curbyte = 0;
-    sfd_size = 0;
-    cur_file_name = luatex_find_file(sfd_name, find_sfd_file_callback);
-    if (cur_file_name) {
-        callback_id = callback_defined(read_sfd_file_callback);
-        if (callback_id > 0) {
-            if (!(run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &sfd_buffer, &sfd_size) &&
-                  file_opened && sfd_size > 0)) {
-                formatted_warning("ttf font","cannot open SFD file for reading '%s'", cur_file_name);
-                cur_file_name = NULL;
-                return NULL;
-            }
-        } else {
-            if (!sfd_open(cur_file_name)) {
-                formatted_warning("ttf font", "cannot open SFD file for reading '%s'", cur_file_name);
-                cur_file_name = NULL;
-                return NULL;
-            }
-            sfd_read_file();
-            sfd_close();
-        }
-    }
-    tex_printf("{");
-    tex_printf("%s", cur_file_name);
-    sfd = new_sfd_entry();
-    sfd->name = xstrdup(sfd_name);
-    while (!sfd_eof()) {
-        sfd_getline(true);
-        if (*sfd_line == 10) {
-            /*tex An empty line indicates |EOF|. */
-            break;
-        }
-        sf = new_subfont_entry();
-        sf->next = sfd->subfont;
-        sfd->subfont = sf;
-        sscanf(sfd_line, "%s %n", buf, &n);
-        sf->infix = xstrdup(buf);
-        /*tex Skip to the next word: */
-        p = sfd_line + n;
-        k = 0;
-      read_ranges:
-        for (;;) {
-            if (*p == '\\') {
-                /*tex Continue on the next line: */
-                sfd_getline(false);
-                p = sfd_line;
-                goto read_ranges;
-            } else if (*p == 0) {
-                /*tex End of subfont. */
-                break;
-            }
-            if (sscanf(p, " %li %n", &i, &n) == 0)
-                formatted_error("sub font","invalid token: %s", p);
-            p += n;
-            if (*p == ':') {
-                /*tex Variant: offset */
-                k = i;
-                p++;
-            } else if (*p == '_') {
-                /*tex Variant: range */
-                if (sscanf(p + 1, " %li %n", &j, &n) == 0)
-                    formatted_error("sub font","invalid token: %s", p);
-                if (i > j || k + (j - i) > 255)
-                    formatted_error("sub font","invalid range: %s", p);
-                while (i <= j)
-                    sf->charcodes[k++] = i++;
-                p += n + 1;
-            } else {
-                /*tex Variant: codepoint */
-                sf->charcodes[k++] = i;
-            }
-        }
-    }
-    tex_printf("}");
-    aa = avl_probe(sfd_tree, sfd);
-    assert(aa != NULL);
-    return sfd;
-}
-
-boolean handle_subfont_fm(fm_entry * fm, int mode)
-{
-    size_t l;
-    char *p, *q, *r;
-    sfd_entry *sfd;
-    subfont_entry *sf;
-    fm_entry *fm2;
-    char buf[SMALL_BUF_SIZE];
-    assert(fm->tfm_name != NULL);
-    p = fm->tfm_name;
-    /*tex Search for the first |@|. */
-    q = strchr(p, '@');
-    if (q == NULL)
-        return false;
-    /*tex Search for the second |@|: */
-    r = strchr(q + 1, '@');
-    if (r == NULL)
-        return false;
-    /*tex Check if prefix or sfd name is empty or the second |@| is not the last char yet. */
-    if (q <= p || r <= q + 1 || r - p != (int) strlen(p) - 1) {
-        return false;
-    }
-    /*tex The length of sfd name: */
-    l = (size_t) (r - (q + 1));
-    strncpy(buf, q + 1, l);
-    buf[l] = 0;
-    check_buf(strlen(buf) + 4, SMALL_BUF_SIZE);
-    strcat(buf, ".sfd");
-    sfd = read_sfd(buf);
-    if (sfd == NULL)
-        return false;
-    /*tex At this point we know fm is a subfont. */
-    set_subfont(fm);
-    xfree(fm->ps_name);
-    /*tex Set default values for |Pid| and |Eid|: */
-    if (fm->pid == -1) {
-        fm->pid = 3;
-        fm->eid = 1;
-    }
-    /*tex Calculate the length of base tfm name (prefix). */
-    l = (size_t) (q - p);
-    for (sf = sfd->subfont; sf != NULL; sf = sf->next) {
-        strncpy(buf, p, l);
-        buf[l] = 0;
-        strcat(buf, sf->infix);
-        fm2 = new_fm_entry();
-        fm2->tfm_name = xstrdup(buf);
-        fm2->ff_name = xstrdup(fm->ff_name);
-        fm2->type = fm->type;
-        fm2->pid = fm->pid;
-        fm2->eid = fm->eid;
-        fm2->subfont = sf;
-        /*tex Try to insert the entry. */
-        if (avl_do_entry(fm2, mode) != 0) {
-            /*tex And delete it if we failed. */
-            delete_fm_entry(fm2);
-        }
-    }
-    delete_fm_entry(fm);
-    return true;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/texfont.w
@@ -0,0 +1,1972 @@
+% texfont.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/>.
+
+@* Main font API implementation for the original pascal parts.
+
+Stuff to watch out for:
+
+\item{} Knuth had a |'null_character'| that was used when a character could
+not be found by the |fetch()| routine, to signal an error. This has
+been deleted, but it may mean that the output of luatex is
+incompatible with TeX after |fetch()| has detected an error condition.
+
+\item{} Knuth also had a |font_glue()| optimization. I've removed that
+because it was a bit of dirty programming and it also was
+problematic |if 0 != null|.
+
+@c
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+@ @c
+#define noDEBUG
+
+#define proper_char_index(c) (c<=font_ec(f) && c>=font_bc(f))
+#define do_realloc(a,b,d)    a = xrealloc(a,(unsigned)((unsigned)(b)*sizeof(d)))
+
+texfont **font_tables = NULL;
+
+static int font_arr_max = 0;
+static int font_id_maxval = 0;
+
+@ @c
+static void grow_font_table(int id)
+{
+    int j;
+    if (id >= font_arr_max) {
+        font_bytes +=
+            (int) (((id + 8 - font_arr_max) * (int) sizeof(texfont *)));
+        font_tables =
+            xrealloc(font_tables,
+                     (unsigned) (((unsigned) id + 8) * sizeof(texfont *)));
+        j = 8;
+        while (j--) {
+            font_tables[id + j] = NULL;
+        }
+        font_arr_max = id + 8;
+    }
+}
+
+int new_font_id(void)
+{
+    int i;
+    for (i = 0; i < font_arr_max; i++) {
+        if (font_tables[i] == NULL) {
+            break;
+        }
+    }
+    if (i >= font_arr_max)
+        grow_font_table(i);
+    if (i > font_id_maxval)
+        font_id_maxval = i;
+    return i;
+}
+
+int max_font_id(void)
+{
+    return font_id_maxval;
+}
+
+void set_max_font_id(int i)
+{
+    font_id_maxval = i;
+}
+
+@ @c
+int new_font(void)
+{
+    int k;
+    int id;
+    charinfo *ci;
+    sa_tree_item sa_value = { 0 };
+    id = new_font_id();
+    font_bytes += (int) sizeof(texfont);
+    /* most stuff is zero */
+    font_tables[id] = xcalloc(1, sizeof(texfont));
+    font_tables[id]->_font_name = NULL;
+    font_tables[id]->_font_area = NULL;
+    font_tables[id]->_font_filename = NULL;
+    font_tables[id]->_font_fullname = NULL;
+    font_tables[id]->_font_psname = NULL;
+    font_tables[id]->_font_encodingname = NULL;
+    font_tables[id]->_font_cidregistry = NULL;
+    font_tables[id]->_font_cidordering = NULL;
+    font_tables[id]->_left_boundary = NULL;
+    font_tables[id]->_right_boundary = NULL;
+    font_tables[id]->_param_base = NULL;
+    font_tables[id]->_math_param_base = NULL;
+
+    set_font_bc(id, 1);          /* ec = 0 */
+    set_font_writingmode(id, 0);
+    set_font_identity(id, 0);
+    set_hyphen_char(id, '-');
+    set_skew_char(id, -1);
+    font_slant(id) = 0;          /* vertical */
+    font_extend(id) = 1000;      /* normal width */
+
+    /* allocate eight values including 0 */
+    set_font_params(id, 7);
+    for (k = 0; k <= 7; k++) {
+        set_font_param(id, k, 0);
+    }
+    /* character info zero is reserved for notdef */
+    font_tables[id]->characters = new_sa_tree(1, 1, sa_value);    /* stack size 1, default item value 0 */
+    ci = xcalloc(1, sizeof(charinfo));
+    set_charinfo_name(ci, xstrdup(".notdef"));
+    font_tables[id]->charinfo = ci;
+    font_tables[id]->charinfo_size = 1;
+    font_tables[id]->charinfo_cache = NULL;
+
+    return id;
+}
+
+@ @c
+void font_malloc_charinfo(internal_font_number f, int num)
+{
+    int glyph = font_tables[f]->charinfo_size;
+    font_bytes += (int) (num * (int) sizeof(charinfo));
+    do_realloc(font_tables[f]->charinfo, (unsigned) (glyph + num), charinfo);
+    memset(&(font_tables[f]->charinfo[glyph]), 0,
+           (size_t) (num * (int) sizeof(charinfo)));
+    font_tables[f]->charinfo_size += num;
+}
+
+@ @c
+#define find_charinfo_id(f,c) (get_sa_item(font_tables[f]->characters,c).int_value)
+
+charinfo *get_charinfo(internal_font_number f, int c)
+{
+    int glyph;
+    charinfo *ci;
+    if (proper_char_index(c)) {
+        glyph = get_sa_item(font_tables[f]->characters, c).int_value;
+        if (!glyph) {
+            sa_tree_item sa_value = { 0 };
+            int tglyph = ++font_tables[f]->charinfo_count;
+            if (tglyph >= font_tables[f]->charinfo_size) {
+                font_malloc_charinfo(f, 256);
+            }
+            font_tables[f]->charinfo[tglyph].ef = 1000; /* init */
+            sa_value.int_value = tglyph;
+            set_sa_item(font_tables[f]->characters, c, sa_value, 1); /* 1 = global */
+            glyph = tglyph;
+        }
+        return &(font_tables[f]->charinfo[glyph]);
+    } else if (c == left_boundarychar) {
+        if (left_boundary(f) == NULL) {
+            ci = xcalloc(1, sizeof(charinfo));
+            font_bytes += (int) sizeof(charinfo);
+            set_left_boundary(f, ci);
+        }
+        return left_boundary(f);
+    } else if (c == right_boundarychar) {
+        if (right_boundary(f) == NULL) {
+            ci = xcalloc(1, sizeof(charinfo));
+            font_bytes += (int) sizeof(charinfo);
+            set_right_boundary(f, ci);
+        }
+        return right_boundary(f);
+    }
+    return &(font_tables[f]->charinfo[0]);
+}
+
+@ @c
+static void set_charinfo(internal_font_number f, int c, charinfo * ci)
+{
+    int glyph;
+    if (proper_char_index(c)) {
+        glyph = get_sa_item(font_tables[f]->characters, c).int_value;
+        if (glyph) {
+            font_tables[f]->charinfo[glyph] = *ci;
+        } else {
+            normal_error("font","character insertion failed");
+        }
+    } else if (c == left_boundarychar) {
+        set_left_boundary(f, ci);
+    } else if (c == right_boundarychar) {
+        set_right_boundary(f, ci);
+    }
+}
+
+@ @c
+charinfo *copy_charinfo(charinfo * ci)
+{
+    int x, k;
+    kerninfo *kern;
+    liginfo *lig;
+    eight_bits *packet;
+    charinfo *co = NULL;
+    if (ci == NULL)
+        return NULL;
+    co = xmalloc(sizeof(charinfo));
+    memcpy(co, ci, sizeof(charinfo));
+    set_charinfo_used(co, false);
+    co->name = NULL;
+    co->tounicode = NULL;
+    co->packets = NULL;
+    co->ligatures = NULL;
+    co->kerns = NULL;
+    co->vert_variants = NULL;
+    co->hor_variants = NULL;
+    if (ci->name != NULL) {
+        co->name = xstrdup(ci->name);
+    }
+    if (ci->tounicode != NULL) {
+        co->tounicode = xstrdup(ci->tounicode);
+    }
+    /* kerns */
+    if ((kern = get_charinfo_kerns(ci)) != NULL) {
+        x = 0;
+        while (!kern_end(kern[x])) {
+            x++;
+        }
+        x++;
+        co->kerns = xmalloc((unsigned) (x * (int) sizeof(kerninfo)));
+        memcpy(co->kerns, ci->kerns, (size_t) (x * (int) sizeof(kerninfo)));
+    }
+    /* ligs */
+    if ((lig = get_charinfo_ligatures(ci)) != NULL) {
+        x = 0;
+        while (!lig_end(lig[x])) {
+            x++;
+        }
+        x++;
+        co->ligatures = xmalloc((unsigned) (x * (int) sizeof(liginfo)));
+        memcpy(co->ligatures, ci->ligatures,
+               (size_t) (x * (int) sizeof(liginfo)));
+    }
+    /* packets */
+    if ((packet = get_charinfo_packets(ci)) != NULL) {
+        x = vf_packet_bytes(ci);
+        co->packets = xmalloc((unsigned) x);
+        memcpy(co->packets, ci->packets, (size_t) x);
+    }
+
+    /* horizontal and vertical extenders */
+    if (get_charinfo_vert_variants(ci) != NULL) {
+        set_charinfo_vert_variants(co, copy_variants(get_charinfo_vert_variants(ci)));
+    }
+    if (get_charinfo_hor_variants(ci) != NULL) {
+        set_charinfo_hor_variants(co, copy_variants(get_charinfo_hor_variants(ci)));
+    }
+    x = ci->top_left_math_kerns;
+    co->top_left_math_kerns = x;
+    if (x > 0) {
+        co->top_left_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
+        for (k = 0; k < co->top_left_math_kerns; k++) {
+            co->top_left_math_kern_array[(2 * k)] = ci->top_left_math_kern_array[(2 * k)];
+            co->top_left_math_kern_array[(2 * k) + 1] = ci->top_left_math_kern_array[(2 * k) + 1];
+        }
+    }
+    x = ci->bottom_left_math_kerns;
+    co->bottom_left_math_kerns = x;
+    if (x > 0) {
+        co->bottom_left_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
+        for (k = 0; k < co->bottom_left_math_kerns; k++) {
+            co->bottom_left_math_kern_array[(2 * k)] = ci->bottom_left_math_kern_array[(2 * k)];
+            co->bottom_left_math_kern_array[(2 * k) + 1] = ci->bottom_left_math_kern_array[(2 * k) + 1];
+        }
+    }
+    x = ci->top_right_math_kerns;
+    co->top_right_math_kerns = x;
+    if (x > 0) {
+        co->top_right_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
+        for (k = 0; k < co->top_right_math_kerns; k++) {
+            co->top_right_math_kern_array[(2 * k)] = ci->top_right_math_kern_array[(2 * k)];
+            co->top_right_math_kern_array[(2 * k) + 1] = ci->top_right_math_kern_array[(2 * k) + 1];
+        }
+    }
+    x = ci->bottom_right_math_kerns;
+    co->bottom_right_math_kerns = x;
+    if (x > 0) {
+        co->bottom_right_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
+        for (k = 0; k < co->bottom_right_math_kerns; k++) {
+            co->bottom_right_math_kern_array[(2 * k)] = ci->bottom_right_math_kern_array[(2 * k)];
+            co->bottom_right_math_kern_array[(2 * k) + 1] = ci->bottom_right_math_kern_array[(2 * k) + 1];
+        }
+    }
+    return co;
+}
+
+charinfo *char_info(internal_font_number f, int c)
+{
+    if (f > font_id_maxval)
+        return 0;
+    if (proper_char_index(c)) {
+        register int glyph = (int) find_charinfo_id(f, c);
+        return &(font_tables[f]->charinfo[glyph]);
+    } else if (c == left_boundarychar && left_boundary(f) != NULL) {
+        return left_boundary(f);
+    } else if (c == right_boundarychar && right_boundary(f) != NULL) {
+        return right_boundary(f);
+    }
+    return &(font_tables[f]->charinfo[0]);
+}
+
+@ @c
+scaled_whd get_charinfo_whd(internal_font_number f, int c)
+{
+    scaled_whd s;
+    charinfo *i;
+    i = char_info(f, c);
+    s.wd = i->width;
+    s.dp = i->depth;
+    s.ht = i->height;
+    return s;
+}
+
+@ @c
+int char_exists(internal_font_number f, int c)
+{
+    if (f > font_id_maxval)
+        return 0;
+    if (proper_char_index(c)) {
+        return (int) find_charinfo_id(f, c);
+    } else if ((c == left_boundarychar) && has_left_boundary(f)) {
+        return 1;
+    } else if ((c == right_boundarychar) && has_right_boundary(f)) {
+        return 1;
+    }
+    return 0;
+}
+
+@ @c
+int lua_glyph_not_found_callback(internal_font_number f, int c)
+{
+    int callback_id;
+    int ret = 0;
+    callback_id = callback_defined(glyph_not_found_callback);
+    if (callback_id != 0) {
+        if (!get_callback(Luas, callback_id)) {
+            lua_pop(Luas, 2);
+            return 0;
+        }
+        lua_pushinteger(Luas, f);
+        lua_pushinteger(Luas, c);
+        if (lua_pcall(Luas, 2, 1, 0) != 0) {       /* two args, 1 result */
+            fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1));
+            lua_pop(Luas, 2);
+            error();
+        } else {
+            ret = lua_toboolean(Luas, -1);
+        }
+    } else {
+        char_warning(f,c);
+    }
+    return ret;
+}
+
+@ @c
+extinfo *new_variant(int glyph, int startconnect, int endconnect,
+                     int advance, int repeater)
+{
+    extinfo *ext;
+    ext = xmalloc(sizeof(extinfo));
+    ext->next = NULL;
+    ext->glyph = glyph;
+    ext->start_overlap = startconnect;
+    ext->end_overlap = endconnect;
+    ext->advance = advance;
+    ext->extender = repeater;
+    return ext;
+}
+
+
+@ @c
+static extinfo *copy_variant(extinfo * old)
+{
+    extinfo *ext;
+    ext = xmalloc(sizeof(extinfo));
+    ext->next = NULL;
+    ext->glyph = old->glyph;
+    ext->start_overlap = old->start_overlap;
+    ext->end_overlap = old->end_overlap;
+    ext->advance = old->advance;
+    ext->extender = old->extender;
+    return ext;
+}
+
+@ @c
+static void dump_variant(extinfo * ext)
+{
+    dump_int(ext->glyph);
+    dump_int(ext->start_overlap);
+    dump_int(ext->end_overlap);
+    dump_int(ext->advance);
+    dump_int(ext->extender);
+    return;
+}
+
+
+@ @c
+static extinfo *undump_variant(void)
+{
+    int x;
+    extinfo *ext;
+    undump_int(x);
+    if (x == 0)
+        return NULL;
+    ext = xmalloc(sizeof(extinfo));
+    ext->next = NULL;
+    ext->glyph = x;
+    undump_int(x);
+    ext->start_overlap = x;
+    undump_int(x);
+    ext->end_overlap = x;
+    undump_int(x);
+    ext->advance = x;
+    undump_int(x);
+    ext->extender = x;
+    return ext;
+}
+
+@ @c
+void add_charinfo_vert_variant(charinfo * ci, extinfo * ext)
+{
+    if (ci->vert_variants == NULL) {
+        ci->vert_variants = ext;
+    } else {
+        extinfo *lst = ci->vert_variants;
+        while (lst->next != NULL)
+            lst = lst->next;
+        lst->next = ext;
+    }
+
+}
+
+@ @c
+void add_charinfo_hor_variant(charinfo * ci, extinfo * ext)
+{
+    if (ci->hor_variants == NULL) {
+        ci->hor_variants = ext;
+    } else {
+        extinfo *lst = ci->hor_variants;
+        while (lst->next != NULL)
+            lst = lst->next;
+        lst->next = ext;
+    }
+
+}
+
+@ @c
+extinfo *copy_variants(extinfo * o)
+{
+    extinfo *c, *t = NULL, *h = NULL;
+    while (o != NULL) {
+        c = copy_variant(o);
+        if (h == null)
+            h = c;
+        else
+            t->next = c;
+        t = c;
+        o = o->next;
+    }
+
+    return h;
+}
+
+@ @c
+static void dump_charinfo_variants(extinfo * o)
+{
+    while (o != NULL) {
+        dump_variant(o);
+        o = o->next;
+    }
+    dump_int(0);
+    return;
+}
+
+@ @c
+static extinfo *undump_charinfo_variants(void)
+{
+    extinfo *c, *t = NULL, *h = NULL;
+    c = undump_variant();
+    while (c != NULL) {
+        if (h == null)
+            h = c;
+        else
+            t->next = c;
+        t = c;
+        c = undump_variant();
+    }
+    return h;
+}
+
+
+@ Note that mant more small things like this are implemented
+as macros in the header file.
+@c
+void set_charinfo_width(charinfo * ci, scaled val)
+{
+    ci->width = val;
+}
+
+void set_charinfo_height(charinfo * ci, scaled val)
+{
+    ci->height = val;
+}
+
+void set_charinfo_depth(charinfo * ci, scaled val)
+{
+    ci->depth = val;
+}
+
+void set_charinfo_italic(charinfo * ci, scaled val)
+{
+    ci->italic = val;
+}
+
+void set_charinfo_vert_italic(charinfo * ci, scaled val)
+{
+    ci->vert_italic = val;
+}
+
+void set_charinfo_top_accent(charinfo * ci, scaled val)
+{
+    ci->top_accent = val;
+}
+
+void set_charinfo_bot_accent(charinfo * ci, scaled val)
+{
+    ci->bot_accent = val;
+}
+
+void set_charinfo_tag(charinfo * ci, scaled val)
+{
+    ci->tag = (char) val;
+}
+
+void set_charinfo_remainder(charinfo * ci, scaled val)
+{
+    ci->remainder = val;
+}
+
+void set_charinfo_used(charinfo * ci, scaled val)
+{
+    ci->used = (char) val;
+}
+
+void set_charinfo_index(charinfo * ci, scaled val)
+{
+    ci->index = (unsigned short) val;
+}
+
+void set_charinfo_name(charinfo * ci, char *val)
+{
+    xfree(ci->name);
+    ci->name = val;
+}
+
+void set_charinfo_tounicode(charinfo * ci, char *val)
+{
+    xfree(ci->tounicode);
+    ci->tounicode = val;
+}
+
+void set_charinfo_ligatures(charinfo * ci, liginfo * val)
+{
+    dxfree(ci->ligatures, val);
+}
+
+void set_charinfo_kerns(charinfo * ci, kerninfo * val)
+{
+    dxfree(ci->kerns, val);
+}
+
+void set_charinfo_packets(charinfo * ci, eight_bits * val)
+{
+    dxfree(ci->packets, val);
+}
+
+void set_charinfo_ef(charinfo * ci, scaled val)
+{
+    ci->ef = val;
+}
+
+void set_charinfo_lp(charinfo * ci, scaled val)
+{
+    ci->lp = val;
+}
+
+void set_charinfo_rp(charinfo * ci, scaled val)
+{
+    ci->rp = val;
+}
+
+@ @c
+void set_charinfo_vert_variants(charinfo * ci, extinfo * ext)
+{
+    extinfo *c, *lst;
+    if (ci->vert_variants != NULL) {
+        lst = ci->vert_variants;
+        while (lst != NULL) {
+            c = lst->next;
+            free(lst);
+            lst = c;
+        }
+    }
+    ci->vert_variants = ext;
+}
+
+@ @c
+void set_charinfo_hor_variants(charinfo * ci, extinfo * ext)
+{
+    extinfo *c, *lst;
+    if (ci->hor_variants != NULL) {
+        lst = ci->hor_variants;
+        while (lst != NULL) {
+            c = lst->next;
+            free(lst);
+            lst = c;
+        }
+    }
+    ci->hor_variants = ext;
+
+}
+
+@ @c
+int get_charinfo_math_kerns(charinfo * ci, int id)
+{
+
+    int k = 0;                  /* all callers check for |result>0| */
+    if (id == top_left_kern) {
+        k = ci->top_left_math_kerns;
+    } else if (id == bottom_left_kern) {
+        k = ci->bottom_left_math_kerns;
+    } else if (id == top_right_kern) {
+        k = ci->top_right_math_kerns;
+    } else if (id == bottom_right_kern) {
+        k = ci->bottom_right_math_kerns;
+    } else {
+        confusion("get_charinfo_math_kerns");
+    }
+    return k;
+}
+
+@ @c
+void add_charinfo_math_kern(charinfo * ci, int id, scaled ht, scaled krn)
+{
+    int k;
+    if (id == top_left_kern) {
+        k = ci->top_left_math_kerns;
+        do_realloc(ci->top_left_math_kern_array, ((k + 1) * 2), sizeof(scaled));
+        ci->top_left_math_kern_array[(2 * (k))] = ht;
+        ci->top_left_math_kern_array[((2 * (k)) + 1)] = krn;
+        ci->top_left_math_kerns++;
+    } else if (id == bottom_left_kern) {
+        k = ci->bottom_left_math_kerns;
+        do_realloc(ci->bottom_left_math_kern_array, ((k + 1) * 2), sizeof(scaled));
+        ci->bottom_left_math_kern_array[(2 * (k))] = ht;
+        ci->bottom_left_math_kern_array[(2 * (k)) + 1] = krn;
+        ci->bottom_left_math_kerns++;
+    } else if (id == top_right_kern) {
+        k = ci->top_right_math_kerns;
+        do_realloc(ci->top_right_math_kern_array, ((k + 1) * 2), sizeof(scaled));
+        ci->top_right_math_kern_array[(2 * (k))] = ht;
+        ci->top_right_math_kern_array[(2 * (k)) + 1] = krn;
+        ci->top_right_math_kerns++;
+    } else if (id == bottom_right_kern) {
+        k = ci->bottom_right_math_kerns;
+        do_realloc(ci->bottom_right_math_kern_array, ((k + 1) * 2), sizeof(scaled));
+        ci->bottom_right_math_kern_array[(2 * (k))] = ht;
+        ci->bottom_right_math_kern_array[(2 * (k)) + 1] = krn;
+        ci->bottom_right_math_kerns++;
+    } else {
+        confusion("add_charinfo_math_kern");
+    }
+}
+
+
+@ @c
+static void dump_math_kerns(charinfo * ci)
+{
+    int k, l;
+    l = ci->top_left_math_kerns;
+    dump_int(l);
+    for (k = 0; k < l; k++) {
+        dump_int(ci->top_left_math_kern_array[(2 * k)]);
+        dump_int(ci->top_left_math_kern_array[(2 * k) + 1]);
+    }
+    l = ci->bottom_left_math_kerns;
+    dump_int(l);
+    for (k = 0; k < l; k++) {
+        dump_int(ci->bottom_left_math_kern_array[(2 * k)]);
+        dump_int(ci->bottom_left_math_kern_array[(2 * k) + 1]);
+    }
+    l = ci->top_right_math_kerns;
+    dump_int(l);
+    for (k = 0; k < l; k++) {
+        dump_int(ci->top_right_math_kern_array[(2 * k)]);
+        dump_int(ci->top_right_math_kern_array[(2 * k) + 1]);
+    }
+    l = ci->bottom_right_math_kerns;
+    dump_int(l);
+    for (k = 0; k < l; k++) {
+        dump_int(ci->bottom_right_math_kern_array[(2 * k)]);
+        dump_int(ci->bottom_right_math_kern_array[(2 * k) + 1]);
+    }
+}
+
+@ @c
+static void undump_math_kerns(charinfo * ci)
+{
+    int k;
+    int x;
+    undump_int(x);
+    ci->top_left_math_kerns = x;
+    if (x > 0)
+        ci->top_left_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
+    for (k = 0; k < ci->top_left_math_kerns; k++) {
+        undump_int(x);
+        ci->top_left_math_kern_array[(2 * k)] = (scaled) x;
+        undump_int(x);
+        ci->top_left_math_kern_array[(2 * k) + 1] = (scaled) x;
+    }
+    undump_int(x);
+    ci->bottom_left_math_kerns = x;
+    if (x > 0)
+        ci->bottom_left_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
+    for (k = 0; k < ci->bottom_left_math_kerns; k++) {
+        undump_int(x);
+        ci->bottom_left_math_kern_array[(2 * k)] = (scaled) x;
+        undump_int(x);
+        ci->bottom_left_math_kern_array[(2 * k) + 1] = (scaled) x;
+    }
+    undump_int(x);
+    ci->top_right_math_kerns = x;
+    if (x > 0)
+        ci->top_right_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
+    for (k = 0; k < ci->top_right_math_kerns; k++) {
+        undump_int(x);
+        ci->top_right_math_kern_array[(2 * k)] = (scaled) x;
+        undump_int(x);
+        ci->top_right_math_kern_array[(2 * k) + 1] = (scaled) x;
+    }
+    undump_int(x);
+    ci->bottom_right_math_kerns = x;
+    if (x > 0)
+        ci->bottom_right_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
+    for (k = 0; k < ci->bottom_right_math_kerns; k++) {
+        undump_int(x);
+        ci->bottom_right_math_kern_array[(2 * k)] = (scaled) x;
+        undump_int(x);
+        ci->bottom_right_math_kern_array[(2 * k) + 1] = (scaled) x;
+    }
+}
+
+
+@ In TeX, extensibles were fairly simple things.
+   This function squeezes a TFM extensible into the vertical extender structures.
+   |advance==0| is a special case for TFM fonts, because finding the proper
+   advance width during tfm reading can be tricky
+
+
+  a small complication arises if |rep| is the only non-zero: it needs to be
+  doubled as a non-repeatable to avoid mayhem */
+
+@c
+void set_charinfo_extensible(charinfo * ci, int top, int bot, int mid, int rep)
+{
+    extinfo *ext;
+    set_charinfo_vert_variants(ci, NULL);       /* clear old */
+    if (bot == 0 && top == 0 && mid == 0 && rep != 0) {
+        ext = new_variant(rep, 0, 0, 0, EXT_NORMAL);
+        add_charinfo_vert_variant(ci, ext);
+        ext = new_variant(rep, 0, 0, 0, EXT_REPEAT);
+        add_charinfo_vert_variant(ci, ext);
+        return;
+    }
+    if (bot != 0) {
+        ext = new_variant(bot, 0, 0, 0, EXT_NORMAL);
+        add_charinfo_vert_variant(ci, ext);
+    }
+    if (rep != 0) {
+        ext = new_variant(rep, 0, 0, 0, EXT_REPEAT);
+        add_charinfo_vert_variant(ci, ext);
+    }
+    if (mid != 0) {
+        ext = new_variant(mid, 0, 0, 0, EXT_NORMAL);
+        add_charinfo_vert_variant(ci, ext);
+        if (rep != 0) {
+            ext = new_variant(rep, 0, 0, 0, EXT_REPEAT);
+            add_charinfo_vert_variant(ci, ext);
+        }
+    }
+    if (top != 0) {
+        ext = new_variant(top, 0, 0, 0, EXT_NORMAL);
+        add_charinfo_vert_variant(ci, ext);
+    }
+}
+
+@ Note that many more simple things like this are implemented as macros
+in the header file.
+
+@c
+scaled get_charinfo_width(charinfo * ci)
+{
+    return ci->width;
+}
+
+scaled get_charinfo_height(charinfo * ci)
+{
+    return ci->height;
+}
+
+scaled get_charinfo_depth(charinfo * ci)
+{
+    return ci->depth;
+}
+
+scaled get_charinfo_italic(charinfo * ci)
+{
+    return ci->italic;
+}
+
+scaled get_charinfo_vert_italic(charinfo * ci)
+{
+    return ci->vert_italic;
+}
+
+scaled get_charinfo_top_accent(charinfo * ci)
+{
+    return ci->top_accent;
+}
+
+scaled get_charinfo_bot_accent(charinfo * ci)
+{
+    return ci->bot_accent;
+}
+
+char get_charinfo_tag(charinfo * ci)
+{
+    return ci->tag;
+}
+
+int get_charinfo_remainder(charinfo * ci)
+{
+    return ci->remainder;
+}
+
+char get_charinfo_used(charinfo * ci)
+{
+    return ci->used;
+}
+
+int get_charinfo_index(charinfo * ci)
+{
+    return ci->index;
+}
+
+char *get_charinfo_name(charinfo * ci)
+{
+    return ci->name;
+}
+
+char *get_charinfo_tounicode(charinfo * ci)
+{
+    return ci->tounicode;
+}
+
+liginfo *get_charinfo_ligatures(charinfo * ci)
+{
+    return ci->ligatures;
+}
+
+kerninfo *get_charinfo_kerns(charinfo * ci)
+{
+    return ci->kerns;
+}
+
+eight_bits *get_charinfo_packets(charinfo * ci)
+{
+    return ci->packets;
+}
+
+int get_charinfo_ef(charinfo * ci)
+{
+    return ci->ef;
+}
+
+int get_charinfo_rp(charinfo * ci)
+{
+    return ci->rp;
+}
+
+int get_charinfo_lp(charinfo * ci)
+{
+    return ci->lp;
+}
+
+extinfo *get_charinfo_vert_variants(charinfo * ci)
+{
+    extinfo *w = NULL;
+    if (ci->vert_variants != NULL)
+        w = ci->vert_variants;
+    return w;
+}
+
+extinfo *get_charinfo_hor_variants(charinfo * ci)
+{
+    extinfo *w = NULL;
+    if (ci->hor_variants != NULL)
+        w = ci->hor_variants;
+    return w;
+}
+
+
+scaled char_width(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    scaled w = get_charinfo_width(ci);
+    return w;
+}
+
+scaled calc_char_width(internal_font_number f, int c, int ex)
+{
+    charinfo *ci = char_info(f, c);
+    scaled w = get_charinfo_width(ci);
+    if (ex != 0)
+        w = round_xn_over_d(w, 1000 + ex, 1000);
+    return w;
+}
+
+scaled char_depth(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    scaled d = get_charinfo_depth(ci);
+    return d;
+}
+
+scaled char_height(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    scaled h = get_charinfo_height(ci);
+    return h;
+}
+
+scaled char_italic(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    scaled i = get_charinfo_italic(ci);
+    return i;
+}
+
+scaled char_vert_italic(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    scaled i = get_charinfo_vert_italic(ci);
+    return i;
+}
+
+scaled char_top_accent(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_top_accent(ci);
+}
+
+scaled char_bot_accent(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_bot_accent(ci);
+}
+
+
+int char_remainder(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_remainder(ci);
+}
+
+char char_tag(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_tag(ci);
+}
+
+char char_used(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_used(ci);
+}
+
+char *char_name(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_name(ci);
+}
+
+int char_index(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_index(ci);
+}
+
+liginfo *char_ligatures(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_ligatures(ci);
+}
+
+kerninfo *char_kerns(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_kerns(ci);
+}
+
+eight_bits *char_packets(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_packets(ci);
+}
+
+@ @c
+void set_font_params(internal_font_number f, int b)
+{
+    int i;
+    i = font_params(f);
+    if (i != b) {
+        font_bytes +=
+            (int) ((b - (int) font_params(f) + 1) * (int) sizeof(scaled));
+        do_realloc(param_base(f), (b + 2), int);
+        font_params(f) = b;
+        if (b > i) {
+            while (i < b) {
+                i++;
+                set_font_param(f, i, 0);
+            }
+        }
+    }
+}
+
+@ @c
+void set_font_math_params(internal_font_number f, int b)
+{
+    int i;
+    i = font_math_params(f);
+    if (i != b) {
+        font_bytes +=
+            ((b - (int) font_math_params(f) + 1) * (int) sizeof(scaled));
+        do_realloc(math_param_base(f), (b + 2), int);
+        font_math_params(f) = b;
+        if (b > i) {
+            while (i < b) {
+                i++;
+                set_font_math_param(f, i, undefined_math_parameter);
+            }
+        }
+    }
+}
+
+@ @c
+int copy_font(int f)
+{
+    int i, ci_cnt, ci_size;
+    charinfo *ci;
+    int k = new_font();
+
+    {
+        ci = font_tables[k]->charinfo;
+        ci_cnt = font_tables[k]->charinfo_count;
+        ci_size = font_tables[k]->charinfo_size;
+        memcpy(font_tables[k], font_tables[f], sizeof(texfont));
+        font_tables[k]->charinfo = ci;
+        font_tables[k]->charinfo_count = ci_cnt;
+        font_tables[k]->charinfo_size = ci_size;
+    }
+
+    font_malloc_charinfo(k, font_tables[f]->charinfo_count);
+    set_font_cache_id(k, 0);
+    set_font_used(k, 0);
+    set_font_touched(k, 0);
+
+    font_tables[k]->_font_name = NULL;
+    font_tables[k]->_font_filename = NULL;
+    font_tables[k]->_font_fullname = NULL;
+    font_tables[k]->_font_psname = NULL;
+    font_tables[k]->_font_encodingname = NULL;
+    font_tables[k]->_font_area = NULL;
+    font_tables[k]->_font_cidregistry = NULL;
+    font_tables[k]->_font_cidordering = NULL;
+    font_tables[k]->_left_boundary = NULL;
+    font_tables[k]->_right_boundary = NULL;
+
+    set_font_name(k, xstrdup(font_name(f)));
+    if (font_filename(f) != NULL)
+        set_font_filename(k, xstrdup(font_filename(f)));
+    if (font_fullname(f) != NULL)
+        set_font_fullname(k, xstrdup(font_fullname(f)));
+    if (font_psname(f) != NULL)
+        set_font_psname(k, xstrdup(font_psname(f)));
+    if (font_encodingname(f) != NULL)
+        set_font_encodingname(k, xstrdup(font_encodingname(f)));
+    if (font_area(f) != NULL)
+        set_font_area(k, xstrdup(font_area(f)));
+    if (font_cidregistry(f) != NULL)
+        set_font_cidregistry(k, xstrdup(font_cidregistry(f)));
+    if (font_cidordering(f) != NULL)
+        set_font_cidordering(k, xstrdup(font_cidordering(f)));
+
+    i = (int) (sizeof(*param_base(f)) * (unsigned) (font_params(f)+1));
+    font_bytes += i;
+    param_base(k) = xmalloc((unsigned) (i+1));
+    memcpy(param_base(k), param_base(f), (size_t) (i));
+
+    if (font_math_params(f) > 0) {
+        i = (int) (sizeof(*math_param_base(f)) *
+                   (unsigned) font_math_params(f));
+        font_bytes += i;
+        math_param_base(k) = xmalloc((unsigned) i);
+        memcpy(math_param_base(k), math_param_base(f), (size_t) i);
+    }
+
+    for (i = 0; i <= font_tables[f]->charinfo_count; i++) {
+        ci = copy_charinfo(&font_tables[f]->charinfo[i]);
+        font_tables[k]->charinfo[i] = *ci;
+    }
+
+    if (left_boundary(f) != NULL) {
+        ci = copy_charinfo(left_boundary(f));
+        set_charinfo(k, left_boundarychar, ci);
+    }
+
+    if (right_boundary(f) != NULL) {
+        ci = copy_charinfo(right_boundary(f));
+        set_charinfo(k, right_boundarychar, ci);
+    }
+    /* not updated yet: */
+    font_tables[k]->charinfo_count = font_tables[f]->charinfo_count;
+    return k;
+}
+
+@ @c
+void delete_font(int f)
+{
+    int i;
+    charinfo *co;
+    assert(f > 0);
+    if (font_tables[f] != NULL) {
+        set_font_name(f, NULL);
+        set_font_filename(f, NULL);
+        set_font_fullname(f, NULL);
+        set_font_psname(f, NULL);
+        set_font_encodingname(f, NULL);
+        set_font_area(f, NULL);
+        set_font_cidregistry(f, NULL);
+        set_font_cidordering(f, NULL);
+        set_left_boundary(f, NULL);
+        set_right_boundary(f, NULL);
+
+        for (i = font_bc(f); i <= font_ec(f); i++) {
+            if (quick_char_exists(f, i)) {
+                co = char_info(f, i);
+                set_charinfo_name(co, NULL);
+                set_charinfo_tounicode(co, NULL);
+                set_charinfo_packets(co, NULL);
+                set_charinfo_ligatures(co, NULL);
+                set_charinfo_kerns(co, NULL);
+                set_charinfo_vert_variants(co, NULL);
+                set_charinfo_hor_variants(co, NULL);
+            }
+        }
+        /* free .notdef */
+        set_charinfo_name(font_tables[f]->charinfo + 0, NULL);
+        free(font_tables[f]->charinfo);
+        destroy_sa_tree(font_tables[f]->characters);
+
+        free(param_base(f));
+        if (math_param_base(f) != NULL)
+            free(math_param_base(f));
+        free(font_tables[f]);
+        font_tables[f] = NULL;
+
+        if (font_id_maxval == f) {
+            font_id_maxval--;
+        }
+    }
+}
+
+@ @c
+void create_null_font(void)
+{
+    int i = new_font();
+    assert(i == 0);
+    set_font_name(i, xstrdup("nullfont"));
+    set_font_area(i, xstrdup(""));
+    set_font_touched(i, 1);
+}
+
+@ @c
+boolean is_valid_font(int id)
+{
+    int ret = 0;
+    if (id >= 0 && id <= font_id_maxval && font_tables[id] != NULL)
+        ret = 1;
+    return ret;
+}
+
+@ @c
+boolean cmp_font_area(int id, str_number t)
+{
+    char *tt = NULL;
+    char *tid = font_area(id);
+    if (t == 0) {
+        if (tid == NULL || strlen(tid) == 0)
+            return 1;
+        else
+            return 0;
+    }
+    tt = makecstring(t);
+    if ((tt == NULL || strlen(tt) == 0) && (tid == NULL || strlen(tid) == 0))
+        return 1;
+    if (tt == NULL || strcmp(tid, tt) != 0)
+        return 0;
+    free(tt);
+    return 1;
+}
+
+@ Here come some subroutines to deal with expanded fonts for HZ-algorithm.
+return 1 == identical
+@c
+static boolean cmp_font_name(int id, char *tt)
+{
+    char *tid;
+    if (!is_valid_font(id))
+        return 0;
+    tid = font_name(id);
+    if (tt == NULL && tid == NULL)
+        return 1;
+    if (tt == NULL || tid == NULL || strcmp(tid, tt) != 0)
+        return 0;
+    return 1;
+}
+
+@ @c
+int test_no_ligatures(internal_font_number f)
+{
+    int c;
+    for (c = font_bc(f); c <= font_ec(f); c++) {
+        if (has_lig(f, c))      /* |char_exists(f,c)| */
+            return 0;
+    }
+    return 1;
+}
+
+@ @c
+int get_tag_code(internal_font_number f, int c)
+{
+    small_number i;
+    if (char_exists(f, c)) {
+        i = char_tag(f, c);
+        if (i == lig_tag)
+            return 1;
+        else if (i == list_tag)
+            return 2;
+        else if (i == ext_tag)
+            return 4;
+        else
+            return 0;
+    }
+    return -1;
+}
+
+@ @c
+int get_lp_code(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_lp(ci);
+}
+
+int get_rp_code(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_rp(ci);
+}
+
+int get_ef_code(internal_font_number f, int c)
+{
+    charinfo *ci = char_info(f, c);
+    return get_charinfo_ef(ci);
+}
+
+@ @c
+void set_tag_code(internal_font_number f, int c, int i)
+{
+    int fixedi;
+    charinfo *co;
+    if (char_exists(f, c)) {
+        /* |abs(fix_int(i-7,0))| */
+        fixedi = -(i < -7 ? -7 : (i > 0 ? 0 : i));
+        co = char_info(f, c);
+        if (fixedi >= 4) {
+            if (char_tag(f, c) == ext_tag)
+                set_charinfo_tag(co, (char_tag(f, c) - ext_tag));
+            fixedi = fixedi - 4;
+        }
+        if (fixedi >= 2) {
+            if (char_tag(f, c) == list_tag)
+                set_charinfo_tag(co, (char_tag(f, c) - list_tag));
+            fixedi = fixedi - 2;
+        };
+        if (fixedi >= 1) {
+            if (has_lig(f, c))
+                set_charinfo_ligatures(co, NULL);
+            if (has_kern(f, c))
+                set_charinfo_kerns(co, NULL);
+        }
+    }
+}
+
+@ @c
+void set_lp_code(internal_font_number f, int c, int i)
+{
+    charinfo *co;
+    if (char_exists(f, c)) {
+        co = char_info(f, c);
+        set_charinfo_lp(co, i);
+    }
+}
+
+void set_rp_code(internal_font_number f, int c, int i)
+{
+    charinfo *co;
+    if (char_exists(f, c)) {
+        co = char_info(f, c);
+        set_charinfo_rp(co, i);
+    }
+}
+
+void set_ef_code(internal_font_number f, int c, int i)
+{
+    charinfo *co;
+    if (char_exists(f, c)) {
+        co = char_info(f, c);
+        set_charinfo_ef(co, i);
+    }
+}
+
+@ @c
+void set_no_ligatures(internal_font_number f)
+{
+    int c;
+    charinfo *co;
+    if (font_tables[f]->ligatures_disabled)
+        return;
+    co = char_info(f, left_boundarychar);
+    set_charinfo_ligatures(co, NULL);
+    co = char_info(f, right_boundarychar);      /* this is weird */
+    set_charinfo_ligatures(co, NULL);
+    for (c = 0; c < font_tables[f]->charinfo_count; c++) {
+        co = font_tables[f]->charinfo + c;
+        set_charinfo_ligatures(co, NULL);
+    }
+    font_tables[f]->ligatures_disabled = 1;
+}
+
+@ @c
+liginfo get_ligature(internal_font_number f, int lc, int rc)
+{
+    int k = 0;
+    liginfo t, u;
+    charinfo *co;
+    t.lig = 0;
+    t.type = 0;
+    t.adj = 0;
+    if (lc == non_boundarychar || rc == non_boundarychar || (!has_lig(f, lc)))
+        return t;
+    co = char_info(f, lc);
+    while (1) {
+        u = charinfo_ligature(co, k);
+        if (lig_end(u))
+            break;
+        if (lig_char(u) == rc) {
+            if (lig_disabled(u)) {
+                return t;
+            } else {
+                return u;
+            }
+        }
+        k++;
+    }
+    return t;
+}
+
+@ @c
+scaled raw_get_kern(internal_font_number f, int lc, int rc)
+{
+    int k = 0;
+    kerninfo u;
+    charinfo *co;
+    if (lc == non_boundarychar || rc == non_boundarychar)
+        return 0;
+    co = char_info(f, lc);
+    while (1) {
+        u = charinfo_kern(co, k);
+        if (kern_end(u))
+            break;
+        if (kern_char(u) == rc) {
+            if (kern_disabled(u))
+                return 0;
+            else
+                return kern_kern(u);
+        }
+        k++;
+    }
+    return 0;
+}
+
+@ @c
+scaled get_kern(internal_font_number f, int lc, int rc)
+{
+    if (lc == non_boundarychar || rc == non_boundarychar || (!has_kern(f, lc)))
+        return 0;
+    return raw_get_kern(f, lc, rc);
+}
+
+
+@ dumping and undumping fonts
+
+@c
+#define dump_string(a)			\
+    if (a!=NULL) {			\
+      x = (int)(strlen(a)+1);		\
+      dump_int(x);  dump_things(*a, x);	\
+    } else {				\
+	x = 0; dump_int(x);		\
+    }
+
+static void dump_charinfo(int f, int c)
+{
+    charinfo *co;
+    int x;
+    liginfo *lig;
+    kerninfo *kern;
+    dump_int(c);
+    co = char_info(f, c);
+    set_charinfo_used(co, 0);
+    dump_int(get_charinfo_width(co));
+    dump_int(get_charinfo_height(co));
+    dump_int(get_charinfo_depth(co));
+    dump_int(get_charinfo_italic(co));
+    dump_int(get_charinfo_vert_italic(co));
+    dump_int(get_charinfo_top_accent(co));
+    dump_int(get_charinfo_bot_accent(co));
+    dump_int(get_charinfo_tag(co));
+    dump_int(get_charinfo_ef(co));
+    dump_int(get_charinfo_rp(co));
+    dump_int(get_charinfo_lp(co));
+    dump_int(get_charinfo_remainder(co));
+    dump_int(get_charinfo_used(co));
+    dump_int(get_charinfo_index(co));
+    dump_string(get_charinfo_name(co));
+    dump_string(get_charinfo_tounicode(co));
+
+    /* ligatures */
+    x = 0;
+    if ((lig = get_charinfo_ligatures(co)) != NULL) {
+        while (!lig_end(lig[x])) {
+            x++;
+        }
+        x++;
+        dump_int(x);
+        dump_things(*lig, x);
+    } else {
+        dump_int(x);
+    }
+    /* kerns */
+    x = 0;
+    if ((kern = get_charinfo_kerns(co)) != NULL) {
+        while (!kern_end(kern[x])) {
+            x++;
+        }
+        x++;
+        dump_int(x);
+        dump_things(*kern, x);
+    } else {
+        dump_int(x);
+    }
+    /* packets */
+    x = vf_packet_bytes(co);
+    dump_int(x);
+    if (x > 0) {
+        dump_things(*get_charinfo_packets(co), x);
+    }
+
+    if (get_charinfo_tag(co) == ext_tag) {
+        dump_charinfo_variants(get_charinfo_vert_variants(co));
+        dump_charinfo_variants(get_charinfo_hor_variants(co));
+    }
+    dump_math_kerns(co);
+}
+
+static void dump_font_entry(texfont * f)
+{
+    int x;
+    dump_int(f->_font_size);
+    dump_int(f->_font_dsize);
+    dump_int(f->_font_cidversion);
+    dump_int(f->_font_cidsupplement);
+    dump_int(f->_font_ec);
+    x = (int) f->_font_checksum;
+    dump_int(x);
+    dump_int(f->_font_used);
+    dump_int(f->_font_touched);
+    dump_int(f->_font_cache_id);
+    dump_int(f->_font_encodingbytes);
+    dump_int(f->_font_oldmath);
+    dump_int(f->_font_slant);
+    dump_int(f->_font_extend);
+    dump_int(f->font_max_shrink);
+    dump_int(f->font_max_stretch);
+    dump_int(f->_font_step);
+    dump_int(f->_font_tounicode);
+    dump_int(f->_font_type);
+    dump_int(f->_font_format);
+    dump_int(f->_font_writingmode);
+    dump_int(f->_font_identity);
+    dump_int(f->_font_embedding);
+    dump_int(f->_font_streamprovider);
+    dump_int(f->_font_bc);
+    dump_int(f->_hyphen_char);
+    dump_int(f->_skew_char);
+    dump_int(f->_font_natural_dir);
+    dump_int(f->_font_params);
+    dump_int(f->_font_math_params);
+    dump_int(f->ligatures_disabled);
+    dump_int(f->_pdf_font_num);
+    dump_int(f->_pdf_font_attr);
+}
+
+void dump_font(int f)
+{
+    int i, x;
+
+    set_font_used(f, 0);
+    font_tables[f]->charinfo_cache = NULL;
+    dump_font_entry(font_tables[f]);
+    dump_string(font_name(f));
+    dump_string(font_area(f));
+    dump_string(font_filename(f));
+    dump_string(font_fullname(f));
+    dump_string(font_psname(f));
+    dump_string(font_encodingname(f));
+    dump_string(font_cidregistry(f));
+    dump_string(font_cidordering(f));
+
+    dump_things(*param_base(f), (font_params(f) + 1));
+
+    if (font_math_params(f) > 0) {
+        dump_things(*math_param_base(f), (font_math_params(f) + 1 ));
+    }
+    if (has_left_boundary(f)) {
+        dump_int(1);
+        dump_charinfo(f, left_boundarychar);
+    } else {
+        dump_int(0);
+    }
+    if (has_right_boundary(f)) {
+        dump_int(1);
+        dump_charinfo(f, right_boundarychar);
+    } else {
+        dump_int(0);
+    }
+
+    for (i = font_bc(f); i <= font_ec(f); i++) {
+        if (quick_char_exists(f, i)) {
+            dump_charinfo(f, i);
+        }
+    }
+}
+
+@ @c
+static int undump_charinfo(int f)
+{
+    charinfo *co;
+    int x, i;
+    char *s = NULL;
+    liginfo *lig = NULL;
+    kerninfo *kern = NULL;
+    eight_bits *packet = NULL;
+
+    undump_int(i);
+    co = get_charinfo(f, i);
+    undump_int(x);
+    set_charinfo_width(co, x);
+    undump_int(x);
+    set_charinfo_height(co, x);
+    undump_int(x);
+    set_charinfo_depth(co, x);
+    undump_int(x);
+    set_charinfo_italic(co, x);
+    undump_int(x);
+    set_charinfo_vert_italic(co, x);
+    undump_int(x);
+    set_charinfo_top_accent(co, x);
+    undump_int(x);
+    set_charinfo_bot_accent(co, x);
+    undump_int(x);
+    set_charinfo_tag(co, x);
+    undump_int(x);
+    set_charinfo_ef(co, x);
+    undump_int(x);
+    set_charinfo_rp(co, x);
+    undump_int(x);
+    set_charinfo_lp(co, x);
+    undump_int(x);
+    set_charinfo_remainder(co, x);
+    undump_int(x);
+    set_charinfo_used(co, x);
+    undump_int(x);
+    set_charinfo_index(co, x);
+
+    /* name */
+    undump_int(x);
+    if (x > 0) {
+        font_bytes += x;
+        s = xmalloc((unsigned) x);
+        undump_things(*s, x);
+    }
+    set_charinfo_name(co, s);
+    /* tounicode */
+    undump_int(x);
+    if (x > 0) {
+        font_bytes += x;
+        s = xmalloc((unsigned) x);
+        undump_things(*s, x);
+    }
+    set_charinfo_tounicode(co, s);
+    /* ligatures */
+    undump_int(x);
+    if (x > 0) {
+        font_bytes += (int) ((unsigned) x * sizeof(liginfo));
+        lig = xmalloc((unsigned) ((unsigned) x * sizeof(liginfo)));
+        undump_things(*lig, x);
+    }
+    set_charinfo_ligatures(co, lig);
+    /* kerns */
+    undump_int(x);
+    if (x > 0) {
+        font_bytes += (int) ((unsigned) x * sizeof(kerninfo));
+        kern = xmalloc((unsigned) ((unsigned) x * sizeof(kerninfo)));
+        undump_things(*kern, x);
+    }
+    set_charinfo_kerns(co, kern);
+
+    /* packets */
+    undump_int(x);
+    if (x > 0) {
+        font_bytes += x;
+        packet = xmalloc((unsigned) x);
+        undump_things(*packet, x);
+    }
+    set_charinfo_packets(co, packet);
+
+    if (get_charinfo_tag(co) == ext_tag) {
+        set_charinfo_vert_variants(co, undump_charinfo_variants());
+        set_charinfo_hor_variants(co, undump_charinfo_variants());
+    }
+    undump_math_kerns(co);
+    return i;
+}
+
+#define undump_font_string(a) \
+    undump_int (x); \
+    if (x>0) { \
+        font_bytes += x; \
+        s = xmalloc((unsigned)x); \
+        undump_things(*s,x); \
+        a(f,s); \
+    }
+
+static void undump_font_entry(texfont * f)
+{
+    int x = 0;
+    /* *INDENT-OFF* */
+    undump_int(x); f->_font_size = x;
+    undump_int(x); f->_font_dsize = x;
+    undump_int(x); f->_font_cidversion = x;
+    undump_int(x); f->_font_cidsupplement = x;
+    undump_int(x); f->_font_ec = x;
+    undump_int(x); f->_font_checksum = (unsigned)x;
+    undump_int(x); f->_font_used = (char)x;
+    undump_int(x); f->_font_touched = (char)x;
+    undump_int(x); f->_font_cache_id = x;
+    undump_int(x); f->_font_encodingbytes = (char)x;
+    undump_int(x); f->_font_oldmath = x;
+    undump_int(x); f->_font_slant = x;
+    undump_int(x); f->_font_extend = x;
+    undump_int(x); f->font_max_shrink = x;
+    undump_int(x); f->font_max_stretch = x;
+    undump_int(x); f->_font_step = x;
+    undump_int(x); f->_font_tounicode = (char)x;
+    undump_int(x); f->_font_type = x;
+    undump_int(x); f->_font_format = x;
+    undump_int(x); f->_font_writingmode = x;
+    undump_int(x); f->_font_identity = x;
+    undump_int(x); f->_font_embedding = x;
+    undump_int(x); f->_font_streamprovider = x;
+    undump_int(x); f->_font_bc = x;
+    undump_int(x); f->_hyphen_char = x;
+    undump_int(x); f->_skew_char = x;
+    undump_int(x); f->_font_natural_dir = x;
+    undump_int(x); f->_font_params = x;
+    undump_int(x); f->_font_math_params = x;
+    undump_int(x); f->ligatures_disabled = x;
+    undump_int(x); f->_pdf_font_num = x;
+    undump_int(x); f->_pdf_font_attr = x;
+    /* *INDENT-ON* */
+}
+
+void undump_font(int f)
+{
+    int x, i;
+    texfont *tt;
+    charinfo *ci;
+    char *s;
+    sa_tree_item sa_value = { 0 };
+    grow_font_table(f);
+    tt = xmalloc(sizeof(texfont));
+    memset(tt, 0, sizeof(texfont));
+    font_bytes += (int) sizeof(texfont);
+    undump_font_entry(tt);
+    font_tables[f] = tt;
+
+    undump_font_string(set_font_name);
+    undump_font_string(set_font_area);
+    undump_font_string(set_font_filename);
+    undump_font_string(set_font_fullname);
+    undump_font_string(set_font_psname);
+    undump_font_string(set_font_encodingname);
+    undump_font_string(set_font_cidregistry);
+    undump_font_string(set_font_cidordering);
+
+    i = (int) (sizeof(*param_base(f)) * ((unsigned) font_params(f) + 1));
+    font_bytes += i;
+    param_base(f) = xmalloc((unsigned) i);
+    undump_things(*param_base(f), (font_params(f) + 1));
+
+    if (font_math_params(f) > 0) {
+        i = (int) (sizeof(*math_param_base(f)) *
+                   ((unsigned) font_math_params(f) + 1));
+        font_bytes += i;
+        math_param_base(f) = xmalloc((unsigned) i);
+        undump_things(*math_param_base(f), (font_math_params(f) + 1));
+    }
+
+    /* stack size 1, default item value 0 */
+    font_tables[f]->characters = new_sa_tree(1, 1, sa_value);
+    ci = xcalloc(1, sizeof(charinfo));
+    set_charinfo_name(ci, xstrdup(".notdef"));
+    font_tables[f]->charinfo = ci;
+    undump_int(x);
+    if (x) {
+        i = undump_charinfo(f); /* left boundary */
+    }
+    undump_int(x);
+    if (x) {
+        i = undump_charinfo(f); /* right boundary */
+    }
+
+    i = font_bc(f);
+    while (i < font_ec(f)) {
+        i = undump_charinfo(f);
+    }
+}
+
+/* moved from pdffont.w */
+
+@ @c
+int pk_dpi; /* PK pixel density value from \.{texmf.cnf} */
+
+@ @c
+internal_font_number tfm_lookup(char *s, scaled fs)
+{   /* looks up for a TFM with name |s| loaded at |fs| size; if found then flushes |s| */
+    internal_font_number k;
+    if (fs != 0) {
+        for (k = 1; k <= max_font_id(); k++) {
+            if (cmp_font_name(k, s) && font_size(k) == fs) {
+                return k;
+            }
+        }
+    } else {
+        for (k = 1; k <= max_font_id(); k++) {
+            if (cmp_font_name(k, s)) {
+                return k;
+            }
+        }
+    }
+    return null_font;
+}
+
+@ @c
+int fix_expand_value(internal_font_number f, int e)
+{                               /* return the multiple of |font_step(f)| that is nearest to |e| */
+    int step;
+    int max_expand;
+    boolean neg;
+    if (e == 0)
+        return 0;
+    if (e < 0) {
+        e = -e;
+        neg = true;
+        max_expand = font_max_shrink(f);
+    } else {
+        neg = false;
+        max_expand = font_max_stretch(f);
+    }
+    if (e > max_expand) {
+        e = max_expand;
+    } else {
+        step = font_step(f);
+        if (e % step > 0)
+            e = step * round_xn_over_d(e, 1, step);
+    }
+    if (neg)
+        e = -e;
+    return e;
+}
+
+@ @c
+void set_expand_params(internal_font_number f, int stretch_limit, int shrink_limit, int font_step)
+{
+    set_font_step(f, font_step);
+    set_font_max_shrink(f, shrink_limit);
+    set_font_max_stretch(f, stretch_limit);
+}
+
+@ @c
+void read_expand_font(void)
+{                               /* read font expansion spec and load expanded font */
+    int shrink_limit, stretch_limit, font_step;
+    internal_font_number f;
+    /* read font expansion parameters */
+    scan_font_ident();
+    f = cur_val;
+    if (f == null_font)
+        normal_error("font expansion", "invalid font identifier");
+    /*if (pdf_font_blink(f) != null_font)*/
+    /*    normal_error("font expansion",*/
+    /*              "\\fontexpand cannot be used this way (the base font has been expanded)");*/
+    scan_optional_equals();
+    scan_int();
+    stretch_limit = fix_int(cur_val, 0, 1000);
+    scan_int();
+    shrink_limit = fix_int(cur_val, 0, 500);
+    scan_int();
+    font_step = fix_int(cur_val, 0, 100);
+    if (font_step == 0)
+        normal_error("font expansion", "invalid step");
+    stretch_limit = stretch_limit - stretch_limit % font_step;
+    if (stretch_limit < 0)
+        stretch_limit = 0;
+    shrink_limit = shrink_limit - shrink_limit % font_step;
+    if (shrink_limit < 0)
+        shrink_limit = 0;
+    if ((stretch_limit == 0) && (shrink_limit == 0))
+        normal_error("font expansion", "invalid limit(s)");
+    if (scan_keyword("autoexpand")) {
+        normal_warning("font expansion", "autoexpand not supported");
+        /* Scan an optional space */
+        get_x_token();
+        if (cur_cmd != spacer_cmd)
+            back_input();
+    }
+    if (font_step(f) != 0) {
+        /* this font has been expanded, ensure the expansion parameters are identical */
+        if (font_step(f) != font_step)
+            normal_error("font expansion","font has been expanded with different expansion step");
+
+        if (((font_max_stretch(f) == 0) && (stretch_limit != 0)) ||
+            ((font_max_stretch(f) > 0)
+             && (font_max_stretch(f) != stretch_limit)))
+            normal_error("font expansion","font has been expanded with different stretch limit");
+
+        if (((font_max_shrink(f) == 0) && (shrink_limit != 0)) ||
+            ((font_max_shrink(f) > 0)
+             && (font_max_shrink(f) != shrink_limit)))
+            normal_error("font expansion","font has been expanded with different shrink limit");
+    } else {
+        if (font_used(f))
+            normal_warning("font expansion", "font should be expanded before its first use");
+        set_expand_params(f, stretch_limit, shrink_limit, font_step);
+    }
+}
+
+@ @c
+void new_letterspaced_font(small_number a)
+{                               /* letter-space a font by creating a virtual font */
+    pointer u;                  /* user's font identifier */
+    str_number t;               /* name for the frozen font identifier */
+    internal_font_number f, k;
+    boolean nolig = false;
+    get_r_token();
+    u = cur_cs;
+    if (u >= hash_base)
+        t = cs_text(u);
+    else
+        t = maketexstring("FONT");
+    define(u, set_font_cmd, null_font);
+    scan_optional_equals();
+    scan_font_ident();
+    k = cur_val;
+    scan_int();
+    if (scan_keyword("nolig"))
+       nolig=true;
+    f = letter_space_font(k, fix_int(cur_val, -1000, 1000), nolig);
+    equiv(u) = f;
+    eqtb[font_id_base + f] = eqtb[u];
+    font_id_text(f) = t;
+}
+
+@ @c
+void make_font_copy(small_number a)
+{                               /* make a font copy for further use with font expansion */
+    pointer u;                  /* user's font identifier */
+    str_number t;               /* name for the frozen font identifier */
+    internal_font_number f, k;
+    get_r_token();
+    u = cur_cs;
+    if (u >= hash_base)
+        t = cs_text(u);
+    else
+        t = maketexstring("FONT");
+    define(u, set_font_cmd, null_font);
+    scan_optional_equals();
+    scan_font_ident();
+    k = cur_val;
+    f = copy_font_info(k);
+    equiv(u) = f;
+    eqtb[font_id_base + f] = eqtb[u];
+    font_id_text(f) = t;
+}
+
+@ @c
+void glyph_to_unicode(void)
+{
+    str_number s1, s2;
+    scan_toks(false, true);
+    s1 = tokens_to_string(def_ref);
+    delete_token_ref(def_ref);
+    scan_toks(false, true);
+    s2 = tokens_to_string(def_ref);
+    delete_token_ref(def_ref);
+    def_tounicode(s1, s2);
+    flush_str(s2);
+    flush_str(s1);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/texfont.c
+++ /dev/null
@@ -1,1950 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-/*tex
-
-    Here is the main font API implementation for the original pascal parts. Stuff
-    to watch out for:
-
-    \startitemize
-
-        \startitem
-            Knuth had a |'null_character'| that was used when a character could
-            not be found by the |fetch()| routine, to signal an error. This has
-            been deleted, but it may mean that the output of luatex is
-            incompatible with TeX after |fetch()| has detected an error
-            condition.
-        \stopitem
-
-        \startitem
-            Knuth also had a |font_glue()| optimization. I've removed that
-            because it was a bit of dirty programming and it also was problematic
-            |if 0 != null|.
-        \stopitem
-
-    \stopitemize
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-#define noDEBUG
-
-#define proper_char_index(c) (c<=font_ec(f) && c>=font_bc(f))
-#define do_realloc(a,b,d)    a = xrealloc(a,(unsigned)((unsigned)(b)*sizeof(d)))
-
-texfont **font_tables = NULL;
-
-static int font_arr_max = 0;
-static int font_id_maxval = 0;
-
-static void grow_font_table(int id)
-{
-    int j;
-    if (id >= font_arr_max) {
-        font_bytes +=
-            (int) (((id + 8 - font_arr_max) * (int) sizeof(texfont *)));
-        font_tables =
-            xrealloc(font_tables,
-                     (unsigned) (((unsigned) id + 8) * sizeof(texfont *)));
-        j = 8;
-        while (j--) {
-            font_tables[id + j] = NULL;
-        }
-        font_arr_max = id + 8;
-    }
-}
-
-int new_font_id(void)
-{
-    int i;
-    for (i = 0; i < font_arr_max; i++) {
-        if (font_tables[i] == NULL) {
-            break;
-        }
-    }
-    if (i >= font_arr_max)
-        grow_font_table(i);
-    if (i > font_id_maxval)
-        font_id_maxval = i;
-    return i;
-}
-
-int max_font_id(void)
-{
-    return font_id_maxval;
-}
-
-void set_max_font_id(int i)
-{
-    font_id_maxval = i;
-}
-
-int new_font(void)
-{
-    int k;
-    int id;
-    charinfo *ci;
-    sa_tree_item sa_value = { 0 };
-    id = new_font_id();
-    font_bytes += (int) sizeof(texfont);
-    /*tex Most stuff is zero */
-    font_tables[id] = xcalloc(1, sizeof(texfont));
-    font_tables[id]->_font_name = NULL;
-    font_tables[id]->_font_area = NULL;
-    font_tables[id]->_font_filename = NULL;
-    font_tables[id]->_font_fullname = NULL;
-    font_tables[id]->_font_psname = NULL;
-    font_tables[id]->_font_encodingname = NULL;
-    font_tables[id]->_font_cidregistry = NULL;
-    font_tables[id]->_font_cidordering = NULL;
-    font_tables[id]->_left_boundary = NULL;
-    font_tables[id]->_right_boundary = NULL;
-    font_tables[id]->_param_base = NULL;
-    font_tables[id]->_math_param_base = NULL;
-    /*tex |ec = 0| */
-    set_font_bc(id, 1);
-    set_font_writingmode(id, 0);
-    set_font_identity(id, 0);
-    set_hyphen_char(id, '-');
-    set_skew_char(id, -1);
-    /*tex vertical */
-    font_slant(id) = 0;
-    /*tex normal width */
-    font_extend(id) = 1000;
-    /*tex normal height */
-    font_squeeze(id) = 1000;
-    font_width(id) = 0;
-    font_mode(id) = 0;
-    /*tex allocate eight values including 0 */
-    set_font_params(id, 7);
-    for (k = 0; k <= 7; k++) {
-        set_font_param(id, k, 0);
-    }
-    /*tex character info zero is reserved for |notdef|. The stack size 1, default item value 0. */
-    font_tables[id]->characters = new_sa_tree(1, 1, sa_value);
-    ci = xcalloc(1, sizeof(charinfo));
-    set_charinfo_name(ci, xstrdup(".notdef"));
-    font_tables[id]->charinfo = ci;
-    font_tables[id]->charinfo_size = 1;
-    font_tables[id]->charinfo_cache = NULL;
-    return id;
-}
-
-void font_malloc_charinfo(internal_font_number f, int num)
-{
-    int glyph = font_tables[f]->charinfo_size;
-    font_bytes += (int) (num * (int) sizeof(charinfo));
-    do_realloc(font_tables[f]->charinfo, (unsigned) (glyph + num), charinfo);
-    memset(&(font_tables[f]->charinfo[glyph]), 0, (size_t) (num * (int) sizeof(charinfo)));
-    font_tables[f]->charinfo_size += num;
-}
-
-#define find_charinfo_id(f,c) (get_sa_item(font_tables[f]->characters,c).int_value)
-
-charinfo *get_charinfo(internal_font_number f, int c)
-{
-    int glyph;
-    charinfo *ci;
-    if (proper_char_index(c)) {
-        glyph = get_sa_item(font_tables[f]->characters, c).int_value;
-        if (!glyph) {
-            sa_tree_item sa_value = { 0 };
-            int tglyph = ++font_tables[f]->charinfo_count;
-            if (tglyph >= font_tables[f]->charinfo_size) {
-                font_malloc_charinfo(f, 256);
-            }
-            font_tables[f]->charinfo[tglyph].ef = 1000;
-            sa_value.int_value = tglyph;
-            /*tex 1 means global */
-            set_sa_item(font_tables[f]->characters, c, sa_value, 1);
-            glyph = tglyph;
-        }
-        return &(font_tables[f]->charinfo[glyph]);
-    } else if (c == left_boundarychar) {
-        if (left_boundary(f) == NULL) {
-            ci = xcalloc(1, sizeof(charinfo));
-            font_bytes += (int) sizeof(charinfo);
-            set_left_boundary(f, ci);
-        }
-        return left_boundary(f);
-    } else if (c == right_boundarychar) {
-        if (right_boundary(f) == NULL) {
-            ci = xcalloc(1, sizeof(charinfo));
-            font_bytes += (int) sizeof(charinfo);
-            set_right_boundary(f, ci);
-        }
-        return right_boundary(f);
-    }
-    return &(font_tables[f]->charinfo[0]);
-}
-
-static void set_charinfo(internal_font_number f, int c, charinfo * ci)
-{
-    int glyph;
-    if (proper_char_index(c)) {
-        glyph = get_sa_item(font_tables[f]->characters, c).int_value;
-        if (glyph) {
-            font_tables[f]->charinfo[glyph] = *ci;
-        } else {
-            normal_error("font","character insertion failed");
-        }
-    } else if (c == left_boundarychar) {
-        set_left_boundary(f, ci);
-    } else if (c == right_boundarychar) {
-        set_right_boundary(f, ci);
-    }
-}
-
-charinfo *copy_charinfo(charinfo * ci)
-{
-    int x, k;
-    kerninfo *kern;
-    liginfo *lig;
-    eight_bits *packet;
-    charinfo *co = NULL;
-    if (ci == NULL)
-        return NULL;
-    co = xmalloc(sizeof(charinfo));
-    memcpy(co, ci, sizeof(charinfo));
-    set_charinfo_used(co, false);
-    co->name = NULL;
-    co->tounicode = NULL;
-    co->packets = NULL;
-    co->ligatures = NULL;
-    co->kerns = NULL;
-    co->vert_variants = NULL;
-    co->hor_variants = NULL;
-    if (ci->name != NULL) {
-        co->name = xstrdup(ci->name);
-    }
-    if (ci->tounicode != NULL) {
-        co->tounicode = xstrdup(ci->tounicode);
-    }
-    /*tex Kerns */
-    if ((kern = get_charinfo_kerns(ci)) != NULL) {
-        x = 0;
-        while (!kern_end(kern[x])) {
-            x++;
-        }
-        x++;
-        co->kerns = xmalloc((unsigned) (x * (int) sizeof(kerninfo)));
-        memcpy(co->kerns, ci->kerns, (size_t) (x * (int) sizeof(kerninfo)));
-    }
-    /*tex Ligatures */
-    if ((lig = get_charinfo_ligatures(ci)) != NULL) {
-        x = 0;
-        while (!lig_end(lig[x])) {
-            x++;
-        }
-        x++;
-        co->ligatures = xmalloc((unsigned) (x * (int) sizeof(liginfo)));
-        memcpy(co->ligatures, ci->ligatures,
-               (size_t) (x * (int) sizeof(liginfo)));
-    }
-    /*tex Packets */
-    if ((packet = get_charinfo_packets(ci)) != NULL) {
-        x = vf_packet_bytes(ci);
-        co->packets = xmalloc((unsigned) x);
-        memcpy(co->packets, ci->packets, (size_t) x);
-    }
-    /*tex Horizontal and vertical extenders */
-    if (get_charinfo_vert_variants(ci) != NULL) {
-        set_charinfo_vert_variants(co, copy_variants(get_charinfo_vert_variants(ci)));
-    }
-    if (get_charinfo_hor_variants(ci) != NULL) {
-        set_charinfo_hor_variants(co, copy_variants(get_charinfo_hor_variants(ci)));
-    }
-    x = ci->top_left_math_kerns;
-    co->top_left_math_kerns = x;
-    if (x > 0) {
-        co->top_left_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
-        for (k = 0; k < co->top_left_math_kerns; k++) {
-            co->top_left_math_kern_array[(2 * k)] = ci->top_left_math_kern_array[(2 * k)];
-            co->top_left_math_kern_array[(2 * k) + 1] = ci->top_left_math_kern_array[(2 * k) + 1];
-        }
-    }
-    x = ci->bottom_left_math_kerns;
-    co->bottom_left_math_kerns = x;
-    if (x > 0) {
-        co->bottom_left_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
-        for (k = 0; k < co->bottom_left_math_kerns; k++) {
-            co->bottom_left_math_kern_array[(2 * k)] = ci->bottom_left_math_kern_array[(2 * k)];
-            co->bottom_left_math_kern_array[(2 * k) + 1] = ci->bottom_left_math_kern_array[(2 * k) + 1];
-        }
-    }
-    x = ci->top_right_math_kerns;
-    co->top_right_math_kerns = x;
-    if (x > 0) {
-        co->top_right_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
-        for (k = 0; k < co->top_right_math_kerns; k++) {
-            co->top_right_math_kern_array[(2 * k)] = ci->top_right_math_kern_array[(2 * k)];
-            co->top_right_math_kern_array[(2 * k) + 1] = ci->top_right_math_kern_array[(2 * k) + 1];
-        }
-    }
-    x = ci->bottom_right_math_kerns;
-    co->bottom_right_math_kerns = x;
-    if (x > 0) {
-        co->bottom_right_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
-        for (k = 0; k < co->bottom_right_math_kerns; k++) {
-            co->bottom_right_math_kern_array[(2 * k)] = ci->bottom_right_math_kern_array[(2 * k)];
-            co->bottom_right_math_kern_array[(2 * k) + 1] = ci->bottom_right_math_kern_array[(2 * k) + 1];
-        }
-    }
-    return co;
-}
-
-charinfo *char_info(internal_font_number f, int c)
-{
-    if (f > font_id_maxval)
-        return 0;
-    if (proper_char_index(c)) {
-        register int glyph = (int) find_charinfo_id(f, c);
-        return &(font_tables[f]->charinfo[glyph]);
-    } else if (c == left_boundarychar && left_boundary(f) != NULL) {
-        return left_boundary(f);
-    } else if (c == right_boundarychar && right_boundary(f) != NULL) {
-        return right_boundary(f);
-    }
-    return &(font_tables[f]->charinfo[0]);
-}
-
-scaled_whd get_charinfo_whd(internal_font_number f, int c)
-{
-    scaled_whd s;
-    charinfo *i;
-    i = char_info(f, c);
-    s.wd = i->width;
-    s.dp = i->depth;
-    s.ht = i->height;
-    return s;
-}
-
-int char_exists(internal_font_number f, int c)
-{
-    if (f > font_id_maxval)
-        return 0;
-    if (proper_char_index(c)) {
-        return (int) find_charinfo_id(f, c);
-    } else if ((c == left_boundarychar) && has_left_boundary(f)) {
-        return 1;
-    } else if ((c == right_boundarychar) && has_right_boundary(f)) {
-        return 1;
-    }
-    return 0;
-}
-
-int lua_glyph_not_found_callback(internal_font_number f, int c)
-{
-    int callback_id;
-    int ret = 0;
-    int top, i;
-    callback_id = callback_defined(glyph_not_found_callback);
-    if (callback_id != 0) {
-        top = lua_gettop(Luas);
-        if (!get_callback(Luas, callback_id)) {
-            lua_settop(Luas, top);
-            return 0;
-        }
-        lua_pushinteger(Luas, f);
-        lua_pushinteger(Luas, c);
-        if ((i=lua_pcall(Luas, 2, 1, 0)) != 0) {
-            formatted_warning   ("glyph not found", "error: %s", lua_tostring(Luas, -1));
-            lua_settop(Luas, top);
-            luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        } else {
-            ret = lua_toboolean(Luas, -1);
-        }
-        lua_settop(Luas, top);
-    } else {
-        char_warning(f,c);
-    }
-    return ret;
-}
-
-extinfo *new_variant(int glyph, int startconnect, int endconnect, int advance, int repeater)
-{
-    extinfo *ext;
-    ext = xmalloc(sizeof(extinfo));
-    ext->next = NULL;
-    ext->glyph = glyph;
-    ext->start_overlap = startconnect;
-    ext->end_overlap = endconnect;
-    ext->advance = advance;
-    ext->extender = repeater;
-    return ext;
-}
-
-static extinfo *copy_variant(extinfo * old)
-{
-    extinfo *ext;
-    ext = xmalloc(sizeof(extinfo));
-    ext->next = NULL;
-    ext->glyph = old->glyph;
-    ext->start_overlap = old->start_overlap;
-    ext->end_overlap = old->end_overlap;
-    ext->advance = old->advance;
-    ext->extender = old->extender;
-    return ext;
-}
-
-static void dump_variant(extinfo * ext)
-{
-    dump_int(ext->glyph);
-    dump_int(ext->start_overlap);
-    dump_int(ext->end_overlap);
-    dump_int(ext->advance);
-    dump_int(ext->extender);
-    return;
-}
-
-static extinfo *undump_variant(void)
-{
-    int x;
-    extinfo *ext;
-    undump_int(x);
-    if (x == 0)
-        return NULL;
-    ext = xmalloc(sizeof(extinfo));
-    ext->next = NULL;
-    ext->glyph = x;
-    undump_int(x);
-    ext->start_overlap = x;
-    undump_int(x);
-    ext->end_overlap = x;
-    undump_int(x);
-    ext->advance = x;
-    undump_int(x);
-    ext->extender = x;
-    return ext;
-}
-
-void add_charinfo_vert_variant(charinfo * ci, extinfo * ext)
-{
-    if (ci->vert_variants == NULL) {
-        ci->vert_variants = ext;
-    } else {
-        extinfo *lst = ci->vert_variants;
-        while (lst->next != NULL)
-            lst = lst->next;
-        lst->next = ext;
-    }
-
-}
-
-void add_charinfo_hor_variant(charinfo * ci, extinfo * ext)
-{
-    if (ci->hor_variants == NULL) {
-        ci->hor_variants = ext;
-    } else {
-        extinfo *lst = ci->hor_variants;
-        while (lst->next != NULL)
-            lst = lst->next;
-        lst->next = ext;
-    }
-
-}
-
-extinfo *copy_variants(extinfo * o)
-{
-    extinfo *c, *t = NULL, *h = NULL;
-    while (o != NULL) {
-        c = copy_variant(o);
-        if (h == null)
-            h = c;
-        else
-            t->next = c;
-        t = c;
-        o = o->next;
-    }
-
-    return h;
-}
-
-static void dump_charinfo_variants(extinfo * o)
-{
-    while (o != NULL) {
-        dump_variant(o);
-        o = o->next;
-    }
-    dump_int(0);
-    return;
-}
-
-static extinfo *undump_charinfo_variants(void)
-{
-    extinfo *c, *t = NULL, *h = NULL;
-    c = undump_variant();
-    while (c != NULL) {
-        if (h == null)
-            h = c;
-        else
-            t->next = c;
-        t = c;
-        c = undump_variant();
-    }
-    return h;
-}
-
-
-/*tex
-
-    Note that many more small things like this are implemented as macros in the
-    header file.
-*/
-
-void set_charinfo_width(charinfo * ci, scaled val)
-{
-    ci->width = val;
-}
-
-void set_charinfo_height(charinfo * ci, scaled val)
-{
-    ci->height = val;
-}
-
-void set_charinfo_depth(charinfo * ci, scaled val)
-{
-    ci->depth = val;
-}
-
-void set_charinfo_italic(charinfo * ci, scaled val)
-{
-    ci->italic = val;
-}
-
-void set_charinfo_vert_italic(charinfo * ci, scaled val)
-{
-    ci->vert_italic = val;
-}
-
-void set_charinfo_top_accent(charinfo * ci, scaled val)
-{
-    ci->top_accent = val;
-}
-
-void set_charinfo_bot_accent(charinfo * ci, scaled val)
-{
-    ci->bot_accent = val;
-}
-
-void set_charinfo_tag(charinfo * ci, scaled val)
-{
-    ci->tag = (char) val;
-}
-
-void set_charinfo_remainder(charinfo * ci, scaled val)
-{
-    ci->remainder = val;
-}
-
-void set_charinfo_used(charinfo * ci, scaled val)
-{
-    ci->used = (char) val;
-}
-
-void set_charinfo_index(charinfo * ci, scaled val)
-{
-    ci->index = (unsigned short) val;
-}
-
-void set_charinfo_name(charinfo * ci, char *val)
-{
-    xfree(ci->name);
-    ci->name = val;
-}
-
-void set_charinfo_tounicode(charinfo * ci, char *val)
-{
-    xfree(ci->tounicode);
-    ci->tounicode = val;
-}
-
-void set_charinfo_ligatures(charinfo * ci, liginfo * val)
-{
-    dxfree(ci->ligatures, val);
-}
-
-void set_charinfo_kerns(charinfo * ci, kerninfo * val)
-{
-    dxfree(ci->kerns, val);
-}
-
-void set_charinfo_packets(charinfo * ci, eight_bits * val)
-{
-    dxfree(ci->packets, val);
-}
-
-void set_charinfo_ef(charinfo * ci, scaled val)
-{
-    ci->ef = val;
-}
-
-void set_charinfo_lp(charinfo * ci, scaled val)
-{
-    ci->lp = val;
-}
-
-void set_charinfo_rp(charinfo * ci, scaled val)
-{
-    ci->rp = val;
-}
-
-void set_charinfo_vert_variants(charinfo * ci, extinfo * ext)
-{
-    extinfo *c, *lst;
-    if (ci->vert_variants != NULL) {
-        lst = ci->vert_variants;
-        while (lst != NULL) {
-            c = lst->next;
-            free(lst);
-            lst = c;
-        }
-    }
-    ci->vert_variants = ext;
-}
-
-void set_charinfo_hor_variants(charinfo * ci, extinfo * ext)
-{
-    extinfo *c, *lst;
-    if (ci->hor_variants != NULL) {
-        lst = ci->hor_variants;
-        while (lst != NULL) {
-            c = lst->next;
-            free(lst);
-            lst = c;
-        }
-    }
-    ci->hor_variants = ext;
-
-}
-
-int get_charinfo_math_kerns(charinfo * ci, int id)
-{
-    /*tex All callers check for |result>0|. */
-    int k = 0;
-    if (id == top_left_kern) {
-        k = ci->top_left_math_kerns;
-    } else if (id == bottom_left_kern) {
-        k = ci->bottom_left_math_kerns;
-    } else if (id == top_right_kern) {
-        k = ci->top_right_math_kerns;
-    } else if (id == bottom_right_kern) {
-        k = ci->bottom_right_math_kerns;
-    } else {
-        confusion("get_charinfo_math_kerns");
-    }
-    return k;
-}
-
-void add_charinfo_math_kern(charinfo * ci, int id, scaled ht, scaled krn)
-{
-    int k;
-    if (id == top_left_kern) {
-        k = ci->top_left_math_kerns;
-        do_realloc(ci->top_left_math_kern_array, ((k + 1) * 2), sizeof(scaled));
-        ci->top_left_math_kern_array[(2 * (k))] = ht;
-        ci->top_left_math_kern_array[((2 * (k)) + 1)] = krn;
-        ci->top_left_math_kerns++;
-    } else if (id == bottom_left_kern) {
-        k = ci->bottom_left_math_kerns;
-        do_realloc(ci->bottom_left_math_kern_array, ((k + 1) * 2), sizeof(scaled));
-        ci->bottom_left_math_kern_array[(2 * (k))] = ht;
-        ci->bottom_left_math_kern_array[(2 * (k)) + 1] = krn;
-        ci->bottom_left_math_kerns++;
-    } else if (id == top_right_kern) {
-        k = ci->top_right_math_kerns;
-        do_realloc(ci->top_right_math_kern_array, ((k + 1) * 2), sizeof(scaled));
-        ci->top_right_math_kern_array[(2 * (k))] = ht;
-        ci->top_right_math_kern_array[(2 * (k)) + 1] = krn;
-        ci->top_right_math_kerns++;
-    } else if (id == bottom_right_kern) {
-        k = ci->bottom_right_math_kerns;
-        do_realloc(ci->bottom_right_math_kern_array, ((k + 1) * 2), sizeof(scaled));
-        ci->bottom_right_math_kern_array[(2 * (k))] = ht;
-        ci->bottom_right_math_kern_array[(2 * (k)) + 1] = krn;
-        ci->bottom_right_math_kerns++;
-    } else {
-        confusion("add_charinfo_math_kern");
-    }
-}
-
-static void dump_math_kerns(charinfo * ci)
-{
-    int k, l;
-    l = ci->top_left_math_kerns;
-    dump_int(l);
-    for (k = 0; k < l; k++) {
-        dump_int(ci->top_left_math_kern_array[(2 * k)]);
-        dump_int(ci->top_left_math_kern_array[(2 * k) + 1]);
-    }
-    l = ci->bottom_left_math_kerns;
-    dump_int(l);
-    for (k = 0; k < l; k++) {
-        dump_int(ci->bottom_left_math_kern_array[(2 * k)]);
-        dump_int(ci->bottom_left_math_kern_array[(2 * k) + 1]);
-    }
-    l = ci->top_right_math_kerns;
-    dump_int(l);
-    for (k = 0; k < l; k++) {
-        dump_int(ci->top_right_math_kern_array[(2 * k)]);
-        dump_int(ci->top_right_math_kern_array[(2 * k) + 1]);
-    }
-    l = ci->bottom_right_math_kerns;
-    dump_int(l);
-    for (k = 0; k < l; k++) {
-        dump_int(ci->bottom_right_math_kern_array[(2 * k)]);
-        dump_int(ci->bottom_right_math_kern_array[(2 * k) + 1]);
-    }
-}
-
-static void undump_math_kerns(charinfo * ci)
-{
-    int k, x;
-    undump_int(x);
-    ci->top_left_math_kerns = x;
-    if (x > 0)
-        ci->top_left_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
-    for (k = 0; k < ci->top_left_math_kerns; k++) {
-        undump_int(x);
-        ci->top_left_math_kern_array[(2 * k)] = (scaled) x;
-        undump_int(x);
-        ci->top_left_math_kern_array[(2 * k) + 1] = (scaled) x;
-    }
-    undump_int(x);
-    ci->bottom_left_math_kerns = x;
-    if (x > 0)
-        ci->bottom_left_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
-    for (k = 0; k < ci->bottom_left_math_kerns; k++) {
-        undump_int(x);
-        ci->bottom_left_math_kern_array[(2 * k)] = (scaled) x;
-        undump_int(x);
-        ci->bottom_left_math_kern_array[(2 * k) + 1] = (scaled) x;
-    }
-    undump_int(x);
-    ci->top_right_math_kerns = x;
-    if (x > 0)
-        ci->top_right_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
-    for (k = 0; k < ci->top_right_math_kerns; k++) {
-        undump_int(x);
-        ci->top_right_math_kern_array[(2 * k)] = (scaled) x;
-        undump_int(x);
-        ci->top_right_math_kern_array[(2 * k) + 1] = (scaled) x;
-    }
-    undump_int(x);
-    ci->bottom_right_math_kerns = x;
-    if (x > 0)
-        ci->bottom_right_math_kern_array = xmalloc((unsigned) (2 * (int) sizeof(scaled) * x));
-    for (k = 0; k < ci->bottom_right_math_kerns; k++) {
-        undump_int(x);
-        ci->bottom_right_math_kern_array[(2 * k)] = (scaled) x;
-        undump_int(x);
-        ci->bottom_right_math_kern_array[(2 * k) + 1] = (scaled) x;
-    }
-}
-
-/*tex
-
-    In TeX, extensibles were fairly simple things. This function squeezes a TFM
-    extensible into the vertical extender structures. |advance==0| is a special
-    case for TFM fonts, because finding the proper advance width during tfm
-    reading can be tricky.
-
-    A small complication arises if |rep| is the only non-zero: it needs to be
-    doubled as a non-repeatable to avoid mayhem.
-
-*/
-
-void set_charinfo_extensible(charinfo * ci, int top, int bot, int mid, int rep)
-{
-    extinfo *ext;
-    /*tex Clear old data: */
-    set_charinfo_vert_variants(ci, NULL);
-    if (bot == 0 && top == 0 && mid == 0 && rep != 0) {
-        ext = new_variant(rep, 0, 0, 0, EXT_NORMAL);
-        add_charinfo_vert_variant(ci, ext);
-        ext = new_variant(rep, 0, 0, 0, EXT_REPEAT);
-        add_charinfo_vert_variant(ci, ext);
-        return;
-    }
-    if (bot != 0) {
-        ext = new_variant(bot, 0, 0, 0, EXT_NORMAL);
-        add_charinfo_vert_variant(ci, ext);
-    }
-    if (rep != 0) {
-        ext = new_variant(rep, 0, 0, 0, EXT_REPEAT);
-        add_charinfo_vert_variant(ci, ext);
-    }
-    if (mid != 0) {
-        ext = new_variant(mid, 0, 0, 0, EXT_NORMAL);
-        add_charinfo_vert_variant(ci, ext);
-        if (rep != 0) {
-            ext = new_variant(rep, 0, 0, 0, EXT_REPEAT);
-            add_charinfo_vert_variant(ci, ext);
-        }
-    }
-    if (top != 0) {
-        ext = new_variant(top, 0, 0, 0, EXT_NORMAL);
-        add_charinfo_vert_variant(ci, ext);
-    }
-}
-
-/*tex
-
-    Note that many more simple things like this are implemented as macros in the
-    header file.
-
-*/
-
-scaled get_charinfo_width(charinfo * ci)
-{
-    return ci->width;
-}
-
-scaled get_charinfo_height(charinfo * ci)
-{
-    return ci->height;
-}
-
-scaled get_charinfo_depth(charinfo * ci)
-{
-    return ci->depth;
-}
-
-scaled get_charinfo_italic(charinfo * ci)
-{
-    return ci->italic;
-}
-
-scaled get_charinfo_vert_italic(charinfo * ci)
-{
-    return ci->vert_italic;
-}
-
-scaled get_charinfo_top_accent(charinfo * ci)
-{
-    return ci->top_accent;
-}
-
-scaled get_charinfo_bot_accent(charinfo * ci)
-{
-    return ci->bot_accent;
-}
-
-char get_charinfo_tag(charinfo * ci)
-{
-    return ci->tag;
-}
-
-int get_charinfo_remainder(charinfo * ci)
-{
-    return ci->remainder;
-}
-
-char get_charinfo_used(charinfo * ci)
-{
-    return ci->used;
-}
-
-int get_charinfo_index(charinfo * ci)
-{
-    return ci->index;
-}
-
-char *get_charinfo_name(charinfo * ci)
-{
-    return ci->name;
-}
-
-char *get_charinfo_tounicode(charinfo * ci)
-{
-    return ci->tounicode;
-}
-
-liginfo *get_charinfo_ligatures(charinfo * ci)
-{
-    return ci->ligatures;
-}
-
-kerninfo *get_charinfo_kerns(charinfo * ci)
-{
-    return ci->kerns;
-}
-
-eight_bits *get_charinfo_packets(charinfo * ci)
-{
-    return ci->packets;
-}
-
-int get_charinfo_ef(charinfo * ci)
-{
-    return ci->ef;
-}
-
-int get_charinfo_rp(charinfo * ci)
-{
-    return ci->rp;
-}
-
-int get_charinfo_lp(charinfo * ci)
-{
-    return ci->lp;
-}
-
-extinfo *get_charinfo_vert_variants(charinfo * ci)
-{
-    extinfo *w = NULL;
-    if (ci->vert_variants != NULL)
-        w = ci->vert_variants;
-    return w;
-}
-
-extinfo *get_charinfo_hor_variants(charinfo * ci)
-{
-    extinfo *w = NULL;
-    if (ci->hor_variants != NULL)
-        w = ci->hor_variants;
-    return w;
-}
-
-
-scaled char_width(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    scaled w = get_charinfo_width(ci);
-    return w;
-}
-
-scaled calc_char_width(internal_font_number f, int c, int ex)
-{
-    charinfo *ci = char_info(f, c);
-    scaled w = get_charinfo_width(ci);
-    if (ex != 0)
-        w = round_xn_over_d(w, 1000 + ex, 1000);
-    return w;
-}
-
-scaled char_depth(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    scaled d = get_charinfo_depth(ci);
-    return d;
-}
-
-scaled char_height(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    scaled h = get_charinfo_height(ci);
-    return h;
-}
-
-scaled char_italic(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    scaled i = get_charinfo_italic(ci);
-    return i;
-}
-
-scaled char_vert_italic(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    scaled i = get_charinfo_vert_italic(ci);
-    return i;
-}
-
-scaled char_top_accent(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_top_accent(ci);
-}
-
-scaled char_bot_accent(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_bot_accent(ci);
-}
-
-int char_remainder(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_remainder(ci);
-}
-
-char char_tag(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_tag(ci);
-}
-
-char char_used(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_used(ci);
-}
-
-char *char_name(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_name(ci);
-}
-
-int char_index(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_index(ci);
-}
-
-liginfo *char_ligatures(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_ligatures(ci);
-}
-
-kerninfo *char_kerns(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_kerns(ci);
-}
-
-eight_bits *char_packets(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_packets(ci);
-}
-
-void set_font_params(internal_font_number f, int b)
-{
-    int i;
-    i = font_params(f);
-    if (i != b) {
-        font_bytes +=
-            (int) ((b - (int) font_params(f) + 1) * (int) sizeof(scaled));
-        do_realloc(param_base(f), (b + 2), int);
-        font_params(f) = b;
-        if (b > i) {
-            while (i < b) {
-                i++;
-                set_font_param(f, i, 0);
-            }
-        }
-    }
-}
-
-void set_font_math_params(internal_font_number f, int b)
-{
-    int i;
-    i = font_math_params(f);
-    if (i != b) {
-        font_bytes +=
-            ((b - (int) font_math_params(f) + 1) * (int) sizeof(scaled));
-        do_realloc(math_param_base(f), (b + 2), int);
-        font_math_params(f) = b;
-        if (b > i) {
-            while (i < b) {
-                i++;
-                set_font_math_param(f, i, undefined_math_parameter);
-            }
-        }
-    }
-}
-
-int copy_font(int f)
-{
-    int i, ci_cnt, ci_size;
-    charinfo *ci;
-    int k = new_font();
-    {
-        ci = font_tables[k]->charinfo;
-        ci_cnt = font_tables[k]->charinfo_count;
-        ci_size = font_tables[k]->charinfo_size;
-        memcpy(font_tables[k], font_tables[f], sizeof(texfont));
-        font_tables[k]->charinfo = ci;
-        font_tables[k]->charinfo_count = ci_cnt;
-        font_tables[k]->charinfo_size = ci_size;
-    }
-    font_malloc_charinfo(k, font_tables[f]->charinfo_count);
-    set_font_cache_id(k, 0);
-    set_font_used(k, 0);
-    set_font_touched(k, 0);
-    font_tables[k]->_font_name = NULL;
-    font_tables[k]->_font_filename = NULL;
-    font_tables[k]->_font_fullname = NULL;
-    font_tables[k]->_font_psname = NULL;
-    font_tables[k]->_font_encodingname = NULL;
-    font_tables[k]->_font_area = NULL;
-    font_tables[k]->_font_cidregistry = NULL;
-    font_tables[k]->_font_cidordering = NULL;
-    font_tables[k]->_left_boundary = NULL;
-    font_tables[k]->_right_boundary = NULL;
-    set_font_name(k, xstrdup(font_name(f)));
-    if (font_filename(f) != NULL)
-        set_font_filename(k, xstrdup(font_filename(f)));
-    if (font_fullname(f) != NULL)
-        set_font_fullname(k, xstrdup(font_fullname(f)));
-    if (font_psname(f) != NULL)
-        set_font_psname(k, xstrdup(font_psname(f)));
-    if (font_encodingname(f) != NULL)
-        set_font_encodingname(k, xstrdup(font_encodingname(f)));
-    if (font_area(f) != NULL)
-        set_font_area(k, xstrdup(font_area(f)));
-    if (font_cidregistry(f) != NULL)
-        set_font_cidregistry(k, xstrdup(font_cidregistry(f)));
-    if (font_cidordering(f) != NULL)
-        set_font_cidordering(k, xstrdup(font_cidordering(f)));
-    i = (int) (sizeof(*param_base(f)) * (unsigned) (font_params(f)+1));
-    font_bytes += i;
-    param_base(k) = xmalloc((unsigned) (i+1));
-    memcpy(param_base(k), param_base(f), (size_t) (i));
-    if (font_math_params(f) > 0) {
-        i = (int) (sizeof(*math_param_base(f)) *
-                   (unsigned) font_math_params(f));
-        font_bytes += i;
-        math_param_base(k) = xmalloc((unsigned) i);
-        memcpy(math_param_base(k), math_param_base(f), (size_t) i);
-    }
-    for (i = 0; i <= font_tables[f]->charinfo_count; i++) {
-        ci = copy_charinfo(&font_tables[f]->charinfo[i]);
-        font_tables[k]->charinfo[i] = *ci;
-    }
-    if (left_boundary(f) != NULL) {
-        ci = copy_charinfo(left_boundary(f));
-        set_charinfo(k, left_boundarychar, ci);
-    }
-    if (right_boundary(f) != NULL) {
-        ci = copy_charinfo(right_boundary(f));
-        set_charinfo(k, right_boundarychar, ci);
-    }
-    /*tex Not updated yet: */
-    font_tables[k]->charinfo_count = font_tables[f]->charinfo_count;
-    return k;
-}
-
-void delete_font(int f)
-{
-    int i;
-    charinfo *co;
-    assert(f > 0);
-    if (font_tables[f] != NULL) {
-        set_font_name(f, NULL);
-        set_font_filename(f, NULL);
-        set_font_fullname(f, NULL);
-        set_font_psname(f, NULL);
-        set_font_encodingname(f, NULL);
-        set_font_area(f, NULL);
-        set_font_cidregistry(f, NULL);
-        set_font_cidordering(f, NULL);
-        set_left_boundary(f, NULL);
-        set_right_boundary(f, NULL);
-        for (i = font_bc(f); i <= font_ec(f); i++) {
-            if (quick_char_exists(f, i)) {
-                co = char_info(f, i);
-                set_charinfo_name(co, NULL);
-                set_charinfo_tounicode(co, NULL);
-                set_charinfo_packets(co, NULL);
-                set_charinfo_ligatures(co, NULL);
-                set_charinfo_kerns(co, NULL);
-                set_charinfo_vert_variants(co, NULL);
-                set_charinfo_hor_variants(co, NULL);
-            }
-        }
-        /*tex free |notdef| */
-        set_charinfo_name(font_tables[f]->charinfo + 0, NULL);
-        free(font_tables[f]->charinfo);
-        destroy_sa_tree(font_tables[f]->characters);
-        free(param_base(f));
-        if (math_param_base(f) != NULL)
-            free(math_param_base(f));
-        free(font_tables[f]);
-        font_tables[f] = NULL;
-        if (font_id_maxval == f) {
-            font_id_maxval--;
-        }
-    }
-}
-
-void create_null_font(void)
-{
-    int i = new_font();
-    assert(i == 0);
-    set_font_name(i, xstrdup("nullfont"));
-    set_font_area(i, xstrdup(""));
-    set_font_touched(i, 1);
-}
-
-boolean is_valid_font(int id)
-{
-    int ret = 0;
-    if (id >= 0 && id <= font_id_maxval && font_tables[id] != NULL)
-        ret = 1;
-    return ret;
-}
-
-boolean cmp_font_area(int id, str_number t)
-{
-    char *tt = NULL;
-    char *tid = font_area(id);
-    if (t == 0) {
-        if (tid == NULL || strlen(tid) == 0)
-            return 1;
-        else
-            return 0;
-    }
-    tt = makecstring(t);
-    if ((tt == NULL || strlen(tt) == 0) && (tid == NULL || strlen(tid) == 0))
-        return 1;
-    if (tt == NULL || strcmp(tid, tt) != 0)
-        return 0;
-    free(tt);
-    return 1;
-}
-
-/*tex
-
-    Here come some subroutines to deal with expanded fonts for HZ-algorithm.
-    returning 1 means that they are identical.
-
-*/
-
-static boolean cmp_font_name(int id, char *tt)
-{
-    char *tid;
-    if (!is_valid_font(id))
-        return 0;
-    tid = font_name(id);
-    if (tt == NULL && tid == NULL)
-        return 1;
-    if (tt == NULL || tid == NULL || strcmp(tid, tt) != 0)
-        return 0;
-    return 1;
-}
-
-int test_no_ligatures(internal_font_number f)
-{
-    int c;
-    for (c = font_bc(f); c <= font_ec(f); c++) {
-        if (has_lig(f, c))
-            return 0;
-    }
-    return 1;
-}
-
-int get_tag_code(internal_font_number f, int c)
-{
-    small_number i;
-    if (char_exists(f, c)) {
-        i = char_tag(f, c);
-        if (i == lig_tag)
-            return 1;
-        else if (i == list_tag)
-            return 2;
-        else if (i == ext_tag)
-            return 4;
-        else
-            return 0;
-    }
-    return -1;
-}
-
-int get_lp_code(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_lp(ci);
-}
-
-int get_rp_code(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_rp(ci);
-}
-
-int get_ef_code(internal_font_number f, int c)
-{
-    charinfo *ci = char_info(f, c);
-    return get_charinfo_ef(ci);
-}
-
-void set_tag_code(internal_font_number f, int c, int i)
-{
-    int fixedi;
-    charinfo *co;
-    if (char_exists(f, c)) {
-        fixedi = -(i < -7 ? -7 : (i > 0 ? 0 : i));
-        co = char_info(f, c);
-        if (fixedi >= 4) {
-            if (char_tag(f, c) == ext_tag)
-                set_charinfo_tag(co, (char_tag(f, c) - ext_tag));
-            fixedi = fixedi - 4;
-        }
-        if (fixedi >= 2) {
-            if (char_tag(f, c) == list_tag)
-                set_charinfo_tag(co, (char_tag(f, c) - list_tag));
-            fixedi = fixedi - 2;
-        };
-        if (fixedi >= 1) {
-            if (has_lig(f, c))
-                set_charinfo_ligatures(co, NULL);
-            if (has_kern(f, c))
-                set_charinfo_kerns(co, NULL);
-        }
-    }
-}
-
-void set_lp_code(internal_font_number f, int c, int i)
-{
-    charinfo *co;
-    if (char_exists(f, c)) {
-        co = char_info(f, c);
-        set_charinfo_lp(co, i);
-    }
-}
-
-void set_rp_code(internal_font_number f, int c, int i)
-{
-    charinfo *co;
-    if (char_exists(f, c)) {
-        co = char_info(f, c);
-        set_charinfo_rp(co, i);
-    }
-}
-
-void set_ef_code(internal_font_number f, int c, int i)
-{
-    charinfo *co;
-    if (char_exists(f, c)) {
-        co = char_info(f, c);
-        set_charinfo_ef(co, i);
-    }
-}
-
-void set_no_ligatures(internal_font_number f)
-{
-    int c;
-    charinfo *co;
-    if (font_tables[f]->ligatures_disabled)
-        return;
-    co = char_info(f, left_boundarychar);
-    set_charinfo_ligatures(co, NULL);
-    co = char_info(f, right_boundarychar);
-    set_charinfo_ligatures(co, NULL);
-    for (c = 0; c < font_tables[f]->charinfo_count; c++) {
-        co = font_tables[f]->charinfo + c;
-        set_charinfo_ligatures(co, NULL);
-    }
-    font_tables[f]->ligatures_disabled = 1;
-}
-
-liginfo get_ligature(internal_font_number f, int lc, int rc)
-{
-    int k = 0;
-    liginfo t, u;
-    charinfo *co;
-    t.lig = 0;
-    t.type = 0;
-    t.adj = 0;
-    if (lc == non_boundarychar || rc == non_boundarychar || (!has_lig(f, lc)))
-        return t;
-    co = char_info(f, lc);
-    while (1) {
-        u = charinfo_ligature(co, k);
-        if (lig_end(u))
-            break;
-        if (lig_char(u) == rc) {
-            if (lig_disabled(u)) {
-                return t;
-            } else {
-                return u;
-            }
-        }
-        k++;
-    }
-    return t;
-}
-
-scaled raw_get_kern(internal_font_number f, int lc, int rc)
-{
-    int k = 0;
-    kerninfo u;
-    charinfo *co;
-    if (lc == non_boundarychar || rc == non_boundarychar)
-        return 0;
-    co = char_info(f, lc);
-    while (1) {
-        u = charinfo_kern(co, k);
-        if (kern_end(u))
-            break;
-        if (kern_char(u) == rc) {
-            if (kern_disabled(u))
-                return 0;
-            else
-                return kern_kern(u);
-        }
-        k++;
-    }
-    return 0;
-}
-
-scaled get_kern(internal_font_number f, int lc, int rc)
-{
-    if (lc == non_boundarychar || rc == non_boundarychar || (!has_kern(f, lc)))
-        return 0;
-    return raw_get_kern(f, lc, rc);
-}
-
-
-/*tex Dumping and undumping fonts. */
-
-#define dump_string(a)                    \
-    if (a!=NULL) {                        \
-        x = (int)(strlen(a)+1);           \
-        dump_int(x);  dump_things(*a, x); \
-    } else {                              \
-        x = 0; dump_int(x);               \
-    }
-
-static void dump_charinfo(int f, int c)
-{
-    charinfo *co;
-    int x;
-    liginfo *lig;
-    kerninfo *kern;
-    dump_int(c);
-    co = char_info(f, c);
-    set_charinfo_used(co, 0);
-    dump_int(get_charinfo_width(co));
-    dump_int(get_charinfo_height(co));
-    dump_int(get_charinfo_depth(co));
-    dump_int(get_charinfo_italic(co));
-    dump_int(get_charinfo_vert_italic(co));
-    dump_int(get_charinfo_top_accent(co));
-    dump_int(get_charinfo_bot_accent(co));
-    dump_int(get_charinfo_tag(co));
-    dump_int(get_charinfo_ef(co));
-    dump_int(get_charinfo_rp(co));
-    dump_int(get_charinfo_lp(co));
-    dump_int(get_charinfo_remainder(co));
-    dump_int(get_charinfo_used(co));
-    dump_int(get_charinfo_index(co));
-    dump_string(get_charinfo_name(co));
-    dump_string(get_charinfo_tounicode(co));
-    /*tex Ligatures */
-    x = 0;
-    if ((lig = get_charinfo_ligatures(co)) != NULL) {
-        while (!lig_end(lig[x])) {
-            x++;
-        }
-        x++;
-        dump_int(x);
-        dump_things(*lig, x);
-    } else {
-        dump_int(x);
-    }
-    /*tex Kerns */
-    x = 0;
-    if ((kern = get_charinfo_kerns(co)) != NULL) {
-        while (!kern_end(kern[x])) {
-            x++;
-        }
-        x++;
-        dump_int(x);
-        dump_things(*kern, x);
-    } else {
-        dump_int(x);
-    }
-    /*tex Packets */
-    x = vf_packet_bytes(co);
-    dump_int(x);
-    if (x > 0) {
-        dump_things(*get_charinfo_packets(co), x);
-    }
-    if (get_charinfo_tag(co) == ext_tag) {
-        dump_charinfo_variants(get_charinfo_vert_variants(co));
-        dump_charinfo_variants(get_charinfo_hor_variants(co));
-    }
-    dump_math_kerns(co);
-}
-
-static void dump_font_entry(texfont * f)
-{
-    int x;
-    dump_int(f->_font_size);
-    dump_int(f->_font_dsize);
-    dump_int(f->_font_cidversion);
-    dump_int(f->_font_cidsupplement);
-    dump_int(f->_font_ec);
-    x = (int) f->_font_checksum;
-    dump_int(x);
-    dump_int(f->_font_used);
-    dump_int(f->_font_touched);
-    dump_int(f->_font_cache_id);
-    dump_int(f->_font_encodingbytes);
-    dump_int(f->_font_oldmath);
-    dump_int(f->_font_slant);
-    dump_int(f->_font_extend);
-    dump_int(f->_font_squeeze);
-    dump_int(f->_font_mode);
-    dump_int(f->_font_width);
-    dump_int(f->font_max_shrink);
-    dump_int(f->font_max_stretch);
-    dump_int(f->_font_step);
-    dump_int(f->_font_tounicode);
-    dump_int(f->_font_type);
-    dump_int(f->_font_format);
-    dump_int(f->_font_writingmode);
-    dump_int(f->_font_identity);
-    dump_int(f->_font_embedding);
-    dump_int(f->_font_streamprovider);
-    dump_int(f->_font_bc);
-    dump_int(f->_hyphen_char);
-    dump_int(f->_skew_char);
-    dump_int(f->_font_natural_dir);
-    dump_int(f->_font_params);
-    dump_int(f->_font_math_params);
-    dump_int(f->ligatures_disabled);
-    dump_int(f->_pdf_font_num);
-    dump_int(f->_pdf_font_attr);
-}
-
-void dump_font(int f)
-{
-    int i, x;
-    set_font_used(f, 0);
-    font_tables[f]->charinfo_cache = NULL;
-    dump_font_entry(font_tables[f]);
-    dump_string(font_name(f));
-    dump_string(font_area(f));
-    dump_string(font_filename(f));
-    dump_string(font_fullname(f));
-    dump_string(font_psname(f));
-    dump_string(font_encodingname(f));
-    dump_string(font_cidregistry(f));
-    dump_string(font_cidordering(f));
-    dump_things(*param_base(f), (font_params(f) + 1));
-    if (font_math_params(f) > 0) {
-        dump_things(*math_param_base(f), (font_math_params(f) + 1 ));
-    }
-    if (has_left_boundary(f)) {
-        dump_int(1);
-        dump_charinfo(f, left_boundarychar);
-    } else {
-        dump_int(0);
-    }
-    if (has_right_boundary(f)) {
-        dump_int(1);
-        dump_charinfo(f, right_boundarychar);
-    } else {
-        dump_int(0);
-    }
-    for (i = font_bc(f); i <= font_ec(f); i++) {
-        if (quick_char_exists(f, i)) {
-            dump_charinfo(f, i);
-        }
-    }
-}
-
-static int undump_charinfo(int f)
-{
-    charinfo *co;
-    int x, i;
-    char *s = NULL;
-    liginfo *lig = NULL;
-    kerninfo *kern = NULL;
-    eight_bits *packet = NULL;
-    undump_int(i);
-    co = get_charinfo(f, i);
-    undump_int(x);
-    set_charinfo_width(co, x);
-    undump_int(x);
-    set_charinfo_height(co, x);
-    undump_int(x);
-    set_charinfo_depth(co, x);
-    undump_int(x);
-    set_charinfo_italic(co, x);
-    undump_int(x);
-    set_charinfo_vert_italic(co, x);
-    undump_int(x);
-    set_charinfo_top_accent(co, x);
-    undump_int(x);
-    set_charinfo_bot_accent(co, x);
-    undump_int(x);
-    set_charinfo_tag(co, x);
-    undump_int(x);
-    set_charinfo_ef(co, x);
-    undump_int(x);
-    set_charinfo_rp(co, x);
-    undump_int(x);
-    set_charinfo_lp(co, x);
-    undump_int(x);
-    set_charinfo_remainder(co, x);
-    undump_int(x);
-    set_charinfo_used(co, x);
-    undump_int(x);
-    set_charinfo_index(co, x);
-    /*tex Name */
-    undump_int(x);
-    if (x > 0) {
-        font_bytes += x;
-        s = xmalloc((unsigned) x);
-        undump_things(*s, x);
-    }
-    set_charinfo_name(co, s);
-    /*tex Tounicode */
-    undump_int(x);
-    if (x > 0) {
-        font_bytes += x;
-        s = xmalloc((unsigned) x);
-        undump_things(*s, x);
-    }
-    set_charinfo_tounicode(co, s);
-    /*tex Ligatures */
-    undump_int(x);
-    if (x > 0) {
-        font_bytes += (int) ((unsigned) x * sizeof(liginfo));
-        lig = xmalloc((unsigned) ((unsigned) x * sizeof(liginfo)));
-        undump_things(*lig, x);
-    }
-    set_charinfo_ligatures(co, lig);
-    /*tex Kerns */
-    undump_int(x);
-    if (x > 0) {
-        font_bytes += (int) ((unsigned) x * sizeof(kerninfo));
-        kern = xmalloc((unsigned) ((unsigned) x * sizeof(kerninfo)));
-        undump_things(*kern, x);
-    }
-    set_charinfo_kerns(co, kern);
-
-    /*tex Packets */
-    undump_int(x);
-    if (x > 0) {
-        font_bytes += x;
-        packet = xmalloc((unsigned) x);
-        undump_things(*packet, x);
-    }
-    set_charinfo_packets(co, packet);
-    if (get_charinfo_tag(co) == ext_tag) {
-        set_charinfo_vert_variants(co, undump_charinfo_variants());
-        set_charinfo_hor_variants(co, undump_charinfo_variants());
-    }
-    undump_math_kerns(co);
-    return i;
-}
-
-#define undump_font_string(a)     \
-    undump_int (x);               \
-    if (x>0) {                    \
-        font_bytes += x;          \
-        s = xmalloc((unsigned)x); \
-        undump_things(*s,x);      \
-        a(f,s);                   \
-    }
-
-static void undump_font_entry(texfont * f)
-{
-    int x = 0;
-    undump_int(x); f->_font_size = x;
-    undump_int(x); f->_font_dsize = x;
-    undump_int(x); f->_font_cidversion = x;
-    undump_int(x); f->_font_cidsupplement = x;
-    undump_int(x); f->_font_ec = x;
-    undump_int(x); f->_font_checksum = (unsigned)x;
-    undump_int(x); f->_font_used = (char)x;
-    undump_int(x); f->_font_touched = (char)x;
-    undump_int(x); f->_font_cache_id = x;
-    undump_int(x); f->_font_encodingbytes = (char)x;
-    undump_int(x); f->_font_oldmath = x;
-    undump_int(x); f->_font_slant = x;
-    undump_int(x); f->_font_extend = x;
-    undump_int(x); f->_font_squeeze = x;
-    undump_int(x); f->_font_mode = x;
-    undump_int(x); f->_font_width = x;
-    undump_int(x); f->font_max_shrink = x;
-    undump_int(x); f->font_max_stretch = x;
-    undump_int(x); f->_font_step = x;
-    undump_int(x); f->_font_tounicode = (char)x;
-    undump_int(x); f->_font_type = x;
-    undump_int(x); f->_font_format = x;
-    undump_int(x); f->_font_writingmode = x;
-    undump_int(x); f->_font_identity = x;
-    undump_int(x); f->_font_embedding = x;
-    undump_int(x); f->_font_streamprovider = x;
-    undump_int(x); f->_font_bc = x;
-    undump_int(x); f->_hyphen_char = x;
-    undump_int(x); f->_skew_char = x;
-    undump_int(x); f->_font_natural_dir = x;
-    undump_int(x); f->_font_params = x;
-    undump_int(x); f->_font_math_params = x;
-    undump_int(x); f->ligatures_disabled = x;
-    undump_int(x); f->_pdf_font_num = x;
-    undump_int(x); f->_pdf_font_attr = x;
-}
-
-void undump_font(int f)
-{
-    int x, i;
-    texfont *tt;
-    charinfo *ci;
-    char *s;
-    sa_tree_item sa_value = { 0 };
-    grow_font_table(f);
-    tt = xmalloc(sizeof(texfont));
-    memset(tt, 0, sizeof(texfont));
-    font_bytes += (int) sizeof(texfont);
-    undump_font_entry(tt);
-    font_tables[f] = tt;
-    undump_font_string(set_font_name);
-    undump_font_string(set_font_area);
-    undump_font_string(set_font_filename);
-    undump_font_string(set_font_fullname);
-    undump_font_string(set_font_psname);
-    undump_font_string(set_font_encodingname);
-    undump_font_string(set_font_cidregistry);
-    undump_font_string(set_font_cidordering);
-    i = (int) (sizeof(*param_base(f)) * ((unsigned) font_params(f) + 1));
-    font_bytes += i;
-    param_base(f) = xmalloc((unsigned) i);
-    undump_things(*param_base(f), (font_params(f) + 1));
-    if (font_math_params(f) > 0) {
-        i = (int) (sizeof(*math_param_base(f)) *
-                   ((unsigned) font_math_params(f) + 1));
-        font_bytes += i;
-        math_param_base(f) = xmalloc((unsigned) i);
-        undump_things(*math_param_base(f), (font_math_params(f) + 1));
-    }
-    /*tex stack size 1, default item value 0 */
-    font_tables[f]->characters = new_sa_tree(1, 1, sa_value);
-    ci = xcalloc(1, sizeof(charinfo));
-    set_charinfo_name(ci, xstrdup(".notdef"));
-    font_tables[f]->charinfo = ci;
-    undump_int(x);
-    if (x) {
-        /*tex left boundary */
-        i = undump_charinfo(f);
-    }
-    undump_int(x);
-    if (x) {
-        /*tex right boundary */
-        i = undump_charinfo(f);
-    }
-    i = font_bc(f);
-    while (i < font_ec(f)) {
-        i = undump_charinfo(f);
-    }
-}
-
-/* The \PK\ pixel density value from |texmf.cnf| */
-
-int pk_dpi;
-
-/*tex
-
-    This one looks up the font for a \TFM\ with name |s| loaded at |fs| size and
-    if found then flushes |s|.
-
-*/
-
-internal_font_number tfm_lookup(char *s, scaled fs)
-{
-    internal_font_number k;
-    if (fs != 0) {
-        for (k = 1; k <= max_font_id(); k++) {
-            if (cmp_font_name(k, s) && font_size(k) == fs) {
-                return k;
-            }
-        }
-    } else {
-        for (k = 1; k <= max_font_id(); k++) {
-            if (cmp_font_name(k, s)) {
-                return k;
-            }
-        }
-    }
-    return null_font;
-}
-
-/*tex
-
-    This returns the multiple of |font_step(f)| that is nearest to |e|.
-
-*/
-
-int fix_expand_value(internal_font_number f, int e)
-{
-    int step;
-    int max_expand;
-    boolean neg;
-    if (e == 0)
-        return 0;
-    if (e < 0) {
-        e = -e;
-        neg = true;
-        max_expand = font_max_shrink(f);
-    } else {
-        neg = false;
-        max_expand = font_max_stretch(f);
-    }
-    if (e > max_expand) {
-        e = max_expand;
-    } else {
-        step = font_step(f);
-        if (e % step > 0)
-            e = step * round_xn_over_d(e, 1, step);
-    }
-    if (neg)
-        e = -e;
-    return e;
-}
-
-void set_expand_params(internal_font_number f, int stretch_limit, int shrink_limit, int font_step)
-{
-    set_font_step(f, font_step);
-    set_font_max_shrink(f, shrink_limit);
-    set_font_max_stretch(f, stretch_limit);
-}
-
-/*tex
-
-    This reads font expansion spec and load expanded font.
-
-*/
-
-void read_expand_font(void)
-{
-    int shrink_limit, stretch_limit, font_step;
-    internal_font_number f;
-    scan_font_ident();
-    f = cur_val;
-    if (f == null_font)
-        normal_error("font expansion", "invalid font identifier");
-    scan_optional_equals();
-    scan_int();
-    stretch_limit = fix_int(cur_val, 0, 1000);
-    scan_int();
-    shrink_limit = fix_int(cur_val, 0, 500);
-    scan_int();
-    font_step = fix_int(cur_val, 0, 100);
-    if (font_step == 0)
-        normal_error("font expansion", "invalid step");
-    stretch_limit = stretch_limit - stretch_limit % font_step;
-    if (stretch_limit < 0)
-        stretch_limit = 0;
-    shrink_limit = shrink_limit - shrink_limit % font_step;
-    if (shrink_limit < 0)
-        shrink_limit = 0;
-    if ((stretch_limit == 0) && (shrink_limit == 0))
-        normal_error("font expansion", "invalid limit(s)");
-    if (scan_keyword("autoexpand")) {
-        normal_warning("font expansion", "autoexpand not supported");
-        /*tex scan an optional space */
-        get_x_token();
-        if (cur_cmd != spacer_cmd)
-            back_input();
-    }
-    if (font_step(f) != 0) {
-        /*tex This font has been expanded, ensure the expansion parameters are identical. */
-        if (font_step(f) != font_step)
-            normal_error("font expansion","font has been expanded with different expansion step");
-        if (((font_max_stretch(f) == 0) && (stretch_limit != 0)) ||
-            ((font_max_stretch(f) > 0) && (font_max_stretch(f) != stretch_limit)))
-            normal_error("font expansion","font has been expanded with different stretch limit");
-
-        if (((font_max_shrink(f) == 0) && (shrink_limit != 0)) ||
-            ((font_max_shrink(f) > 0) && (font_max_shrink(f) != shrink_limit)))
-            normal_error("font expansion","font has been expanded with different shrink limit");
-    } else {
-        if (font_used(f))
-            normal_warning("font expansion", "font should be expanded before its first use");
-        set_expand_params(f, stretch_limit, shrink_limit, font_step);
-    }
-}
-
-/*tex
-
-    Here's an old (sort of obsolete) letterspace-a-font helper. It does so by by
-    creating a virtual font.
-
-*/
-
-void new_letterspaced_font(small_number a)
-{
-    pointer u;
-    str_number t;
-    internal_font_number f, k;
-    boolean nolig = false;
-    get_r_token();
-    u = cur_cs;
-    if (u >= hash_base)
-        t = cs_text(u);
-    else
-        t = maketexstring("FONT");
-    define(u, set_font_cmd, null_font);
-    scan_optional_equals();
-    scan_font_ident();
-    k = cur_val;
-    scan_int();
-    if (scan_keyword("nolig"))
-       nolig=true;
-    f = letter_space_font(k, fix_int(cur_val, -1000, 1000), nolig);
-    equiv(u) = f;
-    eqtb[font_id_base + f] = eqtb[u];
-    font_id_text(f) = t;
-}
-
-/*tex
-
-    This makes a font copy for further use with font expansion. Again a
-    traditional font related helper.
-
-*/
-
-void make_font_copy(small_number a)
-{
-    pointer u;
-    str_number t;
-    internal_font_number f, k;
-    get_r_token();
-    u = cur_cs;
-    if (u >= hash_base)
-        t = cs_text(u);
-    else
-        t = maketexstring("FONT");
-    define(u, set_font_cmd, null_font);
-    scan_optional_equals();
-    scan_font_ident();
-    k = cur_val;
-    f = copy_font_info(k);
-    equiv(u) = f;
-    eqtb[font_id_base + f] = eqtb[u];
-    font_id_text(f) = t;
-}
-
-void glyph_to_unicode(void)
-{
-    str_number s1, s2;
-    scan_toks(false, true);
-    s1 = tokens_to_string(def_ref);
-    delete_token_ref(def_ref);
-    scan_toks(false, true);
-    s2 = tokens_to_string(def_ref);
-    delete_token_ref(def_ref);
-    def_tounicode(s1, s2);
-    flush_str(s2);
-    flush_str(s1);
-}
--- texlive-bin.orig/texk/web2c/luatexdir/font/tfmofm.c
+++ /dev/null
@@ -1,1186 +0,0 @@
-/*
-
-Copyright 2006-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex Here are some macros that help process ligatures and kerns */
-
-#define lig_kern_start(f,c)   char_remainder(f,c)
-
-/*tex A value indicating |STOP| in a lig/kern program: */
-
-#define stop_flag 128
-
-/*tex The op code for a kern step: */
-
-#define kern_flag 128
-
-#define skip_byte(z)        lig_kerns[z].b0
-#define next_char(z)        lig_kerns[z].b1
-#define op_byte(z)          lig_kerns[z].b2
-#define rem_byte(z)         lig_kerns[z].b3
-#define lig_kern_restart(c) (256*op_byte(c)+rem_byte(c))
-
-
-/*tex
-
-    The information in a \TFM\file appears in a sequence of 8-bit bytes. Since
-    the number of bytes is always a multiple of 4, we could also regard the file
-    as a sequence of 32-bit words, but \TeX\ uses the byte interpretation. The
-    format of \TFM\files was designed by Lyle Ramshaw in 1980. The intent is
-    to convey a lot of different kinds of information in a compact but useful
-    form.
-
-    $\Omega$ is capable of reading not only \TFM\files, but also \.{OFM}
-    files, which can describe fonts with up to 65536 characters and with huge
-    lig/kern tables. These fonts will often be virtual fonts built up from real
-    fonts with 256 characters, but $\Omega$ is not aware of this.
-
-    The documentation below describes \TFM\files, with slight additions to
-    show where \.{OFM} files differ.
-
-    The first 24 bytes (6 words) of a \TFM\file contain twelve 16-bit integers
-    that give the lengths of the various subsequent portions of the file. These
-    twelve integers are, in order:
-
-    \starttabulate
-        \NC \type {lf| \NC length of the entire file, in words \NC \NR
-        \NC \type {lh| \NC length of the header data, in words \NC \NR
-        \NC \type {bc| \NC smallest character code in the font \NC \NR
-        \NC \type {ec| \NC largest character code in the font \NC \NR
-        \NC \type {nw| \NC number of words in the width table \NC \NR
-        \NC \type {nh| \NC number of words in the height table \NC \NR
-        \NC \type {nd| \NC number of words in the depth table \NC \NR
-        \NC \type {ni| \NC number of words in the italic correction table \NC \NR
-        \NC \type {nl| \NC number of words in the lig/kern table \NC \NR
-        \NC \type {nk| \NC number of words in the kern table \NC \NR
-        \NC \type {ne| \NC number of words in the extensible character table \NC \NR
-        \NC \type {np| \NC number of font parameter words \NC \NR
-    \stoptabulate
-
-    They are all nonnegative and less than $2^{15}$. We must have
-    |bc-1<=ec<=255|, and $|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|$. Note that
-    a \TFM\font may contain as many as 256 characters (if |bc=0| and
-    |ec=255|), and as few as 0 characters (if |bc=ec+1|).
-
-    Incidentally, when two or more 8-bit bytes are combined to form an integer of
-    16 or more bits, the most significant bytes appear first in the file. This is
-    called BigEndian order.
-
-    The first 52 bytes (13 words) of an \.{OFM} file contains thirteen 32-bit
-    integers that give the lengths of the various subsequent portions of the
-    file. The first word is 0 (future versions of \.{OFM} files could have
-    different values; what is important is that the first two bytes be 0 to
-    differentiate \TFM\and \.{OFM} files). The next twelve integers are as
-    above, all nonegative and less than~$2^{31}$. We must have |bc-1<=ec<=65535|,
-    and $$\hbox{|lf=13+lh+2*(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$ Note that an
-    \.{OFM} font may contain as many as 65536 characters (if |bc=0| and
-    |ec=65535|), and as few as 0 characters (if |bc=ec+1|).
-
-    The rest of the \TFM\file may be regarded as a sequence of ten data arrays
-    having the informal specification
-
-    \starttabulate
-    \NC \type {header}     \NC \type {[0..lh-1]} \NC \type {stuff} \NC \NR
-    \NC \type {char\_info} \NC \type {[bc..ec]}  \NC \type {char_info_word} \NC \NR
-    \NC \type {width}      \NC \type {[0..nw-1]} \NC \type {fix_word} \NC \NR
-    \NC \type {height}     \NC \type {[0..nh-1]} \NC \type {fix_word} \NC \NR
-    \NC \type {depth}      \NC \type {[0..nd-1]} \NC \type {fix_word} \NC \NR
-    \NC \type {italic}     \NC \type {[0..ni-1]} \NC \type {fix_word} \NC \NR
-    \NC \type {lig\_kern}  \NC \type {[0..nl-1]} \NC \type {lig_kern_command} \NC \NR
-    \NC \type {kern}       \NC \type {[0..nk-1]} \NC \type {fix_word} \NC \NR
-    \NC \type {exten}      \NC \type {[0..ne-1]} \NC \type {extensible_recipe} \NC \NR
-    \NC \type {param}      \NC \type {[1..np]}   \NC \type {fix_word} \NC \NR
-    \stoptabulate
-
-    The most important data type used here is a |@!fix_word|, which is a 32-bit
-    representation of a binary fraction. A |fix_word| is a signed quantity, with
-    the two's complement of the entire word used to represent negation. Of the 32
-    bits in a |fix_word|, exactly 12 are to the left of the binary point; thus,
-    the largest |fix_word| value is $2048-2^{-20}$, and the smallest is $-2048$.
-    We will see below, however, that all but two of the |fix_word| values must
-    lie between $-16$ and $+16$.
-
-    The first data array is a block of header information, which contains general
-    facts about the font. The header must contain at least two words, |header[0]|
-    and |header[1]|, whose meaning is explained below. Additional header
-    information of use to other software routines might also be included, but
-    \TeX82 does not need to know about such details. For example, 16 more words
-    of header information are in use at the Xerox Palo Alto Research Center; the
-    first ten specify the character coding scheme used (e.g., `\.{XEROX text}' or
-    `\.{TeX math symbols}'), the next five give the font identifier (e.g.,
-    `\.{HELVETICA}' or `\.{CMSY}'), and the last gives the ``face byte.'' The
-    program that converts \.{DVI} files to Xerox printing format gets this
-    information by looking at the \TFM\file, which it needs to read anyway
-    because of other information that is not explicitly repeated in
-    \.{DVI}~format.
-
-    \startitemize
-
-        \startitem
-            |header[0]| is a 32-bit check sum that \TeX\ will copy into the
-            \.{DVI} output file. Later on when the \.{DVI} file is printed,
-            possibly on another computer, the actual font that gets used is
-            supposed to have a check sum that agrees with the one in the \TFM\
-            file used by \TeX. In this way, users will be warned about potential
-            incompatibilities. (However, if the check sum is zero in either the
-            font file or the \TFM\file, no check is made.) The actual relation
-            between this check sum and the rest of the \TFM\file is not
-            important; the check sum is simply an identification number with the
-            property that incompatible fonts almost always have distinct check
-            sums.
-        \stopitem
-
-        \startitem
-            |header[1]| is a |fix_word| containing the design size of the font,
-            in units of \TeX\ points. This number must be at least 1.0; it is
-            fairly arbitrary, but usually the design size is 10.0 for a ``10
-            point'' font, i.e., a font that was designed to look best at a
-            10-point size, whatever that really means. When a \TeX\ user asks for
-            a font `\.{at} $\delta$ \.{pt}', the effect is to override the design
-            size and replace it by $\delta$, and to multiply the $x$ and~$y$
-            coordinates of the points in the font image by a factor of $\delta$
-            divided by the design size. {\sl All other dimensions in the\/
-            \TFM\file are |fix_word|\kern-1pt\ numbers in design-size units},
-            with the exception of |param[1]| (which denotes the slant ratio).
-            Thus, for example, the value of |param[6]|, which defines the \.{em}
-            unit, is often the |fix_word| value $2^{20}=1.0$, since many fonts
-            have a design size equal to one em. The other dimensions must be less
-            than 16 design-size units in absolute value; thus, |header[1]| and
-            |param[1]| are the only |fix_word| entries in the whole \TFM\file
-            whose first byte might be something besides 0 or 255.
-        \stopitem
-
-    \stopitemize
-
-    Next comes the |char_info| array, which contains one |@!char_info_word| per
-    character. Each word in this part of a \TFM\file contains six fields
-    packed into four bytes as follows.
-
-    \startitemize
-        \startitem
-            first byte: |width_index| (8 bits)
-        \stopitem
-        \startitem
-            second byte: |height_index| (4 bits) times 16, plus |depth_index|
-            (4~bits)
-        \stopitem
-        \startitem
-            third byte: |italic_index| (6 bits) times 4, plus |tag| (2~bits)
-        \stopitem
-        \startitem
-            fourth byte: |remainder| (8 bits)
-        \stopitem
-    \stopitemize
-
-    The actual width of a character is \\{width}|[width_index]|, in design-size
-    units; this is a device for compressing information, since many characters
-    have the same width. Since it is quite common for many characters to have the
-    same height, depth, or italic correction, the \TFM\format imposes a limit
-    of 16 different heights, 16 different depths, and 64 different italic
-    corrections.
-
-    For \.{OFM} files, two words (eight bytes) are used. The arrangement is as
-    follows.
-
-    \startitemize
-        \startitem
-            first and second bytes: |width_index| (16 bits)
-        \stopitem
-            \startitem third byte: |height_index| (8 bits)
-        \stopitem
-        \startitem
-            fourth byte: |depth_index| (8~bits)
-        \stopitem
-        \startitem
-            fifth and sixth bytes: |italic_index| (14 bits) times 4, plus |tag|
-            (2~bits)
-        \startitem
-            seventh and eighth bytes: |remainder| (16 bits)
-        \stopitem
-    \stopitemize
-
-    Therefore the \.{OFM} format imposes a limit of 256 different heights, 256
-    different depths, and 16384 different italic corrections.
-
-    The italic correction of a character has two different uses. (a)~In ordinary
-    text, the italic correction is added to the width only if the \TeX\ user
-    specifies `\.{\\/}' after the character. (b)~In math formulas, the italic
-    correction is always added to the width, except with respect to the
-    positioning of subscripts.
-
-    Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
-    \\{italic}[0]=0$ should always hold, so that an index of zero implies a value
-    of zero. The |width_index| should never be zero unless the character does not
-    exist in the font, since a character is valid if and only if it lies between
-    |bc| and |ec| and has a nonzero |width_index|.
-
-    \TeX\ checks the information of a \TFM\file for validity as the file is
-    being read in, so that no further checks will be needed when typesetting is
-    going on. The somewhat tedious subroutine that does this is called
-    |read_font_info|. It has four parameters: the user font identifier~|u|, the
-    file name and area strings |nom| and |aire|, and the ``at'' size~|s|. If
-    |s|~is negative, it's the negative of a scale factor to be applied to the
-    design size; |s=-1000| is the normal case. Otherwise |s| will be substituted
-    for the design size; in this case, |s| must be positive and less than
-    $2048\rm\,pt$ (i.e., it must be less than $2^{27}$ when considered as an
-    integer).
-
-    The subroutine opens and closes a global file variable called |tfm_file|. It
-    returns the value of the internal font number that was just loaded. If an
-    error is detected, an error message is issued and no font information is
-    stored; |null_font| is returned in this case.
-
-    The |tag| field in a |char_info_word| has four values that explain how to
-    interpret the |remainder| field.
-
-    \startitemize
-        \startitem
-            |tag=0| (|no_tag|) means that |remainder| is unused.
-        \stopitem
-        \startitem
-            |tag=1| (|lig_tag|) means that this character has a ligature/kerning
-            program starting at position |remainder| in the |lig_kern| array
-        \stopitem
-        \startitem
-            |tag=2| (|list_tag|) means that this character is part of a chain of
-            characters of ascending sizes, and not the largest in the chain. The
-            |remainder| field gives the character code of the next larger
-            character
-        \stopitem
-        \startitem
-            |tag=3| (|ext_tag|) means that this character code represents an
-            extensible character, i.e., a character that is built up of smaller
-            pieces so that it can be made arbitrarily large. The pieces are
-            specified in |exten[remainder]|
-        \stopitem
-    \stopitemize
-
-    Characters with |tag=2| and |tag=3| are treated as characters with |tag=0|
-    unless they are used in special circumstances in math formulas. For example,
-    the \.{\\sum} operation looks for a |list_tag|, and the \.{\\left} operation
-    looks for both |list_tag| and |ext_tag|.
-
-    The |lig_kern| array contains instructions in a simple programming language
-    that explains what to do for special letter pairs. Each word in this array,
-    in a \TFM\file, is a |@!lig_kern_command| of four bytes.
-
-    \startitemize
-        \startitem
-            first byte: |skip_byte|, indicates that this is the final program
-            step if the byte is 128 or more, otherwise the next step is obtained
-            by skipping this number of intervening steps
-        \stopitem
-        \startitem
-            second byte: |next_char|, if |next_char| follows the current
-            character, then perform the operation and stop, otherwise
-            continue
-        \stopitem
-        \startitem
-            third byte: |op_byte|, indicates a ligature step if less than~128, a
-            kern step otherwise
-        \stopitem
-        \startitem
-            fourth byte: |remainder|
-        \stopitem
-    \stopitemize
-
-    In an \.{OFM} file, eight bytes are used, two bytes for each field.
-
-    In a kern step, an additional space equal to |kern[256 * (op_byte-128) +
-    remainder]| is inserted between the current character and |next_char|. This
-    amount is often negative, so that the characters are brought closer together
-    by kerning; but it might be positive.
-
-    There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$
-    where $0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
-    |remainder| is inserted between the current character and |next_char|; then
-    the current character is deleted if $b=0$, and |next_char| is deleted if
-    $c=0$; then we pass over $a$~characters to reach the next current character
-    (which may have a ligature/kerning program of its own).
-
-    If the very first instruction of the |lig_kern| array has |skip_byte=255|,
-    the |next_char| byte is the so-called right boundary character of this font;
-    the value of |next_char| need not lie between |bc| and~|ec|. If the very last
-    instruction of the |lig_kern| array has |skip_byte=255|, there is a special
-    ligature/kerning program for a left boundary character, beginning at location
-    |256*op_byte+remainder|. The interpretation is that \TeX\ puts implicit
-    boundary characters before and after each consecutive string of characters
-    from the same font. These implicit characters do not appear in the output,
-    but they can affect ligatures and kerning.
-
-    If the very first instruction of a character's |lig_kern| program has
-    |skip_byte>128|, the program actually begins in location
-    |256*op_byte+remainder|. This feature allows access to large |lig_kern|
-    arrays, because the first instruction must otherwise appear in a location
-    |<=255| in a \TFM\file, |<=65535| in an \.{OFM} file.
-
-    Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy the
-    condition $$\hbox{|256*op_byte+remainder<nl|.}$$ If such an instruction is
-    encountered during normal program execution, it denotes an unconditional
-    halt; no ligature or kerning command is performed.
-
-    Extensible characters are specified by an |@!extensible_recipe|, which
-    consists of four bytes in a \TFM\file, called |@!top|, |@!mid|, |@!bot|,
-    and |@!rep| (in this order). In an \.{OFM} file, each field takes two bytes,
-    for eight in total. These bytes are the character codes of individual pieces
-    used to build up a large symbol. If |top|, |mid|, or |bot| are zero, they are
-    not present in the built-up result. For example, an extensible vertical line
-    is like an extensible bracket, except that the top and bottom pieces are
-    missing.
-
-    Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box if
-    the piece isn't present. Then the extensible characters have the form
-    $TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent; in the
-    latter case we can have $TR^kB$ for both even and odd values of~|k|. The
-    width of the extensible character is the width of $R$; and the
-    height-plus-depth is the sum of the individual height-plus-depths of the
-    components used, since the pieces are butted together in a vertical list.
-
-    The final portion of a \TFM\file is the |param| array, which is another
-    sequence of |fix_word| values.
-
-    \startitemize
-
-    \startitem
-        |param[1]=slant| is the amount of italic slant, which is used to help
-        position accents. For example, |slant=.25| means that when you go up one
-        unit, you also go .25 units to the right. The |slant| is a pure number;
-        it's the only |fix_word| other than the design size itself that is not
-        scaled by the design size.
-    \stopitem
-
-    \startitem
-        |param[2]=space| is the normal spacing between words in text. Note that
-        character |" "| in the font need not have anything to do with blank
-        spaces.
-    \stopitem
-
-    \startitem
-        |param[3]=space_stretch| is the amount of glue stretching between words.
-    \stopitem
-
-    \startitem
-        |param[4]=space_shrink| is the amount of glue shrinking between words.
-    \stopitem
-
-    \startitem
-        |param[5]=x_height| is the size of one ex in the font; it is also the
-        height of letters for which accents don't have to be raised or lowered.
-    \stopitem
-
-    \startitem
-        |param[6]=quad| is the size of one em in the font.
-    \stopitem
-
-    \startitem
-        |param[7]=extra_space| is the amount added to |param[2]| at the ends of
-        sentences.
-    \stopitem
-
-
-    If fewer than seven parameters are present, \TeX\ sets the missing parameters
-    to zero. Fonts used for math symbols are required to have additional
-    parameter information, which is explained later.
-
-
-    There are programs called \.{TFtoPL} and \.{PLtoTF} that convert between the
-    \TFM\format and a symbolic property-list format that can be easily edited.
-    These programs contain extensive diagnostic information, so \TeX\ does not
-    have to bother giving precise details about why it rejects a particular
-    \TFM\file.
-
-*/
-
-#define tfm_abort {                    \
-    font_tables[f]->_font_name = NULL; \
-    font_tables[f]->_font_area = NULL; \
-    xfree(tfm_buffer); xfree(kerns);   \
-    xfree(widths);                     \
-    xfree(heights);                    \
-    xfree(depths);                     \
-    xfree(italics);                    \
-    xfree(extens);                     \
-    xfree(lig_kerns);                  \
-    xfree(xligs);                      \
-    xfree(xkerns);                     \
-    return 0;                          \
-}
-
-#define tfm_success {  \
-    xfree(tfm_buffer); \
-    xfree(kerns);      \
-    xfree(widths);     \
-    xfree(heights);    \
-    xfree(depths);     \
-    xfree(italics);    \
-    xfree(extens);     \
-    xfree(lig_kerns);  \
-    xfree(xligs);      \
-    xfree(xkerns);     \
-    return 1;          \
-}
-
-static int open_tfm_file(const char *nom, unsigned char **tfm_buf, int *tfm_siz)
-{
-    /*tex Was the callback successful? */
-    boolean res;
-    /*tex Was |tfm_file| successfully opened? */
-    boolean opened;
-    int callback_id;
-    FILE *tfm_file;
-    char *fname = luatex_find_file(nom, find_font_file_callback);
-    if (!fname)
-        return -1;
-    callback_id = callback_defined(read_font_file_callback);
-    if (callback_id > 0) {
-        res = run_callback(callback_id, "S->bSd", fname, &opened, tfm_buf, tfm_siz);
-        if (res && opened && (*tfm_siz > 0)) {
-            return 1;
-        }
-        if (!opened)
-            return -1;
-    } else {
-        if (luatex_open_input(&(tfm_file), fname, kpse_ofm_format, FOPEN_RBIN_MODE, true)) {
-            res = read_tfm_file(tfm_file, tfm_buf, tfm_siz);
-            close_file(tfm_file);
-            if (res) {
-                return 1;
-            }
-        } else {
-            return -1;
-        }
-    }
-    return 0;
-}
-
-/*tex
-
-  Note: A malformed \TFM\file might be shorter than it claims to be; thus
-  |eof(tfm_file)| might be true when |read_font_info| refers to |tfm_file^| or
-  when it says |get(tfm_file)|. If such circumstances cause system error
-  messages, you will have to defeat them somehow, for example by defining |fget|
-  to be `\ignorespaces|begin get(tfm_file);| |if eof(tfm_file) then abort;
-  end|'.
-
-*/
-
-#define fget  tfm_byte++
-#define fbyte tfm_buffer[tfm_byte]
-
-#define read_sixteen(a) {           \
-    a=tfm_buffer[tfm_byte++];       \
-    if (a>127) { tfm_abort; }       \
-    a=(a*256)+tfm_buffer[tfm_byte]; \
-}
-
-#define read_sixteen_unsigned(a) {  \
-    a=tfm_buffer[tfm_byte++];       \
-    a=(a*256)+tfm_buffer[tfm_byte]; \
-}
-
-#define read_thirtytwo(a) {           \
-    a=tfm_buffer[++tfm_byte];         \
-    if (a>127) { tfm_abort; }         \
-    a=(a*256)+tfm_buffer[++tfm_byte]; \
-    a=(a*256)+tfm_buffer[++tfm_byte]; \
-    a=(a*256)+tfm_buffer[++tfm_byte]; \
-}
-
-#define store_four_bytes(z) {         \
-    a=tfm_buffer[++tfm_byte];         \
-    a=(a*256)+tfm_buffer[++tfm_byte]; \
-    a=(a*256)+tfm_buffer[++tfm_byte]; \
-    a=(a*256)+tfm_buffer[++tfm_byte]; \
-    z = a;                            \
-}
-
-#define store_char_info(z) {            \
-    if (font_level!=-1) {               \
-        fget; read_sixteen_unsigned(a); \
-        ci._width_index=a;              \
-        fget; read_sixteen_unsigned(b); \
-        ci._height_index=b>>8;          \
-        ci._depth_index=b%256;          \
-        fget; read_sixteen_unsigned(c); \
-        ci._italic_index=c>>8;          \
-        ci._tag=(unsigned char)(c%4);   \
-        fget; read_sixteen_unsigned(d); \
-        ci._remainder=d;                \
-    } else {                            \
-          a=tfm_buffer[++tfm_byte];     \
-          ci._width_index=a;            \
-          b=tfm_buffer[++tfm_byte];     \
-          ci._height_index=b>>4;        \
-          ci._depth_index=b%16;         \
-          c=tfm_buffer[++tfm_byte];     \
-          ci._italic_index=c>>2;        \
-          ci._tag=(unsigned char)(c%4); \
-          d=tfm_buffer[++tfm_byte];     \
-          ci._remainder=d;              \
-    }                                   \
-}
-
-#define read_four_quarters(q) {                              \
-    if (font_level!=-1) {                                    \
-        fget; read_sixteen_unsigned(a); q.b0=(quarterword)a; \
-        fget; read_sixteen_unsigned(b); q.b1=(quarterword)b; \
-        fget; read_sixteen_unsigned(c); q.b2=(quarterword)c; \
-        fget; read_sixteen_unsigned(d); q.b3=(quarterword)d; \
-    } else {                                                 \
-          a=tfm_buffer[++tfm_byte]; q.b0=(quarterword)a;     \
-          b=tfm_buffer[++tfm_byte]; q.b1=(quarterword)b;     \
-          c=tfm_buffer[++tfm_byte]; q.b2=(quarterword)c;     \
-          d=tfm_buffer[++tfm_byte]; q.b3=(quarterword)d;     \
-    }                                                        \
-}
-
-#define check_byte_range(z) { if ((z<bc)||(z>ec)) tfm_abort ; }
-
-/*
-
-    A |fix_word| whose four bytes are $(a,b,c,d)$ from left to right represents
-    the number $$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
-    b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
-    -16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$ (No other
-    choices of |a| are allowed, since the magnitude of a number in design-size
-    units must be less than 16.) We want to multiply this quantity by the
-    integer~|z|, which is known to be less than $2^{27}$. If $|z|<2^{23}$, the
-    individual multiplications $b\cdot z$, $c\cdot z$, $d\cdot z$ cannot
-    overflow; otherwise we will divide |z| by 2, 4, 8, or 16, to obtain a
-    multiplier less than $2^{23}$, and we can compensate for this later. If |z|
-    has thereby been replaced by $|z|^\prime=|z|/2^e$, let $\beta=2^{4-e}$; we
-    shall compute $$\lfloor (b + c \cdot2^{-8} + d \cdot2^{-16}) \, z^\prime /
-    \beta \rfloor$$ if $a=0$, or the same quantity minus $\alpha=2^{4+e}z^\prime$
-    if $a=255$. This calculation must be done exactly, in order to guarantee
-    portability of \TeX\ between computers.
-
-*/
-
-#define store_scaled(zz) {                       \
-    fget;                                        \
-    a = fbyte;                                   \
-    fget;                                        \
-    b = fbyte;                                   \
-    fget;                                        \
-    c = fbyte;                                   \
-    fget;                                        \
-    d = fbyte;                                   \
-    sw = (((((d*z)>>8)+(c*z))>>8)+(b*z)) / beta; \
-    if (a == 0) {                                \
-        zz = sw;                                 \
-    } else if (a == 255) {                       \
-        zz = sw-alpha;                           \
-    } else {                                     \
-        tfm_abort;                               \
-    }                                            \
-}
-
-scaled store_scaled_f(scaled sq, scaled z_in)
-{
-    eight_bits a, b, c, d;
-    scaled sw;
-    /*tex Here beta: runs from 1 upto 16 */
-    static int alpha, beta;
-    static scaled z, z_prev = 0;
-    /*tex Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$ */
-    if (z_in != z_prev || z_prev == 0) {
-        z = z_prev = z_in;
-        alpha = 16;
-        while (z >= 0x800000) {
-            z /= 2;
-            alpha += alpha;
-        }
-        beta = 256 / alpha;
-        alpha *= z;
-    };
-    if (sq >= 0) {
-        d = (eight_bits) (sq % 256);
-        sq = sq / 256;
-        c = (eight_bits) (sq % 256);
-        sq = sq / 256;
-        b = (eight_bits) (sq % 256);
-        sq = sq / 256;
-        a = (eight_bits) (sq % 256);
-    } else {
-        sq = (sq + 1073741824) + 1073741824;
-        d = (eight_bits) (sq % 256);
-        sq = sq / 256;
-        c = (eight_bits) (sq % 256);
-        sq = sq / 256;
-        b = (eight_bits) (sq % 256);
-        sq = sq / 256;
-        a = (eight_bits) ((sq + 128) % 256);
-    }
-    if (beta==0)
-        normal_error("vf", "vf scaling");
-    sw = (((((d * z) >> 8) + (c * z)) >> 8) + (b * z)) / beta;
-    if (a == 0)
-        return sw;
-    else if (a == 255)
-        return (sw - alpha);
-    else
-        normal_error("vf", "vf scaling");
-    return sw;
-}
-
-#define check_existence(z) { \
-    check_byte_range(z);     \
-    if (!char_exists(f,z)) { \
-        tfm_abort;           \
-    }                        \
-}
-
-typedef struct tfmcharacterinfo {
-    int _kern_index;
-    int _lig_index;
-    int _width_index;
-    int _height_index;
-    int _depth_index;
-    int _italic_index;
-    int _remainder;
-    unsigned char _tag;
-} tfmcharacterinfo;
-
-int read_tfm_info(internal_font_number f, const char *cnom, scaled s)
-{
-    /*tex index into |font_info| */
-    int k;
-    /*tex sizes of subfiles */
-    halfword lf, lh, bc, ec, nw, nh, nd, ni, nl, nk, ne, np, slh;
-    scaled *widths, *heights, *depths, *italics, *kerns;
-    halfword font_dir;
-    /*tex byte variables */
-    int a, b, c=0, d=0;
-    /*tex counter */
-    int i;
-    int font_level, header_length;
-    int ncw, nlw, neew;
-    tfmcharacterinfo ci;
-    charinfo *co;
-    four_quarters qw;
-    four_quarters *lig_kerns, *extens;
-    /*tex accumulators */
-    scaled sw;
-    /*tex left boundary start location, or infinity */
-    int bch_label;
-    /*tex 0..too_big_char; right boundary character, or |too_big_char| */
-    int bchar;
-    int first_two;
-    /*tex the design size or the ``at'' size */
-    scaled z;
-    int alpha;
-    /*tex 1..16 */
-    char beta;
-    /*tex aux. for ligkern processing */
-    int *xligs, *xkerns;
-    liginfo *cligs;
-    kerninfo *ckerns;
-    int fligs, fkerns;
-    char *tmpnam;
-    /*tex index into |tfm_buffer| */
-    int tfm_byte = 0;
-    /*tex saved index into |tfm_buffer| */
-    int saved_tfm_byte = 0;
-    /*tex byte buffer for tfm files */
-    unsigned char *tfm_buffer = NULL;
-    /*tex total size of the tfm file */
-    int tfm_size = 0;
-    int tmp;
-    widths = NULL;
-    heights = NULL;
-    depths = NULL;
-    italics = NULL;
-    kerns = NULL;
-    lig_kerns = NULL;
-    extens = NULL;
-    xkerns = NULL;
-    ckerns = NULL;
-    xligs = NULL;
-    cligs = NULL;
-    font_dir = 0;
-    memset(&ci, 0, sizeof(tfmcharacterinfo));
-    if (open_tfm_file(cnom, &tfm_buffer, &tfm_size) != 1)
-        tfm_abort;
-    /*tex When |cnom| is an absolute filename |xbasename| fixes that. */
-    tmpnam = strdup(xbasename(cnom));
-    if (strcmp(tmpnam + strlen(tmpnam) - 4, ".tfm") == 0 || strcmp(tmpnam + strlen(tmpnam) - 4, ".ofm") == 0) {
-        *(tmpnam + strlen(tmpnam) - 4) = 0;
-    }
-    set_font_name(f, tmpnam);
-    set_font_area(f, NULL);
-    /*tex Read the \TFM\ size fields. */
-    ncw = 0;
-    read_sixteen(first_two);
-    if (first_two != 0) {
-        font_level = -1;
-        lf = first_two;
-        fget;
-        read_sixteen(lh);
-        fget;
-        read_sixteen(bc);
-        fget;
-        read_sixteen(ec);
-        if ((bc > ec + 1) || (ec > 255))
-            tfm_abort;
-        if (bc > 255) {
-            /*tex |bc=256| and |ec=255| */
-            bc = 1;
-            ec = 0;
-        };
-        fget;
-        read_sixteen(nw);
-        fget;
-        read_sixteen(nh);
-        fget;
-        read_sixteen(nd);
-        fget;
-        read_sixteen(ni);
-        fget;
-        read_sixteen(nl);
-        fget;
-        read_sixteen(nk);
-        fget;
-        read_sixteen(ne);
-        fget;
-        read_sixteen(np);
-        header_length = 6;
-        ncw = (ec - bc + 1);
-        nlw = nl;
-        neew = ne;
-    } else {
-        fget;
-        read_sixteen(font_level);
-        if (font_level != 0)
-            tfm_abort;
-        read_thirtytwo(lf);
-        read_thirtytwo(lh);
-        read_thirtytwo(bc);
-        read_thirtytwo(ec);
-        if ((bc > ec + 1) || (ec > 65535))
-            tfm_abort;
-        if (bc > 65535) {
-            /*tex |bc=65536| and |ec=65535| */
-            bc = 1;
-            ec = 0;
-        };
-        read_thirtytwo(nw);
-        read_thirtytwo(nh);
-        read_thirtytwo(nd);
-        read_thirtytwo(ni);
-        read_thirtytwo(nl);
-        read_thirtytwo(nk);
-        read_thirtytwo(ne);
-        read_thirtytwo(np);
-        /*tex Some junk: */
-        read_thirtytwo(font_dir);
-        nlw = 2 * nl;
-        neew = 2 * ne;
-        header_length = 14;
-        ncw = 2 * (ec - bc + 1);
-    };
-    if (lf !=
-        (header_length + lh + ncw + nw + nh + nd + ni + nlw + nk + neew + np))
-        tfm_abort;
-    if ((nw == 0) || (nh == 0) || (nd == 0) || (ni == 0))
-        tfm_abort;
-    /*tex
-        We check to see that the \TFM\ file doesn't end prematurely; but no
-        error message is given for files having more than |lf| words.
-     */
-    if (lf * 4 > tfm_size)
-        tfm_abort;
-    /*tex Use size fields to allocate font information. */
-    set_font_natural_dir(f, font_dir);
-    set_font_bc(f, bc);
-    set_font_ec(f, ec);
-    /*tex Read the arrays first. */
-    widths = xmalloc((unsigned) ((unsigned) nw * sizeof(scaled)));
-    heights = xmalloc((unsigned) ((unsigned) nh * sizeof(scaled)));
-    depths = xmalloc((unsigned) ((unsigned) nd * sizeof(scaled)));
-    italics = xmalloc((unsigned) ((unsigned) ni * sizeof(scaled)));
-    extens = xmalloc((unsigned) ((unsigned) ne * sizeof(four_quarters)));
-    lig_kerns = xmalloc((unsigned) ((unsigned) nl * sizeof(four_quarters)));
-    kerns = xmalloc((unsigned) ((unsigned) nk * sizeof(scaled)));
-    /*
-        Read the \TFM\ header. Only the first two words of the header are needed
-        by \TeX82.
-    */
-    slh = lh;
-    if (lh < 2)
-        tfm_abort;
-    store_four_bytes(tmp);
-    font_checksum(f) = (unsigned) tmp;
-    fget;
-    /*tex This rejects a negative design size. */
-    read_sixteen(z);
-    fget;
-    z = z * 256 + fbyte;
-    fget;
-    z = (z * 16) + (fbyte >> 4);
-    if (z < unity)
-        tfm_abort;
-    while (lh > 2) {
-        fget;
-        fget;
-        fget;
-        fget;
-        /*tex Ignore the rest of the header. */
-        lh--;
-    };
-    /*tex Read the arrays before the character info. */
-    set_font_dsize(f, z);
-    if (s != -1000) {
-        z = (s >= 0 ? s : xn_over_d(z, -s, 1000));
-    }
-    set_font_size(f, z);
-    if (np > 7) {
-        set_font_params(f, np);
-    }
-    saved_tfm_byte = tfm_byte;
-    tfm_byte = (header_length + slh + ncw) * 4 - 1;
-    /*tex Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$ */
-    alpha = 16;
-    while (z >= 040000000) {
-        z = z >> 1;
-        alpha = alpha + alpha;
-    };
-    beta = (char) (256 / alpha);
-    /*tex |beta| cannot be zero. */
-    if (beta==0)
-       normal_error("vf", "vf reading");
-    alpha = alpha * z;
-    /*tex Read box dimensions. */
-    for (k = 0; k < nw; k++) {
-        store_scaled(sw);
-        widths[k] = sw;
-    }
-    /*tex |width[0]| must be zero */
-    if (widths[0] != 0)
-        tfm_abort;
-    for (k = 0; k < nh; k++) {
-        store_scaled(sw);
-        heights[k] = sw;
-    }
-    /*tex |height[0]| must be zero */
-    if (heights[0] != 0)
-        tfm_abort;
-    for (k = 0; k < nd; k++) {
-        store_scaled(sw);
-        depths[k] = sw;
-    }
-    /*tex |depth[0]| must be zero */
-    if (depths[0] != 0)
-        tfm_abort;
-    for (k = 0; k < ni; k++) {
-        store_scaled(sw);
-        italics[k] = sw;
-    }
-    /*tex |italic[0]| must be zero */
-    if (italics[0] != 0)
-        tfm_abort;
-    /*tex Read ligature and kern programs */
-    bch_label = nl;
-    bchar = 65536;
-    if (nl > 0) {
-        for (k = 0; k < nl; k++) {
-            read_four_quarters(qw);
-            lig_kerns[k] = qw;
-            if (a > 128) {
-                if (256 * c + d >= nl)
-                    tfm_abort;
-                if (a == 255 && k == 0)
-                    bchar = b;
-            } else {
-                if (c < 128) {
-                    /*tex Do nothing. */
-                } else if (256 * (c - 128) + d >= nk) {
-                    /*tex Check kern. */
-                    tfm_abort;
-                }
-                if ((a < 128) && (k - 0 + a + 1 >= nl))
-                    tfm_abort;
-            };
-        };
-        if (a == 255)
-            bch_label = 256 * c + d;
-    };
-    /*tex The actual kerns */
-    for (k = 0; k < nk; k++) {
-        store_scaled(sw);
-        kerns[k] = sw;
-    }
-    /*tex Read extensible character recipes */
-    for (k = 0; k < ne; k++) {
-        read_four_quarters(qw);
-        extens[k] = qw;
-    }
-    /*tex Read font parameters. */
-    if (np > 7) {
-        set_font_params(f, np);
-    }
-    for (k = 1; k <= np; k++) {
-        if (k == 1) {
-            /*tex The |slant| parameter is a pure number. */
-            fget;
-            sw = fbyte;
-            if (sw > 127)
-                sw = sw - 256;
-            fget;
-            sw = sw * 256 + fbyte;
-            fget;
-            sw = sw * 256 + fbyte;
-            fget;
-            sw = (sw * 16) + (fbyte >> 4);
-            set_font_param(f, k, sw);
-        } else {
-            store_scaled(font_param(f, k));
-        }
-    }
-    tfm_byte = saved_tfm_byte;
-    /*tex Fix up the left boundary character. */
-    fligs = 0;
-    fkerns = 0;
-    if (bch_label != nl) {
-        k = bch_label;
-        while (1) {
-            if (skip_byte(k) <= stop_flag) {
-                if (op_byte(k) >= kern_flag) {
-                    fkerns++;
-                } else {
-                    fligs++;
-                }
-            }
-            if (skip_byte(k) == 0) {
-                k++;
-            } else {
-                if (skip_byte(k) >= stop_flag)
-                    break;
-                k += skip_byte(k) + 1;
-            }
-        }
-    }
-    if (fkerns > 0 || fligs > 0) {
-        if (fligs > 0)
-            cligs = xcalloc((unsigned) (fligs + 1), sizeof(liginfo));
-        if (fkerns > 0)
-            ckerns = xcalloc((unsigned) (fkerns + 1), sizeof(kerninfo));
-        fligs = 0;
-        fkerns = 0;
-        k = bch_label;
-        while (1) {
-            if (skip_byte(k) <= stop_flag) {
-                if (op_byte(k) >= kern_flag) {
-                    set_kern_item(ckerns[fkerns], next_char(k), kerns[256 * (op_byte(k) - 128) + rem_byte(k)]);
-                    fkerns++;
-                } else {
-                    set_ligature_item(cligs[fligs], (char) (op_byte(k) * 2 + 1), next_char(k), rem_byte(k));
-                    fligs++;
-                }
-            }
-            if (skip_byte(k) == 0) {
-                k++;
-            } else {
-                if (skip_byte(k) >= stop_flag)
-                    break;
-                k += skip_byte(k) + 1;
-            }
-        }
-        if (fkerns > 0 || fligs > 0) {
-            co = get_charinfo(f, left_boundarychar);
-            if (fkerns > 0) {
-                set_kern_item(ckerns[fkerns], end_kern, 0);
-                fkerns++;
-                set_charinfo_kerns(co, ckerns);
-            }
-            if (fligs > 0) {
-                set_ligature_item(cligs[fligs], 0, end_ligature, 0);
-                fligs++;
-                set_charinfo_ligatures(co, cligs);
-            }
-            set_charinfo_remainder(co, 0);
-        }
-    }
-    /*tex Read character data. */
-    for (k = bc; k <= ec; k++) {
-        store_char_info(k);
-        if (ci._width_index == 0)
-            continue;
-        if (ci._width_index >= nw || ci._height_index >= nh ||
-            ci._depth_index >= nd || ci._italic_index >= ni)
-            tfm_abort;
-        d = ci._remainder;
-        switch (ci._tag) {
-        case lig_tag:
-            if (d >= nl)
-                tfm_abort;
-            break;
-        case ext_tag:
-            if (d >= ne)
-                tfm_abort;
-            break;
-        case list_tag:
-            /*tex
-
-                We want to make sure that there is no cycle of characters linked
-                together by |list_tag| entries, since such a cycle would get
-                \TEX\ into an endless loop. If such a cycle exists, the routine
-                here detects it when processing the largest character code in the
-                cycle.
-
-             */
-            check_byte_range(d);
-            while (d < k) {
-                /* |current_character == k| */
-                if (char_tag(f, d) != list_tag) {
-                    /*tex Not a cycle. */
-                    goto NOT_FOUND;
-                }
-                /*tex Goto the next character on the list. */
-                d = char_remainder(f, d);
-            };
-            if (d == k) {
-                /*tex Yes, there's a cycle! */
-                tfm_abort;
-            }
-          NOT_FOUND:
-            break;
-        }
-        /*tex Put it in the actual font. */
-        co = get_charinfo(f, k);
-        set_charinfo_index(co, k);
-        set_charinfo_tag(co, ci._tag);
-        if (ci._tag == ext_tag) {
-            /*tex top, bot, mid, rep */
-            set_charinfo_extensible(co,
-                extens[ci._remainder].b0,
-                extens[ci._remainder].b2,
-                extens[ci._remainder].b1,
-                extens[ci._remainder].b3);
-            set_charinfo_remainder(co, 0);
-        } else {
-            set_charinfo_remainder(co, ci._remainder);
-        }
-        set_charinfo_width(co, widths[ci._width_index]);
-        set_charinfo_height(co, heights[ci._height_index]);
-        set_charinfo_depth(co, depths[ci._depth_index]);
-        set_charinfo_italic(co, italics[ci._italic_index]);
-    };
-    /*tex We now know the number of ligatures and kerns. */
-    xligs = xcalloc((unsigned) (ec + 1), sizeof(int));
-    xkerns = xcalloc((unsigned) (ec + 1), sizeof(int));
-    for (i = bc; i <= ec; i++) {
-        if (char_tag(f, i) == lig_tag) {
-            k = lig_kern_start(f, i);
-            if (skip_byte(k) > stop_flag)
-                k = lig_kern_restart(k);
-            /*tex Now k is the start index. */
-            while (1) {
-                if (skip_byte(k) <= stop_flag) {
-                    if (op_byte(k) >= kern_flag) {
-                        xkerns[i]++;
-                        if (next_char(k) == bchar)
-                            xkerns[i]++;
-                    } else {
-                        xligs[i]++;
-                        if (next_char(k) == bchar)
-                            xligs[i]++;
-                    }
-                }
-                if (skip_byte(k) == 0) {
-                    k++;
-                } else {
-                    if (skip_byte(k) >= stop_flag)
-                        break;
-                    k += skip_byte(k) + 1;
-                }
-            }
-        }
-    }
-    cligs = NULL;
-    ckerns = NULL;
-    for (i = bc; i <= ec; i++) {
-        fligs = 0;
-        fkerns = 0;
-        if (char_tag(f, i) == lig_tag) {
-            k = lig_kern_start(f, i);
-            if (skip_byte(k) > stop_flag)
-                k = lig_kern_restart(k);
-            /*tex Now k is the start index. */
-            if (xligs[i] > 0)
-                cligs = xcalloc((unsigned) (xligs[i] + 1), sizeof(liginfo));
-            if (xkerns[i] > 0)
-                ckerns = xcalloc((unsigned) (xkerns[i] + 1), sizeof(kerninfo));
-            while (1) {
-                if (skip_byte(k) <= stop_flag) {
-                    if (op_byte(k) >= kern_flag) {
-                        if (next_char(k) == bchar) {
-                            set_kern_item(ckerns[fkerns], right_boundarychar, kerns[256 * (op_byte(k) - 128) + rem_byte(k)]);
-                            fkerns++;
-                        }
-                        set_kern_item(ckerns[fkerns], next_char(k), kerns[256 * (op_byte(k) - 128) + rem_byte(k)]);
-                        fkerns++;
-                    } else {    /* lig */
-                        if (next_char(k) == bchar) {
-                            set_ligature_item(cligs[fligs], (char) (op_byte(k) * 2 + 1), right_boundarychar, rem_byte(k));
-                            fligs++;
-                        }
-                        set_ligature_item(cligs[fligs], (char) (op_byte(k) * 2 + 1), next_char(k), rem_byte(k));
-                        fligs++;
-                    }
-                }
-                if (skip_byte(k) == 0) {
-                    k++;
-                } else {
-                    if (skip_byte(k) >= stop_flag)
-                        break;
-                    k += skip_byte(k) + 1;
-                }
-            }
-            if (fkerns > 0 || fligs > 0) {
-                co = get_charinfo(f, i);
-                if (fkerns > 0) {
-                    set_kern_item(ckerns[fkerns], end_kern, 0);
-                    fkerns++;
-                    set_charinfo_kerns(co, ckerns);
-                }
-                if (fligs > 0) {
-                    set_ligature_item(cligs[fligs], 0, end_ligature, 0);
-                    fligs++;
-                    set_charinfo_ligatures(co, cligs);
-                }
-                set_charinfo_remainder(co, 0);
-            }
-        }
-    }
-    /*tex
-
-        Now it's time to wrap it up, we have checked all the necessary things
-        about the \TFM\file, and all we need to do is put the finishing
-        touches on the data for the new font.
-
-    */
-    if (bchar != 65536) {
-        co = copy_charinfo(char_info(f, bchar));
-        set_right_boundary(f, co);
-    }
-    tfm_success;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/tfmofm.w
@@ -0,0 +1,1119 @@
+% tfmofm.w
+%
+% Copyright 2006-2012 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"
+
+@ Here are some macros that help process ligatures and kerns
+@c
+#define lig_kern_start(f,c)   char_remainder(f,c)
+#define stop_flag 128           /* value indicating `\.{STOP}' in a lig/kern program */
+#define kern_flag 128           /* op code for a kern step */
+
+#define skip_byte(z)         lig_kerns[z].b0
+#define next_char(z)         lig_kerns[z].b1
+#define op_byte(z)           lig_kerns[z].b2
+#define rem_byte(z)          lig_kerns[z].b3
+#define lig_kern_restart(c)  (256*op_byte(c)+rem_byte(c))
+
+
+@
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of 4, we could
+also regard the file as a sequence of 32-bit words, but \TeX\ uses the
+byte interpretation. The format of \.{TFM} files was designed by
+Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
+@^Ramshaw, Lyle Harold@>
+of information in a compact but useful form.
+
+$\Omega$ is capable of reading not only \.{TFM} files, but also
+\.{OFM} files, which can describe fonts with up to 65536 characters
+and with huge lig/kern tables.  These fonts will often be virtual
+fonts built up from real fonts with 256 characters, but $\Omega$
+is not aware of this.
+
+The documentation below describes \.{TFM} files, with slight additions
+to show where \.{OFM} files differ.
+
+@
+The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|lf|&length of the entire file, in words;\cr
+|lh|&length of the header data, in words;\cr
+|bc|&smallest character code in the font;\cr
+|ec|&largest character code in the font;\cr
+|nw|&number of words in the width table;\cr
+|nh|&number of words in the height table;\cr
+|nd|&number of words in the depth table;\cr
+|ni|&number of words in the italic correction table;\cr
+|nl|&number of words in the lig/kern table;\cr
+|nk|&number of words in the kern table;\cr
+|ne|&number of words in the extensible character table;\cr
+|np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
+and
+$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a \.{TFM} font may contain as many as 256 characters
+(if |bc=0| and |ec=255|), and as few as 0 characters (if |bc=ec+1|).
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+@!@^BigEndian order@>
+
+The first 52 bytes (13 words) of an \.{OFM} file contains thirteen
+32-bit integers that give the lengths of the various subsequent
+portions of the file.  The first word is 0 (future versions of
+\.{OFM} files could have different values;  what is important is that
+the first two bytes be 0 to differentiate \.{TFM} and \.{OFM} files).
+The next twelve integers are as above, all nonegative and less
+than~$2^{31}$.  We must have |bc-1<=ec<=65535|, and
+$$\hbox{|lf=13+lh+2*(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that an \.{OFM} font may contain as many as 65536 characters
+(if |bc=0| and |ec=65535|), and as few as 0 characters (if |bc=ec+1|).
+
+The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\def\doarr\PB#1{\arr#1}
+\vbox{\halign{\hfil\\{#}&$\,:\,$\doarr#\hfil\cr
+header&|[0..lh-1]stuff|\cr
+char\_info&|[bc..ec]char_info_word|\cr
+width&|[0..nw-1]fix_word|\cr
+height&|[0..nh-1]fix_word|\cr
+depth&|[0..nd-1]fix_word|\cr
+italic&|[0..ni-1]fix_word|\cr
+lig\_kern&|[0..nl-1]lig_kern_command|\cr
+kern&|[0..nk-1]fix_word|\cr
+exten&|[0..ne-1]extensible_recipe|\cr
+param&\omit |[1..np]fix_word|\cr}}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but two of
+the |fix_word| values must lie between $-16$ and $+16$.
+
+The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+|header[0]| and |header[1]|, whose meaning is explained below.
+Additional header information of use to other software routines might
+also be included, but \TeX82 does not need to know about such details.
+For example, 16 more words of header information are in use at the Xerox
+Palo Alto Research Center; the first ten specify the character coding
+scheme used (e.g., `\.{XEROX text}' or `\.{TeX math symbols}'), the next five
+give the font identifier (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
+last gives the ``face byte.'' The program that converts \.{DVI} files
+to Xerox printing format gets this information by looking at the \.{TFM}
+file, which it needs to read anyway because of other information that
+is not explicitly repeated in \.{DVI}~format.
+
+\yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into
+the \.{DVI} output file. Later on when the \.{DVI} file is printed,
+possibly on another computer, the actual font that gets used is supposed
+to have a check sum that agrees with the one in the \.{TFM} file used by
+\TeX. In this way, users will be warned about potential incompatibilities.
+(However, if the check sum is zero in either the font file or the \.{TFM}
+file, no check is made.)  The actual relation between this check sum and
+the rest of the \.{TFM} file is not important; the check sum is simply an
+identification number with the property that incompatible fonts almost
+always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of
+the font, in units of \TeX\ points. This number must be at least 1.0; it is
+fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
+font, i.e., a font that was designed to look best at a 10-point size,
+whatever that really means. When a \TeX\ user asks for a font
+`\.{at} $\delta$ \.{pt}', the effect is to override the design size
+and replace it by $\delta$, and to multiply the $x$ and~$y$ coordinates
+of the points in the font image by a factor of $\delta$ divided by the
+design size.  {\sl All other dimensions in the\/ \.{TFM} file are
+|fix_word|\kern-1pt\ numbers in design-size units}, with the exception of
+|param[1]| (which denotes the slant ratio). Thus, for example, the value
+of |param[6]|, which defines the \.{em} unit, is often the |fix_word| value
+$2^{20}=1.0$, since many fonts have a design size equal to one em.
+The other dimensions must be less than 16 design-size units in absolute
+value; thus, |header[1]| and |param[1]| are the only |fix_word|
+entries in the whole \.{TFM} file whose first byte might be something
+besides 0 or 255.
+
+Next comes the |char_info| array, which contains one |@!char_info_word|
+per character. Each word in this part of a \.{TFM} file contains six fields
+packed into four bytes as follows.
+
+\yskip\hang first byte: |@!width_index| (8 bits)\par
+\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index|
+  (4~bits)\par
+\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag|
+  (2~bits)\par
+\hang fourth byte: |@!remainder| (8 bits)\par
+\yskip\noindent
+The actual width of a character is \\{width}|[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+For \.{OFM} files, two words (eight bytes) are used.
+The arrangement is as follows.
+
+\yskip\hang first and second bytes: |@!width_index| (16 bits)\par
+\hang third byte: |@!height_index| (8 bits)\par
+\hang fourth byte: |@!depth_index| (8~bits)\par
+\hang fifth and sixth bytes:
+|@!italic_index| (14 bits) times 4, plus |@!tag| (2~bits)\par
+\hang seventh and eighth bytes: |@!remainder| (16 bits)\par
+\yskip\noindent
+Therefore the \.{OFM} format imposes a limit of 256 different heights,
+256 different depths, and 16384 different italic corrections.
+
+@!@^italic correction@>
+The italic correction of a character has two different uses.
+(a)~In ordinary text, the italic correction is added to the width only if
+the \TeX\ user specifies `\.{\\/}' after the character.
+(b)~In math formulas, the italic correction is always added to the width,
+except with respect to the positioning of subscripts.
+
+Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
+\\{italic}[0]=0$ should always hold, so that an index of zero implies a
+value of zero.  The |width_index| should never be zero unless the
+character does not exist in the font, since a character is valid if and
+only if it lies between |bc| and |ec| and has a nonzero |width_index|.
+
+
+@ \TeX\ checks the information of a \.{TFM} file for validity as the
+file is being read in, so that no further checks will be needed when
+typesetting is going on. The somewhat tedious subroutine that does this
+is called |read_font_info|. It has four parameters: the user font
+identifier~|u|, the file name and area strings |nom| and |aire|, and the
+``at'' size~|s|. If |s|~is negative, it's the negative of a scale factor
+to be applied to the design size; |s=-1000| is the normal case.
+Otherwise |s| will be substituted for the design size; in this
+case, |s| must be positive and less than $2048\rm\,pt$
+(i.e., it must be less than $2^{27}$ when considered as an integer).
+
+The subroutine opens and closes a global file variable called |tfm_file|.
+It returns the value of the internal font number that was just loaded.
+If an error is detected, an error message is issued and no font
+information is stored; |null_font| is returned in this case.
+
+@
+The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |remainder| field.
+
+\yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par
+\hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at position |remainder| in the |lig_kern| array.\par
+\hang|tag=2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain.  The
+|remainder| field gives the character code of the next larger character.\par
+\hang|tag=3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|@!exten[remainder]|.\par
+\yskip\noindent
+Characters with |tag=2| and |tag=3| are treated as characters with |tag=0|
+unless they are used in special circumstances in math formulas. For example,
+the \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
+operation looks for both |list_tag| and |ext_tag|.
+
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array,
+in a \.{TFM} file, is a |@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+  step if the byte is 128 or more, otherwise the next step is obtained by
+  skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+  then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+  a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In an \.{OFM} file, eight bytes are used, two bytes for each field.
+
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255| in a \.{TFM} file, |<=65535| in an \.{OFM} file.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy
+the condition
+$$\hbox{|256*op_byte+remainder<nl|.}$$
+If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+or kerning command is performed.
+
+
+@ Extensible characters are specified by an |@!extensible_recipe|, which
+consists of four bytes in a \.{TFM} file,
+called |@!top|, |@!mid|, |@!bot|, and |@!rep| (in this order).
+In an \.{OFM} file, each field takes two bytes, for eight in total.
+These bytes are the character codes of individual pieces used to
+build up a large symbol.  If |top|, |mid|, or |bot| are zero, they are not
+present in the built-up result. For example, an extensible vertical line is
+like an extensible bracket, except that the top and bottom pieces are missing.
+
+Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
+if the piece isn't present. Then the extensible characters have the form
+$TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent;
+in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
+The width of the extensible character is the width of $R$; and the
+height-plus-depth is the sum of the individual height-plus-depths of the
+components used, since the pieces are butted together in a vertical list.
+
+
+@
+The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]=slant| is the amount of italic slant, which is used
+to help position accents. For example, |slant=.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it's the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+
+\hang|param[2]=space| is the normal spacing between words in text.
+Note that character |" "| in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]=space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]=space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]=x_height| is the size of one ex in the font; it is also
+the height of letters for which accents don't have to be raised or lowered.
+
+\hang|param[6]=quad| is the size of one em in the font.
+
+\hang|param[7]=extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+\yskip\noindent
+If fewer than seven parameters are present, \TeX\ sets the missing parameters
+to zero. Fonts used for math symbols are required to have
+additional parameter information, which is explained later.
+
+
+@
+ There are programs called \.{TFtoPL} and \.{PLtoTF} that convert
+ between the \.{TFM} format and a symbolic property-list format
+ that can be easily edited. These programs contain extensive
+ diagnostic information, so \TeX\ does not have to bother giving
+ precise details about why it rejects a particular \.{TFM} file.
+
+@c
+#define tfm_abort { font_tables[f]->_font_name = NULL;      \
+                    font_tables[f]->_font_area = NULL;      \
+                    xfree(tfm_buffer); xfree(kerns);      \
+        xfree(widths);  xfree(heights);  xfree(depths);     \
+        xfree(italics);  xfree(extens);  xfree(lig_kerns);  \
+        xfree(xligs);  xfree(xkerns);           \
+      return 0; }
+
+#define tfm_success { xfree(tfm_buffer); xfree(kerns);       \
+                xfree(widths);  xfree(heights);  xfree(depths);    \
+          xfree(italics);  xfree(extens);  xfree(lig_kerns); \
+          xfree(xligs);  xfree(xkerns); return 1; }
+
+@ @c
+static int open_tfm_file(const char *nom, unsigned char **tfm_buf, int *tfm_siz)
+{
+    boolean res;                /* was the callback successful? */
+    boolean opened;             /* was |tfm_file| successfully opened? */
+    int callback_id;
+    FILE *tfm_file;
+    char *fname = luatex_find_file(nom, find_font_file_callback);
+    if (!fname)
+        return -1;
+    callback_id = callback_defined(read_font_file_callback);
+    if (callback_id > 0) {
+        res =
+            run_callback(callback_id, "S->bSd", fname, &opened, tfm_buf,
+                         tfm_siz);
+        if (res && opened && (*tfm_siz > 0)) {
+            return 1;
+        }
+        if (!opened)
+            return -1;
+    } else {
+        if (luatex_open_input
+            (&(tfm_file), fname, kpse_ofm_format, FOPEN_RBIN_MODE, true)) {
+            res = read_tfm_file(tfm_file, tfm_buf, tfm_siz);
+            close_file(tfm_file);
+            if (res) {
+                return 1;
+            }
+        } else {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+
+@
+  Note: A malformed \.{TFM} file might be shorter than it claims to be;
+  thus |eof(tfm_file)| might be true when |read_font_info| refers to
+  |tfm_file^| or when it says |get(tfm_file)|. If such circumstances
+  cause system error messages, you will have to defeat them somehow,
+  for example by defining |fget| to be `\ignorespaces|begin get(tfm_file);|
+  |if eof(tfm_file) then abort; end|\unskip'.
+  @^system dependencies@>
+@c
+
+#define fget  tfm_byte++
+#define fbyte tfm_buffer[tfm_byte]
+
+#define read_sixteen(a)                                                 \
+  { a=tfm_buffer[tfm_byte++];                                           \
+    if (a>127) { tfm_abort; }                                               \
+    a=(a*256)+tfm_buffer[tfm_byte]; }
+
+#define read_sixteen_unsigned(a)                                        \
+  { a=tfm_buffer[tfm_byte++];                                           \
+    a=(a*256)+tfm_buffer[tfm_byte]; }
+
+#define read_thirtytwo(a)                                               \
+  { a=tfm_buffer[++tfm_byte];                                           \
+    if (a>127) { tfm_abort; }                                               \
+    a=(a*256)+tfm_buffer[++tfm_byte];                                   \
+    a=(a*256)+tfm_buffer[++tfm_byte];                                   \
+    a=(a*256)+tfm_buffer[++tfm_byte]; }
+
+#define store_four_bytes(z)                                             \
+  { a=tfm_buffer[++tfm_byte];           \
+    a=(a*256)+tfm_buffer[++tfm_byte];         \
+    a=(a*256)+tfm_buffer[++tfm_byte];         \
+    a=(a*256)+tfm_buffer[++tfm_byte];         \
+    z = a; }
+
+#define store_char_info(z)                                              \
+  { if (font_level!=-1) {                                               \
+      fget; read_sixteen_unsigned(a);         \
+      ci._width_index=a;            \
+      fget; read_sixteen_unsigned(b);         \
+      ci._height_index=b>>8;            \
+      ci._depth_index=b%256;            \
+      fget; read_sixteen_unsigned(c);         \
+      ci._italic_index=c>>8;            \
+      ci._tag=(unsigned char)(c%4);	      \
+      fget; read_sixteen_unsigned(d);         \
+      ci._remainder=d;              \
+    } else {                                                            \
+      a=tfm_buffer[++tfm_byte];           \
+      ci._width_index=a;            \
+      b=tfm_buffer[++tfm_byte];           \
+      ci._height_index=b>>4;            \
+      ci._depth_index=b%16;           \
+      c=tfm_buffer[++tfm_byte];           \
+      ci._italic_index=c>>2;            \
+      ci._tag=(unsigned char)(c%4);	  \
+      d=tfm_buffer[++tfm_byte];           \
+      ci._remainder=d;              \
+    } }
+
+#define read_four_quarters(q)           \
+  { if (font_level!=-1) {                                        \
+      fget; read_sixteen_unsigned(a); q.b0=(quarterword)a;	 \
+      fget; read_sixteen_unsigned(b); q.b1=(quarterword)b;       \
+      fget; read_sixteen_unsigned(c); q.b2=(quarterword)c;       \
+      fget; read_sixteen_unsigned(d); q.b3=(quarterword)d;       \
+      } else {							 \
+      a=tfm_buffer[++tfm_byte]; q.b0=(quarterword)a;         \
+      b=tfm_buffer[++tfm_byte]; q.b1=(quarterword)b;         \
+      c=tfm_buffer[++tfm_byte]; q.b2=(quarterword)c;         \
+      d=tfm_buffer[++tfm_byte]; q.b3=(quarterword)d;	     \
+    } }
+
+#define check_byte_range(z)  { if ((z<bc)||(z>ec)) tfm_abort ; }
+
+
+@ A |fix_word| whose four bytes are $(a,b,c,d)$ from left to right represents
+   the number
+   $$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
+   b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
+   -16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
+   (No other choices of |a| are allowed, since the magnitude of a number in
+   design-size units must be less than 16.)  We want to multiply this
+   quantity by the integer~|z|, which is known to be less than $2^{27}$.
+   If $|z|<2^{23}$, the individual multiplications $b\cdot z$,
+   $c\cdot z$, $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2,
+   4, 8, or 16, to obtain a multiplier less than $2^{23}$, and we can
+   compensate for this later. If |z| has thereby been replaced by
+   $|z|^\prime=|z|/2^e$, let $\beta=2^{4-e}$; we shall compute
+   $$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$
+   if $a=0$, or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
+   This calculation must be done exactly, in order to guarantee portability
+   of \TeX\ between computers.
+
+@c
+#define store_scaled(zz)                                                   \
+  { fget; a=fbyte; fget; b=fbyte;                                          \
+    fget; c=fbyte; fget; d=fbyte;                                          \
+    sw=(((((d*z)>>8)+(c*z))>>8)+(b*z)) / beta;                             \
+    if (a==0) { zz=sw; } else if (a==255) { zz=sw-alpha; } else tfm_abort; \
+  }
+
+scaled store_scaled_f(scaled sq, scaled z_in)
+{
+    eight_bits a, b, c, d;
+    scaled sw;
+    static int alpha, beta;     /* beta:1..16 */
+    static scaled z, z_prev = 0;
+    /* Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$ */
+    if (z_in != z_prev || z_prev == 0) {
+        z = z_prev = z_in;
+        alpha = 16;
+        while (z >= 0x800000) {
+            z /= 2;
+            alpha += alpha;
+        }
+        beta = 256 / alpha;
+        alpha *= z;
+    };
+    if (sq >= 0) {
+        d = (eight_bits) (sq % 256);
+        sq = sq / 256;
+        c = (eight_bits) (sq % 256);
+        sq = sq / 256;
+        b = (eight_bits) (sq % 256);
+        sq = sq / 256;
+        a = (eight_bits) (sq % 256);
+    } else {
+        sq = (sq + 1073741824) + 1073741824;    /* braces for optimizing compiler */
+        d = (eight_bits) (sq % 256);
+        sq = sq / 256;
+        c = (eight_bits) (sq % 256);
+        sq = sq / 256;
+        b = (eight_bits) (sq % 256);
+        sq = sq / 256;
+        a = (eight_bits) ((sq + 128) % 256);
+    }
+    if (beta==0)
+        normal_error("vf", "vf scaling");
+    sw = (((((d * z) >> 8) + (c * z)) >> 8) + (b * z)) / beta;
+    if (a == 0)
+        return sw;
+    else if (a == 255)
+        return (sw - alpha);
+    else
+        normal_error("vf", "vf scaling");
+    return sw;                  /* not reached, just to make the compiler happy */
+}
+
+@ @c
+#define  check_existence(z)                                             \
+  { check_byte_range(z);                                                \
+    if (!char_exists(f,z)) tfm_abort;         \
+  }
+
+typedef struct tfmcharacterinfo {
+    int _kern_index;
+    int _lig_index;
+    int _width_index;
+    int _height_index;
+    int _depth_index;
+    int _italic_index;
+    int _remainder;
+    unsigned char _tag;
+} tfmcharacterinfo;
+
+@ @c
+int read_tfm_info(internal_font_number f, const char *cnom, scaled s)
+{
+    int k;                      /* index into |font_info| */
+    halfword lf, lh, bc, ec, nw, nh, nd, ni, nl, nk, ne, np, slh;       /* sizes of subfiles */
+    scaled *widths, *heights, *depths, *italics, *kerns;
+    halfword font_dir;
+    int a, b, c=0, d=0;             /* byte variables */
+    int i;                      /* counter */
+    int font_level, header_length;
+    int ncw, nlw, neew;
+    tfmcharacterinfo ci;
+    charinfo *co;
+    four_quarters qw;
+    four_quarters *lig_kerns, *extens;
+    scaled sw;                  /* accumulators */
+    int bch_label;              /* left boundary start location, or infinity */
+    int bchar;                  /* |:0..too_big_char;| *//* right boundary character, or |too_big_char| */
+    int first_two;
+    scaled z;                   /* the design size or the ``at'' size */
+    int alpha;
+    char beta;                  /* :1..16 */
+    int *xligs, *xkerns;        /* aux. for ligkern processing */
+    liginfo *cligs;
+    kerninfo *ckerns;
+    int fligs, fkerns;
+    char *tmpnam;
+    int tfm_byte = 0;           /* index into |tfm_buffer| */
+    int saved_tfm_byte = 0;     /* saved index into |tfm_buffer| */
+    unsigned char *tfm_buffer = NULL;   /* byte buffer for tfm files */
+    int tfm_size = 0;           /* total size of the tfm file */
+    int tmp;
+
+    widths = NULL;
+    heights = NULL;
+    depths = NULL;
+    italics = NULL;
+    kerns = NULL;
+    lig_kerns = NULL;
+    extens = NULL;
+    xkerns = NULL;
+    ckerns = NULL;
+    xligs = NULL;
+    cligs = NULL;
+
+    font_dir = 0;
+
+    memset(&ci, 0, sizeof(tfmcharacterinfo));
+
+    if (open_tfm_file(cnom, &tfm_buffer, &tfm_size) != 1)
+        tfm_abort;
+
+    /* cnom can be an absolute filename, xbasename() fixes that. */
+
+    tmpnam = strdup(xbasename(cnom));
+    if (strcmp(tmpnam + strlen(tmpnam) - 4, ".tfm") == 0 || strcmp(tmpnam + strlen(tmpnam) - 4, ".ofm") == 0) {
+        *(tmpnam + strlen(tmpnam) - 4) = 0;
+    }
+    set_font_name(f, tmpnam);
+    set_font_area(f, NULL);
+
+    /* Read the {\.{TFM}} size fields */
+    ncw = 0;
+    read_sixteen(first_two);
+    if (first_two != 0) {
+        font_level = -1;
+        lf = first_two;
+        fget;
+        read_sixteen(lh);
+        fget;
+        read_sixteen(bc);
+        fget;
+        read_sixteen(ec);
+        if ((bc > ec + 1) || (ec > 255))
+            tfm_abort;
+        if (bc > 255) {         /* |bc=256| and |ec=255| */
+            bc = 1;
+            ec = 0;
+        };
+        fget;
+        read_sixteen(nw);
+        fget;
+        read_sixteen(nh);
+        fget;
+        read_sixteen(nd);
+        fget;
+        read_sixteen(ni);
+        fget;
+        read_sixteen(nl);
+        fget;
+        read_sixteen(nk);
+        fget;
+        read_sixteen(ne);
+        fget;
+        read_sixteen(np);
+        header_length = 6;
+        ncw = (ec - bc + 1);
+        nlw = nl;
+        neew = ne;
+    } else {
+        fget;
+        read_sixteen(font_level);
+        if (font_level != 0)
+            tfm_abort;
+        read_thirtytwo(lf);
+        read_thirtytwo(lh);
+        read_thirtytwo(bc);
+        read_thirtytwo(ec);
+        if ((bc > ec + 1) || (ec > 65535))
+            tfm_abort;
+        if (bc > 65535) {       /* |bc=65536| and |ec=65535| */
+            bc = 1;
+            ec = 0;
+        };
+        read_thirtytwo(nw);
+        read_thirtytwo(nh);
+        read_thirtytwo(nd);
+        read_thirtytwo(ni);
+        read_thirtytwo(nl);
+        read_thirtytwo(nk);
+        read_thirtytwo(ne);
+        read_thirtytwo(np);
+        read_thirtytwo(font_dir);       /* junk */
+        nlw = 2 * nl;
+        neew = 2 * ne;
+        header_length = 14;
+        ncw = 2 * (ec - bc + 1);
+    };
+    if (lf !=
+        (header_length + lh + ncw + nw + nh + nd + ni + nlw + nk + neew + np))
+        tfm_abort;
+    if ((nw == 0) || (nh == 0) || (nd == 0) || (ni == 0))
+        tfm_abort;
+
+    /*
+       We check to see that the \.{TFM} file doesn't end prematurely; but
+       no error message is given for files having more than |lf| words.
+     */
+    if (lf * 4 > tfm_size)
+        tfm_abort;
+
+    /* Use size fields to allocate font information */
+
+    set_font_natural_dir(f, font_dir);
+    set_font_bc(f, bc);
+    set_font_ec(f, ec);
+
+    /* read the arrays first */
+    widths = xmalloc((unsigned) ((unsigned) nw * sizeof(scaled)));
+    heights = xmalloc((unsigned) ((unsigned) nh * sizeof(scaled)));
+    depths = xmalloc((unsigned) ((unsigned) nd * sizeof(scaled)));
+    italics = xmalloc((unsigned) ((unsigned) ni * sizeof(scaled)));
+    extens = xmalloc((unsigned) ((unsigned) ne * sizeof(four_quarters)));
+    lig_kerns = xmalloc((unsigned) ((unsigned) nl * sizeof(four_quarters)));
+    kerns = xmalloc((unsigned) ((unsigned) nk * sizeof(scaled)));
+
+    /* Read the {\.{TFM}} header */
+
+    /* Only the first two words of the header are needed by \TeX82. */
+    slh = lh;
+    if (lh < 2)
+        tfm_abort;
+    store_four_bytes(tmp);
+    font_checksum(f) = (unsigned) tmp;
+    fget;
+    read_sixteen(z);            /* this rejects a negative design size */
+    fget;
+    z = z * 256 + fbyte;
+    fget;
+    z = (z * 16) + (fbyte >> 4);
+    if (z < unity)
+        tfm_abort;
+    while (lh > 2) {
+        fget;
+        fget;
+        fget;
+        fget;
+        lh--;                   /* ignore the rest of the header */
+    };
+
+    /* read the arrays before the character info */
+
+    set_font_dsize(f, z);
+    if (s != -1000) {
+        z = (s >= 0 ? s : xn_over_d(z, -s, 1000));
+    }
+    set_font_size(f, z);
+
+    if (np > 7)
+        set_font_params(f, np);
+
+    saved_tfm_byte = tfm_byte;
+    tfm_byte = (header_length + slh + ncw) * 4 - 1;
+
+    /* Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$ */
+
+    alpha = 16;
+    while (z >= 040000000) {
+        z = z >> 1;
+        alpha = alpha + alpha;
+    };
+    beta = (char) (256 / alpha);
+
+    /* beta cannot be zero */
+    if (beta==0)
+       normal_error("vf", "vf reading");
+    alpha = alpha * z;
+
+    /* Read box dimensions */
+
+    for (k = 0; k < nw; k++) {
+        store_scaled(sw);
+        widths[k] = sw;
+    }
+    if (widths[0] != 0)         /* \\{width}[0] must be zero */
+        tfm_abort;
+    for (k = 0; k < nh; k++) {
+        store_scaled(sw);
+        heights[k] = sw;
+    }
+    if (heights[0] != 0)
+        tfm_abort;              /* \\{height}[0] must be zero */
+    for (k = 0; k < nd; k++) {
+        store_scaled(sw);
+        depths[k] = sw;
+    }
+    if (depths[0] != 0)
+        tfm_abort;              /* \\{depth}[0] must be zero */
+    for (k = 0; k < ni; k++) {
+        store_scaled(sw);
+        italics[k] = sw;
+    }
+    if (italics[0] != 0)
+        tfm_abort;              /* \\{italic}[0] must be zero */
+
+
+    /* Read ligature/kern program */
+
+    bch_label = nl;             /* infinity */
+    bchar = 65536;
+    if (nl > 0) {
+        for (k = 0; k < nl; k++) {
+            read_four_quarters(qw);
+            lig_kerns[k] = qw;
+            if (a > 128) {
+                if (256 * c + d >= nl)
+                    tfm_abort;
+                if (a == 255 && k == 0)
+                    bchar = b;
+            } else {
+#if 0
+                if (b!=bchar) check_existence(b);
+#endif
+                if (c < 128) {
+#if 0
+                    check_existence(d); /* check ligature */
+#endif
+                } else if (256 * (c - 128) + d >= nk) {
+                    tfm_abort;  /* check kern */
+                }
+                if ((a < 128) && (k - 0 + a + 1 >= nl))
+                    tfm_abort;
+            };
+        };
+        if (a == 255)
+            bch_label = 256 * c + d;
+    };
+
+    /* the actual kerns */
+    for (k = 0; k < nk; k++) {
+        store_scaled(sw);
+        kerns[k] = sw;
+    }
+
+    /* Read extensible character recipes */
+    for (k = 0; k < ne; k++) {
+        read_four_quarters(qw);
+        extens[k] = qw;
+    }
+
+    /* Read font parameters */
+
+    if (np > 7) {
+        set_font_params(f, np);
+    }
+    for (k = 1; k <= np; k++) {
+        if (k == 1) {           /* the |slant| parameter is a pure number */
+            fget;
+            sw = fbyte;
+            if (sw > 127)
+                sw = sw - 256;
+            fget;
+            sw = sw * 256 + fbyte;
+            fget;
+            sw = sw * 256 + fbyte;
+            fget;
+            sw = (sw * 16) + (fbyte >> 4);
+            set_font_param(f, k, sw);
+        } else {
+            store_scaled(font_param(f, k));
+        }
+    }
+
+    tfm_byte = saved_tfm_byte;
+
+    /* fix up the left boundary character */
+    fligs = 0;
+    fkerns = 0;
+    if (bch_label != nl) {
+        k = bch_label;
+#if 0
+           if (skip_byte(k) > stop_flag)
+           k = lig_kern_restart(k);
+#endif
+        while (1) {
+            if (skip_byte(k) <= stop_flag) {
+                if (op_byte(k) >= kern_flag) {  /* kern */
+                    fkerns++;
+                } else {        /* lig */
+                    fligs++;
+                }
+            }
+            if (skip_byte(k) == 0) {
+                k++;
+            } else {
+                if (skip_byte(k) >= stop_flag)
+                    break;
+                k += skip_byte(k) + 1;
+            }
+        }
+    }
+    if (fkerns > 0 || fligs > 0) {
+        if (fligs > 0)
+            cligs = xcalloc((unsigned) (fligs + 1), sizeof(liginfo));
+        if (fkerns > 0)
+            ckerns = xcalloc((unsigned) (fkerns + 1), sizeof(kerninfo));
+        fligs = 0;
+        fkerns = 0;
+        k = bch_label;
+#if 0
+        if (skip_byte(k) > stop_flag)
+           k = lig_kern_restart(k);
+#endif
+        while (1) {
+            if (skip_byte(k) <= stop_flag) {
+                if (op_byte(k) >= kern_flag) {  /* kern */
+                    set_kern_item(ckerns[fkerns], next_char(k),
+                                  kerns[256 * (op_byte(k) - 128) +
+                                        rem_byte(k)]);
+                    fkerns++;
+                } else {        /* lig */
+                    set_ligature_item(cligs[fligs], (char) (op_byte(k) * 2 + 1),
+                                      next_char(k), rem_byte(k));
+                    fligs++;
+                }
+            }
+            if (skip_byte(k) == 0) {
+                k++;
+            } else {
+                if (skip_byte(k) >= stop_flag)
+                    break;
+                k += skip_byte(k) + 1;
+            }
+        }
+        if (fkerns > 0 || fligs > 0) {
+            co = get_charinfo(f, left_boundarychar);
+            if (fkerns > 0) {
+                set_kern_item(ckerns[fkerns], end_kern, 0);
+                fkerns++;
+                set_charinfo_kerns(co, ckerns);
+            }
+            if (fligs > 0) {
+                set_ligature_item(cligs[fligs], 0, end_ligature, 0);
+                fligs++;
+                set_charinfo_ligatures(co, cligs);
+            }
+            set_charinfo_remainder(co, 0);
+        }
+    }
+
+    /* Read character data */
+    for (k = bc; k <= ec; k++) {
+        store_char_info(k);
+        if (ci._width_index == 0)
+            continue;
+        if (ci._width_index >= nw || ci._height_index >= nh ||
+            ci._depth_index >= nd || ci._italic_index >= ni)
+            tfm_abort;
+        d = ci._remainder;
+        switch (ci._tag) {
+        case lig_tag:
+            if (d >= nl)
+                tfm_abort;
+            break;
+        case ext_tag:
+            if (d >= ne)
+                tfm_abort;
+            break;
+        case list_tag:
+            /* We want to make sure that there is no cycle of characters linked together
+               by |list_tag| entries, since such a cycle would get \TeX\ into an endless
+               loop. If such a cycle exists, the routine here detects it when processing
+               the largest character code in the cycle.
+             */
+            check_byte_range(d);
+            while (d < k) {     /* |current_character == k| */
+                if (char_tag(f, d) != list_tag)
+                    goto NOT_FOUND;     /* not a cycle */
+                d = char_remainder(f, d);       /* next character on the list */
+            };
+            if (d == k)
+                tfm_abort;      /* yes, there's a cycle */
+          NOT_FOUND:
+            break;
+        }
+        /* put it in the actual font */
+        co = get_charinfo(f, k);
+        set_charinfo_index(co, k);
+        set_charinfo_tag(co, ci._tag);
+        if (ci._tag == ext_tag) {
+            set_charinfo_extensible(co, extens[ci._remainder].b0,       /* top */
+                                    extens[ci._remainder].b2,   /* bot */
+                                    extens[ci._remainder].b1,   /* mid */
+                                    extens[ci._remainder].b3);  /* rep */
+            set_charinfo_remainder(co, 0);
+        } else {
+            set_charinfo_remainder(co, ci._remainder);
+        }
+        set_charinfo_width(co, widths[ci._width_index]);
+        set_charinfo_height(co, heights[ci._height_index]);
+        set_charinfo_depth(co, depths[ci._depth_index]);
+        set_charinfo_italic(co, italics[ci._italic_index]);
+    };
+
+    /* first pass: count ligs and kerns */
+
+    xligs = xcalloc((unsigned) (ec + 1), sizeof(int));
+    xkerns = xcalloc((unsigned) (ec + 1), sizeof(int));
+
+    for (i = bc; i <= ec; i++) {
+        if (char_tag(f, i) == lig_tag) {
+            k = lig_kern_start(f, i);
+            if (skip_byte(k) > stop_flag)
+                k = lig_kern_restart(k);
+            /* now k is the start index */
+            while (1) {
+                if (skip_byte(k) <= stop_flag) {
+                    if (op_byte(k) >= kern_flag) {      /* kern */
+                        xkerns[i]++;
+                        if (next_char(k) == bchar)
+                            xkerns[i]++;
+                    } else {    /* lig */
+                        xligs[i]++;
+                        if (next_char(k) == bchar)
+                            xligs[i]++;
+                    }
+                }
+                if (skip_byte(k) == 0) {
+                    k++;
+                } else {
+                    if (skip_byte(k) >= stop_flag)
+                        break;
+                    k += skip_byte(k) + 1;
+                }
+            }
+        }
+    }
+
+    cligs = NULL;
+    ckerns = NULL;
+
+    for (i = bc; i <= ec; i++) {
+        fligs = 0;
+        fkerns = 0;
+        if (char_tag(f, i) == lig_tag) {
+            k = lig_kern_start(f, i);
+            if (skip_byte(k) > stop_flag)
+                k = lig_kern_restart(k);
+            /* now k is the start index */
+            if (xligs[i] > 0)
+                cligs = xcalloc((unsigned) (xligs[i] + 1), sizeof(liginfo));
+            if (xkerns[i] > 0)
+                ckerns = xcalloc((unsigned) (xkerns[i] + 1), sizeof(kerninfo));
+            while (1) {
+                if (skip_byte(k) <= stop_flag) {
+                    if (op_byte(k) >= kern_flag) {      /* kern */
+                        if (next_char(k) == bchar) {
+                            set_kern_item(ckerns[fkerns], right_boundarychar,
+                                          kerns[256 * (op_byte(k) - 128) +
+                                                rem_byte(k)]);
+                            fkerns++;
+                        }
+                        set_kern_item(ckerns[fkerns], next_char(k),
+                                      kerns[256 * (op_byte(k) - 128) +
+                                            rem_byte(k)]);
+                        fkerns++;
+                    } else {    /* lig */
+                        if (next_char(k) == bchar) {
+                            set_ligature_item(cligs[fligs],
+                                              (char) (op_byte(k) * 2 + 1),
+                                              right_boundarychar, rem_byte(k));
+                            fligs++;
+                        }
+                        set_ligature_item(cligs[fligs],
+                                          (char) (op_byte(k) * 2 + 1),
+                                          next_char(k), rem_byte(k));
+                        fligs++;
+                    }
+                }
+                if (skip_byte(k) == 0) {
+                    k++;
+                } else {
+                    if (skip_byte(k) >= stop_flag)
+                        break;
+                    k += skip_byte(k) + 1;
+                }
+            }
+            if (fkerns > 0 || fligs > 0) {
+                co = get_charinfo(f, i);
+                if (fkerns > 0) {
+                    set_kern_item(ckerns[fkerns], end_kern, 0);
+                    fkerns++;
+                    set_charinfo_kerns(co, ckerns);
+                }
+                if (fligs > 0) {
+                    set_ligature_item(cligs[fligs], 0, end_ligature, 0);
+                    fligs++;
+                    set_charinfo_ligatures(co, cligs);
+                }
+                set_charinfo_remainder(co, 0);
+            }
+        }
+    }
+
+
+    /* Make final adjustments and |goto done| */
+
+    /* Now to wrap it up, we have checked all the necessary things about the \.{TFM}
+       file, and all we need to do is put the finishing touches on the data for
+       the new font.
+     */
+
+    if (bchar != 65536) {
+        co = copy_charinfo(char_info(f, bchar));
+        set_right_boundary(f, co);
+    }
+
+    tfm_success;
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/tounicode.w
@@ -0,0 +1,612 @@
+% tounicode.w
+%
+% Copyright 2006 Han The Thanh, <thanh@@pdftex.org>
+% 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"
+
+@ @c
+#define isXdigit(c) (isdigit(c) || ('A' <= (c) && (c) <= 'F'))
+#define UNI_UNDEF          -1
+#define UNI_STRING         -2   /* string allocated by |def_tounicode()| */
+#define UNI_EXTRA_STRING   -3   /* string allocated by |set_glyph_unicode()| */
+
+static struct avl_table *glyph_unicode_tree = NULL;
+
+static int comp_glyph_unicode_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return strcmp(((const glyph_unicode_entry *) pa)->name,
+                  ((const glyph_unicode_entry *) pb)->name);
+}
+
+static glyph_unicode_entry *new_glyph_unicode_entry(void)
+{
+    glyph_unicode_entry *e;
+    e = xtalloc(1, glyph_unicode_entry);
+    e->name = NULL;
+    e->code = UNI_UNDEF;
+    e->unicode_seq = NULL;
+    return e;
+}
+
+static void destroy_glyph_unicode_entry(void *pa, void *pb)
+{
+    glyph_unicode_entry *e = (glyph_unicode_entry *) pa;
+    (void) pb;
+    xfree(e->name);
+    if (e->code == UNI_STRING) {
+        assert(e->unicode_seq != NULL);
+        xfree(e->unicode_seq);
+    }
+}
+
+void glyph_unicode_free(void)
+{
+    if (glyph_unicode_tree != NULL)
+        avl_destroy(glyph_unicode_tree, destroy_glyph_unicode_entry);
+}
+
+@ @c
+void def_tounicode(str_number glyph, str_number unistr)
+{
+    char buf[SMALL_BUF_SIZE], *p, *ph;
+    char buf2[SMALL_BUF_SIZE], *q;
+    int valid_unistr;           /* 0: invalid; 1: unicode value; 2: string */
+    int i, l;
+    glyph_unicode_entry *gu, t;
+    void **aa;
+
+    p = makecstring(glyph);
+    assert(strlen(p) < SMALL_BUF_SIZE);
+    strcpy(buf, p);
+    free(p);
+    p = makecstring(unistr);
+    ph = p;
+    while (*p == ' ')
+        p++;                    /* ignore leading spaces */
+    l = (int) strlen(p);
+    while (l > 0 && p[l - 1] == ' ')
+        l--;                    /* ignore traling spaces */
+    valid_unistr = 1;           /* a unicode value is the most common case */
+    for (i = 0; i < l; i++) {
+        if (p[i] == ' ')
+            valid_unistr = 2;   /* if a space occurs we treat this entry as a string */
+        else if (!isXdigit((unsigned char)p[i])) {
+            valid_unistr = 0;
+            break;
+        }
+    }
+    if (l == 0 || valid_unistr == 0 || strlen(buf) == 0 || strcmp(buf, notdef) == 0) {
+        formatted_warning("tounicode", "invalid parameter(s): %s -> %s", buf, p);
+        return;
+    }
+    if (glyph_unicode_tree == NULL) {
+        glyph_unicode_tree =
+            avl_create(comp_glyph_unicode_entry, NULL, &avl_xallocator);
+        assert(glyph_unicode_tree != NULL);
+    }
+    t.name = buf;
+    /* allow overriding existing entries */
+    if ((gu = (glyph_unicode_entry *) avl_find(glyph_unicode_tree, &t)) != NULL) {
+        if (gu->code == UNI_STRING) {
+            assert(gu->unicode_seq != NULL);
+            xfree(gu->unicode_seq);
+        }
+    } else {                    /* make new entry */
+        gu = new_glyph_unicode_entry();
+        gu->name = xstrdup(buf);
+    }
+    if (valid_unistr == 2) {    /* a string with space(s) */
+        /* copy p to buf2, ignoring spaces */
+        for (q = buf2; *p != 0; p++)
+            if (*p != ' ')
+                *q++ = *p;
+        *q = 0;
+        gu->code = UNI_STRING;
+        gu->unicode_seq = xstrdup(buf2);
+    } else {
+        i = sscanf(p, "%lX", &(gu->code));
+        assert(i == 1);
+    }
+    aa = avl_probe(glyph_unicode_tree, gu);
+    assert(aa != NULL);
+    free(ph);
+}
+
+
+@ @c
+static long check_unicode_value(char *s, boolean multiple_value)
+{
+    int l = (int) strlen(s);
+    int i;
+    long code = 0; /* anything that is not |UNI_UNDEF| will do */
+
+    if (l == 0)
+        return UNI_UNDEF;
+    if (multiple_value && l % 4 != 0)
+        return UNI_UNDEF;
+    if (!multiple_value && !(4 <= l && l <= 6))
+        return UNI_UNDEF;
+
+    for (i = 0; i < l; i++) {
+        if (!isXdigit((unsigned char)s[i]))
+            return UNI_UNDEF;
+        if (multiple_value) {
+            if (i % 4 == 3) {
+                if (sscanf(s + i - 3, "%4lX", &code) != 1)
+                    return UNI_UNDEF;
+                if (!((0x0000 <= code && code <= 0xD7FF) ||
+                      (0xE000 <= code && code <= 0xFFFF)))
+                    return UNI_UNDEF;
+            }
+        } else {                /* single value */
+            if (i == l - 1) {
+                if (sscanf(s, "%lX", &code) != 1)
+                    return UNI_UNDEF;
+                if (!((0x0000 <= code && code <= 0xD7FF) ||
+                      (0xE000 <= code && code <= 0x10FFFF)))
+                    return UNI_UNDEF;
+            }
+        }
+    }
+    return code;
+}
+
+@ This function set proper values to |*gp| based on |s|; in case it returns
+ |gp->code == UNI_EXTRA_STRING| then the caller is responsible for freeing
+ |gp->unicode_seq| too.
+@c
+static void set_glyph_unicode(char *s, glyph_unicode_entry * gp)
+{
+    char buf[SMALL_BUF_SIZE], buf2[SMALL_BUF_SIZE], *p;
+    long code;
+    boolean last_component;
+    glyph_unicode_entry tmp, *ptmp;
+
+    /* skip dummy entries */
+    if (s == NULL || s == notdef)
+        return;
+
+    /* strip everything after the first dot */
+    p = strchr(s, '.');
+    if (p != NULL) {
+        *buf = 0;
+        strncat(buf, s, (size_t) (p - s));
+        s = buf;
+    }
+
+    if (strlen(s) == 0)
+        return;
+
+    /* check for case of multiple components separated by |'_'| */
+    p = strchr(s, '_');
+    if (p != NULL) {
+        assert(strlen(s) < sizeof(buf));
+        if (s != buf) {
+            strcpy(buf, s);
+            p = strchr(buf, '_');
+            s = buf;
+        }
+        *buf2 = 0;
+        last_component = false;
+        for (;;) {
+            *p = 0;
+            tmp.code = UNI_UNDEF;
+            set_glyph_unicode(s, &tmp);
+            switch (tmp.code) {
+            case UNI_UNDEF:    /* not found, do nothing */
+                break;
+            case UNI_STRING:   /* s matched an entry with string value in the database */
+                assert(tmp.unicode_seq != NULL);
+                assert(strlen(buf2) + strlen(tmp.unicode_seq) < sizeof(buf2));
+                strcat(buf2, tmp.unicode_seq);
+                break;
+            case UNI_EXTRA_STRING:     /* s is a multiple value of form "uniXXXX" */
+                assert(strlen(buf2) + strlen(tmp.unicode_seq) < sizeof(buf2));
+                strcat(buf2, tmp.unicode_seq);
+                xfree(tmp.unicode_seq);
+                break;
+            default:           /* s matched an entry with numeric value in the
+                                   database, or a value derived from "uXXXX" */
+                assert(tmp.code >= 0);
+                strcat(buf2, utf16be_str(tmp.code));
+            }
+            if (last_component)
+                break;
+            s = p + 1;
+            p = strchr(s, '_');
+            if (p == NULL) {
+                p = strend(s);
+                last_component = true;
+            }
+        }
+        gp->code = UNI_EXTRA_STRING;
+        gp->unicode_seq = xstrdup(buf2);
+        return;
+    }
+
+    /* lookup for glyph name in the database */
+    tmp.name = s;
+    tmp.code = UNI_UNDEF;
+    ptmp = (glyph_unicode_entry *) avl_find(glyph_unicode_tree, &tmp);
+    if (ptmp != NULL) {
+        gp->code = ptmp->code;
+        gp->unicode_seq = ptmp->unicode_seq;
+        return;
+    }
+
+    /* check for case of "uniXXXX" (multiple 4-hex-digit values allowed) */
+    if (str_prefix(s, "uni")) {
+        p = s + strlen("uni");
+        code = check_unicode_value(p, true);
+        if (code != UNI_UNDEF) {
+            if (strlen(p) == 4) /* single value */
+                gp->code = code;
+            else {              /* multiple value */
+                gp->code = UNI_EXTRA_STRING;
+                gp->unicode_seq = xstrdup(p);
+            }
+        }
+        return;                 /* since the last case cannot happen */
+    }
+
+    /* check for case of "uXXXX" (single value up to 6 hex digits) */
+    if (str_prefix(s, "u")) {
+        p = s + strlen("u");
+        code = check_unicode_value(p, false);
+        if (code != UNI_UNDEF) {
+            assert(code >= 0);
+            gp->code = code;
+        }
+    }
+}
+
+@ @c
+static void set_cid_glyph_unicode(long index, glyph_unicode_entry * gp,
+                                  internal_font_number f)
+{
+    char *s;
+    if (font_tounicode(f)) {
+        if ((s = get_charinfo_tounicode(char_info(f, (int) index))) != NULL) {
+            gp->code = UNI_EXTRA_STRING;
+            gp->unicode_seq = xstrdup(s);
+        } else {
+            /* no fallback as we're providing them ourselves */
+        }
+    } else {
+        /* fallback */
+        gp->code = index;
+    }
+}
+
+@ @c
+int write_tounicode(PDF pdf, char **glyph_names, char *name)
+{
+    char buf[SMALL_BUF_SIZE], *p;
+    static char builtin_suffix[] = "-builtin";
+    short range_size[257];
+    glyph_unicode_entry gtab[257];
+    int objnum;
+    int i, j;
+    int bfchar_count, bfrange_count, subrange_count;
+    assert(strlen(name) + strlen(builtin_suffix) < SMALL_BUF_SIZE);
+    if (glyph_unicode_tree == NULL) {
+        pdf->gen_tounicode = 0;
+        return 0;
+    }
+    strcpy(buf, name);
+    if ((p = strrchr(buf, '.')) != NULL && strcmp(p, ".enc") == 0)
+        *p = 0;                 /* strip ".enc" from encoding name */
+    else
+        strcat(buf, builtin_suffix);    /* ".enc" not present, this is a builtin
+                                           encoding so the name is eg "cmr10-builtin" */
+    objnum = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_begin_obj(pdf, objnum, OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    pdf_printf(pdf, "%%!PS-Adobe-3.0 Resource-CMap\n"@/
+               "%%%%DocumentNeededResources: ProcSet (CIDInit)\n"@/
+               "%%%%IncludeResource: ProcSet (CIDInit)\n"@/
+               "%%%%BeginResource: CMap (TeX-%s-0)\n"@/
+               "%%%%Title: (TeX-%s-0 TeX %s 0)\n"@/
+               "%%%%Version: 1.000\n"@/
+               "%%%%EndComments\n"@/
+               "/CIDInit /ProcSet findresource begin\n"@/
+               "12 dict begin\n"@/
+               "begincmap\n"@/
+               "/CIDSystemInfo\n"@/
+               "<< /Registry (TeX)\n"@/
+               "/Ordering (%s)\n"@/
+               "/Supplement 0\n"@/
+               ">> def\n"@/
+               "/CMapName /TeX-%s-0 def\n"@/
+               "/CMapType 2 def\n"@/
+               "1 begincodespacerange\n"@/
+               "<00> <FF>\n" "endcodespacerange\n", buf, buf, buf, buf, buf);
+
+    /* set gtab */
+    for (i = 0; i < 256; ++i) {
+        gtab[i].code = UNI_UNDEF;
+        set_glyph_unicode(glyph_names[i], &gtab[i]);
+    }
+    gtab[256].code = UNI_UNDEF;
+
+    /* set |range_size| */
+    for (i = 0; i < 256;) {
+        if (gtab[i].code == UNI_STRING || gtab[i].code == UNI_EXTRA_STRING) {
+            range_size[i] = 1;  /* single entry */
+            i++;
+        } else if (gtab[i].code == UNI_UNDEF) {
+            range_size[i] = 0;  /* no entry */
+            i++;
+        } else {                /* gtab[i].code >= 0 */
+            j = i;
+            while (i < 256 && gtab[i + 1].code >= 0 &&
+                   gtab[i].code + 1 == gtab[i + 1].code)
+                i++;
+            /* at this point i is the last entry of the subrange */
+            i++;                /* move i to the next entry */
+            range_size[j] = (short) (i - j);
+        }
+    }
+
+    /* calculate |bfrange_count| and |bfchar_count| */
+    bfrange_count = 0;
+    bfchar_count = 0;
+    for (i = 0; i < 256;) {
+        if (range_size[i] == 1) {
+            bfchar_count++;
+            i++;
+        } else if (range_size[i] > 1) {
+            bfrange_count++;
+            i += range_size[i];
+        } else
+            i++;
+    }
+
+    /* write out bfrange */
+    i = 0;
+  write_bfrange:
+    if (bfrange_count > 100)
+        subrange_count = 100;
+    else
+        subrange_count = bfrange_count;
+    bfrange_count -= subrange_count;
+    pdf_printf(pdf, "%i beginbfrange\n", subrange_count);
+    for (j = 0; j < subrange_count; j++) {
+        while (range_size[i] <= 1 && i < 256)
+            i++;
+        assert(i < 256);
+        pdf_printf(pdf, "<%02X> <%02X> <%s>\n", i, i + range_size[i] - 1,
+                   utf16be_str(gtab[i].code));
+        i += range_size[i];
+    }
+    pdf_printf(pdf, "endbfrange\n");
+    if (bfrange_count > 0)
+        goto write_bfrange;
+
+    /* write out bfchar */
+    i = 0;
+  write_bfchar:
+    if (bfchar_count > 100)
+        subrange_count = 100;
+    else
+        subrange_count = bfchar_count;
+    bfchar_count -= subrange_count;
+    pdf_printf(pdf, "%i beginbfchar\n", subrange_count);
+    for (j = 0; j < subrange_count; j++) {
+        while (i < 256) {
+            if (range_size[i] > 1)
+                i += range_size[i];
+            else if (range_size[i] == 0)
+                i++;
+            else                /* |range_size[i] == 1| */
+                break;
+        }
+        assert(i < 256 && gtab[i].code != UNI_UNDEF);
+        if (gtab[i].code == UNI_STRING || gtab[i].code == UNI_EXTRA_STRING) {
+            assert(gtab[i].unicode_seq != NULL);
+            pdf_printf(pdf, "<%02X> <%s>\n", i, gtab[i].unicode_seq);
+        } else
+            pdf_printf(pdf, "<%02X> <%s>\n", i, utf16be_str(gtab[i].code));
+        i++;
+    }
+    pdf_printf(pdf, "endbfchar\n");
+    if (bfchar_count > 0)
+        goto write_bfchar;
+
+    /* free strings allocated by |set_glyph_unicode()| */
+    for (i = 0; i < 256; ++i) {
+        if (gtab[i].code == UNI_EXTRA_STRING)
+            xfree(gtab[i].unicode_seq);
+    }
+
+    pdf_printf(pdf, "endcmap\n"
+               "CMapName currentdict /CMap defineresource pop\n"
+               "end\n" "end\n" "%%%%EndResource\n" "%%%%EOF\n");
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    return objnum;
+}
+
+@ @c
+int write_cid_tounicode(PDF pdf, fo_entry * fo, internal_font_number f)
+{
+
+    static int range_size[65537];
+    static glyph_unicode_entry gtab[65537];
+    int objnum;
+    int i, j, k;
+    int bfchar_count, bfrange_count, subrange_count;
+    char *buf;
+
+    assert(fo->fd->fontname);
+    buf = xmalloc((unsigned) (strlen(fo->fd->fontname) + 8));
+    sprintf(buf, "%s-%s",
+            (fo->fd->subset_tag != NULL ? fo->fd->subset_tag : "UCS"),
+            fo->fd->fontname);
+
+    objnum = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_begin_obj(pdf, objnum, OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    pdf_printf(pdf, "%%!PS-Adobe-3.0 Resource-CMap\n"@/
+               "%%%%DocumentNeededResources: ProcSet (CIDInit)\n"@/
+               "%%%%IncludeResource: ProcSet (CIDInit)\n"@/
+               "%%%%BeginResource: CMap (TeX-%s-0)\n"@/
+               "%%%%Title: (TeX-%s-0 TeX %s 0)\n"@/
+               "%%%%Version: 1.000\n"@/
+               "%%%%EndComments\n"@/
+               "/CIDInit /ProcSet findresource begin\n"@/
+               "12 dict begin\n"@/
+               "begincmap\n"@/
+               "/CIDSystemInfo\n"@/
+               "<< /Registry (TeX)\n"@/
+               "/Ordering (%s)\n"@/
+               "/Supplement 0\n"@/
+               ">> def\n"@/
+               "/CMapName /TeX-Identity-%s def\n"@/
+               "/CMapType 2 def\n"@/
+               "1 begincodespacerange\n"@/
+               "<0000> <FFFF>\n"@/
+               "endcodespacerange\n", buf, buf, buf, buf, buf);
+    xfree(buf);
+    /* set up gtab */
+    for (i = 0; i < 65537; ++i) {
+        gtab[i].code = UNI_UNDEF;
+    }
+    for (k = 1; k <= max_font_id(); k++) {
+        if (k == f || -f == pdf_font_num(k)) {
+            for (i = font_bc(k); i <= font_ec(k); i++) {
+                if (quick_char_exists(k, i) && char_used(k, i)) {
+                    j = char_index(k, i);
+                    if (gtab[j].code == UNI_UNDEF) {
+                        set_cid_glyph_unicode(i, &gtab[j], f);
+                    }
+                }
+            }
+        }
+    }
+
+    /* set |range_size| */
+    for (i = 0; i < 65536;) {
+        if (gtab[i].code == UNI_STRING || gtab[i].code == UNI_EXTRA_STRING) {
+            range_size[i] = 1;  /* single entry */
+            i++;
+        } else if (gtab[i].code == UNI_UNDEF) {
+            range_size[i] = 0;  /* no entry */
+            i++;
+        } else {                /* |gtab[i].code >= 0| */
+            j = i;
+            k = i % 256;
+            while (i < 65536 && k<255 && gtab[i + 1].code >= 0 &&
+                   gtab[i].code + 1 == gtab[i + 1].code) {
+                i++; k++;
+            }
+            /* at this point i is the last entry of the subrange */
+            i++;                /* move i to the next entry */
+            range_size[j] = i - j;
+        }
+    }
+
+    /* calculate |bfrange_count| and |bfchar_count| */
+    bfrange_count = 0;
+    bfchar_count = 0;
+    for (i = 0; i < 65536;) {
+        if (range_size[i] == 1) {
+            bfchar_count++;
+            i++;
+        } else if (range_size[i] > 1) {
+            bfrange_count++;
+            i += range_size[i];
+        } else
+            i++;
+    }
+
+    /* write out bfrange */
+    i = 0;
+  write_bfrange:
+    if (bfrange_count > 100)
+        subrange_count = 100;
+    else
+        subrange_count = bfrange_count;
+    bfrange_count -= subrange_count;
+    pdf_printf(pdf, "%i beginbfrange\n", subrange_count);
+    for (j = 0; j < subrange_count; j++) {
+        while (range_size[i] <= 1 && i < 65536)
+            i++;
+        assert(i < 65536);
+        pdf_printf(pdf, "<%04X> <%04X> <%s>\n", i, i + range_size[i] - 1,
+                   utf16be_str(gtab[i].code));
+        i += range_size[i];
+    }
+    pdf_printf(pdf, "endbfrange\n");
+    if (bfrange_count > 0)
+        goto write_bfrange;
+
+    /* write out bfchar */
+    i = 0;
+  write_bfchar:
+    if (bfchar_count > 100)
+        subrange_count = 100;
+    else
+        subrange_count = bfchar_count;
+    bfchar_count -= subrange_count;
+    pdf_printf(pdf, "%i beginbfchar\n", subrange_count);
+    for (j = 0; j < subrange_count; j++) {
+        while (i < 65536) {
+            if (range_size[i] > 1)
+                i += range_size[i];
+            else if (range_size[i] == 0)
+                i++;
+            else                /* |range_size[i] == 1| */
+                break;
+        }
+        assert(i < 65536 && gtab[i].code != UNI_UNDEF);
+        if (gtab[i].code == UNI_STRING || gtab[i].code == UNI_EXTRA_STRING) {
+            assert(gtab[i].unicode_seq != NULL);
+            pdf_printf(pdf, "<%04X> <%s>\n", i, gtab[i].unicode_seq);
+        } else
+            pdf_printf(pdf, "<%04X> <%s>\n", i, utf16be_str(gtab[i].code));
+        i++;
+    }
+    pdf_printf(pdf, "endbfchar\n");
+    if (bfchar_count > 0)
+        goto write_bfchar;
+
+    /* free strings allocated by |set_glyph_unicode()| */
+    for (i = 0; i < 65536; ++i) {
+        if (gtab[i].code == UNI_EXTRA_STRING)
+            xfree(gtab[i].unicode_seq);
+    }
+
+    pdf_printf(pdf, "endcmap\n"
+               "CMapName currentdict /CMap defineresource pop\n"
+               "end\n" "end\n" "%%%%EndResource\n" "%%%%EOF\n");
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    return objnum;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/tounicode.c
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
-
-Copyright 2006 Han The Thanh, <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#define isXdigit(c) (isdigit(c) || ('A' <= (c) && (c) <= 'F'))
-
-/*tex
-
-    |UNIC_STRING| is a string allocated by |def_tounicode| while
-    |UNI_EXTRA_STRING| is one allocated by |set_glyph_unicode|.
-
-*/
-
-#define UNI_UNDEF        -1
-#define UNI_STRING       -2
-#define UNI_EXTRA_STRING -3
-
-static struct avl_table *glyph_unicode_tree = NULL;
-
-static int comp_glyph_unicode_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return strcmp(((const glyph_unicode_entry *) pa)->name, ((const glyph_unicode_entry *) pb)->name);
-}
-
-static glyph_unicode_entry *new_glyph_unicode_entry(void)
-{
-    glyph_unicode_entry *e;
-    e = xtalloc(1, glyph_unicode_entry);
-    e->name = NULL;
-    e->code = UNI_UNDEF;
-    e->unicode_seq = NULL;
-    return e;
-}
-
-static void destroy_glyph_unicode_entry(void *pa, void *pb)
-{
-    glyph_unicode_entry *e = (glyph_unicode_entry *) pa;
-    (void) pb;
-    xfree(e->name);
-    if (e->code == UNI_STRING) {
-        xfree(e->unicode_seq);
-    }
-}
-
-void glyph_unicode_free(void)
-{
-    if (glyph_unicode_tree != NULL)
-        avl_destroy(glyph_unicode_tree, destroy_glyph_unicode_entry);
-}
-
-void def_tounicode(str_number glyph, str_number unistr)
-{
-    char buf[SMALL_BUF_SIZE], *p, *ph;
-    char buf2[SMALL_BUF_SIZE], *q;
-    /*tex |0| is invalid, |1| an \UNICODE\ value and |2| a string */
-    int valid_unistr;
-    int i, l;
-    glyph_unicode_entry *gu, t;
-    void **aa;
-    p = makecstring(glyph);
-    assert(strlen(p) < SMALL_BUF_SIZE);
-    strcpy(buf, p);
-    free(p);
-    p = makecstring(unistr);
-    ph = p;
-    /*tex Ignore leading spaces. */
-    while (*p == ' ')
-        p++;
-    l = (int) strlen(p);
-    /*tex Ignore traling spaces. */
-    while (l > 0 && p[l - 1] == ' ')
-        l--;
-    /*tex A \UNICODE\ value is the most common case. */
-    valid_unistr = 1;
-    for (i = 0; i < l; i++) {
-        /*tex If a space occurs we treat this entry as a string. */
-        if (p[i] == ' ')
-            valid_unistr = 2;
-        else if (!isXdigit((unsigned char)p[i])) {
-            valid_unistr = 0;
-            break;
-        }
-    }
-    if (l == 0 || valid_unistr == 0 || strlen(buf) == 0 || strcmp(buf, notdef) == 0) {
-        formatted_warning("tounicode", "invalid parameter(s): %s -> %s", buf, p);
-        return;
-    }
-    if (glyph_unicode_tree == NULL) {
-        glyph_unicode_tree = avl_create(comp_glyph_unicode_entry, NULL, &avl_xallocator);
-    }
-    t.name = buf;
-    /*tex Allow overriding existing entries. */
-    if ((gu = (glyph_unicode_entry *) avl_find(glyph_unicode_tree, &t)) != NULL) {
-        if (gu->code == UNI_STRING) {
-            xfree(gu->unicode_seq);
-        }
-    } else {
-        /*tex Make a new entry. */
-        gu = new_glyph_unicode_entry();
-        gu->name = xstrdup(buf);
-    }
-    if (valid_unistr == 2) {
-        /*tex A string can have space(s) that we ignore by copying |p| to |buf2|. */
-        for (q = buf2; *p != 0; p++)
-            if (*p != ' ')
-                *q++ = *p;
-        *q = 0;
-        gu->code = UNI_STRING;
-        gu->unicode_seq = xstrdup(buf2);
-    } else {
-        i = sscanf(p, "%lX", &(gu->code));
-    }
-    aa = avl_probe(glyph_unicode_tree, gu);
-    assert(aa != NULL);
-    free(ph);
-}
-
-static long check_unicode_value(char *s, boolean multiple_value)
-{
-    int l = (int) strlen(s);
-    int i;
-    /*tex Anything that is not |UNI_UNDEF| will do: */
-    long code = 0;
-    if (l == 0)
-        return UNI_UNDEF;
-    if (multiple_value && l % 4 != 0)
-        return UNI_UNDEF;
-    if (!multiple_value && !(4 <= l && l <= 6))
-        return UNI_UNDEF;
-    for (i = 0; i < l; i++) {
-        if (!isXdigit((unsigned char)s[i]))
-            return UNI_UNDEF;
-        if (multiple_value) {
-            if (i % 4 == 3) {
-                if (sscanf(s + i - 3, "%4lX", &code) != 1)
-                    return UNI_UNDEF;
-                if (!((0x0000 <= code && code <= 0xD7FF) ||
-                      (0xE000 <= code && code <= 0xFFFF)))
-                    return UNI_UNDEF;
-            }
-        } else {                /* single value */
-            if (i == l - 1) {
-                if (sscanf(s, "%lX", &code) != 1)
-                    return UNI_UNDEF;
-                if (!((0x0000 <= code && code <= 0xD7FF) ||
-                      (0xE000 <= code && code <= 0x10FFFF)))
-                    return UNI_UNDEF;
-            }
-        }
-    }
-    return code;
-}
-
-/*
-
-    This function set proper values to |*gp| based on |s|; in case it returns
-    |gp->code == UNI_EXTRA_STRING| then the caller is responsible for freeing
-    |gp->unicode_seq| too.
-
-*/
-
-static void set_glyph_unicode(char *s, glyph_unicode_entry * gp)
-{
-    char buf[SMALL_BUF_SIZE], buf2[SMALL_BUF_SIZE], *p;
-    long code;
-    boolean last_component;
-    glyph_unicode_entry tmp, *ptmp;
-    /*tex Skip dummy entries. */
-    if (s == NULL || s == notdef)
-        return;
-    /*tex Strip everything after the first dot. */
-    p = strchr(s, '.');
-    if (p != NULL) {
-        *buf = 0;
-        strncat(buf, s, (size_t) (p - s));
-        s = buf;
-    }
-    if (strlen(s) == 0)
-        return;
-    /*tex Check for case of multiple components separated by |_|. */
-    p = strchr(s, '_');
-    if (p != NULL) {
-        assert(strlen(s) < sizeof(buf));
-        if (s != buf) {
-            strcpy(buf, s);
-            p = strchr(buf, '_');
-            s = buf;
-        }
-        *buf2 = 0;
-        last_component = false;
-        for (;;) {
-            *p = 0;
-            tmp.code = UNI_UNDEF;
-            set_glyph_unicode(s, &tmp);
-            switch (tmp.code) {
-            case UNI_UNDEF:
-                /*tex Not found, do nothing. */
-                break;
-            case UNI_STRING:
-                /*tex |s| matched an entry with string value in the database. */
-                assert(tmp.unicode_seq != NULL);
-                assert(strlen(buf2) + strlen(tmp.unicode_seq) < sizeof(buf2));
-                strcat(buf2, tmp.unicode_seq);
-                break;
-            case UNI_EXTRA_STRING:
-                /*tex |s| is a multiple value of form "uniXXXX" */
-                assert(strlen(buf2) + strlen(tmp.unicode_seq) < sizeof(buf2));
-                strcat(buf2, tmp.unicode_seq);
-                xfree(tmp.unicode_seq);
-                break;
-            default:
-                /*tex
-                    |s| matched an entry with numeric value in the database, or a
-                    value derived from |uXXXX|.
-                */
-                assert(tmp.code >= 0);
-                strcat(buf2, utf16be_str(tmp.code));
-            }
-            if (last_component)
-                break;
-            s = p + 1;
-            p = strchr(s, '_');
-            if (p == NULL) {
-                p = strend(s);
-                last_component = true;
-            }
-        }
-        gp->code = UNI_EXTRA_STRING;
-        gp->unicode_seq = xstrdup(buf2);
-        return;
-    }
-    /*tex Lookup glyph name in the database. */
-    tmp.name = s;
-    tmp.code = UNI_UNDEF;
-    ptmp = (glyph_unicode_entry *) avl_find(glyph_unicode_tree, &tmp);
-    if (ptmp != NULL) {
-        gp->code = ptmp->code;
-        gp->unicode_seq = ptmp->unicode_seq;
-        return;
-    }
-    /*tex Check for case of |uniXXXX|, multiple 4-hex-digit values allowed. */
-    if (str_prefix(s, "uni")) {
-        p = s + strlen("uni");
-        code = check_unicode_value(p, true);
-        if (code != UNI_UNDEF) {
-            if (strlen(p) == 4) {
-                /*tex Single value: */
-                gp->code = code;
-            } else {
-                /*tex Multiple value: */
-                gp->code = UNI_EXTRA_STRING;
-                gp->unicode_seq = xstrdup(p);
-            }
-        }
-        /*tex Since the last case cannot happen: */
-        return;
-    }
-    /*tex Check for case of |uXXXX|, a single value up to 6 hex digits. */
-    if (str_prefix(s, "u")) {
-        p = s + strlen("u");
-        code = check_unicode_value(p, false);
-        if (code != UNI_UNDEF) {
-            assert(code >= 0);
-            gp->code = code;
-        }
-    }
-}
-
-static void set_cid_glyph_unicode(long index, glyph_unicode_entry * gp, internal_font_number f)
-{
-    char *s;
-    if (font_tounicode(f)) {
-        if ((s = get_charinfo_tounicode(char_info(f, (int) index))) != NULL) {
-            gp->code = UNI_EXTRA_STRING;
-            gp->unicode_seq = xstrdup(s);
-        } else {
-            /*tex No fall back as we're providing them ourselves. */
-        }
-    } else {
-        /*tex Fall back. */
-        gp->code = index;
-    }
-}
-
-int write_tounicode(PDF pdf, char **glyph_names, char *name)
-{
-    char buf[SMALL_BUF_SIZE], *p;
-    static char builtin_suffix[] = "-builtin";
-    short range_size[257];
-    glyph_unicode_entry gtab[257];
-    int objnum;
-    int i, j;
-    int bfchar_count, bfrange_count, subrange_count;
-    assert(strlen(name) + strlen(builtin_suffix) < SMALL_BUF_SIZE);
-    if (glyph_unicode_tree == NULL) {
-        pdf->gen_tounicode = 0;
-        return 0;
-    }
-    strcpy(buf, name);
-    if ((p = strrchr(buf, '.')) != NULL && strcmp(p, ".enc") == 0) {
-        /*tex
-            Strip |.enc| from encoding name.
-        */
-        *p = 0;
-    } else {
-        /*
-            The suffix |.enc| is not present so this is a builtin encoding so the
-            name becomes e.g. |cmr10-builtin|.
-        */
-        strcat(buf, builtin_suffix);
-    }
-    objnum = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_begin_obj(pdf, objnum, OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    pdf_printf(pdf,
-        "%%!PS-Adobe-3.0 Resource-CMap\n"
-        "%%%%DocumentNeededResources: ProcSet (CIDInit)\n"
-        "%%%%IncludeResource: ProcSet (CIDInit)\n"
-        "%%%%BeginResource: CMap (TeX-%s-0)\n"
-        "%%%%Title: (TeX-%s-0 TeX %s 0)\n"
-        "%%%%Version: 1.000\n"
-        "%%%%EndComments\n"
-        "/CIDInit /ProcSet findresource begin\n"
-        "12 dict begin\n"
-        "begincmap\n"
-        "/CIDSystemInfo\n"
-        "<< /Registry (TeX)\n"
-        "/Ordering (%s)\n"
-        "/Supplement 0\n"
-        ">> def\n"
-        "/CMapName /TeX-%s-0 def\n"
-        "/CMapType 2 def\n"
-        "1 begincodespacerange\n"
-        "<00> <FF>\n" "endcodespacerange\n",
-        buf, buf, buf, buf, buf);
-    /*tex Set gtab: */
-    for (i = 0; i < 256; ++i) {
-        gtab[i].code = UNI_UNDEF;
-        set_glyph_unicode(glyph_names[i], &gtab[i]);
-    }
-    gtab[256].code = UNI_UNDEF;
-    /*tex Set |range_size|: */
-    for (i = 0; i < 256;) {
-        if (gtab[i].code == UNI_STRING || gtab[i].code == UNI_EXTRA_STRING) {
-            /*tex Single entry: */
-            range_size[i] = 1;
-            i++;
-        } else if (gtab[i].code == UNI_UNDEF) {
-            /*tex No entry: */
-            range_size[i] = 0;
-            i++;
-        } else {
-            /*tex |gtab[i].code >= 0| */
-            j = i;
-            while (i < 256 && gtab[i + 1].code >= 0 && gtab[i].code + 1 == gtab[i + 1].code)
-                i++;
-            /*tex
-                At this point |i| is the last entry of the subrange so we move |i| to
-                the next entry.
-            */
-            i++;
-            range_size[j] = (short) (i - j);
-        }
-    }
-    /*tex Calculate |bfrange_count| and |bfchar_count|. */
-    bfrange_count = 0;
-    bfchar_count = 0;
-    for (i = 0; i < 256;) {
-        if (range_size[i] == 1) {
-            bfchar_count++;
-            i++;
-        } else if (range_size[i] > 1) {
-            bfrange_count++;
-            i += range_size[i];
-        } else
-            i++;
-    }
-    /*tex Write out |bfrange|. */
-    i = 0;
-  write_bfrange:
-    if (bfrange_count > 100)
-        subrange_count = 100;
-    else
-        subrange_count = bfrange_count;
-    bfrange_count -= subrange_count;
-    pdf_printf(pdf, "%i beginbfrange\n", subrange_count);
-    for (j = 0; j < subrange_count; j++) {
-        while (range_size[i] <= 1 && i < 256)
-            i++;
-        assert(i < 256);
-        pdf_printf(pdf, "<%02X> <%02X> <%s>\n", i, i + range_size[i] - 1,
-                   utf16be_str(gtab[i].code));
-        i += range_size[i];
-    }
-    pdf_printf(pdf, "endbfrange\n");
-    if (bfrange_count > 0)
-        goto write_bfrange;
-    /*tex Write out |bfchar|. */
-    i = 0;
-  write_bfchar:
-    if (bfchar_count > 100)
-        subrange_count = 100;
-    else
-        subrange_count = bfchar_count;
-    bfchar_count -= subrange_count;
-    pdf_printf(pdf, "%i beginbfchar\n", subrange_count);
-    for (j = 0; j < subrange_count; j++) {
-        while (i < 256) {
-            if (range_size[i] > 1)
-                i += range_size[i];
-            else if (range_size[i] == 0)
-                i++;
-            else
-                break;
-        }
-        assert(i < 256 && gtab[i].code != UNI_UNDEF);
-        if (gtab[i].code == UNI_STRING || gtab[i].code == UNI_EXTRA_STRING) {
-            assert(gtab[i].unicode_seq != NULL);
-            pdf_printf(pdf, "<%02X> <%s>\n", i, gtab[i].unicode_seq);
-        } else
-            pdf_printf(pdf, "<%02X> <%s>\n", i, utf16be_str(gtab[i].code));
-        i++;
-    }
-    pdf_printf(pdf, "endbfchar\n");
-    if (bfchar_count > 0)
-        goto write_bfchar;
-    /*tex Free strings allocated by |set_glyph_unicode|. */
-    for (i = 0; i < 256; ++i) {
-        if (gtab[i].code == UNI_EXTRA_STRING)
-            xfree(gtab[i].unicode_seq);
-    }
-    pdf_printf(pdf,
-        "endcmap\n"
-        "CMapName currentdict /CMap defineresource pop\n"
-        "end\n"
-        "end\n"
-        "%%%%EndResource\n"
-        "%%%%EOF\n");
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    return objnum;
-}
-
-int write_cid_tounicode(PDF pdf, fo_entry * fo, internal_font_number f)
-{
-    static int range_size[65537];
-    static glyph_unicode_entry gtab[65537];
-    int objnum;
-    int i, j, k;
-    int bfchar_count, bfrange_count, subrange_count;
-    char *buf;
-    buf = xmalloc((unsigned) (strlen(fo->fd->fontname) + 8));
-    sprintf(buf, "%s-%s", (fo->fd->subset_tag != NULL ? fo->fd->subset_tag : "UCS"), fo->fd->fontname);
-    objnum = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_begin_obj(pdf, objnum, OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    pdf_printf(pdf,
-        "%%!PS-Adobe-3.0 Resource-CMap\n"
-        "%%%%DocumentNeededResources: ProcSet (CIDInit)\n"
-        "%%%%IncludeResource: ProcSet (CIDInit)\n"
-        "%%%%BeginResource: CMap (TeX-%s-0)\n"
-        "%%%%Title: (TeX-%s-0 TeX %s 0)\n"
-        "%%%%Version: 1.000\n"
-        "%%%%EndComments\n"
-        "/CIDInit /ProcSet findresource begin\n"
-        "12 dict begin\n"
-        "begincmap\n"
-        "/CIDSystemInfo\n"
-        "<< /Registry (TeX)\n"
-        "/Ordering (%s)\n"
-        "/Supplement 0\n"
-        ">> def\n"
-        "/CMapName /TeX-Identity-%s def\n"
-        "/CMapType 2 def\n"
-        "1 begincodespacerange\n"
-        "<0000> <FFFF>\n"
-        "endcodespacerange\n",
-        buf, buf, buf, buf, buf);
-    xfree(buf);
-    /*tex Set up |gtab|: */
-    for (i = 0; i < 65537; ++i) {
-        gtab[i].code = UNI_UNDEF;
-    }
-    for (k = 1; k <= max_font_id(); k++) {
-        if (k == f || -f == pdf_font_num(k)) {
-            for (i = font_bc(k); i <= font_ec(k); i++) {
-                if (quick_char_exists(k, i) && char_used(k, i)) {
-                    j = char_index(k, i);
-                    if (gtab[j].code == UNI_UNDEF) {
-                        set_cid_glyph_unicode(i, &gtab[j], f);
-                    }
-                }
-            }
-        }
-    }
-    /*tex Set |range_size|: */
-    for (i = 0; i < 65536;) {
-        if (gtab[i].code == UNI_STRING || gtab[i].code == UNI_EXTRA_STRING) {
-            /*tex Single entry. */
-            range_size[i] = 1;
-            i++;
-        } else if (gtab[i].code == UNI_UNDEF) {
-            /*tex No entry. */
-            range_size[i] = 0;
-            i++;
-        } else {
-            /*tex |gtab[i].code >= 0| */
-            j = i;
-            k = i % 256;
-            while (i < 65536 && k<255 && gtab[i + 1].code >= 0 &&
-                   gtab[i].code + 1 == gtab[i + 1].code) {
-                i++; k++;
-            }
-            /* tex
-                At this point |i| is the last entry of the subrange so we move
-                |i| to the next entry
-            */
-            i++;
-            range_size[j] = i - j;
-        }
-    }
-    /*tex Calculate |bfrange_count| and |bfchar_count|. */
-    bfrange_count = 0;
-    bfchar_count = 0;
-    for (i = 0; i < 65536;) {
-        if (range_size[i] == 1) {
-            bfchar_count++;
-            i++;
-        } else if (range_size[i] > 1) {
-            bfrange_count++;
-            i += range_size[i];
-        } else
-            i++;
-    }
-    /*tex Write out |bfrange|. */
-    i = 0;
-  write_bfrange:
-    if (bfrange_count > 100)
-        subrange_count = 100;
-    else
-        subrange_count = bfrange_count;
-    bfrange_count -= subrange_count;
-    pdf_printf(pdf, "%i beginbfrange\n", subrange_count);
-    for (j = 0; j < subrange_count; j++) {
-        while (range_size[i] <= 1 && i < 65536)
-            i++;
-        pdf_printf(pdf, "<%04X> <%04X> <%s>\n", i, i + range_size[i] - 1, utf16be_str(gtab[i].code));
-        i += range_size[i];
-    }
-    pdf_printf(pdf, "endbfrange\n");
-    if (bfrange_count > 0)
-        goto write_bfrange;
-    /*tex Write out |bfchar| */
-    i = 0;
-  write_bfchar:
-    if (bfchar_count > 100)
-        subrange_count = 100;
-    else
-        subrange_count = bfchar_count;
-    bfchar_count -= subrange_count;
-    pdf_printf(pdf, "%i beginbfchar\n", subrange_count);
-    for (j = 0; j < subrange_count; j++) {
-        while (i < 65536) {
-            if (range_size[i] > 1)
-                i += range_size[i];
-            else if (range_size[i] == 0)
-                i++;
-            else
-                /* |range_size[i] == 1| */
-                break;
-        }
-        assert(i < 65536 && gtab[i].code != UNI_UNDEF);
-        if (gtab[i].code == UNI_STRING || gtab[i].code == UNI_EXTRA_STRING) {
-            pdf_printf(pdf, "<%04X> <%s>\n", i, gtab[i].unicode_seq);
-        } else
-            pdf_printf(pdf, "<%04X> <%s>\n", i, utf16be_str(gtab[i].code));
-        i++;
-    }
-    pdf_printf(pdf, "endbfchar\n");
-    if (bfchar_count > 0)
-        goto write_bfchar;
-    /*tex Free strings allocated by |set_glyph_unicode|: */
-    for (i = 0; i < 65536; ++i) {
-        if (gtab[i].code == UNI_EXTRA_STRING)
-            xfree(gtab[i].unicode_seq);
-    }
-    pdf_printf(pdf,
-        "endcmap\n"
-        "CMapName currentdict /CMap defineresource pop\n"
-        "end\n"
-        "end\n"
-        "%%%%EndResource\n"
-        "%%%%EOF\n");
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    return objnum;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/tt_glyf.w
@@ -0,0 +1,695 @@
+% tt_glyf.w
+%
+% Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata,
+% the dvipdfmx project team <dvipdfmx@@project.ktug.or.kr>
+% Copyright 2006-2012 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/>.
+
+@* Subsetting glyf, updating loca, hmtx, etc.
+
+@ @c
+
+
+#include "ptexlib.h"
+
+#include "font/sfnt.h"
+#include "font/tt_table.h"
+#include "font/tt_glyf.h"
+#include "font/writettf.h"
+
+@ @c
+#define NUM_GLYPH_LIMIT        65534
+#define TABLE_DATA_ALLOC_SIZE  40960
+#define GLYPH_ARRAY_ALLOC_SIZE 256
+
+static USHORT find_empty_slot(struct tt_glyphs *g)
+{
+    USHORT gid;
+
+    ASSERT(g);
+
+    for (gid = 0; gid < NUM_GLYPH_LIMIT; gid++) {
+        if (!(g->used_slot[gid / 8] & (1 << (7 - (gid % 8)))))
+            break;
+    }
+    if (gid == NUM_GLYPH_LIMIT)
+        normal_error("ttf","no empty glyph slot available.");
+
+    return gid;
+}
+
+USHORT tt_find_glyph(struct tt_glyphs * g, USHORT gid)
+{
+    USHORT idx, new_gid = 0;
+
+    ASSERT(g);
+
+    for (idx = 0; idx < g->num_glyphs; idx++) {
+        if (gid == g->gd[idx].ogid) {
+            new_gid = g->gd[idx].gid;
+            break;
+        }
+    }
+
+    return new_gid;
+}
+
+USHORT tt_get_index(struct tt_glyphs * g, USHORT gid)
+{
+    USHORT idx;
+
+    ASSERT(g);
+
+    for (idx = 0; idx < g->num_glyphs; idx++) {
+        if (gid == g->gd[idx].gid)
+            break;
+    }
+    if (idx == g->num_glyphs)
+        idx = 0;
+
+    return idx;
+}
+
+USHORT tt_add_glyph(struct tt_glyphs * g, USHORT gid, USHORT new_gid)
+{
+    ASSERT(g);
+
+    if (g->used_slot[new_gid / 8] & (1 << (7 - (new_gid % 8)))) {
+        formatted_warning("ttf","slot %u already used", new_gid);
+    } else {
+        if (g->num_glyphs + 1 >= NUM_GLYPH_LIMIT)
+            normal_error("ttf","too many glyphs");
+
+        if (g->num_glyphs >= g->max_glyphs) {
+            g->max_glyphs = (USHORT) (g->max_glyphs + GLYPH_ARRAY_ALLOC_SIZE);
+            g->gd = RENEW(g->gd, g->max_glyphs, struct tt_glyph_desc);
+        }
+        g->gd[g->num_glyphs].gid = new_gid;
+        g->gd[g->num_glyphs].ogid = gid;
+        g->gd[g->num_glyphs].length = 0;
+        g->gd[g->num_glyphs].data = NULL;
+        g->used_slot[new_gid / 8] =
+            (unsigned char) (g->used_slot[new_gid /
+                                          8] | (1 << (7 - (new_gid % 8))));
+        g->num_glyphs++;
+    }
+
+    if (new_gid > g->last_gid) {
+        g->last_gid = new_gid;
+    }
+
+    return new_gid;
+}
+
+
+@ Initialization
+@c
+struct tt_glyphs *tt_build_init(void)
+{
+    struct tt_glyphs *g;
+
+    g = NEW(1, struct tt_glyphs);
+
+    g->num_glyphs = 0;
+    g->max_glyphs = 0;
+    g->last_gid = 0;
+    g->emsize = 1;
+    g->default_advh = 0;
+    g->default_tsb = 0;
+    g->gd = NULL;
+    g->used_slot = NEW(8192, unsigned char);
+    memset(g->used_slot, 0, 8192);
+    tt_add_glyph(g, 0, 0);
+
+    return g;
+}
+
+void tt_build_finish(struct tt_glyphs *g)
+{
+    if (g) {
+        if (g->gd) {
+            USHORT idx;
+            for (idx = 0; idx < g->num_glyphs; idx++) {
+                if (g->gd[idx].data)
+                    RELEASE(g->gd[idx].data);
+            }
+            RELEASE(g->gd);
+        }
+        if (g->used_slot)
+            RELEASE(g->used_slot);
+        RELEASE(g);
+    }
+}
+
+static int glyf_cmp(const void *v1, const void *v2)
+{
+    int cmp = 0;
+    const struct tt_glyph_desc *sv1, *sv2;
+
+    sv1 = (const struct tt_glyph_desc *) v1;
+    sv2 = (const struct tt_glyph_desc *) v2;
+
+    if (sv1->gid == sv2->gid)
+        cmp = 0;
+    else if (sv1->gid < sv2->gid)
+        cmp = -1;
+    else
+        cmp = 1;
+
+    return cmp;
+}
+
+@ @c
+int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd)
+{
+    char *hmtx_table_data = NULL, *loca_table_data = NULL;
+    char *glyf_table_data = NULL;
+    ULONG hmtx_table_size, loca_table_size, glyf_table_size, glyf_table_used;
+    /* some information available from other TrueType table */
+    struct tt_head_table *head = NULL;
+    struct tt_hhea_table *hhea = NULL;
+    struct tt_maxp_table *maxp = NULL;
+    struct tt_longMetrics *hmtx, *vmtx = NULL;
+    struct tt_os2__table *os2;
+    /* temp */
+    ULONG *location, offset;
+    long i;
+    USHORT *w_stat;             /* Estimate most frequently appeared width */
+
+    int tex_font = fd->tex_font;
+    int streamprovider = 0;
+    int callback_id = 0 ;
+    if ((tex_font > 0) && (font_streamprovider(tex_font) == 2)) {
+        streamprovider = font_streamprovider(tex_font);
+        callback_id = callback_defined(glyph_stream_provider_callback);
+    }
+
+    ASSERT(g);
+
+    if (sfont->type != SFNT_TYPE_TRUETYPE && sfont->type != SFNT_TYPE_TTC)
+        normal_error("ttf","invalid font type");
+
+    if (g->num_glyphs > NUM_GLYPH_LIMIT)
+        normal_error("ttf","too many glyphs");
+
+    /*
+     Read head, hhea, maxp, loca:
+
+     unitsPerEm       --> head
+
+     numHMetrics      --> hhea
+
+     indexToLocFormat --> head
+
+     numGlyphs        --> maxp
+     */
+    head = tt_read_head_table(sfont);
+    hhea = tt_read_hhea_table(sfont);
+    maxp = tt_read_maxp_table(sfont);
+
+    if (hhea->metricDataFormat != 0)
+        normal_error("ttf","unknown metricDataFormat");
+
+    g->emsize = head->unitsPerEm;
+
+    sfnt_locate_table(sfont, "hmtx");
+    hmtx = tt_read_longMetrics(sfont, maxp->numGlyphs, hhea->numberOfHMetrics);
+
+    os2 = tt_read_os2__table(sfont);
+    if (os2) {
+        g->default_advh = (USHORT) (os2->sTypoAscender - os2->sTypoDescender);
+        g->default_tsb = (SHORT) (g->default_advh - os2->sTypoAscender);
+
+        /* dvipdfmx does this elsewhere! */
+        fd_cur->font_dim[STEMV_CODE].val =
+            (os2->usWeightClass / 65) * (os2->usWeightClass / 65) + 50;
+    }
+
+    if (sfnt_find_table_pos(sfont, "vmtx") > 0) {
+        struct tt_vhea_table *vhea;
+        vhea = tt_read_vhea_table(sfont);
+        sfnt_locate_table(sfont, "vmtx");
+        vmtx =
+            tt_read_longMetrics(sfont, maxp->numGlyphs,
+                                vhea->numOfLongVerMetrics);
+        RELEASE(vhea);
+    } else {
+        vmtx = NULL;
+    }
+
+    sfnt_locate_table(sfont, "loca");
+    location = NEW(maxp->numGlyphs + 1, ULONG);
+    if (head->indexToLocFormat == 0) {
+        for (i = 0; i <= maxp->numGlyphs; i++)
+            location[i] = 2 * ((ULONG) sfnt_get_ushort(sfont));
+    } else if (head->indexToLocFormat == 1) {
+        for (i = 0; i <= maxp->numGlyphs; i++)
+            location[i] = sfnt_get_ulong(sfont);
+    } else {
+        normal_error("ttf","unknown IndexToLocFormat");
+    }
+
+    w_stat = NEW(g->emsize + 2, USHORT);
+    memset(w_stat, 0,
+           (size_t) (sizeof(USHORT) * ((long unsigned) g->emsize + 2)));
+    /*
+     * Read glyf table.
+     */
+    offset = sfnt_locate_table(sfont, "glyf");
+    /*
+     The |num_glyphs| may grow when composite glyph is found.
+     A component of glyph refered by a composite glyph is appended
+     to |used_glyphs| if it is not already registered in |used_glyphs|.
+     Glyph programs of composite glyphs are modified so that it
+     correctly refer to new gid of their components.
+     */
+    for (i = 0; i < NUM_GLYPH_LIMIT; i++) {
+        USHORT gid;             /* old gid */
+        ULONG loc, len;
+        BYTE *p, *endptr;
+        SHORT number_of_contours;
+
+        if (i >= g->num_glyphs) /* finished */
+            break;
+
+        gid = g->gd[i].ogid;
+        if (gid >= maxp->numGlyphs)
+            formatted_error("ttf","invalid glyph index (gid %u)", gid);
+
+        loc = location[gid];
+        len = location[gid + 1] - loc;
+        g->gd[i].advw = hmtx[gid].advance;
+        g->gd[i].lsb = hmtx[gid].sideBearing;
+        if (vmtx) {
+            g->gd[i].advh = vmtx[gid].advance;
+            g->gd[i].tsb = vmtx[gid].sideBearing;
+        } else {
+            g->gd[i].advh = g->default_advh;
+            g->gd[i].tsb = g->default_tsb;
+        }
+        g->gd[i].length = len;
+        g->gd[i].data = NULL;
+        if (g->gd[i].advw <= g->emsize) {
+            w_stat[g->gd[i].advw]++;
+        } else {
+            w_stat[g->emsize + 1]++;    /* larger than em */
+        }
+
+        if (len == 0) {         /* Does not contains any data. */
+            continue;
+        } else if (len < 10) {
+            formatted_error("ttf","invalid glyph data (gid %u)", gid);
+        }
+
+/* todo: no need for this */
+        g->gd[i].data = p = NEW(len, BYTE);
+        endptr = p + len;
+
+        sfnt_seek_set(sfont, (long) (offset + loc));
+        number_of_contours = sfnt_get_short(sfont);
+        p += sfnt_put_short(p, number_of_contours);
+
+        /* BoundingBox: FWord x 4 */
+        g->gd[i].llx = sfnt_get_short(sfont);
+        g->gd[i].lly = sfnt_get_short(sfont);
+        g->gd[i].urx = sfnt_get_short(sfont);
+        g->gd[i].ury = sfnt_get_short(sfont);
+        /* |_FIXME_| */
+#if  1
+        if (!vmtx)              /* |vertOriginY == sTypeAscender| */
+            g->gd[i].tsb =
+                (SHORT) (g->default_advh - g->default_tsb - g->gd[i].ury);
+#endif
+        p += sfnt_put_short(p, g->gd[i].llx);
+        p += sfnt_put_short(p, g->gd[i].lly);
+        p += sfnt_put_short(p, g->gd[i].urx);
+        p += sfnt_put_short(p, g->gd[i].ury);
+
+        /* Read evrything else. */
+        sfnt_read(p, (int) len - 10, sfont);
+        /*
+         Fix GIDs of composite glyphs.
+         */
+        if (number_of_contours < 0) {
+            USHORT flags, cgid, new_gid;        /* flag, gid of a component */
+            do {
+                if (p >= endptr)
+                    formatted_error("ttf","invalid glyph data (gid %u): %u bytes", gid, (unsigned int) len);
+                /*
+                 * Flags and gid of component glyph are both USHORT.
+                 */
+                flags = (USHORT) (((*p) << 8) | *(p + 1));
+                p += 2;
+                cgid = (USHORT) (((*p) << 8) | *(p + 1));
+                if (cgid >= maxp->numGlyphs) {
+                    formatted_error("ttf","invalid gid (%u > %u) in composite glyph %u", cgid, maxp->numGlyphs, gid);
+                }
+                new_gid = tt_find_glyph(g, cgid);
+                if (new_gid == 0) {
+                    new_gid = tt_add_glyph(g, cgid, find_empty_slot(g));
+                }
+                p += sfnt_put_ushort(p, new_gid);
+                /*
+                 * Just skip remaining part.
+                 */
+                p += (flags & ARG_1_AND_2_ARE_WORDS) ? 4 : 2;
+                if (flags & WE_HAVE_A_SCALE)    /* F2Dot14 */
+                    p += 2;
+                else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)      /* F2Dot14 x 2 */
+                    p += 4;
+                else if (flags & WE_HAVE_A_TWO_BY_TWO)  /* F2Dot14 x 4 */
+                    p += 8;
+            } while (flags & MORE_COMPONENTS);
+            /*
+             TrueType instructions comes here:
+
+             |length_of_instruction| (|ushort|)
+
+             instruction (|byte * length_of_instruction|)
+             */
+        }
+    }
+    RELEASE(location);
+    RELEASE(hmtx);
+    if (vmtx)
+        RELEASE(vmtx);
+
+    {
+        int max_count = -1;
+
+        g->dw = g->gd[0].advw;
+        for (i = 0; i < g->emsize + 1; i++) {
+            if (w_stat[i] > max_count) {
+                max_count = w_stat[i];
+                g->dw = (USHORT) i;
+            }
+        }
+    }
+    RELEASE(w_stat);
+
+    qsort(g->gd, g->num_glyphs, sizeof(struct tt_glyph_desc), glyf_cmp);
+    {
+        USHORT prev, last_advw;
+        char *p, *q;
+        int padlen, num_hm_known;
+
+        glyf_table_size = 0UL;
+        num_hm_known = 0;
+        last_advw = g->gd[g->num_glyphs - 1].advw;
+        for (i = g->num_glyphs - 1; i >= 0; i--) {
+            padlen =
+                (int) ((g->gd[i].length % 4) ? (4 - (g->gd[i].length % 4)) : 0);
+            glyf_table_size += (ULONG) (g->gd[i].length + (ULONG) padlen);
+            if (!num_hm_known && last_advw != g->gd[i].advw) {
+                hhea->numberOfHMetrics = (USHORT) (g->gd[i].gid + 2);
+                num_hm_known = 1;
+            }
+        }
+        /* All advance widths are same. */
+        if (!num_hm_known) {
+            hhea->numberOfHMetrics = 1;
+        }
+        hmtx_table_size =
+            (ULONG) (hhea->numberOfHMetrics * 2 + (g->last_gid + 1) * 2);
+
+        /*
+         Choosing short format does not always give good result
+         when compressed. Sometimes increases size.
+         */
+        if (glyf_table_size < 0x20000UL) {
+            head->indexToLocFormat = 0;
+            loca_table_size = (ULONG) ((g->last_gid + 2) * 2);
+        } else {
+            head->indexToLocFormat = 1;
+            loca_table_size = (ULONG) ((g->last_gid + 2) * 4);
+        }
+
+        hmtx_table_data = p = NEW(hmtx_table_size, char);
+        loca_table_data = q = NEW(loca_table_size, char);
+        glyf_table_data = NEW(glyf_table_size, char);
+        glyf_table_used = 0;
+
+        offset = 0UL;
+        prev = 0;
+        for (i = 0; i < g->num_glyphs; i++) {
+            long gap, j;
+            gap = (long) g->gd[i].gid - prev - 1;
+            for (j = 1; j <= gap; j++) {
+                if (prev + j == hhea->numberOfHMetrics - 1) {
+                    p += sfnt_put_ushort(p, last_advw);
+                } else if (prev + j < hhea->numberOfHMetrics) {
+                    p += sfnt_put_ushort(p, 0);
+                }
+                p += sfnt_put_short(p, 0);
+                if (head->indexToLocFormat == 0) {
+                    q += sfnt_put_ushort(q, (USHORT) (offset / 2));
+                } else {
+                    q += sfnt_put_ulong(q, (LONG) offset);
+                }
+            }
+            if (g->gd[i].gid < hhea->numberOfHMetrics) {
+                p += sfnt_put_ushort(p, g->gd[i].advw);
+            }
+            p += sfnt_put_short(p, g->gd[i].lsb);
+            if (head->indexToLocFormat == 0) {
+                q += sfnt_put_ushort(q, (USHORT) (offset / 2));
+            } else {
+                q += sfnt_put_ulong(q, (LONG) offset);
+            }
+
+            if (callback_id > 0) {
+
+                lstring * result;
+                long size = 0;
+                run_callback(callback_id, "ddd->L", tex_font, g->gd[i].gid, streamprovider, &result); /* this call can be sped up */
+                padlen = (int) ((result->l % 4) ? (4 - (result->l % 4)) : 0);
+                size = (size_t) result->l + (ULONG) padlen;
+                if (glyf_table_used + size >= glyf_table_size) {
+                    glyf_table_size = glyf_table_size + 20 * size; /* just a guess */
+                    glyf_table_data = xrealloc(glyf_table_data, (unsigned)((unsigned)glyf_table_size*sizeof(char)));
+                }
+                glyf_table_used += size;
+                memset(glyf_table_data + offset, 0, (size_t) size);
+                memcpy(glyf_table_data + offset, (const char *) result->s, (size_t) result->l);
+                offset += size;
+                xfree(result);
+
+            } else {
+
+                padlen = (int) ((g->gd[i].length % 4) ? (4 - (g->gd[i].length % 4)) : 0);
+                memset(glyf_table_data + offset, 0, (size_t) (g->gd[i].length + (ULONG) padlen));
+                memcpy(glyf_table_data + offset, g->gd[i].data, g->gd[i].length);
+                offset += (g->gd[i].length + (ULONG) padlen);
+
+            }
+            prev = g->gd[i].gid;
+            RELEASE(g->gd[i].data);
+            /* free data here since it consume much memory */
+            g->gd[i].length = 0;
+            g->gd[i].data = NULL;
+        }
+        if (head->indexToLocFormat == 0) {
+            q += sfnt_put_ushort(q, (USHORT) (offset / 2));
+        } else {
+            q += sfnt_put_ulong(q, (LONG) offset);
+        }
+
+        sfnt_set_table(sfont, "hmtx", (char *) hmtx_table_data, hmtx_table_size);
+        sfnt_set_table(sfont, "loca", (char *) loca_table_data, loca_table_size);
+        if (callback_id > 0) {
+            glyf_table_size = glyf_table_used;
+        }
+        sfnt_set_table(sfont, "glyf", (char *) glyf_table_data, glyf_table_size);
+    }
+
+    head->checkSumAdjustment = 0;
+    maxp->numGlyphs = (USHORT) (g->last_gid + 1);
+
+    /* TODO */
+    sfnt_set_table(sfont, "maxp", tt_pack_maxp_table(maxp), TT_MAXP_TABLE_SIZE);
+    sfnt_set_table(sfont, "hhea", tt_pack_hhea_table(hhea), TT_HHEA_TABLE_SIZE);
+    sfnt_set_table(sfont, "head", tt_pack_head_table(head), TT_HEAD_TABLE_SIZE);
+    RELEASE(maxp);
+    RELEASE(hhea);
+    RELEASE(head);
+    if (os2)
+        RELEASE(os2);
+
+    return 0;
+}
+
+int tt_get_metrics(sfnt * sfont, struct tt_glyphs *g)
+{
+    struct tt_head_table *head = NULL;
+    struct tt_hhea_table *hhea = NULL;
+    struct tt_maxp_table *maxp = NULL;
+    struct tt_longMetrics *hmtx, *vmtx = NULL;
+    struct tt_os2__table *os2;
+    /* temp */
+    ULONG *location, offset;
+    long i;
+    USHORT *w_stat;
+
+    ASSERT(g);
+
+    if (sfont == NULL ||
+#ifdef XETEX
+        sfont->ft_face == NULL
+#elif defined(pdfTeX)
+        sfont->buffer == NULL
+#else
+        sfont->stream == NULL
+#endif
+        )
+        normal_error("ttf","file not opened");
+
+    if (sfont->type != SFNT_TYPE_TRUETYPE && sfont->type != SFNT_TYPE_TTC)
+        normal_error("ttf","invalid font type");
+
+    /*
+     Read head, hhea, maxp, loca:
+
+     unitsPerEm       --> head
+
+     numHMetrics      --> hhea
+
+     indexToLocFormat --> head
+
+     numGlyphs        --> maxp
+     */
+    head = tt_read_head_table(sfont);
+    hhea = tt_read_hhea_table(sfont);
+    maxp = tt_read_maxp_table(sfont);
+
+    if (hhea->metricDataFormat != 0)
+        normal_error("ttf","unknown metricDataFormat");
+
+    g->emsize = head->unitsPerEm;
+
+    sfnt_locate_table(sfont, "hmtx");
+    hmtx = tt_read_longMetrics(sfont, maxp->numGlyphs, hhea->numberOfHMetrics);
+
+    os2 = tt_read_os2__table(sfont);
+    g->default_advh = (USHORT) (os2->sTypoAscender - os2->sTypoDescender);
+    g->default_tsb = (SHORT) (g->default_advh - os2->sTypoAscender);
+
+    if (sfnt_find_table_pos(sfont, "vmtx") > 0) {
+        struct tt_vhea_table *vhea;
+        vhea = tt_read_vhea_table(sfont);
+        sfnt_locate_table(sfont, "vmtx");
+        vmtx =
+            tt_read_longMetrics(sfont, maxp->numGlyphs,
+                                vhea->numOfLongVerMetrics);
+        RELEASE(vhea);
+    } else {
+        vmtx = NULL;
+    }
+
+    sfnt_locate_table(sfont, "loca");
+    location = NEW(maxp->numGlyphs + 1, ULONG);
+    if (head->indexToLocFormat == 0) {
+        for (i = 0; i <= maxp->numGlyphs; i++)
+            location[i] = 2 * ((ULONG) sfnt_get_ushort(sfont));
+    } else if (head->indexToLocFormat == 1) {
+        for (i = 0; i <= maxp->numGlyphs; i++)
+            location[i] = sfnt_get_ulong(sfont);
+    } else {
+        normal_error("ttf","inknown IndexToLocFormat");
+    }
+
+    w_stat = NEW(g->emsize + 2, USHORT);
+    memset(w_stat, 0, (size_t) ((int) sizeof(USHORT) * (g->emsize + 2)));
+    /*
+     Read glyf table.
+     */
+    offset = sfnt_locate_table(sfont, "glyf");
+    for (i = 0; i < g->num_glyphs; i++) {
+        USHORT gid;             /* old gid */
+        ULONG loc, len;
+        /*SHORT number_of_contours;*/
+
+        gid = g->gd[i].ogid;
+        if (gid >= maxp->numGlyphs)
+            formatted_error("ttf","invalid glyph index (gid %u)", gid);
+
+        loc = location[gid];
+        len = location[gid + 1] - loc;
+        g->gd[i].advw = hmtx[gid].advance;
+        g->gd[i].lsb = hmtx[gid].sideBearing;
+        if (vmtx) {
+            g->gd[i].advh = vmtx[gid].advance;
+            g->gd[i].tsb = vmtx[gid].sideBearing;
+        } else {
+            g->gd[i].advh = g->default_advh;
+            g->gd[i].tsb = g->default_tsb;
+        }
+        g->gd[i].length = len;
+        g->gd[i].data = NULL;
+
+        if (g->gd[i].advw <= g->emsize) {
+            w_stat[g->gd[i].advw]++;
+        } else {
+            w_stat[g->emsize + 1]++;    /* larger than em */
+        }
+
+        if (len == 0) {         /* Does not contains any data. */
+            continue;
+        } else if (len < 10) {
+            formatted_error("ttf","invalid glyph data (gid %u)", gid);
+        }
+
+        sfnt_seek_set(sfont, (long) (offset + loc));
+        /*number_of_contours = */(void)sfnt_get_short(sfont);
+
+        /* BoundingBox: FWord x 4 */
+        g->gd[i].llx = sfnt_get_short(sfont);
+        g->gd[i].lly = sfnt_get_short(sfont);
+        g->gd[i].urx = sfnt_get_short(sfont);
+        g->gd[i].ury = sfnt_get_short(sfont);
+        /* |_FIXME_| */
+#if  1
+        if (!vmtx)              /* |vertOriginY == sTypeAscender| */
+            g->gd[i].tsb =
+                (SHORT) (g->default_advh - g->default_tsb - g->gd[i].ury);
+#endif
+    }
+    RELEASE(location);
+    RELEASE(hmtx);
+    RELEASE(maxp);
+    RELEASE(hhea);
+    RELEASE(head);
+    RELEASE(os2);
+
+    if (vmtx)
+        RELEASE(vmtx);
+
+    {
+        int max_count = -1;
+
+        g->dw = g->gd[0].advw;
+        for (i = 0; i < g->emsize + 1; i++) {
+            if (w_stat[i] > max_count) {
+                max_count = w_stat[i];
+                g->dw = (USHORT) i;
+            }
+        }
+    }
+    RELEASE(w_stat);
+
+
+    return 0;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/tt_glyf.c
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
-
-Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata,
-the dvipdfmx project team <dvipdfmx@project.ktug.or.kr>
-Copyright 2006-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#include "font/sfnt.h"
-#include "font/tt_table.h"
-#include "font/tt_glyf.h"
-#include "font/writettf.h"
-
-#define NUM_GLYPH_LIMIT        65534
-#define TABLE_DATA_ALLOC_SIZE  40960
-#define GLYPH_ARRAY_ALLOC_SIZE 256
-
-static USHORT find_empty_slot(struct tt_glyphs *g)
-{
-    USHORT gid;
-    for (gid = 0; gid < NUM_GLYPH_LIMIT; gid++) {
-        if (!(g->used_slot[gid / 8] & (1 << (7 - (gid % 8)))))
-            break;
-    }
-    if (gid == NUM_GLYPH_LIMIT)
-        normal_error("ttf","no empty glyph slot available.");
-    return gid;
-}
-
-USHORT tt_find_glyph(struct tt_glyphs * g, USHORT gid)
-{
-    USHORT idx, new_gid = 0;
-    for (idx = 0; idx < g->num_glyphs; idx++) {
-        if (gid == g->gd[idx].ogid) {
-            new_gid = g->gd[idx].gid;
-            break;
-        }
-    }
-    return new_gid;
-}
-
-USHORT tt_get_index(struct tt_glyphs * g, USHORT gid)
-{
-    USHORT idx;
-    for (idx = 0; idx < g->num_glyphs; idx++) {
-        if (gid == g->gd[idx].gid)
-            break;
-    }
-    if (idx == g->num_glyphs)
-        idx = 0;
-    return idx;
-}
-
-USHORT tt_add_glyph(struct tt_glyphs * g, USHORT gid, USHORT new_gid)
-{
-    if (g->used_slot[new_gid / 8] & (1 << (7 - (new_gid % 8)))) {
-        formatted_warning("ttf","slot %u already used", new_gid);
-    } else {
-        if (g->num_glyphs + 1 >= NUM_GLYPH_LIMIT)
-            normal_error("ttf","too many glyphs");
-
-        if (g->num_glyphs >= g->max_glyphs) {
-            g->max_glyphs = (USHORT) (g->max_glyphs + GLYPH_ARRAY_ALLOC_SIZE);
-            g->gd = RENEW(g->gd, g->max_glyphs, struct tt_glyph_desc);
-        }
-        g->gd[g->num_glyphs].gid = new_gid;
-        g->gd[g->num_glyphs].ogid = gid;
-        g->gd[g->num_glyphs].length = 0;
-        g->gd[g->num_glyphs].data = NULL;
-        g->used_slot[new_gid / 8] = (unsigned char) (g->used_slot[new_gid / 8] | (1 << (7 - (new_gid % 8))));
-        g->num_glyphs++;
-    }
-    if (new_gid > g->last_gid) {
-        g->last_gid = new_gid;
-    }
-    return new_gid;
-}
-
-/*tex Initialization */
-
-struct tt_glyphs *tt_build_init(void)
-{
-    struct tt_glyphs *g;
-    g = NEW(1, struct tt_glyphs);
-    g->num_glyphs = 0;
-    g->max_glyphs = 0;
-    g->last_gid = 0;
-    g->emsize = 1;
-    g->default_advh = 0;
-    g->default_tsb = 0;
-    g->gd = NULL;
-    g->used_slot = NEW(8192, unsigned char);
-    memset(g->used_slot, 0, 8192);
-    tt_add_glyph(g, 0, 0);
-    return g;
-}
-
-void tt_build_finish(struct tt_glyphs *g)
-{
-    if (g) {
-        if (g->gd) {
-            USHORT idx;
-            for (idx = 0; idx < g->num_glyphs; idx++) {
-                if (g->gd[idx].data)
-                    RELEASE(g->gd[idx].data);
-            }
-            RELEASE(g->gd);
-        }
-        if (g->used_slot)
-            RELEASE(g->used_slot);
-        RELEASE(g);
-    }
-}
-
-static int glyf_cmp(const void *v1, const void *v2)
-{
-    int cmp = 0;
-    const struct tt_glyph_desc *sv1, *sv2;
-    sv1 = (const struct tt_glyph_desc *) v1;
-    sv2 = (const struct tt_glyph_desc *) v2;
-    if (sv1->gid == sv2->gid)
-        cmp = 0;
-    else if (sv1->gid < sv2->gid)
-        cmp = -1;
-    else
-        cmp = 1;
-    return cmp;
-}
-
-int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd)
-{
-    char *hmtx_table_data = NULL, *loca_table_data = NULL;
-    char *glyf_table_data = NULL;
-    ULONG hmtx_table_size, loca_table_size, glyf_table_size, glyf_table_used;
-    /*tex Some information available from other \TRUETYPE\ table. */
-    struct tt_head_table *head = NULL;
-    struct tt_hhea_table *hhea = NULL;
-    struct tt_maxp_table *maxp = NULL;
-    struct tt_longMetrics *hmtx, *vmtx = NULL;
-    struct tt_os2__table *os2;
-    /*tex Something temporary: */
-    ULONG *location, offset;
-    long i;
-    /*tex Estimate the most frequently appeared width. */
-    USHORT *w_stat;
-    int tex_font = fd->tex_font;
-    int streamprovider = 0;
-    int callback_id = 0 ;
-    if ((tex_font > 0) && (font_streamprovider(tex_font) == 2)) {
-        streamprovider = font_streamprovider(tex_font);
-        callback_id = callback_defined(glyph_stream_provider_callback);
-    }
-    if (sfont->type != SFNT_TYPE_TRUETYPE && sfont->type != SFNT_TYPE_TTC)
-        normal_error("ttf","invalid font type");
-    if (g->num_glyphs > NUM_GLYPH_LIMIT)
-        normal_error("ttf","too many glyphs");
-    head = tt_read_head_table(sfont);
-    hhea = tt_read_hhea_table(sfont);
-    maxp = tt_read_maxp_table(sfont);
-    if (hhea->metricDataFormat != 0)
-        normal_error("ttf","unknown metricDataFormat");
-    g->emsize = head->unitsPerEm;
-    sfnt_locate_table(sfont, "hmtx");
-    hmtx = tt_read_longMetrics(sfont, maxp->numGlyphs, hhea->numberOfHMetrics);
-    os2 = tt_read_os2__table(sfont);
-    if (os2) {
-        g->default_advh = (USHORT) (os2->sTypoAscender - os2->sTypoDescender);
-        g->default_tsb = (SHORT) (g->default_advh - os2->sTypoAscender);
-        /*tex |dvipdfmx| does this elsewhere! */
-        fd_cur->font_dim[STEMV_CODE].val =
-            (os2->usWeightClass / 65) * (os2->usWeightClass / 65) + 50;
-    }
-    if (sfnt_find_table_pos(sfont, "vmtx") > 0) {
-        struct tt_vhea_table *vhea;
-        vhea = tt_read_vhea_table(sfont);
-        sfnt_locate_table(sfont, "vmtx");
-        vmtx = tt_read_longMetrics(sfont, maxp->numGlyphs, vhea->numOfLongVerMetrics);
-        RELEASE(vhea);
-    } else {
-        vmtx = NULL;
-    }
-    sfnt_locate_table(sfont, "loca");
-    location = NEW(maxp->numGlyphs + 1, ULONG);
-    if (head->indexToLocFormat == 0) {
-        for (i = 0; i <= maxp->numGlyphs; i++)
-            location[i] = 2 * ((ULONG) sfnt_get_ushort(sfont));
-    } else if (head->indexToLocFormat == 1) {
-        for (i = 0; i <= maxp->numGlyphs; i++)
-            location[i] = sfnt_get_ulong(sfont);
-    } else {
-        normal_error("ttf","unknown IndexToLocFormat");
-    }
-    w_stat = NEW(g->emsize + 2, USHORT);
-    memset(w_stat, 0, (size_t) (sizeof(USHORT) * ((long unsigned) g->emsize + 2)));
-    offset = sfnt_locate_table(sfont, "glyf");
-    /*tex
-
-        The |num_glyphs| may grow when composite glyph is found. A component of
-        glyph refered by a composite glyph is appended to |used_glyphs| if it is
-        not already registered in |used_glyphs|. Glyph programs of composite
-        glyphs are modified so that it correctly refer to new gid of their
-        components.
-    */
-    for (i = 0; i < NUM_GLYPH_LIMIT; i++) {
-        USHORT gid;
-        ULONG loc, len;
-        BYTE *p, *endptr;
-        SHORT number_of_contours;
-        if (i >= g->num_glyphs)
-            break;
-        gid = g->gd[i].ogid;
-        if (gid >= maxp->numGlyphs)
-            formatted_error("ttf","invalid glyph index (gid %u)", gid);
-        loc = location[gid];
-        len = location[gid + 1] - loc;
-        g->gd[i].advw = hmtx[gid].advance;
-        g->gd[i].lsb = hmtx[gid].sideBearing;
-        if (vmtx) {
-            g->gd[i].advh = vmtx[gid].advance;
-            g->gd[i].tsb = vmtx[gid].sideBearing;
-        } else {
-            g->gd[i].advh = g->default_advh;
-            g->gd[i].tsb = g->default_tsb;
-        }
-        g->gd[i].length = len;
-        g->gd[i].data = NULL;
-        if (g->gd[i].advw <= g->emsize) {
-            w_stat[g->gd[i].advw]++;
-        } else {
-            /*tex It's larger than em. */
-            w_stat[g->emsize + 1]++;
-        }
-
-        if (len == 0) {
-            /*tex Does not contain any data. */
-            continue;
-        } else if (len < 10) {
-            formatted_error("ttf","invalid glyph data (gid %u)", gid);
-        }
-        /*tex There is no real need for this. */
-        g->gd[i].data = p = NEW(len, BYTE);
-        endptr = p + len;
-        sfnt_seek_set(sfont, (long) (offset + loc));
-        number_of_contours = sfnt_get_short(sfont);
-        p += sfnt_put_short(p, number_of_contours);
-        /* BoundingBox: FWord x 4 */
-        g->gd[i].llx = sfnt_get_short(sfont);
-        g->gd[i].lly = sfnt_get_short(sfont);
-        g->gd[i].urx = sfnt_get_short(sfont);
-        g->gd[i].ury = sfnt_get_short(sfont);
-        if (!vmtx) {
-            /*tex A fix: |vertOriginY == sTypeAscender| */
-            g->gd[i].tsb = (SHORT) (g->default_advh - g->default_tsb - g->gd[i].ury);
-        }
-        p += sfnt_put_short(p, g->gd[i].llx);
-        p += sfnt_put_short(p, g->gd[i].lly);
-        p += sfnt_put_short(p, g->gd[i].urx);
-        p += sfnt_put_short(p, g->gd[i].ury);
-        /*tex Read evrything else. */
-        sfnt_read(p, (int) len - 10, sfont);
-        /*tex Fix GIDs of composite glyphs. */
-        if (number_of_contours < 0) {
-            USHORT flags, cgid, new_gid;
-            do {
-                if (p >= endptr)
-                    formatted_error("ttf","invalid glyph data (gid %u): %u bytes", gid, (unsigned int) len);
-                flags = (USHORT) (((*p) << 8) | *(p + 1));
-                p += 2;
-                cgid = (USHORT) (((*p) << 8) | *(p + 1));
-                if (cgid >= maxp->numGlyphs) {
-                    formatted_error("ttf","invalid gid (%u > %u) in composite glyph %u", cgid, maxp->numGlyphs, gid);
-                }
-                new_gid = tt_find_glyph(g, cgid);
-                if (new_gid == 0) {
-                    new_gid = tt_add_glyph(g, cgid, find_empty_slot(g));
-                }
-                p += sfnt_put_ushort(p, new_gid);
-                /*tex Just skip remaining part. */
-                p += (flags & ARG_1_AND_2_ARE_WORDS) ? 4 : 2;
-                if (flags & WE_HAVE_A_SCALE) {
-                    /*tex |F2Dot14| */
-                    p += 2;
-                } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
-                    /*tex two times |F2Dot14| */
-                    p += 4;
-                } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
-                    /*tex four times |F2Dot14| */
-                    p += 8;
-                }
-            } while (flags & MORE_COMPONENTS);
-            /*tex
-
-                TrueType instructions comes here. The call pattern is:
-
-                \starttyping
-                |length_of_instruction| (|ushort|)
-                instruction (|byte * length_of_instruction|)
-                \stoptyping
-
-             */
-        }
-    }
-    RELEASE(location);
-    RELEASE(hmtx);
-    if (vmtx) {
-        RELEASE(vmtx);
-    }
-    {
-        int max_count = -1;
-        g->dw = g->gd[0].advw;
-        for (i = 0; i < g->emsize + 1; i++) {
-            if (w_stat[i] > max_count) {
-                max_count = w_stat[i];
-                g->dw = (USHORT) i;
-            }
-        }
-    }
-    RELEASE(w_stat);
-    qsort(g->gd, g->num_glyphs, sizeof(struct tt_glyph_desc), glyf_cmp);
-    {
-        USHORT prev, last_advw;
-        char *p, *q;
-        int padlen, num_hm_known;
-        glyf_table_size = 0UL;
-        num_hm_known = 0;
-        last_advw = g->gd[g->num_glyphs - 1].advw;
-        for (i = g->num_glyphs - 1; i >= 0; i--) {
-            padlen =
-                (int) ((g->gd[i].length % 4) ? (4 - (g->gd[i].length % 4)) : 0);
-            glyf_table_size += (ULONG) (g->gd[i].length + (ULONG) padlen);
-            if (!num_hm_known && last_advw != g->gd[i].advw) {
-                hhea->numberOfHMetrics = (USHORT) (g->gd[i].gid + 2);
-                num_hm_known = 1;
-            }
-        }
-        /*tex All advance widths are the same. */
-        if (!num_hm_known) {
-            hhea->numberOfHMetrics = 1;
-        }
-        hmtx_table_size = (ULONG) (hhea->numberOfHMetrics * 2 + (g->last_gid + 1) * 2);
-        /*tex
-
-            Choosing short format does not always give good result when
-            compressed. Sometimes increases size.
-
-        */
-        if (glyf_table_size < 0x20000UL) {
-            head->indexToLocFormat = 0;
-            loca_table_size = (ULONG) ((g->last_gid + 2) * 2);
-        } else {
-            head->indexToLocFormat = 1;
-            loca_table_size = (ULONG) ((g->last_gid + 2) * 4);
-        }
-        hmtx_table_data = p = NEW(hmtx_table_size, char);
-        loca_table_data = q = NEW(loca_table_size, char);
-        glyf_table_data = NEW(glyf_table_size, char);
-        glyf_table_used = 0;
-        offset = 0UL;
-        prev = 0;
-        for (i = 0; i < g->num_glyphs; i++) {
-            long gap, j;
-            gap = (long) g->gd[i].gid - prev - 1;
-            for (j = 1; j <= gap; j++) {
-                if (prev + j == hhea->numberOfHMetrics - 1) {
-                    p += sfnt_put_ushort(p, last_advw);
-                } else if (prev + j < hhea->numberOfHMetrics) {
-                    p += sfnt_put_ushort(p, 0);
-                }
-                p += sfnt_put_short(p, 0);
-                if (head->indexToLocFormat == 0) {
-                    q += sfnt_put_ushort(q, (USHORT) (offset / 2));
-                } else {
-                    q += sfnt_put_ulong(q, (LONG) offset);
-                }
-            }
-            if (g->gd[i].gid < hhea->numberOfHMetrics) {
-                p += sfnt_put_ushort(p, g->gd[i].advw);
-            }
-            p += sfnt_put_short(p, g->gd[i].lsb);
-            if (head->indexToLocFormat == 0) {
-                q += sfnt_put_ushort(q, (USHORT) (offset / 2));
-            } else {
-                q += sfnt_put_ulong(q, (LONG) offset);
-            }
-            if (callback_id > 0) {
-                lstring * result;
-                long size = 0;
-                run_callback(callback_id, "ddd->L", tex_font, g->gd[i].gid, streamprovider, &result);
-                padlen = (int) ((result->l % 4) ? (4 - (result->l % 4)) : 0);
-                size = (size_t) result->l + (ULONG) padlen;
-                if (glyf_table_used + size >= glyf_table_size) {
-                    glyf_table_size = glyf_table_size + 20 * size; /* just a guess */
-                    glyf_table_data = xrealloc(glyf_table_data, (unsigned)((unsigned)glyf_table_size*sizeof(char)));
-                }
-                glyf_table_used += size;
-                memset(glyf_table_data + offset, 0, (size_t) size);
-                memcpy(glyf_table_data + offset, (const char *) result->s, (size_t) result->l);
-                offset += size;
-                xfree(result);
-            } else {
-                padlen = (int) ((g->gd[i].length % 4) ? (4 - (g->gd[i].length % 4)) : 0);
-                memset(glyf_table_data + offset, 0, (size_t) (g->gd[i].length + (ULONG) padlen));
-                memcpy(glyf_table_data + offset, g->gd[i].data, g->gd[i].length);
-                offset += (g->gd[i].length + (ULONG) padlen);
-            }
-            prev = g->gd[i].gid;
-            RELEASE(g->gd[i].data);
-            /*tex We free data here since it consume much memory. */
-            g->gd[i].length = 0;
-            g->gd[i].data = NULL;
-        }
-        if (head->indexToLocFormat == 0) {
-            q += sfnt_put_ushort(q, (USHORT) (offset / 2));
-        } else {
-            q += sfnt_put_ulong(q, (LONG) offset);
-        }
-        sfnt_set_table(sfont, "hmtx", (char *) hmtx_table_data, hmtx_table_size);
-        sfnt_set_table(sfont, "loca", (char *) loca_table_data, loca_table_size);
-        if (callback_id > 0) {
-            glyf_table_size = glyf_table_used;
-        }
-        sfnt_set_table(sfont, "glyf", (char *) glyf_table_data, glyf_table_size);
-    }
-    head->checkSumAdjustment = 0;
-    maxp->numGlyphs = (USHORT) (g->last_gid + 1);
-    sfnt_set_table(sfont, "maxp", tt_pack_maxp_table(maxp), TT_MAXP_TABLE_SIZE);
-    sfnt_set_table(sfont, "hhea", tt_pack_hhea_table(hhea), TT_HHEA_TABLE_SIZE);
-    sfnt_set_table(sfont, "head", tt_pack_head_table(head), TT_HEAD_TABLE_SIZE);
-    RELEASE(maxp);
-    RELEASE(hhea);
-    RELEASE(head);
-    if (os2) {
-        RELEASE(os2);
-    }
-    return 0;
-}
-
-int tt_get_metrics(sfnt * sfont, struct tt_glyphs *g)
-{
-    struct tt_head_table *head = NULL;
-    struct tt_hhea_table *hhea = NULL;
-    struct tt_maxp_table *maxp = NULL;
-    struct tt_longMetrics *hmtx, *vmtx = NULL;
-    struct tt_os2__table *os2;
-    ULONG *location, offset;
-    long i;
-    USHORT *w_stat;
-    if (sfont == NULL || sfont->buffer == NULL)
-        normal_error("ttf","file not opened");
-    if (sfont->type != SFNT_TYPE_TRUETYPE && sfont->type != SFNT_TYPE_TTC)
-        normal_error("ttf","invalid font type");
-    head = tt_read_head_table(sfont);
-    hhea = tt_read_hhea_table(sfont);
-    maxp = tt_read_maxp_table(sfont);
-    if (hhea->metricDataFormat != 0)
-        normal_error("ttf","unknown metricDataFormat");
-    g->emsize = head->unitsPerEm;
-    sfnt_locate_table(sfont, "hmtx");
-    hmtx = tt_read_longMetrics(sfont, maxp->numGlyphs, hhea->numberOfHMetrics);
-    os2 = tt_read_os2__table(sfont);
-    g->default_advh = (USHORT) (os2->sTypoAscender - os2->sTypoDescender);
-    g->default_tsb = (SHORT) (g->default_advh - os2->sTypoAscender);
-    if (sfnt_find_table_pos(sfont, "vmtx") > 0) {
-        struct tt_vhea_table *vhea;
-        vhea = tt_read_vhea_table(sfont);
-        sfnt_locate_table(sfont, "vmtx");
-        vmtx = tt_read_longMetrics(sfont, maxp->numGlyphs, vhea->numOfLongVerMetrics);
-        RELEASE(vhea);
-    } else {
-        vmtx = NULL;
-    }
-    sfnt_locate_table(sfont, "loca");
-    location = NEW(maxp->numGlyphs + 1, ULONG);
-    if (head->indexToLocFormat == 0) {
-        for (i = 0; i <= maxp->numGlyphs; i++)
-            location[i] = 2 * ((ULONG) sfnt_get_ushort(sfont));
-    } else if (head->indexToLocFormat == 1) {
-        for (i = 0; i <= maxp->numGlyphs; i++)
-            location[i] = sfnt_get_ulong(sfont);
-    } else {
-        normal_error("ttf","inknown IndexToLocFormat");
-    }
-    w_stat = NEW(g->emsize + 2, USHORT);
-    memset(w_stat, 0, (size_t) ((int) sizeof(USHORT) * (g->emsize + 2)));
-    /*tex Read glyf table. */
-    offset = sfnt_locate_table(sfont, "glyf");
-    for (i = 0; i < g->num_glyphs; i++) {
-        USHORT gid;
-        ULONG loc, len;
-        gid = g->gd[i].ogid;
-        if (gid >= maxp->numGlyphs)
-            formatted_error("ttf","invalid glyph index (gid %u)", gid);
-        loc = location[gid];
-        len = location[gid + 1] - loc;
-        g->gd[i].advw = hmtx[gid].advance;
-        g->gd[i].lsb = hmtx[gid].sideBearing;
-        if (vmtx) {
-            g->gd[i].advh = vmtx[gid].advance;
-            g->gd[i].tsb = vmtx[gid].sideBearing;
-        } else {
-            g->gd[i].advh = g->default_advh;
-            g->gd[i].tsb = g->default_tsb;
-        }
-        g->gd[i].length = len;
-        g->gd[i].data = NULL;
-        if (g->gd[i].advw <= g->emsize) {
-            w_stat[g->gd[i].advw]++;
-        } else {
-            /*tex It's larger than em: */
-            w_stat[g->emsize + 1]++;
-        }
-        if (len == 0) {
-            /*tex No data. */
-            continue;
-        } else if (len < 10) {
-            formatted_error("ttf","invalid glyph data (gid %u)", gid);
-        }
-        sfnt_seek_set(sfont, (long) (offset + loc));
-        /*tex Skip the number of contours */
-        (void)sfnt_get_short(sfont);
-        /*tex Fetch the BoundingBox. */
-        g->gd[i].llx = sfnt_get_short(sfont);
-        g->gd[i].lly = sfnt_get_short(sfont);
-        g->gd[i].urx = sfnt_get_short(sfont);
-        g->gd[i].ury = sfnt_get_short(sfont);
-        if (!vmtx) {
-            /*tex We fix |vertOriginY == sTypeAscender|. */
-            g->gd[i].tsb = (SHORT) (g->default_advh - g->default_tsb - g->gd[i].ury);
-        }
-    }
-    RELEASE(location);
-    RELEASE(hmtx);
-    RELEASE(maxp);
-    RELEASE(hhea);
-    RELEASE(head);
-    RELEASE(os2);
-    if (vmtx) {
-        RELEASE(vmtx);
-    }
-    {
-        int max_count = -1;
-        g->dw = g->gd[0].advw;
-        for (i = 0; i < g->emsize + 1; i++) {
-            if (w_stat[i] > max_count) {
-                max_count = w_stat[i];
-                g->dw = (USHORT) i;
-            }
-        }
-    }
-    RELEASE(w_stat);
-    return 0;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/tt_table.w
@@ -0,0 +1,460 @@
+% tt_table.w
+%
+% Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata,
+% the dvipdfmx project team <dvipdfmx@@project.ktug.or.kr>
+% 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 <stdio.h>
+#include "font/sfnt.h"
+#include "font/tt_table.h"
+
+@ tables contains information refered by other tables
+
+  |maxp->numGlyphs, etc --> loca, etc|
+
+  |hhea->numberOfHMetrics --> hmtx|
+
+  |head->indexToLocFormat --> loca|
+
+  |head->glyphDataFormat --> glyf|
+
+@c
+char *tt_pack_head_table(struct tt_head_table *table)
+{
+    int i;
+    char *p, *data;
+
+    if (table == NULL)
+        normal_error("ttf","passed NULL pointer");
+
+    p = data = NEW(TT_HEAD_TABLE_SIZE, char);
+    p += sfnt_put_ulong(p, (LONG) table->version);
+    p += sfnt_put_ulong(p, (LONG) table->fontRevision);
+    p += sfnt_put_ulong(p, (LONG) table->checkSumAdjustment);
+    p += sfnt_put_ulong(p, (LONG) table->magicNumber);
+    p += sfnt_put_ushort(p, table->flags);
+    p += sfnt_put_ushort(p, table->unitsPerEm);
+    for (i = 0; i < 8; i++) {
+        *(p++) = (char) (table->created)[i];
+    }
+    for (i = 0; i < 8; i++) {
+        *(p++) = (char) (table->modified)[i];
+    }
+    p += sfnt_put_short(p, table->xMin);
+    p += sfnt_put_short(p, table->yMin);
+    p += sfnt_put_short(p, table->xMax);
+    p += sfnt_put_short(p, table->yMax);
+    p += sfnt_put_ushort(p, table->macStyle);
+    p += sfnt_put_ushort(p, table->lowestRecPPEM);
+    p += sfnt_put_short(p, table->fontDirectionHint);
+    p += sfnt_put_short(p, table->indexToLocFormat);
+    p += sfnt_put_short(p, table->glyphDataFormat);
+
+    return data;
+}
+
+struct tt_head_table *tt_read_head_table(sfnt * sfont)
+{
+    int i;
+    struct tt_head_table *table = NULL;
+
+    table = NEW(1, struct tt_head_table);
+
+    sfnt_locate_table(sfont, "head");
+
+    table->version = sfnt_get_ulong(sfont);
+    table->fontRevision = sfnt_get_ulong(sfont);
+    table->checkSumAdjustment = sfnt_get_ulong(sfont);
+    table->magicNumber = sfnt_get_ulong(sfont);
+    table->flags = sfnt_get_ushort(sfont);
+    table->unitsPerEm = sfnt_get_ushort(sfont);
+    for (i = 0; i < 8; i++) {
+        (table->created)[i] = sfnt_get_byte(sfont);
+    }
+    for (i = 0; i < 8; i++) {
+        (table->modified)[i] = sfnt_get_byte(sfont);
+    }
+    table->xMin = sfnt_get_short(sfont);
+    table->yMin = sfnt_get_short(sfont);
+    table->xMax = sfnt_get_short(sfont);
+    table->yMax = sfnt_get_short(sfont);
+    table->macStyle = (USHORT) sfnt_get_short(sfont);
+    table->lowestRecPPEM = (USHORT) sfnt_get_short(sfont);
+    table->fontDirectionHint = sfnt_get_short(sfont);
+    table->indexToLocFormat = sfnt_get_short(sfont);
+    table->glyphDataFormat = sfnt_get_short(sfont);
+
+    return table;
+}
+
+char *tt_pack_maxp_table(struct tt_maxp_table *table)
+{
+    char *p, *data;
+
+    p = data = NEW(TT_MAXP_TABLE_SIZE, char);
+    p += sfnt_put_ulong(p, (LONG) table->version);
+    p += sfnt_put_ushort(p, table->numGlyphs);
+    p += sfnt_put_ushort(p, table->maxPoints);
+    p += sfnt_put_ushort(p, table->maxContours);
+    p += sfnt_put_ushort(p, table->maxComponentPoints);
+    p += sfnt_put_ushort(p, table->maxComponentContours);
+    p += sfnt_put_ushort(p, table->maxZones);
+    p += sfnt_put_ushort(p, table->maxTwilightPoints);
+    p += sfnt_put_ushort(p, table->maxStorage);
+    p += sfnt_put_ushort(p, table->maxFunctionDefs);
+    p += sfnt_put_ushort(p, table->maxInstructionDefs);
+    p += sfnt_put_ushort(p, table->maxStackElements);
+    p += sfnt_put_ushort(p, table->maxSizeOfInstructions);
+    p += sfnt_put_ushort(p, table->maxComponentElements);
+    p += sfnt_put_ushort(p, table->maxComponentDepth);
+
+    return data;
+}
+
+struct tt_maxp_table *tt_read_maxp_table(sfnt * sfont)
+{
+    struct tt_maxp_table *table = NULL;
+
+    table = NEW(1, struct tt_maxp_table);
+
+    sfnt_locate_table(sfont, "maxp");
+    table->version = sfnt_get_ulong(sfont);
+    table->numGlyphs = sfnt_get_ushort(sfont);
+    table->maxPoints = sfnt_get_ushort(sfont);
+    table->maxContours = sfnt_get_ushort(sfont);
+    table->maxComponentPoints = sfnt_get_ushort(sfont);
+    table->maxComponentContours = sfnt_get_ushort(sfont);
+    table->maxZones = sfnt_get_ushort(sfont);
+    table->maxTwilightPoints = sfnt_get_ushort(sfont);
+    table->maxStorage = sfnt_get_ushort(sfont);
+    table->maxFunctionDefs = sfnt_get_ushort(sfont);
+    table->maxInstructionDefs = sfnt_get_ushort(sfont);
+    table->maxStackElements = sfnt_get_ushort(sfont);
+    table->maxSizeOfInstructions = sfnt_get_ushort(sfont);
+    table->maxComponentElements = sfnt_get_ushort(sfont);
+    table->maxComponentDepth = sfnt_get_ushort(sfont);
+
+    return table;
+}
+
+char *tt_pack_hhea_table(struct tt_hhea_table *table)
+{
+    int i;
+    char *p, *data;
+
+    p = data = NEW(TT_HHEA_TABLE_SIZE, char);
+    p += sfnt_put_ulong(p, (LONG) table->version);
+    p += sfnt_put_short(p, table->Ascender);
+    p += sfnt_put_short(p, table->Descender);
+    p += sfnt_put_short(p, table->LineGap);
+    p += sfnt_put_ushort(p, table->advanceWidthMax);
+    p += sfnt_put_short(p, table->minLeftSideBearing);
+    p += sfnt_put_short(p, table->minRightSideBearing);
+    p += sfnt_put_short(p, table->xMaxExtent);
+    p += sfnt_put_short(p, table->caretSlopeRise);
+    p += sfnt_put_short(p, table->caretSlopeRun);
+    for (i = 0; i < 5; i++) {
+        p += sfnt_put_short(p, table->reserved[i]);
+    }
+    p += sfnt_put_short(p, table->metricDataFormat);
+    p += sfnt_put_ushort(p, table->numberOfHMetrics);
+
+    return data;
+}
+
+struct tt_hhea_table *tt_read_hhea_table(sfnt * sfont)
+{
+    int i;
+    struct tt_hhea_table *table = NULL;
+
+    table = NEW(1, struct tt_hhea_table);
+
+    sfnt_locate_table(sfont, "hhea");
+    table->version = sfnt_get_ulong(sfont);
+    table->Ascender = sfnt_get_short(sfont);
+    table->Descender = sfnt_get_short(sfont);
+    table->LineGap = sfnt_get_short(sfont);
+    table->advanceWidthMax = sfnt_get_ushort(sfont);
+    table->minLeftSideBearing = sfnt_get_short(sfont);
+    table->minRightSideBearing = sfnt_get_short(sfont);
+    table->xMaxExtent = sfnt_get_short(sfont);
+    table->caretSlopeRise = sfnt_get_short(sfont);
+    table->caretSlopeRun = sfnt_get_short(sfont);
+    for (i = 0; i < 5; i++) {
+        table->reserved[i] = sfnt_get_short(sfont);
+    }
+    table->metricDataFormat = sfnt_get_short(sfont);
+    if (table->metricDataFormat != 0)
+        normal_error("ttf","unknown metricDaraFormat");
+    table->numberOfHMetrics = sfnt_get_ushort(sfont);
+
+    return table;
+}
+
+@ vhea
+@c
+char *tt_pack_vhea_table(struct tt_vhea_table *table)
+{
+    int i;
+    char *p, *data;
+
+    p = data = NEW(TT_VHEA_TABLE_SIZE, char);
+    p += sfnt_put_ulong(p, (LONG) table->version);
+    p += sfnt_put_short(p, table->vertTypoAscender);
+    p += sfnt_put_short(p, table->vertTypoDescender);
+    p += sfnt_put_short(p, table->vertTypoLineGap);
+    p += sfnt_put_short(p, table->advanceHeightMax);    /* ushort ? */
+    p += sfnt_put_short(p, table->minTopSideBearing);
+    p += sfnt_put_short(p, table->minBottomSideBearing);
+    p += sfnt_put_short(p, table->yMaxExtent);
+    p += sfnt_put_short(p, table->caretSlopeRise);
+    p += sfnt_put_short(p, table->caretSlopeRun);
+    p += sfnt_put_short(p, table->caretOffset);
+    for (i = 0; i < 5; i++) {
+        p += sfnt_put_short(p, table->reserved[i]);
+    }
+    p += sfnt_put_ushort(p, table->numOfLongVerMetrics);
+
+    return data;
+}
+
+struct tt_vhea_table *tt_read_vhea_table(sfnt * sfont)
+{
+    int i;
+    struct tt_vhea_table *table = NULL;
+
+    table = NEW(1, struct tt_vhea_table);
+
+    sfnt_locate_table(sfont, "vhea");
+    table->version = sfnt_get_ulong(sfont);
+    table->vertTypoAscender = sfnt_get_short(sfont);
+    table->vertTypoDescender = sfnt_get_short(sfont);
+    table->vertTypoLineGap = sfnt_get_short(sfont);
+    table->advanceHeightMax = sfnt_get_short(sfont);    /* ushort ? */
+    table->minTopSideBearing = sfnt_get_short(sfont);
+    table->minBottomSideBearing = sfnt_get_short(sfont);
+    table->yMaxExtent = sfnt_get_short(sfont);
+    table->caretSlopeRise = sfnt_get_short(sfont);
+    table->caretSlopeRun = sfnt_get_short(sfont);
+    table->caretOffset = sfnt_get_short(sfont);
+    for (i = 0; i < 5; i++) {
+        (table->reserved)[i] = sfnt_get_short(sfont);
+    }
+    table->numOfLongVerMetrics = sfnt_get_ushort(sfont);
+
+    return table;
+}
+
+
+struct tt_VORG_table *tt_read_VORG_table(sfnt * sfont)
+{
+    struct tt_VORG_table *vorg;
+    ULONG offset;
+    USHORT i;
+
+    offset = sfnt_find_table_pos(sfont, "VORG");
+
+    if (offset > 0) {
+        vorg = NEW(1, struct tt_VORG_table);
+
+        sfnt_locate_table(sfont, "VORG");
+        if (sfnt_get_ushort(sfont) != 1 || sfnt_get_ushort(sfont) != 0)
+            normal_error("ttf","unsupported VORG version");
+
+        vorg->defaultVertOriginY = sfnt_get_short(sfont);
+        vorg->numVertOriginYMetrics = sfnt_get_ushort(sfont);
+        vorg->vertOriginYMetrics = NEW(vorg->numVertOriginYMetrics,
+                                       struct tt_vertOriginYMetrics);
+        /*
+         * The vertOriginYMetrics array must be sorted in increasing
+         * glyphIndex order.
+         */
+        for (i = 0; i < vorg->numVertOriginYMetrics; i++) {
+            vorg->vertOriginYMetrics[i].glyphIndex = sfnt_get_ushort(sfont);
+            vorg->vertOriginYMetrics[i].vertOriginY = sfnt_get_short(sfont);
+        }
+    } else {
+        vorg = NULL;
+    }
+
+    return vorg;
+}
+
+
+@ hmtx and vmtx
+
+Reading/writing hmtx and vmtx depend on other tables, maxp and hhea/vhea.
+
+@c
+struct tt_longMetrics *tt_read_longMetrics(sfnt * sfont, USHORT numGlyphs,
+                                           USHORT numLongMetrics)
+{
+    struct tt_longMetrics *m;
+    USHORT gid, last_adv = 0;
+
+    m = NEW(numGlyphs, struct tt_longMetrics);
+    for (gid = 0; gid < numGlyphs; gid++) {
+        if (gid < numLongMetrics)
+            last_adv = sfnt_get_ushort(sfont);
+        m[gid].sideBearing = sfnt_get_short(sfont);
+        m[gid].advance = last_adv;
+    }
+
+    return m;
+}
+
+@ OS/2 table
+
+this table may not exist
+@c
+struct tt_os2__table *tt_read_os2__table(sfnt * sfont)
+{
+    struct tt_os2__table *table = NULL;
+    int i;
+
+    if (sfnt_find_table_pos(sfont, "OS/2") == 0)
+        return NULL;
+
+    sfnt_locate_table(sfont, "OS/2");
+
+    table = NEW(1, struct tt_os2__table);
+
+    table->version = sfnt_get_ushort(sfont);
+    table->xAvgCharWidth = sfnt_get_short(sfont);
+    table->usWeightClass = sfnt_get_ushort(sfont);
+    table->usWidthClass = sfnt_get_ushort(sfont);
+    table->fsType = sfnt_get_short(sfont);
+    table->ySubscriptXSize = sfnt_get_short(sfont);
+    table->ySubscriptYSize = sfnt_get_short(sfont);
+    table->ySubscriptXOffset = sfnt_get_short(sfont);
+    table->ySubscriptYOffset = sfnt_get_short(sfont);
+    table->ySuperscriptXSize = sfnt_get_short(sfont);
+    table->ySuperscriptYSize = sfnt_get_short(sfont);
+    table->ySuperscriptXOffset = sfnt_get_short(sfont);
+    table->ySuperscriptYOffset = sfnt_get_short(sfont);
+    table->yStrikeoutSize = sfnt_get_short(sfont);
+    table->yStrikeoutPosition = sfnt_get_short(sfont);
+    table->sFamilyClass = sfnt_get_short(sfont);
+    for (i = 0; i < 10; i++) {
+        table->panose[i] = sfnt_get_byte(sfont);
+    }
+    table->ulUnicodeRange1 = sfnt_get_ulong(sfont);
+    table->ulUnicodeRange2 = sfnt_get_ulong(sfont);
+    table->ulUnicodeRange3 = sfnt_get_ulong(sfont);
+    table->ulUnicodeRange4 = sfnt_get_ulong(sfont);
+    for (i = 0; i < 4; i++) {
+        table->achVendID[i] = sfnt_get_char(sfont);
+    }
+    table->fsSelection = sfnt_get_ushort(sfont);
+    table->usFirstCharIndex = sfnt_get_ushort(sfont);
+    table->usLastCharIndex = sfnt_get_ushort(sfont);
+    table->sTypoAscender = sfnt_get_short(sfont);
+    table->sTypoDescender = sfnt_get_short(sfont);
+    table->sTypoLineGap = sfnt_get_short(sfont);
+    table->usWinAscent = sfnt_get_ushort(sfont);
+    table->usWinDescent = sfnt_get_ushort(sfont);
+    table->ulCodePageRange1 = sfnt_get_ulong(sfont);
+    table->ulCodePageRange2 = sfnt_get_ulong(sfont);
+    if (table->version == 0x0002) {
+        table->sxHeight = sfnt_get_short(sfont);
+        table->sCapHeight = sfnt_get_short(sfont);
+        table->usDefaultChar = sfnt_get_ushort(sfont);
+        table->usBreakChar = sfnt_get_ushort(sfont);
+        table->usMaxContext = sfnt_get_ushort(sfont);
+    }
+
+    return table;
+}
+
+USHORT
+tt_get_name(sfnt * sfont, char *dest, USHORT destlen,
+            USHORT plat_id, USHORT enco_id, USHORT lang_id, USHORT name_id)
+{
+    USHORT length = 0;
+    USHORT num_names, string_offset;
+    ULONG name_offset;
+    int i;
+
+    name_offset = sfnt_locate_table(sfont, "name");
+
+    if (sfnt_get_ushort(sfont))
+        normal_error("ttf","expecting zero");
+
+    num_names = sfnt_get_ushort(sfont);
+    string_offset = sfnt_get_ushort(sfont);
+    for (i = 0; i < num_names; i++) {
+        USHORT p_id, e_id, n_id, l_id;
+        USHORT offset;
+
+        p_id = sfnt_get_ushort(sfont);
+        e_id = sfnt_get_ushort(sfont);
+        l_id = sfnt_get_ushort(sfont);
+        n_id = sfnt_get_ushort(sfont);
+        length = sfnt_get_ushort(sfont);
+        offset = sfnt_get_ushort(sfont);
+        /* language ID value 0xffffu for `accept any language ID' */
+        if ((p_id == plat_id) && (e_id == enco_id) &&
+            (lang_id == 0xffffu || l_id == lang_id) && (n_id == name_id)) {
+            if (length > destlen - 1) {
+                normal_warning("ttf","truncating a very long name");
+                length = (USHORT) (destlen - 1);
+            }
+            sfnt_seek_set(sfont, (long) (name_offset + string_offset + offset));
+            sfnt_read((unsigned char *) dest, length, sfont);
+            dest[length] = '\0';
+            break;
+        }
+    }
+    if (i == num_names) {
+        length = 0;
+    }
+
+    return length;
+}
+
+USHORT tt_get_ps_fontname(sfnt * sfont, char *dest, USHORT destlen)
+{
+    USHORT namelen = 0;
+
+    /* First try Mac-Roman PS name and then Win-Unicode PS name */
+    if ((namelen = tt_get_name(sfont, dest, destlen, 1, 0, 0, 6)) != 0 ||
+        (namelen = tt_get_name(sfont, dest, destlen, 3, 1, 0x409u, 6)) != 0 ||
+        (namelen = tt_get_name(sfont, dest, destlen, 3, 5, 0x412u, 6)) != 0)
+        return namelen;
+
+    normal_warning("ttf","no valid PostScript name available");
+    /*
+       Wrokaround for some bad TTfonts:
+       Language ID value 0xffffu for `accept any language ID'
+     */
+    if ((namelen = tt_get_name(sfont, dest, destlen, 1, 0, 0xffffu, 6)) == 0) {
+        /*
+           Finally falling back to Mac Roman name field.
+           Warning: Some bad Japanese TTfonts using SJIS encoded string in the
+           Mac Roman name field.
+         */
+        namelen = tt_get_name(sfont, dest, destlen, 1, 0, 0, 1);
+    }
+
+    return namelen;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/tt_table.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
-
-Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project
-    team <dvipdfmx@project.ktug.or.kr>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <stdio.h>
-#include "font/sfnt.h"
-#include "font/tt_table.h"
-
-/*tex
-
-    Tables contain information referred by other tables.
-
-*/
-
-char *tt_pack_head_table(struct tt_head_table *table)
-{
-    int i;
-    char *p, *data;
-    if (table == NULL)
-        normal_error("ttf","passed NULL pointer");
-    p = data = NEW(TT_HEAD_TABLE_SIZE, char);
-    p += sfnt_put_ulong(p, (LONG) table->version);
-    p += sfnt_put_ulong(p, (LONG) table->fontRevision);
-    p += sfnt_put_ulong(p, (LONG) table->checkSumAdjustment);
-    p += sfnt_put_ulong(p, (LONG) table->magicNumber);
-    p += sfnt_put_ushort(p, table->flags);
-    p += sfnt_put_ushort(p, table->unitsPerEm);
-    for (i = 0; i < 8; i++) {
-        *(p++) = (char) (table->created)[i];
-    }
-    for (i = 0; i < 8; i++) {
-        *(p++) = (char) (table->modified)[i];
-    }
-    p += sfnt_put_short(p, table->xMin);
-    p += sfnt_put_short(p, table->yMin);
-    p += sfnt_put_short(p, table->xMax);
-    p += sfnt_put_short(p, table->yMax);
-    p += sfnt_put_ushort(p, table->macStyle);
-    p += sfnt_put_ushort(p, table->lowestRecPPEM);
-    p += sfnt_put_short(p, table->fontDirectionHint);
-    p += sfnt_put_short(p, table->indexToLocFormat);
-    p += sfnt_put_short(p, table->glyphDataFormat);
-    return data;
-}
-
-struct tt_head_table *tt_read_head_table(sfnt * sfont)
-{
-    int i;
-    struct tt_head_table *table = NULL;
-    table = NEW(1, struct tt_head_table);
-    sfnt_locate_table(sfont, "head");
-    table->version = sfnt_get_ulong(sfont);
-    table->fontRevision = sfnt_get_ulong(sfont);
-    table->checkSumAdjustment = sfnt_get_ulong(sfont);
-    table->magicNumber = sfnt_get_ulong(sfont);
-    table->flags = sfnt_get_ushort(sfont);
-    table->unitsPerEm = sfnt_get_ushort(sfont);
-    for (i = 0; i < 8; i++) {
-        (table->created)[i] = sfnt_get_byte(sfont);
-    }
-    for (i = 0; i < 8; i++) {
-        (table->modified)[i] = sfnt_get_byte(sfont);
-    }
-    table->xMin = sfnt_get_short(sfont);
-    table->yMin = sfnt_get_short(sfont);
-    table->xMax = sfnt_get_short(sfont);
-    table->yMax = sfnt_get_short(sfont);
-    table->macStyle = (USHORT) sfnt_get_short(sfont);
-    table->lowestRecPPEM = (USHORT) sfnt_get_short(sfont);
-    table->fontDirectionHint = sfnt_get_short(sfont);
-    table->indexToLocFormat = sfnt_get_short(sfont);
-    table->glyphDataFormat = sfnt_get_short(sfont);
-    return table;
-}
-
-char *tt_pack_maxp_table(struct tt_maxp_table *table)
-{
-    char *p, *data;
-    p = data = NEW(TT_MAXP_TABLE_SIZE, char);
-    p += sfnt_put_ulong(p, (LONG) table->version);
-    p += sfnt_put_ushort(p, table->numGlyphs);
-    p += sfnt_put_ushort(p, table->maxPoints);
-    p += sfnt_put_ushort(p, table->maxContours);
-    p += sfnt_put_ushort(p, table->maxComponentPoints);
-    p += sfnt_put_ushort(p, table->maxComponentContours);
-    p += sfnt_put_ushort(p, table->maxZones);
-    p += sfnt_put_ushort(p, table->maxTwilightPoints);
-    p += sfnt_put_ushort(p, table->maxStorage);
-    p += sfnt_put_ushort(p, table->maxFunctionDefs);
-    p += sfnt_put_ushort(p, table->maxInstructionDefs);
-    p += sfnt_put_ushort(p, table->maxStackElements);
-    p += sfnt_put_ushort(p, table->maxSizeOfInstructions);
-    p += sfnt_put_ushort(p, table->maxComponentElements);
-    p += sfnt_put_ushort(p, table->maxComponentDepth);
-    return data;
-}
-
-struct tt_maxp_table *tt_read_maxp_table(sfnt * sfont)
-{
-    struct tt_maxp_table *table = NULL;
-    table = NEW(1, struct tt_maxp_table);
-    sfnt_locate_table(sfont, "maxp");
-    table->version = sfnt_get_ulong(sfont);
-    table->numGlyphs = sfnt_get_ushort(sfont);
-    table->maxPoints = sfnt_get_ushort(sfont);
-    table->maxContours = sfnt_get_ushort(sfont);
-    table->maxComponentPoints = sfnt_get_ushort(sfont);
-    table->maxComponentContours = sfnt_get_ushort(sfont);
-    table->maxZones = sfnt_get_ushort(sfont);
-    table->maxTwilightPoints = sfnt_get_ushort(sfont);
-    table->maxStorage = sfnt_get_ushort(sfont);
-    table->maxFunctionDefs = sfnt_get_ushort(sfont);
-    table->maxInstructionDefs = sfnt_get_ushort(sfont);
-    table->maxStackElements = sfnt_get_ushort(sfont);
-    table->maxSizeOfInstructions = sfnt_get_ushort(sfont);
-    table->maxComponentElements = sfnt_get_ushort(sfont);
-    table->maxComponentDepth = sfnt_get_ushort(sfont);
-    return table;
-}
-
-char *tt_pack_hhea_table(struct tt_hhea_table *table)
-{
-    int i;
-    char *p, *data;
-    p = data = NEW(TT_HHEA_TABLE_SIZE, char);
-    p += sfnt_put_ulong(p, (LONG) table->version);
-    p += sfnt_put_short(p, table->Ascender);
-    p += sfnt_put_short(p, table->Descender);
-    p += sfnt_put_short(p, table->LineGap);
-    p += sfnt_put_ushort(p, table->advanceWidthMax);
-    p += sfnt_put_short(p, table->minLeftSideBearing);
-    p += sfnt_put_short(p, table->minRightSideBearing);
-    p += sfnt_put_short(p, table->xMaxExtent);
-    p += sfnt_put_short(p, table->caretSlopeRise);
-    p += sfnt_put_short(p, table->caretSlopeRun);
-    for (i = 0; i < 5; i++) {
-        p += sfnt_put_short(p, table->reserved[i]);
-    }
-    p += sfnt_put_short(p, table->metricDataFormat);
-    p += sfnt_put_ushort(p, table->numberOfHMetrics);
-    return data;
-}
-
-struct tt_hhea_table *tt_read_hhea_table(sfnt * sfont)
-{
-    int i;
-    struct tt_hhea_table *table = NULL;
-    table = NEW(1, struct tt_hhea_table);
-    sfnt_locate_table(sfont, "hhea");
-    table->version = sfnt_get_ulong(sfont);
-    table->Ascender = sfnt_get_short(sfont);
-    table->Descender = sfnt_get_short(sfont);
-    table->LineGap = sfnt_get_short(sfont);
-    table->advanceWidthMax = sfnt_get_ushort(sfont);
-    table->minLeftSideBearing = sfnt_get_short(sfont);
-    table->minRightSideBearing = sfnt_get_short(sfont);
-    table->xMaxExtent = sfnt_get_short(sfont);
-    table->caretSlopeRise = sfnt_get_short(sfont);
-    table->caretSlopeRun = sfnt_get_short(sfont);
-    for (i = 0; i < 5; i++) {
-        table->reserved[i] = sfnt_get_short(sfont);
-    }
-    table->metricDataFormat = sfnt_get_short(sfont);
-    if (table->metricDataFormat != 0)
-        normal_error("ttf","unknown metricDaraFormat");
-    table->numberOfHMetrics = sfnt_get_ushort(sfont);
-    return table;
-}
-
-char *tt_pack_vhea_table(struct tt_vhea_table *table)
-{
-    int i;
-    char *p, *data;
-    p = data = NEW(TT_VHEA_TABLE_SIZE, char);
-    p += sfnt_put_ulong(p, (LONG) table->version);
-    p += sfnt_put_short(p, table->vertTypoAscender);
-    p += sfnt_put_short(p, table->vertTypoDescender);
-    p += sfnt_put_short(p, table->vertTypoLineGap);
-    p += sfnt_put_short(p, table->advanceHeightMax);
-    p += sfnt_put_short(p, table->minTopSideBearing);
-    p += sfnt_put_short(p, table->minBottomSideBearing);
-    p += sfnt_put_short(p, table->yMaxExtent);
-    p += sfnt_put_short(p, table->caretSlopeRise);
-    p += sfnt_put_short(p, table->caretSlopeRun);
-    p += sfnt_put_short(p, table->caretOffset);
-    for (i = 0; i < 5; i++) {
-        p += sfnt_put_short(p, table->reserved[i]);
-    }
-    p += sfnt_put_ushort(p, table->numOfLongVerMetrics);
-    return data;
-}
-
-struct tt_vhea_table *tt_read_vhea_table(sfnt * sfont)
-{
-    int i;
-    struct tt_vhea_table *table = NULL;
-    table = NEW(1, struct tt_vhea_table);
-    sfnt_locate_table(sfont, "vhea");
-    table->version = sfnt_get_ulong(sfont);
-    table->vertTypoAscender = sfnt_get_short(sfont);
-    table->vertTypoDescender = sfnt_get_short(sfont);
-    table->vertTypoLineGap = sfnt_get_short(sfont);
-    table->advanceHeightMax = sfnt_get_short(sfont);
-    table->minTopSideBearing = sfnt_get_short(sfont);
-    table->minBottomSideBearing = sfnt_get_short(sfont);
-    table->yMaxExtent = sfnt_get_short(sfont);
-    table->caretSlopeRise = sfnt_get_short(sfont);
-    table->caretSlopeRun = sfnt_get_short(sfont);
-    table->caretOffset = sfnt_get_short(sfont);
-    for (i = 0; i < 5; i++) {
-        (table->reserved)[i] = sfnt_get_short(sfont);
-    }
-    table->numOfLongVerMetrics = sfnt_get_ushort(sfont);
-    return table;
-}
-
-struct tt_VORG_table *tt_read_VORG_table(sfnt * sfont)
-{
-    struct tt_VORG_table *vorg;
-    ULONG offset;
-    USHORT i;
-    offset = sfnt_find_table_pos(sfont, "VORG");
-    if (offset > 0) {
-        vorg = NEW(1, struct tt_VORG_table);
-        sfnt_locate_table(sfont, "VORG");
-        if (sfnt_get_ushort(sfont) != 1 || sfnt_get_ushort(sfont) != 0)
-            normal_error("ttf","unsupported VORG version");
-        vorg->defaultVertOriginY = sfnt_get_short(sfont);
-        vorg->numVertOriginYMetrics = sfnt_get_ushort(sfont);
-        vorg->vertOriginYMetrics = NEW(vorg->numVertOriginYMetrics, struct tt_vertOriginYMetrics);
-        /*tex
-            The |vertOriginYMetrics| array must be sorted in increasing |glyphIndex| order.
-        */
-        for (i = 0; i < vorg->numVertOriginYMetrics; i++) {
-            vorg->vertOriginYMetrics[i].glyphIndex = sfnt_get_ushort(sfont);
-            vorg->vertOriginYMetrics[i].vertOriginY = sfnt_get_short(sfont);
-        }
-    } else {
-        vorg = NULL;
-    }
-    return vorg;
-}
-
-/*tex
-
-    Reading and writing |hmtx| and |vmtx| depends on other tables, like |maxp|,
-    |hhea| and |vhea|.
-
-*/
-
-struct tt_longMetrics *tt_read_longMetrics(sfnt * sfont, USHORT numGlyphs, USHORT numLongMetrics)
-{
-    struct tt_longMetrics *m;
-    USHORT gid, last_adv = 0;
-    m = NEW(numGlyphs, struct tt_longMetrics);
-    for (gid = 0; gid < numGlyphs; gid++) {
-        if (gid < numLongMetrics)
-            last_adv = sfnt_get_ushort(sfont);
-        m[gid].sideBearing = sfnt_get_short(sfont);
-        m[gid].advance = last_adv;
-    }
-    return m;
-}
-
-/*tex
-
-    The |OS/2| table may not exist.
-
-*/
-
-struct tt_os2__table *tt_read_os2__table(sfnt * sfont)
-{
-    struct tt_os2__table *table = NULL;
-    int i;
-    if (sfnt_find_table_pos(sfont, "OS/2") == 0)
-        return NULL;
-    sfnt_locate_table(sfont, "OS/2");
-    table = NEW(1, struct tt_os2__table);
-    table->version = sfnt_get_ushort(sfont);
-    table->xAvgCharWidth = sfnt_get_short(sfont);
-    table->usWeightClass = sfnt_get_ushort(sfont);
-    table->usWidthClass = sfnt_get_ushort(sfont);
-    table->fsType = sfnt_get_short(sfont);
-    table->ySubscriptXSize = sfnt_get_short(sfont);
-    table->ySubscriptYSize = sfnt_get_short(sfont);
-    table->ySubscriptXOffset = sfnt_get_short(sfont);
-    table->ySubscriptYOffset = sfnt_get_short(sfont);
-    table->ySuperscriptXSize = sfnt_get_short(sfont);
-    table->ySuperscriptYSize = sfnt_get_short(sfont);
-    table->ySuperscriptXOffset = sfnt_get_short(sfont);
-    table->ySuperscriptYOffset = sfnt_get_short(sfont);
-    table->yStrikeoutSize = sfnt_get_short(sfont);
-    table->yStrikeoutPosition = sfnt_get_short(sfont);
-    table->sFamilyClass = sfnt_get_short(sfont);
-    for (i = 0; i < 10; i++) {
-        table->panose[i] = sfnt_get_byte(sfont);
-    }
-    table->ulUnicodeRange1 = sfnt_get_ulong(sfont);
-    table->ulUnicodeRange2 = sfnt_get_ulong(sfont);
-    table->ulUnicodeRange3 = sfnt_get_ulong(sfont);
-    table->ulUnicodeRange4 = sfnt_get_ulong(sfont);
-    for (i = 0; i < 4; i++) {
-        table->achVendID[i] = sfnt_get_char(sfont);
-    }
-    table->fsSelection = sfnt_get_ushort(sfont);
-    table->usFirstCharIndex = sfnt_get_ushort(sfont);
-    table->usLastCharIndex = sfnt_get_ushort(sfont);
-    table->sTypoAscender = sfnt_get_short(sfont);
-    table->sTypoDescender = sfnt_get_short(sfont);
-    table->sTypoLineGap = sfnt_get_short(sfont);
-    table->usWinAscent = sfnt_get_ushort(sfont);
-    table->usWinDescent = sfnt_get_ushort(sfont);
-    table->ulCodePageRange1 = sfnt_get_ulong(sfont);
-    table->ulCodePageRange2 = sfnt_get_ulong(sfont);
-    if (table->version == 0x0002) {
-        table->sxHeight = sfnt_get_short(sfont);
-        table->sCapHeight = sfnt_get_short(sfont);
-        table->usDefaultChar = sfnt_get_ushort(sfont);
-        table->usBreakChar = sfnt_get_ushort(sfont);
-        table->usMaxContext = sfnt_get_ushort(sfont);
-    }
-    return table;
-}
-
-USHORT tt_get_name(sfnt * sfont, char *dest, USHORT destlen,
-            USHORT plat_id, USHORT enco_id, USHORT lang_id, USHORT name_id)
-{
-    USHORT length = 0;
-    USHORT num_names, string_offset;
-    ULONG name_offset;
-    int i;
-    name_offset = sfnt_locate_table(sfont, "name");
-    if (sfnt_get_ushort(sfont))
-        normal_error("ttf","expecting zero");
-    num_names = sfnt_get_ushort(sfont);
-    string_offset = sfnt_get_ushort(sfont);
-    for (i = 0; i < num_names; i++) {
-        USHORT p_id, e_id, n_id, l_id;
-        USHORT offset;
-        p_id = sfnt_get_ushort(sfont);
-        e_id = sfnt_get_ushort(sfont);
-        l_id = sfnt_get_ushort(sfont);
-        n_id = sfnt_get_ushort(sfont);
-        length = sfnt_get_ushort(sfont);
-        offset = sfnt_get_ushort(sfont);
-        /*tex The language |ID| value |0xffffu| stands for ``accept any language ID''. */
-        if ((p_id == plat_id) && (e_id == enco_id) &&
-            (lang_id == 0xffffu || l_id == lang_id) && (n_id == name_id)) {
-            if (length > destlen - 1) {
-                normal_warning("ttf","truncating a very long name");
-                length = (USHORT) (destlen - 1);
-            }
-            sfnt_seek_set(sfont, (long) (name_offset + string_offset + offset));
-            sfnt_read((unsigned char *) dest, length, sfont);
-            dest[length] = '\0';
-            break;
-        }
-    }
-    if (i == num_names) {
-        length = 0;
-    }
-    return length;
-}
-
-USHORT tt_get_ps_fontname(sfnt * sfont, char *dest, USHORT destlen)
-{
-    USHORT namelen = 0;
-    /*tex First try Mac-Roman PS name and then Win-Unicode PS name. */
-    if ((namelen = tt_get_name(sfont, dest, destlen, 1, 0, 0, 6)) != 0 ||
-        (namelen = tt_get_name(sfont, dest, destlen, 3, 1, 0x409u, 6)) != 0 ||
-        (namelen = tt_get_name(sfont, dest, destlen, 3, 5, 0x412u, 6)) != 0)
-        return namelen;
-    normal_warning("ttf","no valid PostScript name available");
-    /*tex
-        This is a workaround for some bad TTfonts: the language ID value |0xffffu|
-        indicates ``accept any language ID''.
-    */
-    if ((namelen = tt_get_name(sfont, dest, destlen, 1, 0, 0xffffu, 6)) == 0) {
-        /*tex
-            Finally we're falling back to Mac Roman name field. Some bad Japanese TTfonts
-            using SJIS encoded string in the Mac Roman name field.
-        */
-        namelen = tt_get_name(sfont, dest, destlen, 1, 0, 0, 1);
-    }
-    return namelen;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/font/vfovf.c
+++ /dev/null
@@ -1,1444 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#define font_max 5000
-
-/* The instruction set: */
-
-#define set_char_0 0   /* typeset character 0 and move right */
-#define set1 128       /* typeset a character and move right */
-#define set2 129       /* typeset a character and move right */
-#define set3 130       /* typeset a character and move right */
-#define set4 131       /* typeset a character and move right */
-#define set_rule 132   /* typeset a rule and move right */
-#define put1  133      /* typeset a character without moving */
-#define put2  134      /* typeset a character without moving */
-#define put3  135      /* typeset a character without moving */
-#define put4  136      /* typeset a character without moving */
-#define put_rule 137   /* typeset a rule */
-#define nop 138        /* no operation */
-#define bop 139        /* beginning of page */
-#define eop 140        /* ending of page */
-#define push 141       /* save the current positions */
-#define pop 142        /* restore previous positions */
-#define right1  143    /* move right */
-#define right2  144    /* move right */
-#define right3  145    /* move right */
-#define right4  146    /* move right, 4 bytes */
-#define w0 147         /* move right by |w| */
-#define w1 148         /* move right and set |w| */
-#define w2 149         /* move right and set |w| */
-#define w3 150         /* move right and set |w| */
-#define w4 151         /* move right and set |w| */
-#define x0 152         /* move right by |x| */
-#define x1 153         /* move right and set |x| */
-#define x2 154         /* move right and set |x| */
-#define x3 155         /* move right and set |x| */
-#define x4 156         /* move right and set |x| */
-#define down1 157      /* move down */
-#define down2 158      /* move down */
-#define down3 159      /* move down */
-#define down4 160      /* move down, 4 bytes */
-#define y0 161         /* move down by |y| */
-#define y1 162         /* move down and set |y| */
-#define y2 163         /* move down and set |y| */
-#define y3 164         /* move down and set |y| */
-#define y4 165         /* move down and set |y| */
-#define z0 166         /* move down by |z| */
-#define z1 167         /* move down and set |z| */
-#define z2 168         /* move down and set |z| */
-#define z3 169         /* move down and set |z| */
-#define z4 170         /* move down and set |z| */
-#define fnt_num_0 171  /* set current font to 0 */
-#define fnt1 235       /* set current font */
-#define fnt2 236       /* set current font */
-#define fnt3 237       /* set current font */
-#define fnt4 238       /* set current font */
-#define xxx1 239       /* extension to DVI  primitives */
-#define xxx2 240       /* extension to DVI  primitives */
-#define xxx3 241       /* extension to DVI  primitives */
-#define xxx4 242       /* potentially long extension to DVI primitives */
-#define fnt_def1 243   /* define the meaning of a font number */
-#define pre 247        /* preamble */
-#define post 248       /* postamble beginning */
-#define post_post 249  /* postamble ending */
-#define yyy1 250       /* PDF literal text */
-#define yyy2 251       /* PDF literal text */
-#define yyy3 252       /* PDF literal text */
-#define yyy4 253       /* PDF literal text */
-
-#define null_font 0
-
-#define long_char 242  /* |VF| command for general character packet */
-
-#define vf_id 202      /* identifies \VF\ files */
-
-/*tex
-
-    Quit |VF| processing with an error message.
-
-*/
-
-#define bad_vf(a) { \
-    xfree(vf_buffer); \
-    print_nlp(); \
-    formatted_warning("virtual font","file '%s', %s, font will be ignored",font_name(f),a); \
-    print_ln(); \
-    return; \
-}
-
-#define lua_bad_vf(a) { \
-    xfree(vf_buffer); \
-    lua_settop(L,s_top); \
-    lua_pushnil(L); \
-    lua_pushstring(L,a); \
-    return 2; \
-}
-
-#define tmp_b0  tmp_w.qqqq.b0
-#define tmp_b1  tmp_w.qqqq.b1
-#define tmp_b2  tmp_w.qqqq.b2
-#define tmp_b3  tmp_w.qqqq.b3
-#define tmp_int tmp_w.cint
-
-/*tex \DVI\ files shouldn't |push| beyond this depth: */
-
-#define vf_stack_size 100
-
-/*tex An index into the stack: */
-
-typedef unsigned char vf_stack_index;
-
-typedef struct vf_stack_record {
-    scaled stack_w, stack_x, stack_y, stack_z;
-} vf_stack_record;
-
-/*tex Get a byte from the \VF\ file: */
-
-#define vf_byte(a)                                     \
-{                                                      \
-  eight_bits vf_tmp_b;                                 \
-    if (vf_cur >= vf_size) {                           \
-        normal_error("virtual font","unexpected eof"); \
-    }                                                  \
-    vf_tmp_b = vf_buffer[vf_cur++];                    \
-    a = vf_tmp_b;                                      \
-}
-
-#define vf_replace_z()                          \
-{                                               \
-    vf_alpha = 16;                              \
-    while (vf_z >= 040000000) {                 \
-        vf_z = vf_z / 2;                        \
-        vf_alpha += vf_alpha;                   \
-    }                                           \
-    /*tex |vf_beta = (char)(256 / vf_alpha)| */ \
-    vf_alpha = (vf_alpha * vf_z);               \
-}
-
-/*tex
-
-    Read |k| bytes as an integer from \VF\ file. Beware: the |vf_read| macro
-    differs from |vf_read| in |vftovp.web| for 1 upto 3 byte words.
-
-*/
-
-#define vf_read(k, l)                        \
-{                                            \
-    int itmp = 0, dtmp = (int)(k), jtmp = 0; \
-    while (dtmp > 0) {                       \
-        vf_byte(jtmp);                       \
-        if ((dtmp == (int) k) && jtmp > 127) \
-            jtmp = jtmp - 256;               \
-        itmp = itmp * 256 + jtmp;            \
-        decr(dtmp);                          \
-    }                                        \
-    l = itmp;                                \
-}
-
-#define vf_read_u(k, l)              \
-{                                    \
-    int dtmp = (int)(k);             \
-    unsigned int itmp = 0, jtmp = 0; \
-    while (dtmp-- > 0) {             \
-        vf_byte(jtmp);               \
-        itmp = itmp * 256 + jtmp;    \
-    }                                \
-    l = itmp;                        \
-}
-
-void pdf_check_vf(internal_font_number f)
-{
-    if (font_type(f) == virtual_font_type)
-        normal_error("font", "command cannot be used with virtual font");
-}
-
-static void vf_local_font_warning(internal_font_number f, internal_font_number k, const char *s, int a, int b)
-{
-    print_nlp();
-    tprint(s);
-    tprint(" in local font ");
-    tprint(font_name(k));
-    tprint(" (");
-    print_int(b);
-    tprint(" != ");
-    print_int(a);
-    tprint(") in virtual font ");
-    tprint(font_name(f));
-    tprint(".vf ignored.");
-}
-
-/*tex Process a local font in the \VF\ file. */
-
-int level = 0;
-
-static internal_font_number vf_def_font(internal_font_number f, unsigned char *vf_buffer, int *vf_cr)
-{
-    internal_font_number k;
-    str_number s;
-    char *st;
-    scaled ds, fs;
-    four_quarters cs;
-    /*tex The accumulator: */
-    memory_word tmp_w;
-    int junk;
-    unsigned int checksum;
-    cs.b0 = vf_buffer[(*vf_cr)];
-    cs.b1 = vf_buffer[(*vf_cr) + 1];
-    cs.b2 = vf_buffer[(*vf_cr) + 2];
-    cs.b3 = vf_buffer[(*vf_cr) + 3];
-    (*vf_cr) += 4;
-    checksum = (unsigned) (cs.b0 * 256 * 256 * 256 + cs.b1 * 256 * 256 + cs.b2 * 256 + cs.b3);
-    k = vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    if (k > 127)
-        k -= 256;
-    k = k * 256 + vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    k = k * 256 + vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    k = k * 256 + vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    fs = store_scaled_f(k, font_size(f));
-    k = vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    if (k > 127)
-        k -= 256;
-    k = k * 256 + vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    k = k * 256 + vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    k = k * 256 + vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    ds = k / 16;
-    tmp_b0 = vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    tmp_b1 = vf_buffer[(*vf_cr)];
-    (*vf_cr)++;
-    while (tmp_b0 > 0) {
-        /*tex Skip the font path. */
-        tmp_b0--;
-        (*vf_cr)++;
-    }
-    str_room((unsigned) tmp_b1);
-    while (tmp_b1 > 0) {
-        tmp_b1--;
-        junk = vf_buffer[(*vf_cr)];
-        (*vf_cr)++;
-        append_char(junk);
-    }
-    if (level > 5) {
-        normal_warning("vf","quitting at recurse depth > 5");
-        k = f ;
-    } else if ((level > 1) && (fs > 65536*1024)) {
-        normal_warning("vf","quitting when recursing at size > 65536*1024");
-        k = f ;
-    } else {
-        level += 1 ;
-        s = make_string();
-        st = makecstring(s);
-        k = tfm_lookup(st, fs);
-        if (k == null_font)
-            k = read_font_info(null_cs, st, fs, -1);
-        free(st);
-        level -= 1 ;
-        if (k != null_font) {
-            if (checksum != 0 && font_checksum(k) != 0
-                && checksum != font_checksum(k))
-                vf_local_font_warning(f, k, "checksum mismatch", (int) checksum, (int) font_checksum(k));
-            if (ds != font_dsize(k))
-                vf_local_font_warning(f, k, "design size mismatch", ds, font_dsize(k));
-        }
-    }
-    return k;
-}
-
-static int open_vf_file(const char *fn, unsigned char **vbuffer, int *vsize)
-{
-    /*tex Was the callback successful? */
-    boolean res;
-    int callback_id;
-    /*tex Was |vf_file| successfully read? */
-    boolean file_read = false;
-    FILE *vf_file;
-    const char *fname = luatex_find_file(fn, find_vf_file_callback);
-    if (fname == NULL || strlen(fname) == 0) {
-       return 0;
-    }
-
-    callback_id = callback_defined(read_vf_file_callback);
-    if (callback_id > 0) {
-        res = run_callback(callback_id, "S->bSd", fname,
-                           &file_read, vbuffer, vsize);
-        if (res && file_read && (*vsize > 0)) {
-            return 1;
-        }
-        if (!file_read)
-            return 0;
-    } else {
-        if (luatex_open_input
-            (&(vf_file), fname, kpse_ovf_format, FOPEN_RBIN_MODE, false)
-            || luatex_open_input(&(vf_file), fname, kpse_vf_format, FOPEN_RBIN_MODE, false)) {
-                res = read_vf_file(vf_file, vbuffer, vsize);
-                close_file(vf_file);
-                if (res) {
-                    return 1;
-            }
-        } else {
-            return 0;
-        }
-    }
-    return 0;
-}
-
-/*tex
-
-    The |do_vf| procedure attempts to read the \VF\ file for a font, and sets
-    |font_type()| to |real_font_type| if the \VF\ file could not be found or
-    loaded, otherwise sets |font_type()| to |virtual_font_type|. At this time,
-    |tmp_f| is the internal font number of the current \TFM\ font. To process
-    font definitions in virtual font we call |vf_def_font|.
-
-*/
-
-#define append_packet(k) vpackets[vf_np++] = (eight_bits)(k)
-
-/*tex
-
-    Life is easier if all internal font commands are |fnt4| and all character
-    commands are |set4| or |put4|.
-
-*/
-
-#define append_fnt_set(k)            \
-{                                    \
-    assert(k > 0);                   \
-    append_packet(packet_font_code); \
-    append_four(k);                  \
-}
-
-#define append_four(k)                     \
-{                                          \
-    append_packet((k & 0xFF000000) >> 24); \
-    append_packet((k & 0x00FF0000) >> 16); \
-    append_packet((k & 0x0000FF00) >> 8);  \
-    append_packet((k & 0x000000FF));       \
-}
-
-/*tex Some of these things happen twice, adding a define is simplest. */
-
-#define test_checksum()  { vf_byte(tmp_b0); vf_byte(tmp_b1); \
-    vf_byte(tmp_b2); vf_byte(tmp_b3); \
-    if (((tmp_b0 != 0) || (tmp_b1 != 0) || (tmp_b2 != 0) || (tmp_b3 != 0)) && \
-  ((font_check_0(f) != 0) || (font_check_1(f) != 0) || \
-   (font_check_2(f) != 0) || (font_check_3(f) != 0)) && \
-  ((tmp_b0 != font_check_0(f)) || (tmp_b1 != font_check_1(f)) ||  \
-   (tmp_b2 != font_check_2(f)) || (tmp_b3 != font_check_3(f)))) { \
-      print_nlp(); \
-      tprint("checksum mismatch in font "); \
-      tprint(font_name(f)); \
-      tprint(".vf ignored "); } }
-
-#define test_dsize()                             \
-{                                                \
-    int read_tmp;                                \
-    vf_read(4, read_tmp);                        \
-    if ((read_tmp / 16) != font_dsize(f)) {      \
-        print_nlp();                             \
-        tprint("design size mismatch in font "); \
-        tprint(font_name(f));                    \
-        tprint(".vf ignored");                   \
-    }                                            \
-}
-
-static int count_packet_bytes(eight_bits * vf_buf, int cur_bute, int count)
-{
-    unsigned k = 0;
-    int ff = 0;
-    int acc = 0;
-    unsigned int cmd = 0;
-    unsigned int d = 0;
-    while (k < (unsigned) count) {
-        cmd = vf_buf[cur_bute + (int) k];
-        k++;
-        if (cmd < set1) {
-            if (ff == 0) {
-                ff = 1;
-                acc += 5;
-            }
-            acc += 5;
-        } else if ((fnt_num_0 <= cmd) && (cmd <= fnt_num_0 + 63)) {
-            ff = 1;
-            acc += 5;
-        } else {
-            switch (cmd) {
-                case fnt1:
-                    acc += 5;
-                    k += 1;
-                    ff = 1;
-                    break;
-                case fnt2:
-                    acc += 5;
-                    k += 2;
-                    ff = 1;
-                    break;
-                case fnt3:
-                    acc += 5;
-                    k += 3;
-                    ff = 1;
-                    break;
-                case fnt4:
-                    acc += 5;
-                    k += 4;
-                    ff = 1;
-                    break;
-                case set_rule:
-                    acc += 9;
-                    k += 8;
-                    break;
-                case put_rule:
-                    acc += 11;
-                    k += 8;
-                    break;
-                case set1:
-                    acc += 5;
-                    k += 1;
-                    if (ff == 0) {
-                        ff = 1;
-                        acc += 5;
-                    }
-                    break;
-                case set2:
-                    acc += 5;
-                    k += 2;
-                    if (ff == 0) {
-                        ff = 1;
-                        acc += 5;
-                    }
-                    break;
-                case set3:
-                    acc += 5;
-                    k += 3;
-                    if (ff == 0) {
-                        ff = 1;
-                        acc += 5;
-                    }
-                    break;
-                case set4:
-                    acc += 5;
-                    k += 4;
-                    if (ff == 0) {
-                        ff = 1;
-                        acc += 5;
-                    }
-                    break;
-                case put1:
-                    acc += 7;
-                    k += 1;
-                    if (ff == 0) {
-                        ff = 1;
-                        acc += 5;
-                    }
-                    break;
-                case put2:
-                    acc += 7;
-                    k += 2;
-                    if (ff == 0) {
-                        ff = 1;
-                        acc += 5;
-                    }
-                    break;
-                case put3:
-                    acc += 7;
-                    k += 3;
-                    if (ff == 0) {
-                        ff = 1;
-                        acc += 5;
-                    }
-                    break;
-                case put4:
-                    acc += 7;
-                    k += 4;
-                    if (ff == 0) {
-                        ff = 1;
-                        acc += 5;
-                    }
-                    break;
-                case right1:
-                    acc += 5;
-                    k += 1;
-                    break;
-                case right2:
-                    acc += 5;
-                    k += 2;
-                    break;
-                case right3:
-                    acc += 5;
-                    k += 3;
-                    break;
-                case right4:
-                    acc += 5;
-                    k += 4;
-                    break;
-                case w1:
-                    acc += 5;
-                    k += 1;
-                    break;
-                case w2:
-                    acc += 5;
-                    k += 2;
-                    break;
-                case w3:
-                    acc += 5;
-                    k += 3;
-                    break;
-                case w4:
-                    acc += 5;
-                    k += 4;
-                    break;
-                case x1:
-                    acc += 5;
-                    k += 1;
-                    break;
-                case x2:
-                    acc += 5;
-                    k += 2;
-                    break;
-                case x3:
-                    acc += 5;
-                    k += 3;
-                    break;
-                case x4:
-                    acc += 5;
-                    k += 4;
-                    break;
-                case down1:
-                    acc += 5;
-                    k += 1;
-                    break;
-                case down2:
-                    acc += 5;
-                    k += 2;
-                    break;
-                case down3:
-                    acc += 5;
-                    k += 3;
-                    break;
-                case down4:
-                    acc += 5;
-                    k += 4;
-                    break;
-                case y1:
-                    acc += 5;
-                    k += 1;
-                    break;
-                case y2:
-                    acc += 5;
-                    k += 2;
-                    break;
-                case y3:
-                    acc += 5;
-                    k += 3;
-                    break;
-                case y4:
-                    acc += 5;
-                    k += 4;
-                    break;
-                case z1:
-                    acc += 5;
-                    k += 1;
-                    break;
-                case z2:
-                    acc += 5;
-                    k += 2;
-                    break;
-                case z3:
-                    acc += 5;
-                    k += 3;
-                    break;
-                case z4:
-                    acc += 5;
-                    k += 4;
-                    break;
-                case xxx1:
-                    d = vf_buf[cur_bute + (int) k];
-                    k++;
-                    k += d;
-                    acc += 5 + (int) d;
-                    break;
-                case xxx2:
-                    d = vf_buf[cur_bute + (int) k];
-                    k++;
-                    d = d * 256 + vf_buf[cur_bute + (int) k];
-                    k++;
-                    k += d;
-                    acc += 5 + (int) d;
-                    break;
-                case xxx3:
-                    d = vf_buf[cur_bute + (int) k];
-                    k++;
-                    d = d * 256 + vf_buf[cur_bute + (int) k];
-                    k++;
-                    d = d * 256 + vf_buf[cur_bute + (int) k];
-                    k++;
-                    k += d;
-                    acc += 5 + (int) d;
-                    break;
-                case xxx4:
-                    d = vf_buf[cur_bute + (int) k];
-                    k++;
-                    d = d * 256 + vf_buf[cur_bute + (int) k];
-                    k++;
-                    d = d * 256 + vf_buf[cur_bute + (int) k];
-                    k++;
-                    d = d * 256 + vf_buf[cur_bute + (int) k];
-                    k++;
-                    k += d;
-                    acc += 5 + (int) d;
-                    break;
-                case w0:
-                    acc += 5;
-                    break;
-                case x0:
-                    acc += 5;
-                    break;
-                case y0:
-                    acc += 5;
-                    break;
-                case z0:
-                    acc += 5;
-                    break;
-                case nop:
-                    break;
-                case push:
-                    acc += 1;
-                    break;
-                case pop:
-                    acc += 1;
-                    break;
-            }
-        }
-    }
-    return (acc + 1);
-}
-
-void do_vf(internal_font_number f)
-{
-    int k, i;
-    unsigned cmd, n;
-    scaled x, y, w, z, h, v;
-    int cc, cmd_length;
-    unsigned packet_length;
-    charinfo *co;
-    scaled tfm_width;
-    int save_cur_byte;
-    vf_stack_index stack_level;
-    /*tex multiplier */
-    int vf_z;
-    /*tex correction for negative values */
-    int vf_alpha;
-    int vf_np;
-    eight_bits *vpackets;
-    /*tex accumulator */
-    memory_word tmp_w;
-    vf_stack_record vf_stack[256];
-    int junk;
-    unsigned utmp;
-    unsigned char *vf_buffer;
-    int vf_size;
-    int vf_cur;
-    /*tex external font ids */
-    unsigned *vf_local_fnts = NULL;
-    /*tex internal font ids */
-    unsigned *vf_real_fnts = NULL;
-    /*tex local font counter */
-    unsigned vf_nf = 0;
-    if (font_type(f) != unknown_font_type)
-        return;
-    set_font_type(f, real_font_type);
-    stack_level = 0;
-    /*tex Open |vf_file|, return if not found */
-    vf_cur = 0;
-    vf_buffer = NULL;
-    vf_size = 0;
-    if (!open_vf_file(font_name(f), &vf_buffer, &vf_size))
-        return;
-    /*tex Process the preamble */
-    set_font_type(f, virtual_font_type);
-    vf_byte(k);
-    if (k != pre)
-        bad_vf("PRE command expected");
-    vf_byte(k);
-    if (k != vf_id)
-        bad_vf("wrong id byte");
-    vf_byte(cmd_length);
-    for (k = 1; k <= cmd_length; k++)
-        vf_byte(junk);
-    test_checksum();
-    test_dsize();
-    vf_z = font_size(f);
-    vf_replace_z();
-    /*tex Process the font definitions; scan forward to find the number of internal fonts. */
-    vf_nf = 0;
-    save_cur_byte = vf_cur;
-    vf_byte(cmd);
-    while ((cmd >= fnt_def1) && (cmd <= (fnt_def1 + 3))) {
-        vf_read_u((cmd - fnt_def1 + 1), utmp);
-        vf_read(4, junk);
-        vf_read(4, junk);
-        vf_read(4, junk);
-        vf_byte(k);
-        vf_byte(junk);
-        k += junk;
-        while (k-- > 0) {
-            vf_byte(junk);
-        }
-        incr(vf_nf);
-        vf_byte(cmd);
-    }
-    vf_cur = save_cur_byte;
-    vf_byte(cmd);
-    /*tex Do a |malloc| and fill the local font arrays. */
-    if (vf_nf > 0) {
-        unsigned ii = (unsigned) ((unsigned) vf_nf * sizeof(int));
-        vf_local_fnts = xmalloc(ii);
-        memset(vf_local_fnts, 0, ii);
-        vf_real_fnts = xmalloc(ii);
-        memset(vf_real_fnts, 0, ii);
-        vf_nf = 0;
-        while ((cmd >= fnt_def1) && (cmd <= (fnt_def1 + 3))) {
-            vf_read_u((cmd - fnt_def1 + 1), vf_local_fnts[vf_nf]);
-            vf_real_fnts[vf_nf] = (unsigned) vf_def_font(f, vf_buffer, &vf_cur);
-            incr(vf_nf);
-            vf_byte(cmd);
-        }
-    }
-    while (cmd <= long_char) {
-        /*tex Build a character packet. */
-        vf_np = 0;
-        if (cmd == long_char) {
-            vf_read_u(4, packet_length);
-            vf_read_u(4, utmp);
-            cc = (int) utmp;
-            if (!char_exists(f, cc)) {
-                bad_vf("invalid character code");
-            }
-            vf_read(4, k);
-            tfm_width = store_scaled_f(k, font_size(f));
-        } else {
-            packet_length = cmd;
-            vf_byte(cc);
-            if (!char_exists(f, cc)) {
-                bad_vf("invalid character code");
-            }
-            vf_read_u(3, utmp);
-            /*tex cf. |vftovp.web|, line 1028 */
-            k = (int) utmp;
-            tfm_width = store_scaled_f(k, font_size(f));
-        }
-        if (tfm_width != char_width(f, cc)) {
-            if (tfm_width != char_width(f, cc)) {
-                print_nlp();
-                tprint("character width mismatch in font ");
-                tprint(font_name(f));
-                tprint(".vf ignored");
-            }
-        }
-        k = count_packet_bytes(vf_buffer, vf_cur, (int) packet_length);
-        /*tex We need one extra extra for |packet_end|. */
-        vpackets = xmalloc((unsigned) (k + 1));
-        co = get_charinfo(f, cc);
-        k = 0;
-        w = 0;
-        x = 0;
-        y = 0;
-        z = 0;
-        while (packet_length > 0) {
-            vf_byte(cmd);
-            decr(packet_length);
-            if (cmd < set1) {
-                if (k == 0) {
-                    k = (int) vf_real_fnts[0];
-                    append_fnt_set(k);
-                }
-                append_packet(packet_char_code);
-                append_four(cmd);
-                cmd_length = 0;
-                cmd = nop;
-            } else if (((fnt_num_0 <= cmd) && (cmd <= fnt_num_0 + 63)) ||
-                       ((fnt1 <= cmd) && (cmd <= fnt1 + 3))) {
-                if (cmd >= fnt1) {
-                    vf_read_u((cmd - fnt1 + 1), utmp);
-                    k = (int) utmp;
-                    packet_length -= (cmd - fnt1 + 1);
-                } else {
-                    k = (int) cmd - fnt_num_0;
-                }
-                /*tex Change from local to external font id. */
-                n = 0;
-                while ((n < vf_nf) && (vf_local_fnts[n] != (unsigned) k))
-                    n++;
-                if (n == vf_nf)
-                    bad_vf("undefined local font");
-                k = (int) vf_real_fnts[n];
-                append_fnt_set(k);
-                cmd_length = 0;
-                cmd = nop;
-            } else {
-                switch (cmd) {
-                    case set_rule:
-                        vf_read(4, h);
-                        vf_read(4, v);
-                        append_packet(packet_rule_code);
-                        append_four(h);
-                        append_four(v);
-                        packet_length -= 8;
-                        break;
-                    case put_rule:
-                        vf_read(4, h);
-                        vf_read(4, v);
-                        append_packet(packet_push_code);
-                        append_packet(packet_rule_code);
-                        append_four(h);
-                        append_four(v);
-                        append_packet(packet_pop_code);
-                        packet_length -= 8;
-                        break;
-                    case set1:
-                    case set2:
-                    case set3:
-                    case set4:
-                        if (k == 0) {
-                            k = (int) vf_real_fnts[0];
-                            append_fnt_set(k);
-                        }
-                        vf_read_u((cmd - set1 + 1), utmp);
-                        i = (int) utmp;
-                        append_packet(packet_char_code);
-                        append_four(i);
-                        packet_length -= (cmd - set1 + 1);
-                        break;
-                    case put1:
-                    case put2:
-                    case put3:
-                    case put4:
-                        if (k == 0) {
-                            k = (int) vf_real_fnts[0];
-                            append_fnt_set(k);
-                        }
-                        vf_read_u((cmd - put1 + 1), utmp);
-                        i = (int) utmp;
-                        append_packet(packet_push_code);
-                        append_packet(packet_char_code);
-                        append_four(i);
-                        append_packet(packet_pop_code);
-                        packet_length -= (cmd - put1 + 1);
-                        break;
-                    case right1:
-                    case right2:
-                    case right3:
-                    case right4:
-                        vf_read((cmd - right1 + 1), i);
-                        append_packet(packet_right_code);
-                        append_four(i);
-                        packet_length -= (cmd - right1 + 1);
-                        break;
-                    case w1:
-                    case w2:
-                    case w3:
-                    case w4:
-                        vf_read((cmd - w1 + 1), w);
-                        append_packet(packet_right_code);
-                        append_four(w);
-                        packet_length -= (cmd - w1 + 1);
-                        break;
-                    case x1:
-                    case x2:
-                    case x3:
-                    case x4:
-                        vf_read((cmd - x1 + 1), x);
-                        append_packet(packet_right_code);
-                        append_four(x);
-                        packet_length -= (cmd - x1 + 1);
-                        break;
-                    case down1:
-                    case down2:
-                    case down3:
-                    case down4:
-                        vf_read((cmd - down1 + 1), i);
-                        append_packet(packet_down_code);
-                        append_four(i);
-                        packet_length -= (cmd - down1 + 1);
-                        break;
-                    case y1:
-                    case y2:
-                    case y3:
-                    case y4:
-                        vf_read((cmd - y1 + 1), y);
-                        append_packet(packet_down_code);
-                        append_four(y);
-                        packet_length -= (cmd - y1 + 1);
-                        break;
-                    case z1:
-                    case z2:
-                    case z3:
-                    case z4:
-                        vf_read((cmd - z1 + 1), z);
-                        append_packet(packet_down_code);
-                        append_four(z);
-                        packet_length -= (cmd - z1 + 1);
-                        break;
-                    case xxx1:
-                    case xxx2:
-                    case xxx3:
-                    case xxx4:
-                        vf_read_u((cmd - xxx1 + 1), utmp);
-                        cmd_length = (int) utmp;
-                        packet_length -= (cmd - xxx1 + 1);
-                        if (cmd_length <= 0)
-                            bad_vf("special of negative length");
-                        packet_length -= (unsigned) cmd_length;
-                        append_packet(packet_special_code);
-                        append_four(cmd_length);
-                        while (cmd_length > 0) {
-                            cmd_length--;
-                            vf_byte(i);
-                            append_packet(i);
-                        }
-                        break;
-                    case w0:
-                        append_packet(packet_right_code);
-                        append_four(w);
-                        break;
-                    case x0:
-                        append_packet(packet_right_code);
-                        append_four(x);
-                        break;
-                    case y0:
-                        append_packet(packet_down_code);
-                        append_four(y);
-                        break;
-                    case z0:
-                        append_packet(packet_down_code);
-                        append_four(z);
-                        break;
-                    case nop:
-                        break;
-                    case push:
-                        if (stack_level == vf_stack_size) {
-                            overflow("virtual font stack size", vf_stack_size);
-                        } else {
-                            vf_stack[stack_level].stack_w = w;
-                            vf_stack[stack_level].stack_x = x;
-                            vf_stack[stack_level].stack_y = y;
-                            vf_stack[stack_level].stack_z = z;
-                            incr(stack_level);
-                            append_packet(packet_push_code);
-                        }
-                        break;
-                    case pop:
-                        if (stack_level == 0) {
-                            bad_vf("more POPs than PUSHs in character");
-                        } else {
-                            decr(stack_level);
-                            w = vf_stack[stack_level].stack_w;
-                            x = vf_stack[stack_level].stack_x;
-                            y = vf_stack[stack_level].stack_y;
-                            z = vf_stack[stack_level].stack_z;
-                            append_packet(packet_pop_code);
-                        }
-                        break;
-                    default:
-                        bad_vf("improver DVI command");
-                }
-            }
-        }
-        /*tex Signal end of packet. */
-        append_packet(packet_end_code);
-        if (stack_level != 0)
-            bad_vf("more PUSHs than POPs in character packet");
-        if (packet_length != 0)
-            bad_vf("invalid packet length or DVI command in packet");
-        /*tex Store the packet being built. */
-        set_charinfo_packets(co, vpackets);
-        vf_byte(cmd);
-    }
-    if (cmd != post)
-        bad_vf("POST command expected");
-
-    xfree(vf_buffer);
-}
-
-#define make_command0(N,K) { \
-    lua_newtable(L);         \
-    lua_pushstring(L, N);    \
-    lua_rawseti(L,-2, 1);    \
-    lua_rawseti(L,-2, K);    \
-    K++; }
-
-#define make_command1(N,V,K) { \
-    lua_newtable(L);           \
-    lua_pushstring(L, N);      \
-    lua_rawseti(L,-2, 1);      \
-    lua_pushinteger(L, V);     \
-    lua_rawseti(L,-2, 2);      \
-    lua_rawseti(L,-2, K);      \
-    K++; }
-
-#define make_command2(N,V,W,K) { \
-    lua_newtable(L);             \
-    lua_pushstring(L, N);        \
-    lua_rawseti(L,-2, 1);        \
-    lua_pushinteger(L, V);       \
-    lua_rawseti(L,-2, 2);        \
-    lua_pushinteger(L, W);       \
-    lua_rawseti(L,-2, 3);        \
-    lua_rawseti(L,-2, K);        \
-    K++; }
-
-#define make_commands(N,S,V,K) { \
-    lua_newtable(L);             \
-    lua_pushstring(L, N);        \
-    lua_rawseti(L,-2, 1);        \
-    lua_pushlstring(L, S, V);    \
-    lua_rawseti(L,-2, 2);        \
-    lua_rawseti(L,-2, K);        \
-    K++; }
-
-int make_vf_table(lua_State * L, const char *cnom, scaled atsize)
-{
-    int cmd, k, i;
-    int cc;
-    unsigned cmd_length, packet_length;
-    scaled tfm_width;
-    vf_stack_index stack_level;
-    /*tex multiplier */
-    int vf_z;
-    /*tex correction for negative values */
-    int vf_alpha;
-    eight_bits *s;
-    scaled h, v;
-    scaled w, x, y, z;
-    /*tex \LUA\ stack */
-    int s_top;
-    /*tex local font counter */
-    int vf_nf;
-    scaled ds, fs;
-    four_quarters cs;
-    /*tex accumulator */
-    memory_word tmp_w;
-    vf_stack_record vf_stack[256];
-    unsigned char *vf_buffer;
-    int vf_size;
-    int vf_cur;
-    unsigned utmp;
-    stack_level = 0;
-    /*tex Open |vf_file|, return if not found. */
-    vf_cur = 0;
-    vf_buffer = NULL;
-    vf_size = 0;
-    if (!open_vf_file(cnom, &vf_buffer, &vf_size)) {
-        lua_pushnil(L);
-        return 1;
-    }
-    /*tex Start by creating a table. */
-    s_top = lua_gettop(L);
-    lua_newtable(L);
-    /*tex Process the preamble. */
-    vf_byte(k);
-    if (k != pre)
-        lua_bad_vf("PRE command expected");
-    vf_byte(k);
-    if (k != vf_id)
-        lua_bad_vf("wrong id byte");
-    vf_byte(cmd_length);
-    s = xmalloc(cmd_length);
-    for (k = 1; k <= (int) cmd_length; k++)
-        vf_byte(s[(k - 1)]);
-    lua_pushlstring(L, (char *) s, (size_t) cmd_length);
-    free(s);
-    lua_setfield(L, -2, "header");
-    vf_byte(cs.b0);
-    vf_byte(cs.b1);
-    vf_byte(cs.b2);
-    vf_byte(cs.b3);
-    lua_pushinteger(L,  (lua_Number) ((cs.b0 << 24) + (cs.b1 << 16) + (cs.b2 << 8) + cs.b3));
-    lua_setfield(L, -2, "checksum");
-    vf_read(4, k);
-    ds = k / 16;
-    lua_pushinteger(L, ds);
-    lua_setfield(L, -2, "designsize");
-    lua_pushstring(L, cnom);
-    lua_setfield(L, -2, "name");
-    lua_pushinteger(L, atsize);
-    lua_setfield(L, -2, "size");
-    vf_z = atsize;
-    vf_replace_z();
-    /*tex Process the font definitions. */
-    vf_byte(cmd);
-    lua_newtable(L);
-    i = 1;
-    while ((cmd >= fnt_def1) && (cmd <= fnt_def1 + 3)) {
-        lua_newtable(L);
-        vf_read_u((cmd - fnt_def1 + 1), utmp);
-        vf_nf = (int) utmp;
-        vf_nf++;
-        /*tex Add a checksum. */
-        vf_byte(cs.b0);
-        vf_byte(cs.b1);
-        vf_byte(cs.b2);
-        vf_byte(cs.b3);
-        vf_read(4, k);
-        fs = store_scaled_f(k, atsize);
-        lua_pushstring(L, "size");
-        lua_pushinteger(L, fs);
-        lua_rawset(L, -3);
-        vf_read(4, k);
-        /*tex |dsize| is not used */
-        ds = k / 16;
-        vf_byte(tmp_b0);
-        vf_byte(tmp_b1);
-        /*tex Skip the font path. */
-        while (tmp_b0 > 0) {
-            tmp_b0--;
-            vf_byte(k);
-        }
-        s = xmalloc((unsigned) (tmp_b1 + 1));
-        k = 0;
-        while (tmp_b1-- > 0)
-            vf_byte(s[k++]);
-        s[k] = 0;
-        lua_pushstring(L, "name");
-        lua_pushstring(L, xstrdup((char *) s));
-        free(s);
-        lua_rawset(L, -3);
-        lua_rawseti(L, -2, vf_nf);
-        i++;
-        vf_byte(cmd);
-    }
-    if (i > 1) {
-        lua_setfield(L, -2, "fonts");
-    } else {
-        lua_pop(L, 1);
-    }
-    /*tex The table; with characters comes next. */
-    lua_newtable(L);
-    while (cmd <= long_char) {
-        /*tex Build a character packet. */
-        if (cmd == long_char) {
-            vf_read_u(4, packet_length);
-            vf_read_u(4, utmp);
-            cc = (int) utmp;
-            vf_read(4, tfm_width);
-        } else {
-            packet_length = (unsigned) cmd;
-            vf_byte(cc);
-            vf_read_u(3, utmp);
-            tfm_width = (int) utmp;
-        }
-        /*tex For this character entry. */
-        lua_newtable(L);
-        lua_pushinteger(L, tfm_width);
-        lua_setfield(L, -2, "width");
-        /*tex for |commands|: */
-        lua_newtable(L);
-        k = 1;
-        vf_nf = 0;
-        w = 0;
-        x = 0;
-        y = 0;
-        z = 0;
-        while (packet_length > 0) {
-            vf_byte(cmd);
-            decr(packet_length);
-            if ((cmd >= set_char_0) && (cmd < set1)) {
-                if (vf_nf == 0) {
-                    vf_nf = 1;
-                    make_command1("font", vf_nf, k);
-                }
-                make_command1("char", cmd, k);
-            } else if (((fnt_num_0 <= cmd) && (cmd <= fnt_num_0 + 63)) || ((fnt1 <= cmd) && (cmd <= fnt1 + 3))) {
-                if (cmd >= fnt1) {
-                    vf_read_u((cmd - fnt1 + 1), utmp);
-                    vf_nf = (int) utmp;
-                    vf_nf++;
-                    packet_length -= (unsigned) (cmd - fnt1 + 1);
-                } else {
-                    vf_nf = cmd - fnt_num_0 + 1;
-                }
-                make_command1("font", vf_nf, k);
-            } else {
-                switch (cmd) {
-                    case set_rule:
-                        vf_read(4, h);
-                        vf_read(4, v);
-                        make_command2("rule", store_scaled_f(h, atsize),
-                                      store_scaled_f(v, atsize), k);
-                        packet_length -= 8;
-                        break;
-                    case put_rule:
-                        vf_read(4, h);
-                        vf_read(4, v);
-                        make_command0("push", k);
-                        make_command2("rule", store_scaled_f(h, atsize),
-                                      store_scaled_f(v, atsize), k);
-                        make_command0("pop", k);
-                        packet_length -= 8;
-                        break;
-                    case set1:
-                    case set2:
-                    case set3:
-                    case set4:
-                        if (vf_nf == 0) {
-                            vf_nf = 1;
-                            make_command1("font", vf_nf, k);
-                        }
-                        vf_read_u((cmd - set1 + 1), utmp);
-                        i = (int) utmp;
-                        make_command1("char", i, k);
-                        packet_length -= (unsigned) (cmd - set1 + 1);
-                        break;
-                    case put1:
-                    case put2:
-                    case put3:
-                    case put4:
-                        if (vf_nf == 0) {
-                            vf_nf = 1;
-                            make_command1("font", vf_nf, k);
-                        }
-                        vf_read_u((cmd - put1 + 1), utmp);
-                        i = (int) utmp;
-                        make_command0("push", k);
-                        make_command1("char", i, k);
-                        make_command0("pop", k);
-                        packet_length -= (unsigned) (cmd - put1 + 1);
-                        break;
-                    case right1:
-                    case right2:
-                    case right3:
-                    case right4:
-                        vf_read((cmd - right1 + 1), i);
-                        make_command1("right", store_scaled_f(i, atsize), k);
-                        packet_length -= (unsigned) (cmd - right1 + 1);
-                        break;
-                    case w1:
-                    case w2:
-                    case w3:
-                    case w4:
-                        vf_read((cmd - w1 + 1), w);
-                        make_command1("right", store_scaled_f(w, atsize), k);
-                        packet_length -= (unsigned) (cmd - w1 + 1);
-                        break;
-                    case x1:
-                    case x2:
-                    case x3:
-                    case x4:
-                        vf_read((cmd - x1 + 1), x);
-                        make_command1("right", store_scaled_f(x, atsize), k);
-                        packet_length -= (unsigned) (cmd - x1 + 1);
-                        break;
-                    case down1:
-                    case down2:
-                    case down3:
-                    case down4:
-                        vf_read((cmd - down1 + 1), i);
-                        make_command1("down", store_scaled_f(i, atsize), k);
-                        packet_length -= (unsigned) (cmd - down1 + 1);
-                        break;
-                    case y1:
-                    case y2:
-                    case y3:
-                    case y4:
-                        vf_read((cmd - y1 + 1), y);
-                        make_command1("down", store_scaled_f(y, atsize), k);
-                        packet_length -= (unsigned) (cmd - y1 + 1);
-                        break;
-                    case z1:
-                    case z2:
-                    case z3:
-                    case z4:
-                        vf_read((cmd - z1 + 1), z);
-                        make_command1("down", store_scaled_f(z, atsize), k);
-                        packet_length -= (unsigned) (cmd - z1 + 1);
-                        break;
-                    case xxx1:
-                    case xxx2:
-                    case xxx3:
-                    case xxx4:
-                        vf_read_u((cmd - xxx1 + 1), cmd_length);
-                        packet_length -= (unsigned) (cmd - xxx1 + 1);
-                        if (cmd_length <= 0)
-                            lua_bad_vf("special of negative length");
-                        packet_length -= cmd_length;
-                        s = xmalloc((cmd_length + 1));
-                        i = 0;
-                        while (cmd_length > 0) {
-                            cmd_length--;
-                            vf_byte(s[i]);
-                            i++;
-                        }
-                        s[i] = 0;
-                        make_commands("special", xstrdup((char *) s), (size_t) i, k);
-                        free(s);
-                        break;
-                    case w0:
-                        make_command1("right", store_scaled_f(w, atsize), k);
-                        break;
-                    case x0:
-                        make_command1("right", store_scaled_f(x, atsize), k);
-                        break;
-                    case y0:
-                        make_command1("down", store_scaled_f(y, atsize), k);
-                        break;
-                    case z0:
-                        make_command1("down", store_scaled_f(z, atsize), k);
-                        break;
-                    case nop:
-                        break;
-                    case push:
-                        if (stack_level == vf_stack_size) {
-                            overflow("virtual font stack size", vf_stack_size);
-                        } else {
-                            vf_stack[stack_level].stack_w = w;
-                            vf_stack[stack_level].stack_x = x;
-                            vf_stack[stack_level].stack_y = y;
-                            vf_stack[stack_level].stack_z = z;
-                            incr(stack_level);
-                            make_command0("push", k);
-                        }
-                        break;
-                    case pop:
-                        if (stack_level == 0) {
-                            lua_bad_vf("more POPs than PUSHs in character");
-                        } else {
-                            decr(stack_level);
-                            w = vf_stack[stack_level].stack_w;
-                            x = vf_stack[stack_level].stack_x;
-                            y = vf_stack[stack_level].stack_y;
-                            z = vf_stack[stack_level].stack_z;
-                            make_command0("pop", k);
-                        }
-                        break;
-                    default:
-                        lua_bad_vf("improver DVI command");
-                }
-            }
-        }
-        /*tex Signal end of packet. */
-        lua_setfield(L, -2, "commands");
-        if (stack_level != 0)
-            lua_bad_vf("more PUSHs than POPs in character packet");
-        if (packet_length != 0)
-            lua_bad_vf("invalid packet length or DVI command in packet");
-        lua_rawseti(L, -2, cc);
-        vf_byte(cmd);
-    }
-    lua_setfield(L, -2, "characters");
-    if (cmd != post)
-        lua_bad_vf("POST command expected");
-    xfree(vf_buffer);
-    return 1;
-}
-
-internal_font_number letter_space_font(internal_font_number f, int e, boolean nolig)
-{
-    internal_font_number k;
-    scaled w;
-    int c;
-    charinfo *co;
-    char *new_font_name;
-    /*tex Read a new font and expand the character widths. */
-    k = copy_font(f);
-    if (nolig) {
-        /*tex Disable ligatures for letter-spaced fonts. */
-        set_no_ligatures(k);
-    }
-    /*tex append e.g. |+100ls| to font name; |abs(e) <= 1000|. */
-    new_font_name = xmalloc((unsigned) (strlen(font_name(k)) + 8));
-    if (e > 0) {
-        sprintf(new_font_name, "%s+%ils", font_name(k), (int) e);
-    } else {
-        /*tex Minus from |%i|: */
-        sprintf(new_font_name, "%s%ils", font_name(k), (int) e);
-    }
-    set_font_name(k, new_font_name);
-    /* Create the corresponding virtual font. */
-    set_font_type(k, virtual_font_type);
-    for (c=font_bc(k);c<=font_ec(k);c++) {
-       if (quick_char_exists(k, c)) {
-           int half_w;
-           int vf_np = 0;
-           eight_bits *vpackets = xmalloc((unsigned) (10+10+1));
-           if (e<0) {
-             half_w = -round_xn_over_d(quad(k), -e, 2000);
-           } else {
-             half_w  = round_xn_over_d(quad(k), e, 2000);
-           }
-           co = get_charinfo(k, c);
-           w = char_width(k, c)+2*half_w;
-           set_charinfo_width(co, w);
-           append_packet(packet_right_code);
-           append_four(half_w);
-           append_fnt_set(f);
-           append_packet(packet_char_code);
-           append_four(c);
-           append_packet(packet_right_code);
-           append_four(half_w);
-           append_packet(packet_end_code);
-           set_charinfo_packets(co, vpackets);
-       }
-    }
-    /*tex Now patch the quad size. Ok, not in order to remain compatible with \PDFTEX: */
-#if 0
-    if (e<0) {
-       set_font_param(k, quad_code, -round_xn_over_d(quad(k), 1000-e, 1000));
-    } else {
-       set_font_param(k, quad_code, round_xn_over_d(quad(k), 1000+e, 1000));
-    }
-#endif
-    return k;
-}
-
-internal_font_number copy_font_info(internal_font_number f)
-{
-    return copy_font(f);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/vfovf.w
@@ -0,0 +1,1480 @@
+% vfovf.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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"
+
+@ @c
+/* this is a hack! */
+#define font_max 5000
+
+#define set_char_0 0            /* typeset character 0 and move right */
+#define set1 128                /* typeset a character and move right */
+#define set2 129                /* typeset a character and move right */
+#define set3 130                /* typeset a character and move right */
+#define set4 131                /* typeset a character and move right */
+#define set_rule 132            /* typeset a rule and move right */
+#define put1  133               /* typeset a character without moving */
+#define put2  134               /* typeset a character without moving */
+#define put3  135               /* typeset a character without moving */
+#define put4  136               /* typeset a character without moving */
+#define put_rule 137            /* typeset a rule */
+#define nop 138                 /* no operation */
+#define bop 139                 /* beginning of page */
+#define eop 140                 /* ending of page */
+#define push 141                /* save the current positions */
+#define pop 142                 /* restore previous positions */
+#define right1  143             /* move right */
+#define right2  144             /* move right */
+#define right3  145             /* move right */
+#define right4  146             /* move right, 4 bytes */
+#define w0 147                  /* move right by |w| */
+#define w1 148                  /* move right and set |w| */
+#define w2 149                  /* move right and set |w| */
+#define w3 150                  /* move right and set |w| */
+#define w4 151                  /* move right and set |w| */
+#define x0 152                  /* move right by |x| */
+#define x1 153                  /* move right and set |x| */
+#define x2 154                  /* move right and set |x| */
+#define x3 155                  /* move right and set |x| */
+#define x4 156                  /* move right and set |x| */
+#define down1 157               /* move down */
+#define down2 158               /* move down */
+#define down3 159               /* move down */
+#define down4 160               /* move down, 4 bytes */
+#define y0 161                  /* move down by |y| */
+#define y1 162                  /* move down and set |y| */
+#define y2 163                  /* move down and set |y| */
+#define y3 164                  /* move down and set |y| */
+#define y4 165                  /* move down and set |y| */
+#define z0 166                  /* move down by |z| */
+#define z1 167                  /* move down and set |z| */
+#define z2 168                  /* move down and set |z| */
+#define z3 169                  /* move down and set |z| */
+#define z4 170                  /* move down and set |z| */
+#define fnt_num_0 171           /* set current font to 0 */
+#define fnt1 235                /* set current font */
+#define fnt2 236                /* set current font */
+#define fnt3 237                /* set current font */
+#define fnt4 238                /* set current font */
+#define xxx1 239                /* extension to DVI  primitives */
+#define xxx2 240                /* extension to DVI  primitives */
+#define xxx3 241                /* extension to DVI  primitives */
+#define xxx4 242                /* potentially long extension to DVI primitives */
+#define fnt_def1 243            /* define the meaning of a font number */
+#define pre 247                 /* preamble */
+#define post 248                /* postamble beginning */
+#define post_post 249           /* postamble ending */
+#define yyy1 250                /* PDF literal text */
+#define yyy2 251                /* PDF literal text */
+#define yyy3 252                /* PDF literal text */
+#define yyy4 253                /* PDF literal text */
+
+#define null_font 0
+
+#define long_char 242           /* \.{VF} command for general character packet */
+#define vf_id 202               /* identifies \.{VF} files */
+
+@ go out \.{VF} processing with an error message
+@c
+#define bad_vf(a) { \
+    xfree(vf_buffer); \
+    print_nlp(); \
+    formatted_warning("virtual font","file '%s', %s, font will be ignored",font_name(f),a); \
+    print_ln(); \
+    return; \
+}
+
+#define lua_bad_vf(a) { \
+    xfree(vf_buffer); \
+    lua_settop(L,s_top); \
+    lua_pushnil(L); \
+    lua_pushstring(L,a); \
+    return 2; \
+}
+
+#define tmp_b0  tmp_w.qqqq.b0
+#define tmp_b1  tmp_w.qqqq.b1
+#define tmp_b2  tmp_w.qqqq.b2
+#define tmp_b3  tmp_w.qqqq.b3
+#define tmp_int tmp_w.cint
+
+#define vf_stack_size 100       /* \.{DVI} files shouldn't |push| beyond this depth */
+
+@ @c
+typedef unsigned char vf_stack_index;   /* an index into the stack */
+
+typedef struct vf_stack_record {
+    scaled stack_w, stack_x, stack_y, stack_z;
+} vf_stack_record;
+
+@ get a byte from\.{VF} file
+@c
+#define vf_byte(a)                                     \
+{                                                      \
+  eight_bits vf_tmp_b;				                   \
+    if (vf_cur >= vf_size) {                           \
+        normal_error("virtual font","unexpected eof"); \
+    }                                                  \
+    vf_tmp_b = vf_buffer[vf_cur++];                    \
+    a = vf_tmp_b;                                      \
+}
+
+@ @c
+#define vf_replace_z()                           \
+{                                                \
+    vf_alpha = 16;                               \
+    while (vf_z >= 040000000) {                  \
+        vf_z = vf_z / 2;                         \
+        vf_alpha += vf_alpha;                    \
+    }                                            \
+    /*vf_beta = (char)(256 / vf_alpha);*/	 \
+    vf_alpha = (vf_alpha * vf_z);		 \
+}
+
+
+@ read |k| bytes as an integer from \.{VF} file
+beware: the |vf_read()| macro differs from |vf_read()| in vftovp.web for 1...3 byte words.
+@c
+#define vf_read(k, l)                            \
+{                                                \
+    int itmp = 0, dtmp = (int)(k), jtmp = 0;	 \
+    while (dtmp > 0) {                           \
+        vf_byte(jtmp);                           \
+        if ((dtmp == (int) k) && jtmp > 127)     \
+            jtmp = jtmp - 256;                   \
+        itmp = itmp * 256 + jtmp;                \
+        decr(dtmp);                              \
+    }                                            \
+    l = itmp;                                    \
+}
+
+#define vf_read_u(k, l)                          \
+{                                                \
+    int dtmp = (int)(k);			 \
+    unsigned int itmp = 0, jtmp = 0;		 \
+    while (dtmp-- > 0) {                         \
+        vf_byte(jtmp);                           \
+        itmp = itmp * 256 + jtmp;                \
+    }                                            \
+    l = itmp;                                    \
+}
+
+@ @c
+void pdf_check_vf(internal_font_number f)
+{
+    if (font_type(f) == virtual_font_type)
+        normal_error("font", "command cannot be used with virtual font");
+}
+
+static void
+vf_local_font_warning(internal_font_number f, internal_font_number k, const char *s, int a, int b)
+{
+    print_nlp();
+    tprint(s);
+    tprint(" in local font ");
+    tprint(font_name(k));
+    tprint(" (");
+    print_int(b);
+    tprint(" != ");
+    print_int(a);
+    tprint(") in virtual font ");
+    tprint(font_name(f));
+    tprint(".vf ignored.");
+}
+
+
+@ process a local font in \.{VF} file
+@c
+int level = 0;
+
+static internal_font_number
+vf_def_font(internal_font_number f, unsigned char *vf_buffer, int *vf_cr)
+{
+    internal_font_number k;
+    str_number s;
+    char *st;
+    scaled ds, fs;
+    four_quarters cs;
+    memory_word tmp_w;          /* accumulator */
+    int junk;
+    unsigned int checksum;
+    cs.b0 = vf_buffer[(*vf_cr)];
+    cs.b1 = vf_buffer[(*vf_cr) + 1];
+    cs.b2 = vf_buffer[(*vf_cr) + 2];
+    cs.b3 = vf_buffer[(*vf_cr) + 3];
+    (*vf_cr) += 4;
+    checksum = (unsigned)
+        (cs.b0 * 256 * 256 * 256 + cs.b1 * 256 * 256 + cs.b2 * 256 + cs.b3);
+    k = vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+    if (k > 127)
+        k -= 256;
+    k = k * 256 + vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+    k = k * 256 + vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+    k = k * 256 + vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+
+    fs = store_scaled_f(k, font_size(f));
+
+    k = vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+    if (k > 127)
+        k -= 256;
+    k = k * 256 + vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+    k = k * 256 + vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+    k = k * 256 + vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+
+    ds = k / 16;
+
+
+    tmp_b0 = vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+    tmp_b1 = vf_buffer[(*vf_cr)];
+    (*vf_cr)++;
+    while (tmp_b0 > 0) {
+        tmp_b0--;
+        (*vf_cr)++;             /* skip the font path */
+    }
+    str_room((unsigned) tmp_b1);
+    while (tmp_b1 > 0) {
+        tmp_b1--;
+        junk = vf_buffer[(*vf_cr)];
+        (*vf_cr)++;
+        append_char(junk);
+    }
+    if (level > 5) {
+        normal_warning("vf","quitting at recurse depth > 5");
+        k = f ;
+    } else if ((level > 1) && (fs > 65536*1024)) {
+        normal_warning("vf","quitting when recursing at size > 65536*1024");
+        k = f ;
+    } else {
+        level += 1 ;
+        s = make_string();
+        st = makecstring(s);
+        k = tfm_lookup(st, fs);
+        if (k == null_font)
+            k = read_font_info(null_cs, st, fs, -1);
+        free(st);
+        level -= 1 ;
+        if (k != null_font) {
+            if (checksum != 0 && font_checksum(k) != 0
+                && checksum != font_checksum(k))
+                vf_local_font_warning(f, k, "checksum mismatch", (int) checksum, (int) font_checksum(k));
+            if (ds != font_dsize(k))
+                vf_local_font_warning(f, k, "design size mismatch", ds, font_dsize(k));
+        }
+    }
+    return k;
+}
+
+@ @c
+static int open_vf_file(const char *fn, unsigned char **vbuffer, int *vsize)
+{
+    boolean res;                /* was the callback successful? */
+    int callback_id;
+    boolean file_read = false;  /* was |vf_file| successfully read? */
+    FILE *vf_file;
+    const char *fname = luatex_find_file(fn, find_vf_file_callback);
+    if (fname == NULL || strlen(fname) == 0) {
+       /* fname = fn; */
+       return 0;
+    }
+
+    callback_id = callback_defined(read_vf_file_callback);
+    if (callback_id > 0) {
+        res = run_callback(callback_id, "S->bSd", fname,
+                           &file_read, vbuffer, vsize);
+        if (res && file_read && (*vsize > 0)) {
+            return 1;
+        }
+        if (!file_read)
+            return 0;           /* -1 */
+    } else {
+        if (luatex_open_input
+            (&(vf_file), fname, kpse_ovf_format, FOPEN_RBIN_MODE, false)
+            || luatex_open_input(&(vf_file), fname, kpse_vf_format,
+                                 FOPEN_RBIN_MODE, false)) {
+            res = read_vf_file(vf_file, vbuffer, vsize);
+            close_file(vf_file);
+            if (res) {
+                return 1;
+            }
+        } else {
+            return 0;           /* -1 */
+        }
+    }
+    return 0;
+}
+
+
+
+@ The |do_vf| procedure attempts to read the \.{VF} file for a font, and sets
+  |font_type()| to |real_font_type| if the \.{VF} file could not be found
+  or loaded, otherwise sets |font_type()| to |virtual_font_type|.  At this
+  time, |tmp_f| is the internal font number of the current \.{TFM} font.  To
+  process font definitions in virtual font we call |vf_def_font|.
+
+@c
+#define append_packet(k) vpackets[vf_np++] = (eight_bits)(k)
+
+@ life is easier if all internal font commands are fnt4 and
+   all character commands are set4 or put4
+
+@c
+#define append_fnt_set(k)            \
+{                                    \
+    assert(k > 0);                   \
+    append_packet(packet_font_code); \
+    append_four(k);                  \
+}
+
+#define append_four(k)                     \
+{                                          \
+    append_packet((k & 0xFF000000) >> 24); \
+    append_packet((k & 0x00FF0000) >> 16); \
+    append_packet((k & 0x0000FF00) >> 8);  \
+    append_packet((k & 0x000000FF));       \
+}
+
+@ some of these things happen twice, adding a define is simplest
+
+@c
+#define test_checksum()  { vf_byte(tmp_b0); vf_byte(tmp_b1);    \
+    vf_byte(tmp_b2); vf_byte(tmp_b3);         \
+    if (((tmp_b0 != 0) || (tmp_b1 != 0) || (tmp_b2 != 0) || (tmp_b3 != 0)) && \
+  ((font_check_0(f) != 0) || (font_check_1(f) != 0) ||    \
+   (font_check_2(f) != 0) || (font_check_3(f) != 0)) &&   \
+  ((tmp_b0 != font_check_0(f)) || (tmp_b1 != font_check_1(f)) ||  \
+   (tmp_b2 != font_check_2(f)) || (tmp_b3 != font_check_3(f)))) { \
+      print_nlp();              \
+      tprint("checksum mismatch in font ");     \
+      tprint(font_name(f));         \
+      tprint(".vf ignored "); } }
+
+#define test_dsize()                                   \
+{                                                      \
+    int read_tmp;                                      \
+    vf_read(4, read_tmp);                              \
+    if ((read_tmp / 16) != font_dsize(f)) {            \
+        print_nlp();                                   \
+        tprint("design size mismatch in font ");       \
+        tprint(font_name(f));			       \
+        tprint(".vf ignored");			       \
+    }                                                  \
+}
+
+@ @c
+static int count_packet_bytes(eight_bits * vf_buf, int cur_bute, int count)
+{
+    unsigned k = 0;
+    int ff = 0;
+    int acc = 0;
+    unsigned int cmd = 0;
+    unsigned int d = 0;
+    while (k < (unsigned) count) {
+        cmd = vf_buf[cur_bute + (int) k];
+        k++;
+        if (cmd < set1) {
+            if (ff == 0) {
+                ff = 1;
+                acc += 5;
+            }
+            acc += 5;
+        } else if ((fnt_num_0 <= cmd) && (cmd <= fnt_num_0 + 63)) {
+            ff = 1;
+            acc += 5;
+        } else {
+            switch (cmd) {
+            case fnt1:
+                acc += 5;
+                k += 1;
+                ff = 1;
+                break;
+            case fnt2:
+                acc += 5;
+                k += 2;
+                ff = 1;
+                break;
+            case fnt3:
+                acc += 5;
+                k += 3;
+                ff = 1;
+                break;
+            case fnt4:
+                acc += 5;
+                k += 4;
+                ff = 1;
+                break;
+            case set_rule:
+                acc += 9;
+                k += 8;
+                break;
+            case put_rule:
+                acc += 11;
+                k += 8;
+                break;
+            case set1:
+                acc += 5;
+                k += 1;
+                if (ff == 0) {
+                    ff = 1;
+                    acc += 5;
+                }
+                break;
+            case set2:
+                acc += 5;
+                k += 2;
+                if (ff == 0) {
+                    ff = 1;
+                    acc += 5;
+                }
+                break;
+            case set3:
+                acc += 5;
+                k += 3;
+                if (ff == 0) {
+                    ff = 1;
+                    acc += 5;
+                }
+                break;
+            case set4:
+                acc += 5;
+                k += 4;
+                if (ff == 0) {
+                    ff = 1;
+                    acc += 5;
+                }
+                break;
+            case put1:
+                acc += 7;
+                k += 1;
+                if (ff == 0) {
+                    ff = 1;
+                    acc += 5;
+                }
+                break;
+            case put2:
+                acc += 7;
+                k += 2;
+                if (ff == 0) {
+                    ff = 1;
+                    acc += 5;
+                }
+                break;
+            case put3:
+                acc += 7;
+                k += 3;
+                if (ff == 0) {
+                    ff = 1;
+                    acc += 5;
+                }
+                break;
+            case put4:
+                acc += 7;
+                k += 4;
+                if (ff == 0) {
+                    ff = 1;
+                    acc += 5;
+                }
+                break;
+            case right1:
+                acc += 5;
+                k += 1;
+                break;
+            case right2:
+                acc += 5;
+                k += 2;
+                break;
+            case right3:
+                acc += 5;
+                k += 3;
+                break;
+            case right4:
+                acc += 5;
+                k += 4;
+                break;
+            case w1:
+                acc += 5;
+                k += 1;
+                break;
+            case w2:
+                acc += 5;
+                k += 2;
+                break;
+            case w3:
+                acc += 5;
+                k += 3;
+                break;
+            case w4:
+                acc += 5;
+                k += 4;
+                break;
+            case x1:
+                acc += 5;
+                k += 1;
+                break;
+            case x2:
+                acc += 5;
+                k += 2;
+                break;
+            case x3:
+                acc += 5;
+                k += 3;
+                break;
+            case x4:
+                acc += 5;
+                k += 4;
+                break;
+            case down1:
+                acc += 5;
+                k += 1;
+                break;
+            case down2:
+                acc += 5;
+                k += 2;
+                break;
+            case down3:
+                acc += 5;
+                k += 3;
+                break;
+            case down4:
+                acc += 5;
+                k += 4;
+                break;
+            case y1:
+                acc += 5;
+                k += 1;
+                break;
+            case y2:
+                acc += 5;
+                k += 2;
+                break;
+            case y3:
+                acc += 5;
+                k += 3;
+                break;
+            case y4:
+                acc += 5;
+                k += 4;
+                break;
+            case z1:
+                acc += 5;
+                k += 1;
+                break;
+            case z2:
+                acc += 5;
+                k += 2;
+                break;
+            case z3:
+                acc += 5;
+                k += 3;
+                break;
+            case z4:
+                acc += 5;
+                k += 4;
+                break;
+            case xxx1:
+                d = vf_buf[cur_bute + (int) k];
+                k++;
+                k += d;
+                acc += 5 + (int) d;
+                break;
+            case xxx2:
+                d = vf_buf[cur_bute + (int) k];
+                k++;
+                d = d * 256 + vf_buf[cur_bute + (int) k];
+                k++;
+                k += d;
+                acc += 5 + (int) d;
+                break;
+            case xxx3:
+                d = vf_buf[cur_bute + (int) k];
+                k++;
+                d = d * 256 + vf_buf[cur_bute + (int) k];
+                k++;
+                d = d * 256 + vf_buf[cur_bute + (int) k];
+                k++;
+                k += d;
+                acc += 5 + (int) d;
+                break;
+            case xxx4:
+                d = vf_buf[cur_bute + (int) k];
+                k++;
+                d = d * 256 + vf_buf[cur_bute + (int) k];
+                k++;
+                d = d * 256 + vf_buf[cur_bute + (int) k];
+                k++;
+                d = d * 256 + vf_buf[cur_bute + (int) k];
+                k++;
+                k += d;
+                acc += 5 + (int) d;
+                break;
+            case w0:
+                acc += 5;
+                break;
+            case x0:
+                acc += 5;
+                break;
+            case y0:
+                acc += 5;
+                break;
+            case z0:
+                acc += 5;
+                break;
+            case nop:
+                break;
+            case push:
+                acc += 1;
+                break;
+            case pop:
+                acc += 1;
+                break;
+            }
+        }
+    }
+    return (acc + 1);
+}
+
+@ @c
+void do_vf(internal_font_number f)
+{
+    int k, i;
+    unsigned cmd, n;
+    scaled x, y, w, z, h, v;
+    int cc, cmd_length;
+    unsigned packet_length;
+    charinfo *co;
+    scaled tfm_width;
+    int save_cur_byte;
+    vf_stack_index stack_level;
+    int vf_z;                   /* multiplier */
+    int vf_alpha;               /* correction for negative values */
+    /*char vf_beta;*/               /* divisor */
+    int vf_np;
+    eight_bits *vpackets;
+    memory_word tmp_w;          /* accumulator */
+    vf_stack_record vf_stack[256];
+    int junk;
+    unsigned utmp;
+    unsigned char *vf_buffer;
+    int vf_size;
+    int vf_cur;
+    unsigned *vf_local_fnts = NULL;     /* external font ids */
+    unsigned *vf_real_fnts = NULL;      /* internal font ids */
+    unsigned vf_nf = 0;         /* local font counter */
+
+    if (font_type(f) != unknown_font_type)
+        return;
+    set_font_type(f, real_font_type);
+    stack_level = 0;
+    /* Open |vf_file|, return if not found */
+    vf_cur = 0;
+    vf_buffer = NULL;
+    vf_size = 0;
+    if (!open_vf_file(font_name(f), &vf_buffer, &vf_size))
+        return;
+    /* Process the preamble */
+    set_font_type(f, virtual_font_type);
+    vf_byte(k);
+    if (k != pre)
+        bad_vf("PRE command expected");
+    vf_byte(k);
+    if (k != vf_id)
+        bad_vf("wrong id byte");
+    vf_byte(cmd_length);
+    for (k = 1; k <= cmd_length; k++)
+        vf_byte(junk);
+    test_checksum();
+    test_dsize();
+    vf_z = font_size(f);
+    vf_replace_z();
+    /* Process the font definitions */
+    /* scan forward to find the number of internal fonts */
+    vf_nf = 0;
+    save_cur_byte = vf_cur;
+    vf_byte(cmd);
+    while ((cmd >= fnt_def1) && (cmd <= (fnt_def1 + 3))) {
+        vf_read_u((cmd - fnt_def1 + 1), utmp);
+        vf_read(4, junk);
+        vf_read(4, junk);
+        vf_read(4, junk);
+        vf_byte(k);
+        vf_byte(junk);
+        k += junk;
+        while (k-- > 0) {
+            vf_byte(junk);
+        }
+        incr(vf_nf);
+        vf_byte(cmd);
+    }
+    vf_cur = save_cur_byte;
+    vf_byte(cmd);
+    /* malloc and fill the local font arrays */
+    if (vf_nf > 0) {
+        unsigned ii = (unsigned) ((unsigned) vf_nf * sizeof(int));
+        vf_local_fnts = xmalloc(ii);
+        memset(vf_local_fnts, 0, ii);
+        vf_real_fnts = xmalloc(ii);
+        memset(vf_real_fnts, 0, ii);
+        vf_nf = 0;
+        while ((cmd >= fnt_def1) && (cmd <= (fnt_def1 + 3))) {
+            vf_read_u((cmd - fnt_def1 + 1), vf_local_fnts[vf_nf]);
+            vf_real_fnts[vf_nf] = (unsigned) vf_def_font(f, vf_buffer, &vf_cur);
+            incr(vf_nf);
+            vf_byte(cmd);
+        }
+    }
+
+
+    while (cmd <= long_char) {
+        /* Build a character packet */
+        vf_np = 0;
+        if (cmd == long_char) {
+            vf_read_u(4, packet_length);
+            vf_read_u(4, utmp);
+            cc = (int) utmp;
+            if (!char_exists(f, cc)) {
+                bad_vf("invalid character code");
+            }
+            vf_read(4, k);
+            tfm_width = store_scaled_f(k, font_size(f));
+        } else {
+            packet_length = cmd;
+            vf_byte(cc);
+            if (!char_exists(f, cc)) {
+                bad_vf("invalid character code");
+            }
+            vf_read_u(3, utmp);
+            k = (int) utmp;     /* cf. vftovp.web, line 1028 */
+            tfm_width = store_scaled_f(k, font_size(f));
+        }
+
+
+        if (tfm_width != char_width(f, cc)) {
+            if (tfm_width != char_width(f, cc)) {
+                print_nlp();
+                tprint("character width mismatch in font ");
+                tprint(font_name(f));
+                tprint(".vf ignored");
+            }
+        }
+        k = count_packet_bytes(vf_buffer, vf_cur, (int) packet_length);
+        vpackets = xmalloc((unsigned) (k + 1)); /* need one extra extra for |packet_end| */
+        co = get_charinfo(f, cc);
+        k = 0;
+        w = 0;
+        x = 0;
+        y = 0;
+        z = 0;
+        while (packet_length > 0) {
+            vf_byte(cmd);
+            decr(packet_length);
+
+            if (cmd < set1) {
+                if (k == 0) {
+                    k = (int) vf_real_fnts[0];
+                    append_fnt_set(k);
+                }
+                append_packet(packet_char_code);
+                append_four(cmd);
+                cmd_length = 0;
+                cmd = nop;
+
+            } else if (((fnt_num_0 <= cmd) && (cmd <= fnt_num_0 + 63)) ||
+                       ((fnt1 <= cmd) && (cmd <= fnt1 + 3))) {
+                if (cmd >= fnt1) {
+                    vf_read_u((cmd - fnt1 + 1), utmp);
+                    k = (int) utmp;
+                    packet_length -= (cmd - fnt1 + 1);
+                } else {
+                    k = (int) cmd - fnt_num_0;
+                }
+
+                /*  change from local to external font id */
+                n = 0;
+                while ((n < vf_nf) && (vf_local_fnts[n] != (unsigned) k))
+                    n++;
+                if (n == vf_nf)
+                    bad_vf("undefined local font");
+
+                k = (int) vf_real_fnts[n];
+                append_fnt_set(k);
+                cmd_length = 0;
+                cmd = nop;
+            } else {
+                switch (cmd) {
+                case set_rule:
+                    vf_read(4, h);
+                    vf_read(4, v);
+                    append_packet(packet_rule_code);
+                    append_four(h);
+                    append_four(v);
+                    packet_length -= 8;
+                    break;
+                case put_rule:
+                    vf_read(4, h);
+                    vf_read(4, v);
+                    append_packet(packet_push_code);
+                    append_packet(packet_rule_code);
+                    append_four(h);
+                    append_four(v);
+                    append_packet(packet_pop_code);
+                    packet_length -= 8;
+                    break;
+                case set1:
+                case set2:
+                case set3:
+                case set4:
+                    if (k == 0) {
+                        k = (int) vf_real_fnts[0];
+                        append_fnt_set(k);
+                    }
+                    vf_read_u((cmd - set1 + 1), utmp);
+                    i = (int) utmp;
+                    append_packet(packet_char_code);
+                    append_four(i);
+                    packet_length -= (cmd - set1 + 1);
+                    break;
+                case put1:
+                case put2:
+                case put3:
+                case put4:
+                    if (k == 0) {
+                        k = (int) vf_real_fnts[0];
+                        append_fnt_set(k);
+                    }
+                    vf_read_u((cmd - put1 + 1), utmp);
+                    i = (int) utmp;
+                    append_packet(packet_push_code);
+                    append_packet(packet_char_code);
+                    append_four(i);
+                    append_packet(packet_pop_code);
+                    packet_length -= (cmd - put1 + 1);
+                    break;
+                case right1:
+                case right2:
+                case right3:
+                case right4:
+                    vf_read((cmd - right1 + 1), i);
+                    append_packet(packet_right_code);
+                    append_four(i);
+                    packet_length -= (cmd - right1 + 1);
+                    break;
+                case w1:
+                case w2:
+                case w3:
+                case w4:
+                    vf_read((cmd - w1 + 1), w);
+                    append_packet(packet_right_code);
+                    append_four(w);
+                    packet_length -= (cmd - w1 + 1);
+                    break;
+                case x1:
+                case x2:
+                case x3:
+                case x4:
+                    vf_read((cmd - x1 + 1), x);
+                    append_packet(packet_right_code);
+                    append_four(x);
+                    packet_length -= (cmd - x1 + 1);
+                    break;
+                case down1:
+                case down2:
+                case down3:
+                case down4:
+                    vf_read((cmd - down1 + 1), i);
+                    append_packet(packet_down_code);
+                    append_four(i);
+                    packet_length -= (cmd - down1 + 1);
+                    break;
+                case y1:
+                case y2:
+                case y3:
+                case y4:
+                    vf_read((cmd - y1 + 1), y);
+                    append_packet(packet_down_code);
+                    append_four(y);
+                    packet_length -= (cmd - y1 + 1);
+                    break;
+                case z1:
+                case z2:
+                case z3:
+                case z4:
+                    vf_read((cmd - z1 + 1), z);
+                    append_packet(packet_down_code);
+                    append_four(z);
+                    packet_length -= (cmd - z1 + 1);
+                    break;
+                case xxx1:
+                case xxx2:
+                case xxx3:
+                case xxx4:
+                    vf_read_u((cmd - xxx1 + 1), utmp);
+                    cmd_length = (int) utmp;
+                    packet_length -= (cmd - xxx1 + 1);
+                    if (cmd_length <= 0)
+                        bad_vf("special of negative length");
+                    packet_length -= (unsigned) cmd_length;
+
+                    append_packet(packet_special_code);
+                    append_four(cmd_length);
+                    while (cmd_length > 0) {
+                        cmd_length--;
+                        vf_byte(i);
+                        append_packet(i);
+                    }
+                    break;
+                case w0:
+                    append_packet(packet_right_code);
+                    append_four(w);
+                    break;
+                case x0:
+                    append_packet(packet_right_code);
+                    append_four(x);
+                    break;
+                case y0:
+                    append_packet(packet_down_code);
+                    append_four(y);
+                    break;
+                case z0:
+                    append_packet(packet_down_code);
+                    append_four(z);
+                    break;
+                case nop:
+                    break;
+                case push:
+                    if (stack_level == vf_stack_size) {
+                        overflow("virtual font stack size", vf_stack_size);
+                    } else {
+                        vf_stack[stack_level].stack_w = w;
+                        vf_stack[stack_level].stack_x = x;
+                        vf_stack[stack_level].stack_y = y;
+                        vf_stack[stack_level].stack_z = z;
+                        incr(stack_level);
+                        append_packet(packet_push_code);
+                    }
+                    break;
+                case pop:
+                    if (stack_level == 0) {
+                        bad_vf("more POPs than PUSHs in character");
+                    } else {
+                        decr(stack_level);
+                        w = vf_stack[stack_level].stack_w;
+                        x = vf_stack[stack_level].stack_x;
+                        y = vf_stack[stack_level].stack_y;
+                        z = vf_stack[stack_level].stack_z;
+                        append_packet(packet_pop_code);
+                    }
+                    break;
+                default:
+                    bad_vf("improver DVI command");
+                }
+            }
+        }
+        /* signal end of packet */
+        append_packet(packet_end_code);
+
+        if (stack_level != 0)
+            bad_vf("more PUSHs than POPs in character packet");
+        if (packet_length != 0)
+            bad_vf("invalid packet length or DVI command in packet");
+        /* \.{Store the packet being built} */
+        set_charinfo_packets(co, vpackets);
+        vf_byte(cmd);
+    }
+    if (cmd != post)
+        bad_vf("POST command expected");
+
+    xfree(vf_buffer);
+}
+
+@ @c
+#define make_command0(N,K) { \
+    lua_newtable(L);         \
+    lua_pushstring(L, N);    \
+    lua_rawseti(L,-2, 1);    \
+    lua_rawseti(L,-2, K);    \
+    K++; }
+
+#define make_command1(N,V,K) { \
+    lua_newtable(L);           \
+    lua_pushstring(L, N);      \
+    lua_rawseti(L,-2, 1);      \
+    lua_pushinteger(L, V);     \
+    lua_rawseti(L,-2, 2);      \
+    lua_rawseti(L,-2, K);      \
+    K++; }
+
+#define make_command2(N,V,W,K) { \
+    lua_newtable(L);             \
+    lua_pushstring(L, N);        \
+    lua_rawseti(L,-2, 1);        \
+    lua_pushinteger(L, V);       \
+    lua_rawseti(L,-2, 2);        \
+    lua_pushinteger(L, W);       \
+    lua_rawseti(L,-2, 3);        \
+    lua_rawseti(L,-2, K);        \
+    K++; }
+
+#define make_commands(N,S,V,K) {    \
+    lua_newtable(L);        \
+    lua_pushstring(L, N);     \
+    lua_rawseti(L,-2, 1);     \
+    lua_pushlstring(L, S, V);     \
+    lua_rawseti(L,-2, 2);     \
+    lua_rawseti(L,-2, K);     \
+    K++; }
+
+
+int make_vf_table(lua_State * L, const char *cnom, scaled atsize)
+{
+    int cmd, k, i;
+    int cc;
+    unsigned cmd_length, packet_length;
+    scaled tfm_width;
+    vf_stack_index stack_level;
+    int vf_z;                   /* multiplier */
+    int vf_alpha;               /* correction for negative values */
+    /*char vf_beta;*/               /* divisor */
+    eight_bits *s;
+    scaled h, v;
+    scaled w, x, y, z;
+    int s_top;                  /* lua stack */
+    int vf_nf;                  /* local font counter */
+    scaled ds, fs;
+    four_quarters cs;
+    memory_word tmp_w;          /* accumulator */
+    vf_stack_record vf_stack[256];
+    unsigned char *vf_buffer;
+    int vf_size;
+    int vf_cur;
+    unsigned utmp;
+
+
+    stack_level = 0;
+    /* Open |vf_file|, return if not found */
+    vf_cur = 0;
+    vf_buffer = NULL;
+    vf_size = 0;
+    if (!open_vf_file(cnom, &vf_buffer, &vf_size)) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    /* start by creating a table */
+    s_top = lua_gettop(L);
+    lua_newtable(L);
+
+    /* Process the preamble */
+    vf_byte(k);
+    if (k != pre)
+        lua_bad_vf("PRE command expected");
+    vf_byte(k);
+    if (k != vf_id)
+        lua_bad_vf("wrong id byte");
+    vf_byte(cmd_length);
+
+    s = xmalloc(cmd_length);
+    for (k = 1; k <= (int) cmd_length; k++)
+        vf_byte(s[(k - 1)]);
+
+    lua_pushlstring(L, (char *) s, (size_t) cmd_length);
+    free(s);
+    lua_setfield(L, -2, "header");
+
+    vf_byte(cs.b0);
+    vf_byte(cs.b1);
+    vf_byte(cs.b2);
+    vf_byte(cs.b3);
+    lua_pushinteger(L,  (lua_Number) ((cs.b0 << 24) + (cs.b1 << 16) + (cs.b2 << 8) + cs.b3));
+    lua_setfield(L, -2, "checksum");
+
+    vf_read(4, k);
+    ds = k / 16;
+    lua_pushinteger(L, ds);
+    lua_setfield(L, -2, "designsize");
+
+
+    lua_pushstring(L, cnom);
+    lua_setfield(L, -2, "name");
+
+    lua_pushinteger(L, atsize);
+    lua_setfield(L, -2, "size");
+
+    vf_z = atsize;
+    vf_replace_z();
+    /* Process the font definitions */
+    vf_byte(cmd);
+    lua_newtable(L);
+
+    i = 1;
+    while ((cmd >= fnt_def1) && (cmd <= fnt_def1 + 3)) {
+
+        lua_newtable(L);
+        vf_read_u((cmd - fnt_def1 + 1), utmp);
+        vf_nf = (int) utmp;
+        vf_nf++;
+        /* checksum */
+        vf_byte(cs.b0);
+        vf_byte(cs.b1);
+        vf_byte(cs.b2);
+        vf_byte(cs.b3);
+
+        vf_read(4, k);
+        fs = store_scaled_f(k, atsize);
+        lua_pushstring(L, "size");
+        lua_pushinteger(L, fs);
+        lua_rawset(L, -3);
+
+        vf_read(4, k);
+        ds = k / 16;            /* dsize, not used */
+
+        vf_byte(tmp_b0);
+        vf_byte(tmp_b1);
+        while (tmp_b0 > 0) {
+            tmp_b0--;
+            vf_byte(k);
+        }                       /* skip the font path */
+
+        s = xmalloc((unsigned) (tmp_b1 + 1));
+        k = 0;
+        while (tmp_b1-- > 0)
+            vf_byte(s[k++]);
+        s[k] = 0;
+        lua_pushstring(L, "name");
+        lua_pushstring(L, xstrdup((char *) s));
+        free(s);
+        lua_rawset(L, -3);
+
+        lua_rawseti(L, -2, vf_nf);
+        i++;
+        vf_byte(cmd);
+    }
+
+    if (i > 1) {
+        lua_setfield(L, -2, "fonts");
+    } else {
+        lua_pop(L, 1);
+    }
+
+    lua_newtable(L);            /* 'characters' */
+    while (cmd <= long_char) {
+        /* Build a character packet */
+        if (cmd == long_char) {
+            vf_read_u(4, packet_length);
+            vf_read_u(4, utmp);
+            cc = (int) utmp;
+            vf_read(4, tfm_width);
+        } else {
+            packet_length = (unsigned) cmd;
+            vf_byte(cc);
+            vf_read_u(3, utmp);
+            tfm_width = (int) utmp;
+        }
+        lua_newtable(L);        /* for this character */
+        lua_pushinteger(L, tfm_width);
+        lua_setfield(L, -2, "width");
+        lua_newtable(L);        /* for 'commands' */
+        k = 1;
+        vf_nf = 0;
+        w = 0;
+        x = 0;
+        y = 0;
+        z = 0;
+        while (packet_length > 0) {
+            vf_byte(cmd);
+            decr(packet_length);
+            if ((cmd >= set_char_0) && (cmd < set1)) {
+                if (vf_nf == 0) {
+                    vf_nf = 1;
+                    make_command1("font", vf_nf, k);
+                }
+                make_command1("char", cmd, k);
+            } else if (((fnt_num_0 <= cmd) && (cmd <= fnt_num_0 + 63)) ||
+                       ((fnt1 <= cmd) && (cmd <= fnt1 + 3))) {
+                if (cmd >= fnt1) {
+                    vf_read_u((cmd - fnt1 + 1), utmp);
+                    vf_nf = (int) utmp;
+                    vf_nf++;
+                    packet_length -= (unsigned) (cmd - fnt1 + 1);
+                } else {
+                    vf_nf = cmd - fnt_num_0 + 1;
+                }
+                make_command1("font", vf_nf, k);
+            } else {
+                switch (cmd) {
+                case set_rule:
+                    vf_read(4, h);
+                    vf_read(4, v);
+                    make_command2("rule", store_scaled_f(h, atsize),
+                                  store_scaled_f(v, atsize), k);
+                    packet_length -= 8;
+                    break;
+                case put_rule:
+                    vf_read(4, h);
+                    vf_read(4, v);
+                    make_command0("push", k);
+                    make_command2("rule", store_scaled_f(h, atsize),
+                                  store_scaled_f(v, atsize), k);
+                    make_command0("pop", k);
+                    packet_length -= 8;
+                    break;
+                case set1:
+                case set2:
+                case set3:
+                case set4:
+                    if (vf_nf == 0) {
+                        vf_nf = 1;
+                        make_command1("font", vf_nf, k);
+                    }
+                    vf_read_u((cmd - set1 + 1), utmp);
+                    i = (int) utmp;
+                    make_command1("char", i, k);
+                    packet_length -= (unsigned) (cmd - set1 + 1);
+                    break;
+                case put1:
+                case put2:
+                case put3:
+                case put4:
+                    if (vf_nf == 0) {
+                        vf_nf = 1;
+                        make_command1("font", vf_nf, k);
+                    }
+                    vf_read_u((cmd - put1 + 1), utmp);
+                    i = (int) utmp;
+                    make_command0("push", k);
+                    make_command1("char", i, k);
+                    make_command0("pop", k);
+                    packet_length -= (unsigned) (cmd - put1 + 1);
+                    break;
+                case right1:
+                case right2:
+                case right3:
+                case right4:
+                    vf_read((cmd - right1 + 1), i);
+                    make_command1("right", store_scaled_f(i, atsize), k);
+                    packet_length -= (unsigned) (cmd - right1 + 1);
+                    break;
+                case w1:
+                case w2:
+                case w3:
+                case w4:
+                    vf_read((cmd - w1 + 1), w);
+                    make_command1("right", store_scaled_f(w, atsize), k);
+                    packet_length -= (unsigned) (cmd - w1 + 1);
+                    break;
+                case x1:
+                case x2:
+                case x3:
+                case x4:
+                    vf_read((cmd - x1 + 1), x);
+                    make_command1("right", store_scaled_f(x, atsize), k);
+                    packet_length -= (unsigned) (cmd - x1 + 1);
+                    break;
+                case down1:
+                case down2:
+                case down3:
+                case down4:
+                    vf_read((cmd - down1 + 1), i);
+                    make_command1("down", store_scaled_f(i, atsize), k);
+                    packet_length -= (unsigned) (cmd - down1 + 1);
+                    break;
+                case y1:
+                case y2:
+                case y3:
+                case y4:
+                    vf_read((cmd - y1 + 1), y);
+                    make_command1("down", store_scaled_f(y, atsize), k);
+                    packet_length -= (unsigned) (cmd - y1 + 1);
+                    break;
+                case z1:
+                case z2:
+                case z3:
+                case z4:
+                    vf_read((cmd - z1 + 1), z);
+                    make_command1("down", store_scaled_f(z, atsize), k);
+                    packet_length -= (unsigned) (cmd - z1 + 1);
+                    break;
+                case xxx1:
+                case xxx2:
+                case xxx3:
+                case xxx4:
+                    vf_read_u((cmd - xxx1 + 1), cmd_length);
+                    packet_length -= (unsigned) (cmd - xxx1 + 1);
+                    if (cmd_length <= 0)
+                        lua_bad_vf("special of negative length");
+                    packet_length -= cmd_length;
+
+                    s = xmalloc((cmd_length + 1));
+                    i = 0;
+                    while (cmd_length > 0) {
+                        cmd_length--;
+                        vf_byte(s[i]);
+                        i++;
+                    }
+                    s[i] = 0;
+                    make_commands("special", xstrdup((char *) s), (size_t) i,
+                                  k);
+                    free(s);
+                    break;
+                case w0:
+                    make_command1("right", store_scaled_f(w, atsize), k);
+                    break;
+                case x0:
+                    make_command1("right", store_scaled_f(x, atsize), k);
+                    break;
+                case y0:
+                    make_command1("down", store_scaled_f(y, atsize), k);
+                    break;
+                case z0:
+                    make_command1("down", store_scaled_f(z, atsize), k);
+                    break;
+                case nop:
+                    break;
+                case push:
+                    if (stack_level == vf_stack_size) {
+                        overflow("virtual font stack size", vf_stack_size);
+                    } else {
+                        vf_stack[stack_level].stack_w = w;
+                        vf_stack[stack_level].stack_x = x;
+                        vf_stack[stack_level].stack_y = y;
+                        vf_stack[stack_level].stack_z = z;
+                        incr(stack_level);
+                        make_command0("push", k);
+                    }
+                    break;
+                case pop:
+                    if (stack_level == 0) {
+                        lua_bad_vf("more POPs than PUSHs in character");
+                    } else {
+                        decr(stack_level);
+                        w = vf_stack[stack_level].stack_w;
+                        x = vf_stack[stack_level].stack_x;
+                        y = vf_stack[stack_level].stack_y;
+                        z = vf_stack[stack_level].stack_z;
+                        make_command0("pop", k);
+                    }
+                    break;
+                default:
+                    lua_bad_vf("improver DVI command");
+                }
+            }
+        }
+        /* signal end of packet */
+        lua_setfield(L, -2, "commands");
+
+        if (stack_level != 0)
+            lua_bad_vf("more PUSHs than POPs in character packet");
+        if (packet_length != 0)
+            lua_bad_vf("invalid packet length or DVI command in packet");
+
+        lua_rawseti(L, -2, cc);
+
+        vf_byte(cmd);
+    }
+    lua_setfield(L, -2, "characters");
+
+    if (cmd != post)
+        lua_bad_vf("POST command expected");
+    xfree(vf_buffer);
+    return 1;
+}
+
+@ @c
+internal_font_number
+letter_space_font(internal_font_number f, int e, boolean nolig)
+{
+    internal_font_number k;
+    scaled w;
+    int c;
+    charinfo *co;
+    char *new_font_name;
+
+    /* read a new font and expand the character widths */
+    k = copy_font(f);
+
+    if (nolig)
+      set_no_ligatures(k);        /* disable ligatures for letter-spaced fonts */
+
+    /* append eg '+100ls' to font name */
+    new_font_name = xmalloc((unsigned) (strlen(font_name(k)) + 8));     /* |abs(e) <= 1000| */
+    if (e > 0) {
+        sprintf(new_font_name, "%s+%ils", font_name(k), (int) e);
+    } else {
+        /* minus from \%i */
+        sprintf(new_font_name, "%s%ils", font_name(k), (int) e);
+    }
+    set_font_name(k, new_font_name);
+
+    /* create the corresponding virtual font */
+    set_font_type(k, virtual_font_type);
+
+    for (c=font_bc(k);c<=font_ec(k);c++) {
+       if (quick_char_exists(k, c)) {
+           int half_w;
+           int vf_np = 0;
+           eight_bits *vpackets = xmalloc((unsigned) (10+10+1));
+           if (e<0) {
+             half_w = -round_xn_over_d(quad(k), -e, 2000);
+           } else {
+             half_w  = round_xn_over_d(quad(k), e, 2000);
+           }
+           co = get_charinfo(k, c);
+           w = char_width(k, c)+2*half_w;
+           set_charinfo_width(co, w);
+
+           append_packet(packet_right_code);
+           append_four(half_w);
+           append_fnt_set(f);
+           append_packet(packet_char_code);
+           append_four(c);
+           append_packet(packet_right_code);
+           append_four(half_w);
+           append_packet(packet_end_code);
+
+           set_charinfo_packets(co, vpackets);
+       }
+    }
+    /* now patch the quad size */
+    /* Patch 20100922:  do not do this, to remain compatible with pdftex */
+#if 0
+    if (e<0) {
+       set_font_param(k, quad_code, -round_xn_over_d(quad(k), 1000-e, 1000));
+    } else {
+       set_font_param(k, quad_code, round_xn_over_d(quad(k), 1000+e, 1000));
+    }
+#endif
+    return k;
+}
+
+@ @c
+internal_font_number copy_font_info(internal_font_number f)
+{
+    return copy_font(f);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/vfpacket.c
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-/*tex
-
-    Some macros for processing character packets.
-
-*/
-
-#define packet_number(fw) {    \
-    fw = *(vfp++);             \
-    fw = fw * 256 + *(vfp++);  \
-    fw = fw * 256 + *(vfp++);  \
-    fw = fw * 256 + *(vfp++);  \
-}
-
-#define packet_scaled(a, fs) {  \
-    int fw;                     \
-    fw = *(vfp++);              \
-    if (fw > 127)               \
-        fw = fw - 256;          \
-    fw = fw * 256 + *(vfp++);   \
-    fw = fw * 256 + *(vfp++);   \
-    fw = fw * 256 + *(vfp++);   \
-    a = store_scaled_f(fw, fs); \
-}
-
-vf_struct *new_vfstruct(void)
-{
-    vf_struct *vp = (vf_struct *) xmalloc(sizeof(vf_struct));
-    vp->packet_stack_level = vp->packet_stack_minlevel = 0;
-    vp->packet_stack = (packet_stack_record *) xmalloc(packet_stack_size * sizeof(packet_stack_record));
-    vp->lf = 0;
-    vp->fs_f = 0;
-    vp->packet_cur_s = 0;
-    vp->refpos = NULL;
-    vp->vflua = false;
-    return vp;
-}
-
-/*tex
-
-     Count the number of bytes in a command packet.
-*/
-
-int vf_packet_bytes(charinfo * co)
-{
-    eight_bits *vf_packets, *vfp;
-    unsigned k;
-    int cmd;
-    vfp = vf_packets = get_charinfo_packets(co);
-    if (vf_packets == NULL) {
-        return 0;
-    }
-    while ((cmd = *(vfp++)) != packet_end_code) {
-        switch (cmd) {
-        case packet_nop_code:
-        case packet_pop_code:
-        case packet_push_code:
-            break;
-        case packet_char_code:
-        case packet_down_code:
-        case packet_font_code:
-        case packet_image_code:
-        case packet_node_code:
-        case packet_right_code:
-            vfp += 4;
-            break;
-        case packet_rule_code:
-            vfp += 8;
-            break;
-        case packet_pdf_mode:
-            vfp += 4;
-            break;
-        case packet_pdf_code:
-            vfp += 4;
-            /*tex Plus a string so we fall through: */
-        case packet_special_code:
-            /*tex |+4| */
-            packet_number(k);
-            vfp += (int) k;
-            break;
-        default:
-            normal_error("vf", "invalid DVI command (1)");
-        }
-    };
-    return (vfp - vf_packets);
-}
-
-/*tex
-
-    Typeset the \.{DVI} commands in the character packet for character |c| in
-    current font |f|.
-*/
-
-const char *packet_command_names[] = {
-    /*tex |slot| maps to |char| and |font| */
-    "char",
-    "font",
-    "pop",
-    "push",
-    "special",
-    "image",
-    "right",
-    "down",
-    "rule",
-    "node",
-    "nop",
-    "end",
-    /*tex the next one is not (yet) supported */
-    "scale",
-    "lua",
-    "pdf",
-    NULL
-};
-
-static float packet_float(eight_bits ** vfpp)
-{
-    unsigned int i;
-    union U {
-        float a;
-        eight_bits b[sizeof(float)];
-    } u;
-    eight_bits *vfp = *vfpp;
-    for (i = 0; i < sizeof(float); i++)
-        u.b[i] = *(vfp++);
-    *vfpp = vfp;
-    return u.a;
-}
-
-/*tex
-
-    The |do_vf_packet| procedure is called in order to interpret the character
-    packet for a virtual character. Such a packet may contain the instruction to
-    typeset a character from the same or an other virtual font; in such cases
-    |do_vf_packet| calls itself recursively. The recursion level, i.e., the
-    number of times this has happened, is kept in the global variable
-    |packet_cur_s| and should not exceed |packet_max_recursion|.
-*/
-
-void do_vf_packet(PDF pdf, internal_font_number vf_f, int c, int ex_glyph)
-{
-    eight_bits *vfp;
-    posstructure *save_posstruct, localpos;
-    vf_struct *save_vfstruct, localvfstruct, *vp;
-    int cmd, w, mode;
-    unsigned k;
-    scaledpos size;
-    scaled i;
-    str_number s;
-    float f;
-    packet_stack_record *mat_p;
-    vfp = get_charinfo_packets(get_charinfo(vf_f, c));
-    save_posstruct = pdf->posstruct;
-    /*tex use local structure for recursion */
-    pdf->posstruct = &localpos;
-    localpos.pos = save_posstruct->pos;
-    /*tex invariably for vf */
-    localpos.dir = dir_TLT;
-    save_vfstruct = pdf->vfstruct;
-    vp = pdf->vfstruct = &localvfstruct;
-    localvfstruct = *save_vfstruct;
-    vp->packet_stack_minlevel = ++(vp->packet_stack_level);
-    vp->lf = 0;
-    vp->fs_f = font_size(vf_f);
-    vp->ex_glyph = ex_glyph;
-    vp->packet_cur_s++;
-    if (vp->packet_cur_s == packet_max_recursion)
-        overflow("max level recursion of virtual fonts", packet_max_recursion);
-    vp->refpos = save_posstruct;
-    vp->vflua = false;
-    mat_p = &(vp->packet_stack[vp->packet_stack_level]);
-    mat_p->c0 = 1.0;
-    mat_p->c1 = 0.0;
-    mat_p->c2 = 0.0;
-    mat_p->c3 = 1.0;
-    mat_p->pos.h = 0;
-    mat_p->pos.v = 0;
-    while ((cmd = *(vfp++)) != packet_end_code) {
-        switch (cmd) {
-            case packet_font_code:
-                packet_number(vp->lf);
-                break;
-            case packet_push_code:
-                vp->packet_stack_level++;
-                if (vp->packet_stack_level == packet_stack_size)
-                    normal_error("vf", "packet_stack_level overflow");
-                vp->packet_stack[vp->packet_stack_level] = *mat_p;
-                mat_p = &(vp->packet_stack[vp->packet_stack_level]);
-                break;
-            case packet_pop_code:
-                if (vp->packet_stack_level == vp->packet_stack_minlevel)
-                    normal_error("vf", "packet_stack_level underflow");
-                vp->packet_stack_level--;
-                mat_p = &(vp->packet_stack[vp->packet_stack_level]);
-                break;
-            case packet_char_code:
-                packet_number(k);
-                /*tex We also check if |c == k| and |font(c) == font(k)| */
-                if (!char_exists(vp->lf, (int) k)) {
-                    char_warning(vp->lf, (int) k);
-                } else if (! ((c == k && vp->lf == vf_f)) && (has_packet(vp->lf, (int) k))) {
-                    do_vf_packet(pdf, vp->lf, (int) k, ex_glyph);
-                } else {
-                    backend_out[glyph_node] (pdf, vp->lf, (int) k, ex_glyph);
-                }
-                w = char_width(vp->lf, (int) k);
-                if (ex_glyph != 0 && w != 0)
-                    w = round_xn_over_d(w, 1000 + ex_glyph, 1000);
-                mat_p->pos.h += w;
-                break;
-            case packet_rule_code:
-                packet_scaled(size.v, vp->fs_f);
-                packet_scaled(size.h, vp->fs_f);
-                if (ex_glyph != 0 && size.h > 0)
-                    size.h = round_xn_over_d(size.h, 1000 + ex_glyph, 1000);
-                if (size.h > 0 && size.v > 0)
-                    backend_out[rule_node](pdf, 0, size);
-                mat_p->pos.h += size.h;
-                break;
-            case packet_right_code:
-                packet_scaled(i, vp->fs_f);
-                if (ex_glyph != 0 && i != 0)
-                    i = round_xn_over_d(i, 1000 + ex_glyph, 1000);
-                mat_p->pos.h += i;
-                break;
-            case packet_down_code:
-                packet_scaled(i, vp->fs_f);
-                mat_p->pos.v += i;
-                break;
-            case packet_pdf_code:
-                packet_number(mode);
-                packet_number(k);
-                str_room(k);
-                while (k > 0) {
-                    k--;
-                    append_char(*(vfp++));
-                }
-                s = make_string();
-                pdf_literal(pdf, s, mode, false);
-                flush_str(s);
-                break;
-            case packet_pdf_mode:
-                packet_number(mode);
-                pdf_literal_set_mode(pdf, mode);
-                break;
-            case packet_special_code:
-                packet_number(k);
-                str_room(k);
-                while (k > 0) {
-                    k--;
-                    append_char(*(vfp++));
-                }
-                s = make_string();
-                pdf_literal(pdf, s, scan_special, false);
-                flush_str(s);
-                break;
-            case packet_lua_code:
-                packet_number(k);
-                vp->vflua = true;
-                luacall_vf(k, vf_f, c);
-                /*tex
-
-                    We don't release as we (can ) flush multiple times, so no:
-
-                    \starttyping
-                    luaL_unref(Luas, LUA_REGISTRYINDEX, k);
-                    \stoptyping
-
-                    here!
-
-                */
-                vp->vflua = false;
-                break;
-            case packet_image_code:
-                packet_number(k);
-                vf_out_image(pdf, k);
-                break;
-            case packet_node_code:
-                packet_number(k);
-                hlist_out(pdf, (halfword) k, 0);
-                break;
-            case packet_nop_code:
-                break;
-            case packet_scale_code:
-                /*tex This is not yet supported in the backend. */
-                f = packet_float(&vfp);
-                mat_p->c0 = mat_p->c0 * f;
-                mat_p->c3 = mat_p->c3 * f;
-                /* pdf->pstruct->scale = f; */
-                pdf->pstruct->need_tm = true;
-                pdf->pstruct->need_tf = true;
-                break;
-            default:
-                normal_error("vf", "invalid DVI command (2)");
-        }
-        /*tex The trivial case, always |TLT|. */
-        synch_pos_with_cur(&localpos, save_posstruct, mat_p->pos);
-    }
-    pdf->posstruct = save_posstruct;
-    pdf->vfstruct = save_vfstruct;
-}
-
-int *packet_local_fonts(internal_font_number f, int *num)
-{
-    int c, cmd, lf, k, l, i;
-    int localfonts[256] = { 0 };
-    int *lfs;
-    charinfo *co;
-    eight_bits *vf_packets, *vfp;
-    k = 0;
-    for (c = font_bc(f); c <= font_ec(f); c++) {
-        if (quick_char_exists(f, c)) {
-            co = get_charinfo(f, c);
-            vfp = vf_packets = get_charinfo_packets(co);
-            if (vf_packets == NULL)
-                continue;
-            while ((cmd = *(vfp++)) != packet_end_code) {
-                switch (cmd) {
-                    case packet_font_code:
-                        packet_number(lf);
-                        for (l = 0; l < k; l++) {
-                            if (localfonts[l] == lf) {
-                                break;
-                            }
-                        }
-                        if (l == k) {
-                            localfonts[k++] = lf;
-                        }
-                        break;
-                    case packet_nop_code:
-                    case packet_pop_code:
-                    case packet_push_code:
-                        break;
-                    case packet_char_code:
-                    case packet_down_code:
-                    case packet_image_code:
-                    case packet_node_code:
-                    case packet_right_code:
-                        vfp += 4;
-                        break;
-                    case packet_rule_code:
-                        vfp += 8;
-                        break;
-                    case packet_special_code:
-                        packet_number(i);
-                        vfp += i;
-                        break;
-                    default:
-                        normal_error("vf", "invalid DVI command (3)");
-                }
-            }
-        }
-    }
-    *num = k;
-    if (k > 0) {
-        lfs = xmalloc((unsigned) ((unsigned) k * sizeof(int)));
-        memcpy(lfs, localfonts, (size_t) ((unsigned) k * sizeof(int)));
-        return lfs;
-    }
-    return NULL;
-}
-
-void replace_packet_fonts(internal_font_number f, int *old_fontid, int *new_fontid, int count)
-{
-    int c, cmd, lf, k, l;
-    charinfo *co;
-    eight_bits *vf_packets, *vfp;
-    for (c = font_bc(f); c <= font_ec(f); c++) {
-        if (quick_char_exists(f, c)) {
-            co = get_charinfo(f, c);
-            vfp = vf_packets = get_charinfo_packets(co);
-            if (vf_packets == NULL)
-                continue;
-            while ((cmd = *(vfp++)) != packet_end_code) {
-                switch (cmd) {
-                    case packet_font_code:
-                        packet_number(lf);
-                        for (l = 0; l < count; l++) {
-                            if (old_fontid[l] == lf) {
-                                break;
-                            }
-                        }
-                        if (l < count) {
-                            k = new_fontid[l];
-                            *(vfp - 4) = (eight_bits)
-                                ((k & 0xFF000000) >> 24);
-                            *(vfp - 3) = (eight_bits)
-                                ((k & 0x00FF0000) >> 16);
-                            *(vfp - 2) = (eight_bits)
-                                ((k & 0x0000FF00) >> 8);
-                            *(vfp - 1) = (eight_bits) (k & 0x000000FF);
-                        }
-                        break;
-                    case packet_nop_code:
-                    case packet_pop_code:
-                    case packet_push_code:
-                        break;
-                    case packet_char_code:
-                    case packet_down_code:
-                    case packet_image_code:
-                    case packet_node_code:
-                    case packet_right_code:
-                    case packet_rule_code:
-                        vfp += 8;
-                        break;
-                    case packet_pdf_mode:
-                        vfp += 4;
-                        break;
-                    case packet_pdf_code:
-                        vfp += 4;
-                        /*tex Plus a string so we fall through. */
-                    case packet_special_code:
-                        packet_number(k);
-                        vfp += k;
-                        break;
-                    default:
-                        normal_error("vf", "invalid DVI command (4)");
-                }
-            }
-        }
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/vfpacket.w
@@ -0,0 +1,424 @@
+% vfpacket.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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"
+
+@ Some macros for processing character packets.
+@c
+#define packet_number(fw) {    \
+    fw = *(vfp++);             \
+    fw = fw * 256 + *(vfp++);  \
+    fw = fw * 256 + *(vfp++);  \
+    fw = fw * 256 + *(vfp++);  \
+}
+
+#define packet_scaled(a, fs) {  \
+    int fw;                     \
+    fw = *(vfp++);              \
+    if (fw > 127)               \
+        fw = fw - 256;          \
+    fw = fw * 256 + *(vfp++);   \
+    fw = fw * 256 + *(vfp++);   \
+    fw = fw * 256 + *(vfp++);   \
+    a = store_scaled_f(fw, fs); \
+}
+
+@ @c
+vf_struct *new_vfstruct(void)
+{
+    vf_struct *vp = (vf_struct *) xmalloc(sizeof(vf_struct));
+    vp->packet_stack_level = vp->packet_stack_minlevel = 0;
+    vp->packet_stack =
+        (packet_stack_record *) xmalloc(packet_stack_size *
+                                        sizeof(packet_stack_record));
+    vp->lf = 0;
+    vp->fs_f = 0;
+    vp->packet_cur_s = 0;
+    vp->refpos = NULL;
+    vp->vflua = false;
+    return vp;
+}
+
+@ Count the number of bytes in a command packet.
+@c
+int vf_packet_bytes(charinfo * co)
+{
+    eight_bits *vf_packets, *vfp;
+    unsigned k;
+    int cmd;
+
+    vfp = vf_packets = get_charinfo_packets(co);
+    if (vf_packets == NULL) {
+        return 0;
+    }
+    while ((cmd = *(vfp++)) != packet_end_code) {
+        switch (cmd) {
+        case packet_nop_code:
+        case packet_pop_code:
+        case packet_push_code:
+            break;
+        case packet_char_code:
+        case packet_down_code:
+        case packet_font_code:
+        case packet_image_code:
+        case packet_node_code:
+        case packet_right_code:
+        case packet_rule_code:
+            vfp += 8;
+            break;
+        case packet_pdf_mode:
+            vfp += 4;
+            break;
+        case packet_pdf_code:
+            vfp += 4;
+            /* plus a string so we fall through */
+        case packet_special_code:
+            packet_number(k);   /* +4 */
+            vfp += (int) k;
+            break;
+        default:
+            normal_error("vf", "invalid DVI command (1)");
+        }
+    };
+    return (vfp - vf_packets);
+}
+
+@ Typeset the \.{DVI} commands in the character packet
+  for character |c| in current font |f|.
+@c
+const char *packet_command_names[] = {
+    "char", "font", "pop", "push", "special", "image",
+    "right", "down", "rule", "node", "nop", "end", "scale", "lua", "pdf", NULL
+};
+
+@ @c
+static float packet_float(eight_bits ** vfpp)
+{
+    unsigned int i;
+    union U {
+        float a;
+        eight_bits b[sizeof(float)];
+    } u;
+    eight_bits *vfp = *vfpp;
+    for (i = 0; i < sizeof(float); i++)
+        u.b[i] = *(vfp++);
+    *vfpp = vfp;
+    return u.a;
+}
+
+@ The |do_vf_packet| procedure is called in order to interpret the
+  character packet for a virtual character. Such a packet may contain
+  the instruction to typeset a character from the same or an other
+  virtual font; in such cases |do_vf_packet| calls itself
+  recursively. The recursion level, i.e., the number of times this has
+  happened, is kept in the global variable |packet_cur_s| and should
+  not exceed |packet_max_recursion|.
+@c
+void do_vf_packet(PDF pdf, internal_font_number vf_f, int c, int ex_glyph)
+{
+    eight_bits *vfp;
+    posstructure *save_posstruct, localpos;
+    vf_struct *save_vfstruct, localvfstruct, *vp;
+    int cmd, w, mode;
+    unsigned k;
+    scaledpos size;
+    scaled i;
+    str_number s;
+    float f;
+    packet_stack_record *mat_p;
+
+    vfp = get_charinfo_packets(get_charinfo(vf_f, c));
+    assert(vfp != NULL);
+
+    save_posstruct = pdf->posstruct;
+    pdf->posstruct = &localpos; /* use local structure for recursion */
+    localpos.pos = save_posstruct->pos;
+    localpos.dir = dir_TLT;     /* invariably for vf */
+
+    save_vfstruct = pdf->vfstruct;
+    vp = pdf->vfstruct = &localvfstruct;
+    localvfstruct = *save_vfstruct;
+
+    vp->packet_stack_minlevel = ++(vp->packet_stack_level);
+    vp->lf = 0;
+    vp->fs_f = font_size(vf_f);
+    vp->ex_glyph = ex_glyph;
+    vp->packet_cur_s++;
+    if (vp->packet_cur_s == packet_max_recursion)
+        overflow("max level recursion of virtual fonts", packet_max_recursion);
+    vp->refpos = save_posstruct;
+    vp->vflua = false;
+
+    mat_p = &(vp->packet_stack[vp->packet_stack_level]);
+    mat_p->c0 = 1.0;
+    mat_p->c1 = 0.0;
+    mat_p->c2 = 0.0;
+    mat_p->c3 = 1.0;
+    mat_p->pos.h = 0;
+    mat_p->pos.v = 0;
+
+    while ((cmd = *(vfp++)) != packet_end_code) {
+#ifdef DEBUG
+        if (cmd > packet_end_code) {
+            fprintf(stdout, "do_vf_packet(%i,%i) command code = illegal \n",
+                    vf_f, c);
+        } else {
+            fprintf(stdout, "do_vf_packet(%i,%i) command code = %s\n", vf_f, c,
+                    packet_command_names[cmd]);
+        }
+#endif
+        switch (cmd) {
+        case packet_font_code:
+            packet_number(vp->lf);
+            break;
+        case packet_push_code:
+            vp->packet_stack_level++;
+            if (vp->packet_stack_level == packet_stack_size)
+                normal_error("vf", "packet_stack_level overflow");
+            vp->packet_stack[vp->packet_stack_level] = *mat_p;
+            mat_p = &(vp->packet_stack[vp->packet_stack_level]);
+            break;
+        case packet_pop_code:
+            if (vp->packet_stack_level == vp->packet_stack_minlevel)
+                normal_error("vf", "packet_stack_level underflow");
+            vp->packet_stack_level--;
+            mat_p = &(vp->packet_stack[vp->packet_stack_level]);
+            break;
+        case packet_char_code:
+            packet_number(k);
+            /* we also check if c == k and font(c) == font)k) */
+            if (!char_exists(vp->lf, (int) k)) {
+                char_warning(vp->lf, (int) k);
+            } else if (! ((c == k && vp->lf == vf_f)) && (has_packet(vp->lf, (int) k))) {
+                do_vf_packet(pdf, vp->lf, (int) k, ex_glyph);
+            } else {
+                backend_out[glyph_node] (pdf, vp->lf, (int) k, ex_glyph);
+            }
+            w = char_width(vp->lf, (int) k);
+            mat_p->pos.h += round_xn_over_d(w, 1000 + ex_glyph, 1000);
+            break;
+        case packet_rule_code:
+            packet_scaled(size.v, vp->fs_f);    /* height (where is depth?) */
+            packet_scaled(size.h, vp->fs_f);
+            if (size.h > 0 && size.v > 0)
+                backend_out[rule_node](pdf, 0, size);  /* the 0 is unused */
+            mat_p->pos.h += size.h;
+            break;
+        case packet_right_code:
+            packet_scaled(i, vp->fs_f);
+            mat_p->pos.h += i;
+            break;
+        case packet_down_code:
+            packet_scaled(i, vp->fs_f);
+            mat_p->pos.v += i;
+            break;
+        case packet_pdf_code:
+            packet_number(mode);
+            packet_number(k);
+            str_room(k);
+            while (k > 0) {
+                k--;
+                append_char(*(vfp++));
+            }
+            s = make_string();
+            pdf_literal(pdf, s, mode, false);
+            flush_str(s);
+            break;
+        case packet_pdf_mode:
+            packet_number(mode);
+            pdf_literal_set_mode(pdf, mode);
+            break;
+        case packet_special_code:
+            packet_number(k);
+            str_room(k);
+            while (k > 0) {
+                k--;
+                append_char(*(vfp++));
+            }
+            s = make_string();
+            pdf_literal(pdf, s, scan_special, false);
+            flush_str(s);
+            break;
+        case packet_lua_code:
+            packet_number(k);
+            vp->vflua = true;
+            if (luaL_loadbuffer
+                (Luas, (const char *) vfp, (size_t) k, "packet_lua_code")
+                || lua_pcall(Luas, 0, LUA_MULTRET, 0))
+                lua_error(Luas);
+            vp->vflua = false;
+            vfp += k;
+            break;
+        case packet_image_code:
+            packet_number(k);
+            vf_out_image(pdf, k);
+            break;
+        case packet_node_code:
+            packet_number(k);
+            hlist_out(pdf, (halfword) k, 0);
+            break;
+        case packet_nop_code:
+            break;
+        case packet_scale_code:
+            f = packet_float(&vfp);
+            mat_p->c0 = mat_p->c0 * f;
+            mat_p->c3 = mat_p->c3 * f;
+            /* pdf->pstruct->scale = f; *//* scale is still NOP */
+            pdf->pstruct->need_tm = true;
+            pdf->pstruct->need_tf = true;
+            break;
+        default:
+            normal_error("vf", "invalid DVI command (2)");
+        }
+        synch_pos_with_cur(&localpos, save_posstruct, mat_p->pos);      /* trivial case, always TLT */
+    }
+    pdf->posstruct = save_posstruct;
+    pdf->vfstruct = save_vfstruct;
+}
+
+@ @c
+int *packet_local_fonts(internal_font_number f, int *num)
+{
+    int c, cmd, lf, k, l, i;
+    int localfonts[256] = { 0 };
+    int *lfs;
+    charinfo *co;
+
+    eight_bits *vf_packets, *vfp;
+    k = 0;
+    for (c = font_bc(f); c <= font_ec(f); c++) {
+        if (quick_char_exists(f, c)) {
+            co = get_charinfo(f, c);
+            vfp = vf_packets = get_charinfo_packets(co);
+            if (vf_packets == NULL)
+                continue;
+            while ((cmd = *(vfp++)) != packet_end_code) {
+                switch (cmd) {
+                case packet_font_code:
+                    packet_number(lf);
+                    for (l = 0; l < k; l++) {
+                        if (localfonts[l] == lf) {
+                            break;
+                        }
+                    }
+                    if (l == k) {
+                        localfonts[k++] = lf;
+                    }
+                    break;
+                case packet_nop_code:
+                case packet_pop_code:
+                case packet_push_code:
+                    break;
+                case packet_char_code:
+                case packet_down_code:
+                case packet_image_code:
+                case packet_node_code:
+                case packet_right_code:
+                    vfp += 4;
+                    break;
+                case packet_rule_code:
+                    vfp += 8;
+                    break;
+                case packet_special_code:
+                    packet_number(i);
+                    vfp += i;
+                    break;
+                default:
+                    normal_error("vf", "invalid DVI command (3)");
+                }
+            }
+        }
+    }
+    *num = k;
+    if (k > 0) {
+        lfs = xmalloc((unsigned) ((unsigned) k * sizeof(int)));
+        memcpy(lfs, localfonts, (size_t) ((unsigned) k * sizeof(int)));
+        return lfs;
+    }
+    return NULL;
+}
+
+@ @c
+void
+replace_packet_fonts(internal_font_number f, int *old_fontid,
+                     int *new_fontid, int count)
+{
+    int c, cmd, lf, k, l;
+    charinfo *co;
+    eight_bits *vf_packets, *vfp;
+
+    for (c = font_bc(f); c <= font_ec(f); c++) {
+        if (quick_char_exists(f, c)) {
+            co = get_charinfo(f, c);
+            vfp = vf_packets = get_charinfo_packets(co);
+            if (vf_packets == NULL)
+                continue;
+            while ((cmd = *(vfp++)) != packet_end_code) {
+                switch (cmd) {
+                case packet_font_code:
+                    packet_number(lf);
+                    for (l = 0; l < count; l++) {
+                        if (old_fontid[l] == lf) {
+                            break;
+                        }
+                    }
+                    if (l < count) {
+                        k = new_fontid[l];
+                        *(vfp - 4) = (eight_bits)
+                            ((k & 0xFF000000) >> 24);
+                        *(vfp - 3) = (eight_bits)
+                            ((k & 0x00FF0000) >> 16);
+                        *(vfp - 2) = (eight_bits)
+                            ((k & 0x0000FF00) >> 8);
+                        *(vfp - 1) = (eight_bits) (k & 0x000000FF);
+                    }
+                    break;
+                case packet_nop_code:
+                case packet_pop_code:
+                case packet_push_code:
+                    break;
+                case packet_char_code:
+                case packet_down_code:
+                case packet_image_code:
+                case packet_node_code:
+                case packet_right_code:
+                case packet_rule_code:
+                    vfp += 8;
+                    break;
+                case packet_pdf_mode:
+                    vfp += 4;
+                    break;
+                case packet_pdf_code:
+                    vfp += 4;
+                    /* plus a string so we fall through */
+                case packet_special_code:
+                    packet_number(k);
+                    vfp += k;
+                    break;
+                default:
+                    normal_error("vf", "invalid DVI command (4)");
+                }
+            }
+        }
+    }
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/writecff.w
@@ -0,0 +1,3381 @@
+% writecff.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"
+#include "font/writecff.h"
+
+extern int cidset;
+
+@ @c
+#define get_offset(s,n) get_unsigned(s, (n))
+#define get_card8(a)  (card8)(a->stream[a->offset++])
+#define get_card16(a) (card16)(get_unsigned(a,2))
+#define get_card32(a) (get_unsigned(a,4))
+
+#undef b0
+#undef b1
+#undef b2
+#undef b3
+
+#define WORK_BUFFER_SIZE 1024
+
+static char work_buffer[WORK_BUFFER_SIZE];
+
+static unsigned long get_unsigned(cff_font * cff, int n)
+{
+    unsigned long v = 0;
+    while (n-- > 0)
+        v = v * 256 + get_card8(cff);
+    return v;
+}
+
+@ @c
+
+const char *const cff_stdstr[CFF_STDSTR_MAX] = {
+    ".notdef", "space", "exclam", "quotedbl", "numbersign",
+    "dollar", "percent", "ampersand", "quoteright", "parenleft",
+    "parenright", "asterisk", "plus", "comma", "hyphen",
+    "period", "slash", "zero", "one", "two",
+    "three", "four", "five", "six", "seven",
+    "eight", "nine", "colon", "semicolon", "less",
+    "equal", "greater", "question", "at", "A",
+    "B", "C", "D", "E", "F",
+    "G", "H", "I", "J", "K",
+    "L", "M", "N", "O", "P",
+    "Q", "R", "S", "T", "U",
+    "V", "W", "X", "Y", "Z",
+    "bracketleft", "backslash", "bracketright", "asciicircum", "underscore",
+    "quoteleft", "a", "b", "c", "d",
+    "e", "f", "g", "h", "i",
+    "j", "k", "l", "m", "n",
+    "o", "p", "q", "r", "s",
+    "t", "u", "v", "w", "x",
+    "y", "z", "braceleft", "bar", "braceright",
+    "asciitilde", "exclamdown", "cent", "sterling", "fraction",
+    "yen", "florin", "section", "currency", "quotesingle",
+    "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi",
+    "fl", "endash", "dagger", "daggerdbl", "periodcentered",
+    "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright",
+    "guillemotright", "ellipsis", "perthousand", "questiondown", "grave",
+    "acute", "circumflex", "tilde", "macron", "breve",
+    "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
+    "ogonek", "caron", "emdash", "AE", "ordfeminine",
+    "Lslash", "Oslash", "OE", "ordmasculine", "ae",
+    "dotlessi", "lslash", "oslash", "oe", "germandbls",
+    "onesuperior", "logicalnot", "mu", "trademark", "Eth",
+    "onehalf", "plusminus", "Thorn", "onequarter", "divide",
+    "brokenbar", "degree", "thorn", "threequarters", "twosuperior",
+    "registered", "minus", "eth", "multiply", "threesuperior",
+    "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
+    "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex",
+    "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis",
+    "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis",
+    "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex",
+    "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron",
+    "aacute", "acircumflex", "adieresis", "agrave", "aring",
+    "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis",
+    "egrave", "iacute", "icircumflex", "idieresis", "igrave",
+    "ntilde", "oacute", "ocircumflex", "odieresis", "ograve",
+    "otilde", "scaron", "uacute", "ucircumflex", "udieresis",
+    "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall",
+    "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall",
+    "Acutesmall",
+    "parenleftsuperior", "parenrightsuperior", "twodotenleader",
+    "onedotenleader", "zerooldstyle",
+    "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle",
+    "fiveoldstyle",
+    "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle",
+    "commasuperior",
+    "threequartersemdash", "periodsuperior", "questionsmall", "asuperior",
+    "bsuperior",
+    "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior",
+    "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
+    "tsuperior", "ff", "ffi", "ffl", "parenleftinferior",
+    "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
+    "Asmall",
+    "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall",
+    "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall",
+    "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
+    "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall",
+    "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall",
+    "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall",
+    "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
+    "Dieresissmall",
+    "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash",
+    "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
+    "questiondownsmall",
+    "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird",
+    "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior",
+    "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior",
+    "oneinferior",
+    "twoinferior", "threeinferior", "fourinferior", "fiveinferior",
+    "sixinferior",
+    "seveninferior", "eightinferior", "nineinferior", "centinferior",
+    "dollarinferior",
+    "periodinferior", "commainferior", "Agravesmall", "Aacutesmall",
+    "Acircumflexsmall",
+    "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall",
+    "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall",
+    "Igravesmall",
+    "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall",
+    "Ntildesmall",
+    "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
+    "Odieresissmall",
+    "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall",
+    "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall",
+    "001.000", "001.001", "001.002", "001.003",
+    "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold"
+};
+
+@ Only read header part but not body
+@c
+cff_index *cff_get_index_header(cff_font * cff)
+{
+    cff_index *idx;
+    card16 i, count;
+    idx = xcalloc(1, sizeof(cff_index));
+    if (cff->header_major == 2) {
+        idx->count = count = get_card32(cff);
+    } else {
+        idx->count = count = get_card16(cff);
+    }
+    if (count > 0) {
+        idx->offsize = get_card8(cff);
+        if (idx->offsize < 1 || idx->offsize > 4)
+            normal_error("cff","invalid offsize data (1)");
+        idx->offset = xmalloc((unsigned) (((unsigned) count + 1) * sizeof(l_offset)));
+        for (i = 0; i <count + 1 ; i++) {
+            (idx->offset)[i] = get_offset(cff, idx->offsize);
+            if (i == USHRT_MAX)
+                break;
+        }
+        if (idx->offset[0] != 1)
+            normal_error("cff","invalid index data");
+        idx->data = NULL;
+    } else {
+        idx->offsize = 0;
+        idx->offset = NULL;
+        idx->data = NULL;
+    }
+    return idx;
+}
+
+@ @c
+cff_index *cff_get_index(cff_font * cff)
+{
+    cff_index *idx;
+    card16 i, count;
+    size_t length;
+    idx = xcalloc(1, sizeof(cff_index));
+    idx->count = count = get_card16(cff);
+    if (count > 0) {
+        idx->offsize = get_card8(cff);
+        if (idx->offsize < 1 || idx->offsize > 4)
+            normal_error("cff","invalid offsize data (2)");
+        idx->offset = xmalloc((unsigned) (((unsigned) count + 1) * sizeof(l_offset)));
+        for (i = 0; i < count + 1; i++) {
+            idx->offset[i] = get_offset(cff, idx->offsize);
+        }
+        if (idx->offset[0] != 1)
+            normal_error("cff","invalid index offset data");
+        length = (size_t) (idx->offset[count] - idx->offset[0]);
+        idx->data = xmalloc((unsigned) length * sizeof(card8));
+        memcpy(idx->data, &cff->stream[cff->offset], length);
+        cff->offset += length;
+    } else {
+        idx->offsize = 0;
+        idx->offset = NULL;
+        idx->data = NULL;
+    }
+    return idx;
+}
+
+static cff_index *cff_empty_index(cff_font * cff)
+{
+    cff_index *idx;
+    idx = xcalloc(1, sizeof(cff_index));
+    idx->count = 0;
+    idx->offsize = 0;
+    idx->offset = NULL;
+    idx->data = NULL;
+    return idx;
+}
+
+static cff_index *cff_get_index2(cff_font * cff)
+{
+    /* we fake a dict array */
+
+    cff_index *idx;
+    size_t length;
+
+    idx = xcalloc(1, sizeof(cff_index));
+
+    length = (size_t) cff->header_offsize;
+
+    idx->offsize = 2;
+    idx->count = 1;
+    idx->offset = xmalloc((unsigned) (((unsigned) 2) * sizeof(l_offset)));
+    idx->offset[0] = 1; // get_offset(cff, idx->offsize);
+    idx->offset[1] = length + 1; // idx->offset[0] + length;
+
+    idx->data = xmalloc((unsigned) length * sizeof(card8));
+    memcpy(idx->data, &cff->stream[cff->offset], length );
+    cff->offset += length ;
+
+    return idx;
+}
+
+@ @c
+long cff_pack_index(cff_index * idx, card8 * dest, long destlen)
+{
+    long len = 0;
+    unsigned long datalen;
+    card16 i;
+    if (idx->count < 1) {
+        if (destlen < 2)
+            normal_error("cff","not enough space available");
+        memset(dest, 0, 2);
+        return 2;
+    }
+    len = cff_index_size(idx);
+    datalen = idx->offset[idx->count] - 1;
+    if (destlen < len)
+        normal_error("cff","not enough space available");
+    *(dest++) = (card8) ((idx->count >> 8) & 0xff);
+    *(dest++) = (card8) (idx->count & 0xff);
+    if (datalen < 0xffUL) {
+        idx->offsize = 1;
+        *(dest++) = 1;
+        for (i = 0; i <= idx->count; i++) {
+            *(dest++) = (card8) (idx->offset[i] & 0xff);
+        }
+    } else if (datalen < 0xffffUL) {
+        idx->offsize = 2;
+        *(dest++) = 2;
+        for (i = 0; i <= idx->count; i++) {
+            *(dest++) = (card8) ((idx->offset[i] >> 8) & 0xff);
+            *(dest++) = (card8) (idx->offset[i] & 0xff);
+        }
+    } else if (datalen < 0xffffffUL) {
+        idx->offsize = 3;
+        *(dest++) = 3;
+        for (i = 0; i <= idx->count; i++) {
+            *(dest++) = (card8) ((idx->offset[i] >> 16) & 0xff);
+            *(dest++) = (card8) ((idx->offset[i] >> 8) & 0xff);
+            *(dest++) = (card8) (idx->offset[i] & 0xff);
+        }
+    } else {
+        idx->offsize = 4;
+        *(dest++) = 4;
+        for (i = 0; i <= idx->count; i++) {
+            *(dest++) = (card8) ((idx->offset[i] >> 24) & 0xff);
+            *(dest++) = (card8) ((idx->offset[i] >> 16) & 0xff);
+            *(dest++) = (card8) ((idx->offset[i] >> 8) & 0xff);
+            *(dest++) = (card8) (idx->offset[i] & 0xff);
+        }
+    }
+    memmove(dest, idx->data, idx->offset[idx->count] - 1);
+    return len;
+}
+
+@ @c
+long cff_index_size(cff_index * idx)
+{
+    if (idx->count > 0) {
+        l_offset datalen;
+        datalen = idx->offset[idx->count] - 1;
+        if (datalen < 0xffUL) {
+            idx->offsize = 1;
+        } else if (datalen < 0xffffUL) {
+            idx->offsize = 2;
+        } else if (datalen < 0xffffffUL) {
+            idx->offsize = 3;
+        } else {
+            idx->offsize = 4;
+        }
+        return (3 + (idx->offsize) * (idx->count + 1) + (long) datalen);
+    } else {
+        return 2;
+    }
+}
+
+@ @c
+cff_index *cff_new_index(card16 count)
+{
+    cff_index *idx;
+    idx = xcalloc(1, sizeof(cff_index));
+    idx->count = count;
+    idx->offsize = 0;
+    if (count > 0) {
+        idx->offset = xcalloc((unsigned) (count + 1), sizeof(l_offset));
+        (idx->offset)[0] = 1;
+    } else {
+        idx->offset = NULL;
+    }
+    idx->data = NULL;
+    return idx;
+}
+
+@ @c
+void cff_release_index(cff_index * idx)
+{
+    if (idx) {
+        xfree(idx->data);
+        xfree(idx->offset);
+        xfree(idx);
+    }
+}
+
+@ @c
+void cff_release_dict(cff_dict * dict)
+{
+    if (dict) {
+        if (dict->entries) {
+            int i;
+            for (i = 0; i < dict->count; i++) {
+                xfree((dict->entries)[i].values);
+            }
+            xfree(dict->entries);
+        }
+        xfree(dict);
+    }
+}
+
+@ @c
+void cff_release_encoding(cff_encoding * encoding)
+{
+    if (encoding) {
+        switch (encoding->format & (~0x80)) {
+        case 0:
+            xfree(encoding->data.codes);
+            break;
+        case 1:
+            xfree(encoding->data.range1);
+            break;
+        default:
+            normal_error("cff","unknown encoding format");
+        }
+        if (encoding->format & 0x80)
+            xfree(encoding->supp);
+        xfree(encoding);
+    }
+}
+
+@ @c
+void cff_release_charsets(cff_charsets * charset)
+{
+    if (charset) {
+        switch (charset->format) {
+        case 0:
+            xfree(charset->data.glyphs);
+            break;
+        case 1:
+            xfree(charset->data.range1);
+            break;
+        case 2:
+            xfree(charset->data.range2);
+            break;
+        default:
+            break;
+        }
+        xfree(charset);
+    }
+}
+
+@ @c
+void cff_release_fdselect(cff_fdselect * fdselect)
+{
+    if (fdselect) {
+        if (fdselect->format == 0) {
+            xfree(fdselect->data.fds);
+        } else if (fdselect->format == 3) {
+            xfree(fdselect->data.ranges);
+        }
+        xfree(fdselect);
+    }
+}
+
+@ @c
+void cff_close(cff_font * cff)
+{
+    card16 i;
+    if (cff) {
+        xfree(cff->fontname);
+        if (cff->name)
+            cff_release_index(cff->name);
+        if (cff->topdict)
+            cff_release_dict(cff->topdict);
+        if (cff->string)
+            cff_release_index(cff->string);
+        if (cff->gsubr)
+            cff_release_index(cff->gsubr);
+        if (cff->encoding)
+            cff_release_encoding(cff->encoding);
+        if (cff->charsets)
+            cff_release_charsets(cff->charsets);
+        if (cff->fdselect)
+            cff_release_fdselect(cff->fdselect);
+        if (cff->cstrings)
+            cff_release_index(cff->cstrings);
+        if (cff->fdarray) {
+            for (i = 0; i < cff->num_fds; i++) {
+                if (cff->fdarray[i])
+                    cff_release_dict(cff->fdarray[i]);
+            }
+            xfree(cff->fdarray);
+        }
+        if (cff->private) {
+            for (i = 0; i < cff->num_fds; i++) {
+                if (cff->private[i])
+                    cff_release_dict(cff->private[i]);
+            }
+            xfree(cff->private);
+        }
+        if (cff->subrs) {
+            for (i = 0; i < cff->num_fds; i++) {
+                if (cff->subrs[i])
+                    cff_release_index(cff->subrs[i]);
+            }
+            xfree(cff->subrs);
+        }
+        if (cff->_string)
+            cff_release_index(cff->_string);
+        xfree(cff);
+    }
+    return;
+}
+
+@ @c
+char *cff_get_name(cff_font * cff)
+{
+    char *fontname;
+    l_offset len;
+    cff_index *idx;
+    idx = cff->name;
+    len = idx->offset[cff->index + 1] - idx->offset[cff->index];
+    fontname = xmalloc((unsigned) (len + 1) * sizeof(char));
+    memcpy(fontname, idx->data + idx->offset[cff->index] - 1, len);
+    fontname[len] = '\0';
+    return fontname;
+}
+
+@ @c
+long cff_set_name(cff_font * cff, char *name)
+{
+    cff_index *idx;
+    if (strlen(name) > 127)
+        normal_error("cff","FontName string length too large");
+    if (cff->name)
+        cff_release_index(cff->name);
+    cff->name = idx = xcalloc(1, sizeof(cff_index));
+    idx->count = 1;
+    idx->offsize = 1;
+    idx->offset = xmalloc(2 * sizeof(l_offset));
+    (idx->offset)[0] = 1;
+    (idx->offset)[1] = strlen(name) + 1;
+    idx->data = xmalloc((unsigned) strlen(name) * sizeof(card8));
+    memmove(idx->data, name, strlen(name)); /* no trailing |'\0'| */
+    return (long) (5 + strlen(name));
+}
+
+long cff_put_header(cff_font * cff, card8 * dest, long destlen)
+{
+    if (destlen < 4)
+        normal_error("cff","not enough space available");
+
+    *(dest++) = 1; /* cff->header_major; */
+    *(dest++) = cff->header_minor;
+    *(dest++) = 4;
+    /*
+        Additional data in between header and Name INDEX is ignored.
+
+        We will set all offset (0) to a four-byte integer.
+    */
+    *(dest++) = 4;
+    cff->header_offsize = 4;
+    return 4;
+}
+
+@ @c
+#define CFF_PARSE_OK                0
+#define CFF_CFF_ERROR_PARSE_CFF_ERROR      -1
+#define CFF_CFF_ERROR_STACK_OVERFLOW   -2
+#define CFF_CFF_ERROR_STACK_UNDERFLOW  -3
+#define CFF_CFF_ERROR_STACK_RANGECHECK -4
+
+#define DICT_ENTRY_MAX 16
+
+cff_dict *cff_new_dict(void)
+{
+    cff_dict *dict;
+    dict = xcalloc(1, sizeof(cff_dict));
+    dict->max = DICT_ENTRY_MAX;
+    dict->count = 0;
+    dict->entries = xcalloc((unsigned) dict->max, sizeof(cff_dict_entry));
+    return dict;
+}
+
+@ Operand stack: only numbers are stored (as double). Operand types are:
+
+\item number:  double (integer or real)
+\item boolean: stored as a number
+\item SID:     stored as a number
+\item array:   array of numbers
+\item delta:   array of numbers
+
+@c
+#define CFF_DICT_STACK_LIMIT 64
+static int stack_top = 0;
+static double arg_stack[CFF_DICT_STACK_LIMIT];
+
+@ CFF DICT encoding.
+@c
+#define CFF_LAST_DICT_OP1 26  /* 22 */
+#define CFF_LAST_DICT_OP2 39
+#define CFF_LAST_DICT_OP (CFF_LAST_DICT_OP1 + CFF_LAST_DICT_OP2)
+
+static struct {
+    const char *opname;
+    int argtype;
+} dict_operator[CFF_LAST_DICT_OP] = {
+    { "version", CFF_TYPE_SID },
+    { "Notice", CFF_TYPE_SID },
+    { "FullName", CFF_TYPE_SID },
+    { "FamilyName", CFF_TYPE_SID },
+    { "Weight", CFF_TYPE_SID },
+    { "FontBBox", CFF_TYPE_ARRAY },
+    { "BlueValues", CFF_TYPE_DELTA },
+    { "OtherBlues", CFF_TYPE_DELTA },
+    { "FamilyBlues", CFF_TYPE_DELTA },
+    { "FamilyOtherBlues", CFF_TYPE_DELTA },
+    { "StdHW", CFF_TYPE_NUMBER },
+    { "StdVW", CFF_TYPE_NUMBER },
+    { NULL, -1 },  /* first byte of two-byte operator 12 */
+    { "UniqueID", CFF_TYPE_NUMBER },
+    { "XUID", CFF_TYPE_ARRAY },
+    { "charset", CFF_TYPE_OFFSET },
+    { "Encoding", CFF_TYPE_OFFSET },
+    { "CharStrings", CFF_TYPE_OFFSET },
+    { "Private", CFF_TYPE_SZOFF },  /* two numbers (size and offset) */
+    { "Subrs", CFF_TYPE_OFFSET },
+    { "defaultWidthX", CFF_TYPE_NUMBER },
+    { "nominalWidthX", CFF_TYPE_NUMBER },
+    { NULL, -1 },
+    { NULL, -1 },
+    { "vstore", CFF_TYPE_OFFSET },   /* cff2 */
+    { "maxstack", CFF_TYPE_NUMBER }, /* cff2 */
+    /* Here we start with operator 2 of 12 */
+    { "Copyright", CFF_TYPE_SID },
+    { "IsFixedPitch", CFF_TYPE_BOOLEAN },
+    { "ItalicAngle", CFF_TYPE_NUMBER },
+    { "UnderlinePosition", CFF_TYPE_NUMBER },
+    { "UnderlineThickness", CFF_TYPE_NUMBER },
+    { "PaintType", CFF_TYPE_NUMBER },
+    { "CharstringType", CFF_TYPE_NUMBER },
+    { "FontMatrix", CFF_TYPE_ARRAY },
+    { "StrokeWidth", CFF_TYPE_NUMBER },
+    { "BlueScale", CFF_TYPE_NUMBER },
+    { "BlueShift", CFF_TYPE_NUMBER },
+    { "BlueFuzz", CFF_TYPE_NUMBER },
+    { "StemSnapH", CFF_TYPE_DELTA },
+    { "StemSnapV", CFF_TYPE_DELTA },
+    { "ForceBold", CFF_TYPE_BOOLEAN },
+    { NULL, -1 },
+    { NULL, -1 },
+    { "LanguageGroup", CFF_TYPE_NUMBER },
+    { "ExpansionFactor", CFF_TYPE_NUMBER },
+    { "InitialRandomSeed", CFF_TYPE_NUMBER },
+    { "SyntheticBase", CFF_TYPE_NUMBER },
+    { "PostScript", CFF_TYPE_SID },
+    { "BaseFontName", CFF_TYPE_SID },
+    { "BaseFontBlend", CFF_TYPE_DELTA },
+    { NULL, -1 },
+    { NULL, -1 },
+    { NULL, -1 },
+    { NULL, -1 },
+    { NULL, -1 },
+    { NULL, -1 },
+    { "ROS", CFF_TYPE_ROS },
+    { "CIDFontVersion", CFF_TYPE_NUMBER },
+    { "CIDFontRevision", CFF_TYPE_NUMBER },
+    { "CIDFontType", CFF_TYPE_NUMBER },
+    { "CIDCount", CFF_TYPE_NUMBER },
+    { "UIDBase", CFF_TYPE_NUMBER },
+    { "FDArray", CFF_TYPE_OFFSET },
+    { "FDSelect", CFF_TYPE_OFFSET },
+    { "FontName", CFF_TYPE_SID }
+};
+
+@ Parse DICT data
+@c
+static double get_integer(card8 ** data, card8 * endptr, int *status)
+{
+    long result = 0;
+    card8 b0, b1, b2;
+
+    b0 = *(*data)++;
+    if (b0 == 28 && *data < endptr - 2) {       /* shortint */
+        b1 = *(*data)++;
+        b2 = *(*data)++;
+        result = b1 * 256 + b2;
+        if (result > 0x7fffL)
+            result -= 0x10000L;
+    } else if (b0 == 29 && *data < endptr - 4) {        /* longint */
+        int i;
+        result = *(*data)++;
+        if (result > 0x7f)
+            result -= 0x100;
+        for (i = 0; i < 3; i++) {
+            result = result * 256 + (**data);
+            *data += 1;
+        }
+    } else if (b0 >= 32 && b0 <= 246) { /* int (1) */
+        result = b0 - 139;
+    } else if (b0 >= 247 && b0 <= 250) {        /* int (2) */
+        b1 = *(*data)++;
+        result = (b0 - 247) * 256 + b1 + 108;
+    } else if (b0 >= 251 && b0 <= 254) {
+        b1 = *(*data)++;
+        result = -(b0 - 251) * 256 - b1 - 108;
+    } else {
+        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
+    }
+
+    return (double) result;
+}
+
+@ Simply uses strtod
+@c
+static double get_real(card8 ** data, card8 * endptr, int *status)
+{
+    double result = 0.0;
+    int nibble = 0, pos = 0;
+    int len = 0, fail = 0;
+
+    if (**data != 30 || *data >= endptr - 1) {
+        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
+        return 0.0;
+    }
+
+    *data += 1;                 /* skip first byte (30) */
+
+    pos = 0;
+    while ((!fail) && len < WORK_BUFFER_SIZE - 2 && *data < endptr) {
+        /* get nibble */
+        if (pos % 2) {
+            nibble = **data & 0x0f;
+            *data += 1;
+        } else {
+            nibble = (**data >> 4) & 0x0f;
+        }
+        if (nibble >= 0x00 && nibble <= 0x09) {
+            work_buffer[len++] = (char) (nibble + '0');
+        } else if (nibble == 0x0a) {    /* . */
+            work_buffer[len++] = '.';
+        } else if (nibble == 0x0b || nibble == 0x0c) {  /* E, E- */
+            work_buffer[len++] = 'e';
+            if (nibble == 0x0c)
+                work_buffer[len++] = '-';
+        } else if (nibble == 0x0e) {    /* `-' */
+            work_buffer[len++] = '-';
+        } else if (nibble == 0x0d) {    /* skip */
+            /* do nothing */
+        } else if (nibble == 0x0f) {    /* end */
+            work_buffer[len++] = '\0';
+            if (((pos % 2) == 0) && (**data != 0xff)) {
+                fail = 1;
+            }
+            break;
+        } else {                /* invalid */
+            fail = 1;
+        }
+        pos++;
+    }
+
+    /* returned values */
+    if (fail || nibble != 0x0f) {
+        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
+    } else {
+        char *s;
+	/* strtod sets errno for  OVERFLOW and _maybe_ UNDERFLOW */
+        /* but not for an invalid  conversion (as for example  if we try to convert "foo" in a double )*/
+        /* At least in glib sets errno also for UNDERFLOW */
+        /* We don't save/restore the prev. errno */
+        errno=0;
+        result = strtod(work_buffer, &s);
+        if ( (result==0.0 && work_buffer==s) || errno ) {
+              /* conversion is not possible */
+             *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
+         }
+    }
+    return result;
+}
+
+@ operators
+@c
+static void add_dict(cff_dict * dict,
+                     card8 ** data, card8 * endptr, int *status)
+{
+    int id, argtype, t;
+
+    id = **data;
+    if (id == 0x0c) {
+        *data += 1;
+        if (*data >= endptr ||
+            (id = **data + CFF_LAST_DICT_OP1) >= CFF_LAST_DICT_OP) {
+            *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
+            return;
+        }
+    } else if (id >= CFF_LAST_DICT_OP1) {
+        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
+        return;
+    }
+
+    argtype = dict_operator[id].argtype;
+    if (dict_operator[id].opname == NULL || argtype < 0) {
+        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
+        return;
+    }
+
+    if (dict->count >= dict->max) {
+        dict->max += DICT_ENTRY_MAX;
+        /* not zeroed! */
+        dict->entries =
+            xrealloc(dict->entries,
+                     (unsigned) ((unsigned) dict->max *
+                                 sizeof(cff_dict_entry)));
+    }
+
+    (dict->entries)[dict->count].id = id;
+    (dict->entries)[dict->count].key = dict_operator[id].opname;
+    if (argtype == CFF_TYPE_NUMBER ||
+        argtype == CFF_TYPE_BOOLEAN ||
+        argtype == CFF_TYPE_SID || argtype == CFF_TYPE_OFFSET) {
+        /* check for underflow here, as exactly one operand is expected */
+        if (stack_top < 1) {
+            *status = CFF_CFF_ERROR_STACK_UNDERFLOW;
+            return;
+        }
+        stack_top--;
+        (dict->entries)[dict->count].count = 1;
+        (dict->entries)[dict->count].values = xcalloc(1, sizeof(double));
+        (dict->entries)[dict->count].values[0] = arg_stack[stack_top];
+        dict->count += 1;
+    } else {
+        /* just ignore operator if there were no operands provided;
+           don't treat this as underflow (e.g. StemSnapV in TemporaLGCUni-Italic.otf) */
+        if ((t = stack_top) > 0) {
+            (dict->entries)[dict->count].count = stack_top;
+            (dict->entries)[dict->count].values =
+                xmalloc((unsigned) ((unsigned) stack_top * sizeof(double)));
+            while (stack_top > 0) {
+                stack_top--;
+                (dict->entries)[dict->count].values[stack_top] =
+                    arg_stack[stack_top];
+            }
+            if (t > 3 && strcmp(dict_operator[id].opname, "FontMatrix") == 0) {
+                /* reset FontMatrix to [0.001 * * 0.001 * *],
+                   fix mantis bug \# 0000200 (acroread "feature") */
+                (dict->entries)[dict->count].values[0] = 0.001;
+                (dict->entries)[dict->count].values[3] = 0.001;
+            }
+            dict->count += 1;
+        }
+    }
+
+    *data += 1;
+
+    return;
+}
+
+
+@ All operands are treated as number or array of numbers.
+  Private: two numbers, size and offset
+  ROS    : three numbers, SID, SID, and a number
+
+@c
+cff_dict *cff_dict_unpack(card8 * data, card8 * endptr)
+{
+    cff_dict *dict;
+    int status = CFF_PARSE_OK;
+    stack_top = 0;
+    dict = cff_new_dict();
+    while (data < endptr && status == CFF_PARSE_OK) {
+        if (*data < CFF_LAST_DICT_OP1) {       /* operator */
+            add_dict(dict, &data, endptr, &status);
+        } else if (*data == 30) {       /* real - First byte of a sequence (variable) */
+            if (stack_top < CFF_DICT_STACK_LIMIT) {
+                arg_stack[stack_top] = get_real(&data, endptr, &status);
+                stack_top++;
+            } else {
+                status = CFF_CFF_ERROR_STACK_OVERFLOW;
+            }
+        } else if (*data == 255 || (*data >= CFF_LAST_DICT_OP1 && *data <= 27)) {      /* reserved */
+            data++;
+        } else {                /* everything else are integer */
+            if (stack_top < CFF_DICT_STACK_LIMIT) {
+                arg_stack[stack_top] = get_integer(&data, endptr, &status);
+                stack_top++;
+            } else {
+                status = CFF_CFF_ERROR_STACK_OVERFLOW;
+            }
+        }
+    }
+    if (status != CFF_PARSE_OK) {
+        formatted_error("cff","parsing DICT failed (error=%d)", status);
+    } else if (stack_top != 0) {
+        normal_warning("cff","garbage in DICT data");
+        stack_top = 0;
+    }
+    return dict;
+}
+
+@ @c
+int cff_dict_known(cff_dict * dict, const char *key)
+{
+    int i;
+    for (i = 0; i < dict->count; i++) {
+        if (key && strcmp(key, (dict->entries)[i].key) == 0
+            && (dict->entries)[i].count > 0)
+            return 1;
+    }
+    return 0;
+}
+
+@ @c
+double cff_dict_get(cff_dict * dict, const char *key, int idx)
+{
+    double value = 0.0;
+    int i;
+    assert(key && dict);
+    for (i = 0; i < dict->count; i++) {
+        if (strcmp(key, (dict->entries)[i].key) == 0) {
+           if ((dict->entries)[i].count > idx)
+                value = (dict->entries)[i].values[idx];
+            else
+                normal_error("cff","invalid index number");
+            break;
+        }
+    }
+    if (i == dict->count)
+        formatted_error("cff","DICT entry '%s' not found", key);
+    return value;
+}
+
+@ @c
+card8 cff_fdselect_lookup(cff_font * cff, card16 gid)
+{
+    card8 fd = 0xff;
+    cff_fdselect *fdsel;
+
+    if (cff->fdselect == NULL)
+        normal_error("cff","FDSelect not available");
+
+    fdsel = cff->fdselect;
+
+    if (gid >= cff->num_glyphs)
+        normal_error("cff","invalid glyph index");
+
+    switch (fdsel->format) {
+    case 0:
+        fd = fdsel->data.fds[gid];
+        break;
+    case 3:
+        {
+            if (gid == 0) {
+                fd = (fdsel->data).ranges[0].fd;
+            } else {
+                card16 i;
+                for (i = 1; i < (fdsel->num_entries); i++) {
+                    if (gid < (fdsel->data).ranges[i].first)
+                        break;
+                }
+                fd = (fdsel->data).ranges[i - 1].fd;
+            }
+        }
+        break;
+    default:
+        normal_error("cff","invalid FDSelect format");
+        break;
+    }
+
+    if (fd >= cff->num_fds)
+        normal_error("cff","invalid Font DICT index");
+
+    return fd;
+}
+
+@ @c
+long cff_read_subrs(cff_font * cff)
+{
+    long len = 0;
+    long offset;
+    int i;
+
+    if ((cff->flag & FONTTYPE_CIDFONT) && cff->fdselect == NULL) {
+        cff_read_fdselect(cff);
+    }
+
+    if ((cff->flag & FONTTYPE_CIDFONT) && cff->fdarray == NULL) {
+        cff_read_fdarray(cff);
+    }
+
+    if (cff->private == NULL)
+        cff_read_private(cff);
+
+    if (cff->gsubr == NULL) {
+        cff->offset = cff->gsubr_offset;
+        cff->gsubr = cff_get_index(cff);
+    }
+
+    cff->subrs = xcalloc(cff->num_fds, sizeof(cff_index *));
+    if (cff->flag & FONTTYPE_CIDFONT) {
+        for (i = 0; i < cff->num_fds; i++) {
+            if (cff->private[i] == NULL ||
+                !cff_dict_known(cff->private[i], "Subrs")) {
+                (cff->subrs)[i] = NULL;
+            } else {
+                offset = (long) cff_dict_get(cff->fdarray[i], "Private", 1);
+                offset += (long) cff_dict_get(cff->private[i], "Subrs", 0);
+                cff->offset = (l_offset) offset;
+                (cff->subrs)[i] = cff_get_index(cff);
+                len += cff_index_size((cff->subrs)[i]);
+            }
+        }
+    } else if (cff->private[0] == NULL || !cff_dict_known(cff->private[0], "Subrs")) {
+        (cff->subrs)[0] = NULL;
+    } else {
+        offset = (long) cff_dict_get(cff->topdict, "Private", 1);
+        offset += (long) cff_dict_get(cff->private[0], "Subrs", 0);
+        cff->offset = (l_offset) offset;
+        (cff->subrs)[0] = cff_get_index(cff);
+        len += cff_index_size((cff->subrs)[0]);
+    }
+
+    return len;
+}
+
+@ @c
+long cff_read_fdarray(cff_font * cff)
+{
+    long len = 0;
+    cff_index *idx;
+    long offset, size;
+    card16 i;
+
+    if (cff->topdict == NULL)
+        normal_error("cff","top DICT not found");
+
+    if (!(cff->flag & FONTTYPE_CIDFONT))
+        return 0;
+
+    /* must exist */
+    offset = (long) cff_dict_get(cff->topdict, "FDArray", 0);
+    cff->offset = (l_offset) offset;
+    idx = cff_get_index(cff);
+    cff->num_fds = (card8) idx->count;
+    cff->fdarray = xmalloc((unsigned) (idx->count * sizeof(cff_dict *)));
+    for (i = 0; i < idx->count; i++) {
+        card8 *data = idx->data + (idx->offset)[i] - 1;
+        size = (long) ((idx->offset)[i + 1] - (idx->offset)[i]);
+        if (size > 0) {
+            (cff->fdarray)[i] = cff_dict_unpack(data, data + size);
+        } else {
+            (cff->fdarray)[i] = NULL;
+        }
+    }
+    len = cff_index_size(idx);
+    cff_release_index(idx);
+
+    return len;
+}
+
+
+@ @c
+long cff_read_private(cff_font * cff)
+{
+    long len = 0;
+    card8 *data;
+    long offset, size;
+
+    if (cff->flag & FONTTYPE_CIDFONT) {
+        int i;
+
+        if (cff->fdarray == NULL)
+            cff_read_fdarray(cff);
+
+        cff->private = xmalloc((unsigned) (cff->num_fds * sizeof(cff_dict *)));
+        for (i = 0; i < cff->num_fds; i++) {
+            if (cff->fdarray[i] != NULL &&
+                    cff_dict_known(cff->fdarray[i], "Private") &&
+                    (size = (long) cff_dict_get(cff->fdarray[i], "Private", 0)) > 0) {
+                offset = (long) cff_dict_get(cff->fdarray[i], "Private", 1);
+                cff->offset = (l_offset) offset;
+                data = xmalloc((unsigned) size * sizeof(card8));
+                memcpy(data, &cff->stream[cff->offset], (size_t) size);
+                cff->offset = (l_offset) size;
+                (cff->private)[i] = cff_dict_unpack(data, data + size);
+                xfree(data);
+                len += size;
+            } else {
+                (cff->private)[i] = NULL;
+            }
+        }
+    } else {
+        cff->num_fds = 1;
+        cff->private = xmalloc(sizeof(cff_dict *));
+        if (cff_dict_known(cff->topdict, "Private") &&
+                (size = (long) cff_dict_get(cff->topdict, "Private", 0)) > 0) {
+            offset = (long) cff_dict_get(cff->topdict, "Private", 1);
+            cff->offset = (l_offset) offset;
+            data = xmalloc((unsigned) size * sizeof(card8));
+            memcpy(data, &cff->stream[cff->offset], (size_t) size);
+            cff->offset = (l_offset) size;
+            cff->private[0] = cff_dict_unpack(data, data + size);
+            xfree(data);
+            len += size;
+        } else {
+            (cff->private)[0] = NULL;
+            len = 0;
+        }
+    }
+
+    return len;
+}
+
+
+@ @c
+cff_font *read_cff(unsigned char *buf, long buflength, int n)
+{
+    cff_font *cff;
+    cff_index *idx;
+    long offset;
+
+    cff = xcalloc(1, sizeof(cff_font));
+
+    cff->stream = buf;
+    cff->stream_size = (l_offset) buflength;
+    cff->index = n;
+
+    cff->header_major = get_card8(cff);
+    cff->header_minor = get_card8(cff);
+    cff->header_hdr_size = get_card8(cff);
+    if (cff->header_major == 2) {
+        /* we have only one top dictionary */
+        cff->header_offsize = get_card16(cff);
+    } else {
+        cff->header_offsize = get_card8(cff);
+        if (cff->header_offsize < 1 || cff->header_offsize > 4) {
+            normal_warning("cff","invalid offsize data (4)");
+            cff_close(cff);
+            return NULL;
+        }
+    }
+    if (cff->header_major > 2) {
+        formatted_warning("cff","major version %u not supported", cff->header_major);
+        cff_close(cff);
+        return NULL;
+    }
+    cff->offset = cff->header_hdr_size;
+
+    /* Name INDEX */
+    if (cff->header_major == 2) {
+        cff->name = cff_empty_index(cff);
+    } else {
+        idx = cff_get_index(cff);
+        if (n > idx->count - 1) {
+            normal_warning("cff","invalid fontset index number");
+            cff_close(cff);
+            return NULL;
+        }
+        cff->name = idx;
+        cff->fontname = cff_get_name(cff);
+    }
+
+    /* Top DICT INDEX */
+    if (cff->header_major == 2) {
+        /* we fake an index (just one entry) */
+        idx = cff_get_index2(cff);
+    } else {
+        idx = cff_get_index(cff);
+    }
+
+    if (n > idx->count - 1) {
+        normal_warning("cff","top DICT not exist");
+        cff_close(cff);
+        return NULL;
+    }
+    cff->topdict = cff_dict_unpack(idx->data + idx->offset[n] - 1,
+                                   idx->data + idx->offset[n + 1] - 1);
+    if (!cff->topdict) {
+        normal_warning("cff","parsing top DICT data failed");
+        cff_close(cff);
+        return NULL;
+    }
+    cff_release_index(idx);
+
+    if (cff_dict_known(cff->topdict, "CharstringType") &&
+        cff_dict_get(cff->topdict, "CharstringType", 0) != 2) {
+        normal_warning("cff","only type 2 charstrings supported");
+        cff_close(cff);
+        return NULL;
+    }
+
+    if (cff_dict_known(cff->topdict, "SyntheticBase")) {
+        normal_warning("cff","synthetic font not supported");
+        cff_close(cff);
+        return NULL;
+    }
+
+    /* String INDEX */
+    if (cff->header_major == 2) {
+     //   cff->string = cff_empty_index(cff);
+    } else {
+        cff->string = cff_get_index(cff);
+    }
+
+    /* offset to GSubr */
+    cff->gsubr_offset = cff->offset;
+
+    /* Number of glyphs */
+    offset = (long) cff_dict_get(cff->topdict, "CharStrings", 0);
+    cff->offset = (l_offset) offset;
+    cff->num_glyphs = get_card16(cff);
+
+    /* Check for font type */
+    if (cff_dict_known(cff->topdict, "ROS")) {
+        cff->flag |= FONTTYPE_CIDFONT;
+    } else {
+        cff->flag |= FONTTYPE_FONT;
+    }
+
+    /* Check for encoding */
+    if (cff_dict_known(cff->topdict, "Encoding")) {
+        offset = (long) cff_dict_get(cff->topdict, "Encoding", 0);
+        if (offset == 0) {      /* predefined */
+            cff->flag |= ENCODING_STANDARD;
+        } else if (offset == 1) {
+            cff->flag |= ENCODING_EXPERT;
+        }
+    } else {
+        cff->flag |= ENCODING_STANDARD;
+    }
+
+    cff->offset = cff->gsubr_offset;    /* seek back to GSubr */
+
+    return cff;
+}
+
+@* write a cff for opentype.
+
+@ Pack DICT data
+@c
+static long pack_integer(card8 * dest, long destlen, long value)
+{
+    long len = 0;
+
+    if (value >= -107 && value <= 107) {
+        if (destlen < 1)
+            normal_error("cff","buffer overflow (1)");
+        dest[0] = (card8) ((value + 139) & 0xff);
+        len = 1;
+    } else if (value >= 108 && value <= 1131) {
+        if (destlen < 2)
+            normal_error("cff","buffer overflow (2)");
+        value = (long) 0xf700u + value - 108;
+        dest[0] = (card8) ((value >> 8) & 0xff);
+        dest[1] = (card8) (value & 0xff);
+        len = 2;
+    } else if (value >= -1131 && value <= -108) {
+        if (destlen < 2)
+            normal_error("cff","buffer overflow (3)");
+        value = (long) 0xfb00u - value - 108;
+        dest[0] = (card8) ((value >> 8) & 0xff);
+        dest[1] = (card8) (value & 0xff);
+        len = 2;
+    } else if (value >= -32768 && value <= 32767) {     /* shortint */
+        if (destlen < 3)
+            normal_error("cff","buffer overflow (4)");
+        dest[0] = 28;
+        dest[1] = (card8) ((value >> 8) & 0xff);
+        dest[2] = (card8) (value & 0xff);
+        len = 3;
+    } else {                    /* longint */
+        if (destlen < 5)
+            normal_error("cff","buffer overflow (5)");
+        dest[0] = 29;
+        dest[1] = (card8) ((value >> 24) & 0xff);
+        dest[2] = (card8) ((value >> 16) & 0xff);
+        dest[3] = (card8) ((value >> 8) & 0xff);
+        dest[4] = (card8) (value & 0xff);
+        len = 5;
+    }
+    return len;
+}
+
+@ @c
+static long pack_real(card8 * dest, long destlen, double value)
+{
+    long e;
+    int i = 0, pos = 2;
+    int res;
+#define CFF_REAL_MAX_LEN 17
+
+    if (destlen < 2)
+        normal_error("cff","buffer overflow (6)");
+
+    dest[0] = 30;
+
+    if (value == 0.0) {
+        dest[1] = 0x0f;
+        return 2;
+    }
+
+    if (value < 0.0) {
+        dest[1] = 0xe0;
+        value *= -1.0;
+        pos++;
+    }
+
+    e = 0;
+    if (value >= 10.0) {
+        while (value >= 10.0) {
+            value /= 10.0;
+            e++;
+        }
+    } else if (value < 1.0) {
+        while (value < 1.0) {
+            value *= 10.0;
+            e--;
+        }
+    }
+
+    res=sprintf(work_buffer, "%1.14g", value);
+    if (res<0) normal_error("cff","invalid conversion");
+    if (res>CFF_REAL_MAX_LEN) res=CFF_REAL_MAX_LEN;
+
+    for (i = 0; i < res; i++) {
+        unsigned char ch = 0;
+
+        if (work_buffer[i] == '\0') {
+	  /* res should prevent this.  */
+	  /* normal_error("cff","cannot happen"); */
+            break;
+        } else if (work_buffer[i] == '.') {
+            ch = 0x0a;
+        } else if (work_buffer[i] >= '0' && work_buffer[i] <= '9') {
+            ch = (unsigned char) (work_buffer[i] - '0');
+        } else {
+            normal_error("cff","invalid character");
+        }
+
+        if (destlen < pos / 2 + 1)
+            normal_error("cff","buffer overflow (7)");
+
+        if (pos % 2) {
+            dest[pos / 2] = (card8) (dest[pos / 2] + ch);
+        } else {
+            dest[pos / 2] = (card8) (ch << 4);
+        }
+        pos++;
+    }
+
+    if (e > 0) {
+        if (pos % 2) {
+            dest[pos / 2] = (card8) (dest[pos / 2] + 0x0b);
+        } else {
+            if (destlen < pos / 2 + 1)
+                normal_error("cff","buffer overflow (8)");
+            dest[pos / 2] = (card8) (0xb0);
+        }
+        pos++;
+    } else if (e < 0) {
+        if (pos % 2) {
+            dest[pos / 2] = (card8) (dest[pos / 2] + 0x0c);
+        } else {
+            if (destlen < pos / 2 + 1)
+                normal_error("cff","buffer overflow (9)");
+            dest[pos / 2] = (card8) (0xc0);
+        }
+        e *= -1;
+        pos++;
+    }
+
+    if (e != 0) {
+        sprintf(work_buffer, "%ld", e);
+        for (i = 0; i < CFF_REAL_MAX_LEN; i++) {
+            unsigned char ch = 0;
+            if (work_buffer[i] == '\0') {
+                break;
+            } else if (work_buffer[i] == '.') {
+                ch = 0x0a;
+            } else if (work_buffer[i] >= '0' && work_buffer[i] <= '9') {
+                ch = (unsigned char) (work_buffer[i] - '0');
+            } else {
+                normal_error("cff","invalid character");
+            }
+
+            if (destlen < pos / 2 + 1)
+                normal_error("cff","buffer overflow (10)");
+
+            if (pos % 2) {
+                dest[pos / 2] = (card8) (dest[pos / 2] + ch);
+            } else {
+                dest[pos / 2] = (card8) (ch << 4);
+            }
+            pos++;
+        }
+    }
+
+    if (pos % 2) {
+        dest[pos / 2] = (card8) (dest[pos / 2] + 0x0f);
+        pos++;
+    } else {
+        if (destlen < pos / 2 + 1)
+            normal_error("cff","buffer overflow (11)");
+        dest[pos / 2] = (card8) (0xff);
+        pos += 2;
+    }
+
+    return pos / 2;
+}
+
+@ @c
+static long cff_dict_put_number(double value,
+                                card8 * dest, long destlen, int type)
+{
+    long len = 0;
+    double nearint;
+
+    nearint = floor(value + 0.5);
+    /* set offset to longint */
+    if (type == CFF_TYPE_OFFSET) {
+        long lvalue;
+        lvalue = (long) value;
+        if (destlen < 5)
+            normal_error("cff","buffer overflow (12)");
+        dest[0] = 29;
+        dest[1] = (card8) ((lvalue >> 24) & 0xff);
+        dest[2] = (card8) ((lvalue >> 16) & 0xff);
+        dest[3] = (card8) ((lvalue >> 8) & 0xff);
+        dest[4] = (card8) (lvalue & 0xff);
+        len = 5;
+    } else if (value > CFF_INT_MAX || value < CFF_INT_MIN || (fabs(value - nearint) > 1.0e-5)) {        /* real */
+        len = pack_real(dest, destlen, value);
+    } else {                    /* integer */
+        len = pack_integer(dest, destlen, (long) nearint);
+    }
+
+    return len;
+}
+
+@ @c
+static long put_dict_entry(cff_dict_entry * de, card8 * dest, long destlen)
+{
+    long len = 0;
+    int i, type, id;
+
+    if (de->count > 0) {
+        id = de->id;
+        if (dict_operator[id].argtype == CFF_TYPE_OFFSET ||
+            dict_operator[id].argtype == CFF_TYPE_SZOFF) {
+            type = CFF_TYPE_OFFSET;
+        } else {
+            type = CFF_TYPE_NUMBER;
+        }
+        for (i = 0; i < de->count; i++) {
+            len += cff_dict_put_number(de->values[i], dest + len, destlen - len, type);
+        }
+        if (id >= 0 && id < CFF_LAST_DICT_OP1) {
+            if (len + 1 > destlen)
+                normal_error("cff","buffer overflow (13)");
+            dest[len++] = (card8) id;
+        } else if (id >= 0 && id < CFF_LAST_DICT_OP) {
+            if (len + 2 > destlen)
+                normal_error("cff","buffer overflow (14)");
+            dest[len++] = 12;
+            dest[len++] = (card8) (id - CFF_LAST_DICT_OP1);
+        } else {
+            normal_error("cff","invalid DICT operator ID");
+        }
+    }
+
+    return len;
+}
+
+@ @c
+long cff_dict_pack(cff_dict * dict, card8 * dest, long destlen)
+{
+    long len = 0;
+    int i;
+
+    for (i = 0; i < dict->count; i++) {
+        if (!strcmp(dict->entries[i].key, "ROS")) {
+            len += put_dict_entry(&dict->entries[i], dest, destlen);
+            break;
+        }
+    }
+    for (i = 0; i < dict->count; i++) {
+        if (strcmp(dict->entries[i].key, "ROS")) {
+            len += put_dict_entry(&dict->entries[i], dest + len, destlen - len);
+        }
+    }
+
+    return len;
+}
+
+@ @c
+void cff_dict_add(cff_dict * dict, const char *key, int count)
+{
+    int id, i;
+
+    for (id = 0; id < CFF_LAST_DICT_OP; id++) {
+        if (key && dict_operator[id].opname &&
+            strcmp(dict_operator[id].opname, key) == 0)
+            break;
+    }
+
+    if (id == CFF_LAST_DICT_OP)
+        normal_error("cff","unknown DICT operator");
+
+    for (i = 0; i < dict->count; i++) {
+        if ((dict->entries)[i].id == id) {
+            if ((dict->entries)[i].count != count)
+                normal_error("cff","inconsistent DICT argument number");
+            return;
+        }
+    }
+
+    if (dict->count + 1 >= dict->max) {
+        dict->max += 8;
+        dict->entries =
+            xrealloc(dict->entries,
+                     (unsigned) ((unsigned) dict->max *
+                                 sizeof(cff_dict_entry)));
+    }
+
+    (dict->entries)[dict->count].id = id;
+    (dict->entries)[dict->count].key = dict_operator[id].opname;
+    (dict->entries)[dict->count].count = count;
+    if (count > 0) {
+        (dict->entries)[dict->count].values =
+            xcalloc((unsigned) count, sizeof(double));
+    } else {
+        (dict->entries)[dict->count].values = NULL;
+    }
+    dict->count += 1;
+
+    return;
+}
+
+@ @c
+void cff_dict_remove(cff_dict * dict, const char *key)
+{
+    int i;
+    for (i = 0; i < dict->count; i++) {
+        if (key && strcmp(key, (dict->entries)[i].key) == 0) {
+            (dict->entries)[i].count = 0;
+            xfree((dict->entries)[i].values);
+        }
+    }
+}
+
+@ @c
+void cff_dict_set(cff_dict * dict, const char *key, int idx, double value)
+{
+    int i;
+
+    assert(dict && key);
+
+    for (i = 0; i < dict->count; i++) {
+        if (strcmp(key, (dict->entries)[i].key) == 0) {
+            if ((dict->entries)[i].count > idx)
+                (dict->entries)[i].values[idx] = value;
+            else
+                normal_error("cff","invalid index number");
+            break;
+        }
+    }
+
+    if (i == dict->count)
+        formatted_error("cff","DICT entry '%s' not found", key);
+}
+
+
+@ Strings
+@c
+char *cff_get_string(cff_font * cff, s_SID id)
+{
+    char *result = NULL;
+    size_t len;
+
+    if (id < CFF_STDSTR_MAX) {
+        len = strlen(cff_stdstr[id]);
+        result = xmalloc((unsigned) (len + 1) * sizeof(char));
+        memcpy(result, cff_stdstr[id], len);
+        result[len] = '\0';
+    } else if (cff && cff->string) {
+        cff_index *strings = cff->string;
+        id = (s_SID) (id - CFF_STDSTR_MAX);
+        if (id < strings->count) {
+            len = (strings->offset)[id + 1] - (strings->offset)[id];
+            result = xmalloc((unsigned) (len + 1) * sizeof(char));
+            memmove(result, strings->data + (strings->offset)[id] - 1, len);
+            result[len] = '\0';
+        }
+    }
+
+    return result;
+}
+
+@ @c
+long cff_get_sid(cff_font * cff, const char *str)
+{
+    card16 i;
+
+    if (!cff || !str)
+        return -1;
+
+    /* I search String INDEX first. */
+    if (cff && cff->string) {
+        cff_index *idx = cff->string;
+        for (i = 0; i < idx->count; i++) {
+            if (strlen(str) == (idx->offset)[i + 1] - (idx->offset)[i] &&
+                !memcmp(str, (idx->data) + (idx->offset)[i] - 1, strlen(str)))
+                return (i + CFF_STDSTR_MAX);
+        }
+    }
+
+    for (i = 0; i < CFF_STDSTR_MAX; i++) {
+        if (!strcmp(str, cff_stdstr[i]))
+            return i;
+    }
+
+    return -1;
+}
+
+@ @c
+void cff_update_string(cff_font * cff)
+{
+    if (cff == NULL)
+        normal_error("cff","CFF font not opened");
+    if (cff->string)
+        cff_release_index(cff->string);
+    cff->string = cff->_string;
+    cff->_string = NULL;
+}
+
+@ @c
+s_SID cff_add_string(cff_font * cff, const char *str)
+{
+    card16 idx;
+    cff_index *strings;
+    l_offset offset, size;
+    if (cff == NULL) {
+        normal_error("cff","CFF font not opened");
+    }
+    if (cff->_string == NULL) {
+        cff->_string = cff_new_index(0);
+    }
+    strings = cff->_string;
+    for (idx = 0; idx < strings->count; idx++) {
+        size = strings->offset[idx + 1] - strings->offset[idx];
+        offset = strings->offset[idx];
+        if (size == strlen(str) && !memcmp(strings->data + offset - 1, str, strlen(str))) {
+            return (s_SID) (idx + CFF_STDSTR_MAX);
+        }
+    }
+    for (idx = 0; idx < CFF_STDSTR_MAX; idx++) {
+        if (cff_stdstr[idx] && !strcmp(cff_stdstr[idx], str)) {
+            return idx;
+        }
+    }
+    offset = (strings->count > 0) ? strings->offset[strings->count] : 1;
+    strings->offset = xrealloc(strings->offset, (unsigned) (((unsigned) strings->count + 2) * sizeof(l_offset)));
+    if (strings->count == 0)
+        strings->offset[0] = 1;
+    idx = strings->count;
+    strings->count = (card16) (strings->count + 1);
+    strings->offset[strings->count] = offset + strlen(str);
+    strings->data = xrealloc(strings->data, (unsigned) ((offset + strlen(str) - 1) * sizeof(card8)));
+    memcpy(strings->data + offset - 1, str, strlen(str));
+    return (s_SID) (idx + CFF_STDSTR_MAX);
+}
+
+@ @c
+void cff_dict_update(cff_dict * dict, cff_font * cff)
+{
+    int i;
+    for (i = 0; i < dict->count; i++) {
+        if ((dict->entries)[i].count > 0) {
+            char *str;
+            int id;
+            id = (dict->entries)[i].id;
+            if (dict_operator[id].argtype == CFF_TYPE_SID) {
+                str = cff_get_string(cff, (s_SID) (dict->entries)[i].values[0]);
+                if (str != NULL) {
+                    (dict->entries)[i].values[0] = cff_add_string(cff, str);
+                    xfree(str);
+                }
+            } else if (dict_operator[id].argtype == CFF_TYPE_ROS) {
+                str = cff_get_string(cff, (s_SID) (dict->entries)[i].values[0]);
+                if (str != NULL) {
+                    (dict->entries)[i].values[0] = cff_add_string(cff, str);
+                    xfree(str);
+                }
+                str = cff_get_string(cff, (s_SID) (dict->entries)[i].values[1]);
+                if (str != NULL) {
+                    (dict->entries)[i].values[1] = cff_add_string(cff, str);
+                    xfree(str);
+                }
+            }
+        }
+    }
+}
+
+@ charsets
+@c
+long cff_read_charsets(cff_font * cff)
+{
+    cff_charsets *charset;
+    long offset, length;
+    card16 count, i;
+
+    if (cff->topdict == NULL)
+        normal_error("cff","top DICT not available");
+
+    if (!cff_dict_known(cff->topdict, "charset")) {
+        cff->flag |= CHARSETS_ISOADOBE;
+        cff->charsets = NULL;
+        return 0;
+    }
+
+    offset = (long) cff_dict_get(cff->topdict, "charset", 0);
+
+    if (offset == 0) {          /* predefined */
+        cff->flag |= CHARSETS_ISOADOBE;
+        cff->charsets = NULL;
+        return 0;
+    } else if (offset == 1) {
+        cff->flag |= CHARSETS_EXPERT;
+        cff->charsets = NULL;
+        return 0;
+    } else if (offset == 2) {
+        cff->flag |= CHARSETS_EXPSUB;
+        cff->charsets = NULL;
+        return 0;
+    }
+
+    cff->offset = (l_offset) offset;
+    cff->charsets = charset = xcalloc(1, sizeof(cff_charsets));
+    charset->format = get_card8(cff);
+    charset->num_entries = 0;
+
+    count = (card16) (cff->num_glyphs - 1);
+    length = 1;
+
+    /* Not sure. Not well documented. */
+    switch (charset->format) {
+    case 0:
+        charset->num_entries = (card16) (cff->num_glyphs - 1);  /* no .notdef */
+        charset->data.glyphs =
+            xmalloc((unsigned) (charset->num_entries * sizeof(s_SID)));
+        length += (charset->num_entries) * 2;
+        for (i = 0; i < (charset->num_entries); i++) {
+            charset->data.glyphs[i] = get_card16(cff);
+        }
+        count = 0;
+        break;
+    case 1:
+        {
+            cff_range1 *ranges = NULL;
+            while (count > 0 && charset->num_entries < cff->num_glyphs) {
+                ranges =
+                    xrealloc(ranges,
+                             (unsigned) (((unsigned) charset->num_entries +
+                                          1) * sizeof(cff_range1)));
+                ranges[charset->num_entries].first = get_card16(cff);
+                ranges[charset->num_entries].n_left = get_card8(cff);
+                count = (card16) (count - ranges[charset->num_entries].n_left + 1);     /* no-overrap */
+                charset->num_entries++;
+                charset->data.range1 = ranges;
+            }
+            length += (charset->num_entries) * 3;
+        }
+        break;
+    case 2:
+        {
+            cff_range2 *ranges = NULL;
+            while (count > 0 && charset->num_entries < cff->num_glyphs) {
+                ranges =
+                    xrealloc(ranges,
+                             (unsigned) (((unsigned) charset->num_entries +
+                                          1) * sizeof(cff_range2)));
+                ranges[charset->num_entries].first = get_card16(cff);
+                ranges[charset->num_entries].n_left = get_card16(cff);
+                count = (card16) (count - (ranges[charset->num_entries].n_left + 1));   /* non-overrapping */
+                charset->num_entries++;
+            }
+            charset->data.range2 = ranges;
+            length += (charset->num_entries) * 4;
+        }
+        break;
+    default:
+        xfree(charset);
+        normal_error("cff","unknown charset format");
+        break;
+    }
+
+    if (count > 0) {
+        normal_warning("cff","charset data possibly broken (too many glyphs)");
+    }
+
+    return length;
+}
+
+@ @c
+long cff_pack_charsets(cff_font * cff, card8 * dest, long destlen)
+{
+    long len = 0;
+    card16 i;
+    cff_charsets *charset;
+
+    if (cff->flag & HAVE_STANDARD_CHARSETS || cff->charsets == NULL)
+        return 0;
+
+    if (destlen < 1)
+        normal_error("cff","buffer overflow (15)");
+
+    charset = cff->charsets;
+
+    dest[len++] = charset->format;
+    switch (charset->format) {
+    case 0:
+        if (destlen < len + (charset->num_entries) * 2)
+            normal_error("cff","buffer overflow (16)");
+        for (i = 0; i < (charset->num_entries); i++) {
+            s_SID sid = (charset->data).glyphs[i];      /* or CID */
+            dest[len++] = (card8) ((sid >> 8) & 0xff);
+            dest[len++] = (card8) (sid & 0xff);
+        }
+        break;
+    case 1:
+        {
+            if (destlen < len + (charset->num_entries) * 3)
+                normal_error("cff","buffer overflow (17)");
+            for (i = 0; i < (charset->num_entries); i++) {
+                dest[len++] = (card8) (((charset->data).range1[i].first >> 8) & 0xff);
+                dest[len++] = (card8) ((charset->data).range1[i].first & 0xff);
+                dest[len++] = (card8) ((charset->data).range1[i].n_left);
+            }
+        }
+        break;
+    case 2:
+        {
+            if (destlen < len + (charset->num_entries) * 4)
+                normal_error("cff","buffer overflow (18)");
+            for (i = 0; i < (charset->num_entries); i++) {
+                dest[len++] = (card8) (((charset->data).range2[i].first >> 8) & 0xff);
+                dest[len++] = (card8) ((charset->data).range2[i].first & 0xff);
+                dest[len++] = (card8) (((charset->data).range2[i].n_left >> 8) & 0xff);
+                dest[len++] = (card8) ((charset->data).range2[i].n_left & 0xff);
+            }
+        }
+        break;
+    default:
+        normal_error("cff","unknown charset format");
+        break;
+    }
+
+    return len;
+}
+
+
+
+@* Type 2 Charstring support.
+
+Decode and encode Type 2 charstring
+
+All local/global subroutine calls in a given charstring is replace by the
+content of subroutine charstrings. We do this because some PostScript RIP
+may have problems with sparse subroutine array. Workaround for this is to
+re-order subroutine array so that no gap appears in the subroutine array,
+or put dummy charstrings that contains only `return' in the gap. However,
+re-ordering of subroutine is rather difficult for Type 2 charstrings due
+to the bias which depends on the total number of subroutines. Replacing
+callgsubr/callsubr calls with the content of the corresponding subroutine
+charstring may be more efficient than putting dummy subroutines in the
+case of subsetted font. Adobe distiller seems doing same thing.
+
+And also note that subroutine numbers within subroutines can depend on the
+content of operand stack as follows:
+
+\.{  ... l m callsubr << subr \#(m+bias): n add callsubr >> ...}
+
+I've not implemented the `random' operator which generates a pseudo-random
+number in the range (0, 1] and push them into argument stack.
+How pseudo-random sequences are generated is not documented in the Type 2
+charstring spec..
+
+@c
+#define CS_TYPE2_DEBUG_STR "Type2 Charstring Parser"
+#define CS_TYPE2_DEBUG     5
+
+/* decoder/encoder status codes */
+#define CS_BUFFER_CFF_ERROR -3
+#define CS_STACK_CFF_ERROR  -2
+#define CS_PARSE_CFF_ERROR  -1
+#define CS_PARSE_OK      0
+#define CS_PARSE_END     1
+#define CS_SUBR_RETURN   2
+#define CS_CHAR_END      3
+
+static int status = CS_PARSE_CFF_ERROR;
+
+#define DST_NEED(a,b) {if ((a) < (b)) { status = CS_BUFFER_CFF_ERROR ; return ; }}
+#define SRC_NEED(a,b) {if ((a) < (b)) { status = CS_PARSE_CFF_ERROR  ; return ; }}
+#define NEED(a,b)     {if ((a) < (b)) { status = CS_STACK_CFF_ERROR  ; return ; }}
+
+/* hintmask and cntrmask need number of stem zones */
+static int num_stems = 0;
+static int phase = 0;
+
+/* subroutine nesting */
+static int cs2_nest = 0;
+
+/* advance width */
+static int have_width = 0;
+static double width = 0.0;
+
+@ Standard Encoding Accented Characters: Optional four arguments for
+endchar. See, CFF spec., p.35. This is obsolete feature and is no longer
+supported.
+@c
+#if 0
+/* adx ady bchar achar endchar */
+static double seac[4] = { 0.0, 0.0, 0.0, 0.0 };
+#endif
+
+@  Operand stack and Transient array
+@c
+static int cs2_stack_top = 0;
+static double cs2_arg_stack[CS_ARG_STACK_MAX];
+static double trn_array[CS_TRANS_ARRAY_MAX];
+
+@ Type 2 CharString encoding
+@c
+
+/*
+ 1-byte CharString operators:
+ |cs_escape| is first byte of two-byte operator
+*/
+
+/*      RESERVED      0 */
+#define cs_hstem      1
+/*      RESERVED      2 */
+#define cs_vstem      3
+#define cs_vmoveto    4
+#define cs_rlineto    5
+#define cs_hlineto    6
+#define cs_vlineto    7
+#define cs_rrcurveto  8
+/*      |cs_closepath|  9  : TYPE1 */
+#define cs_callsubr   10
+#define cs_return     11
+#define cs_escape     12
+/*      |cs_hsbw|       13 : TYPE1 */
+#define cs_endchar    14
+#define cs_setvsindex 15
+#define cs_blend      16
+/*      RESERVED      17 */
+#define cs_hstemhm    18
+#define cs_hintmask   19
+#define cs_cntrmask   20
+#define cs_rmoveto    21
+#define cs_hmoveto    22
+#define cs_vstemhm    23
+#define cs_rcurveline 24
+#define cs_rlinecurve 25
+#define cs_vvcurveto  26
+#define cs_hhcurveto  27
+/*      SHORTINT      28 : first byte of shortint*/
+#define cs_callgsubr  29
+#define cs_vhcurveto  30
+#define cs_hvcurveto  31
+
+/*
+ 2-byte CharString operaotrs:
+ "dotsection" is obsoleted in Type 2 charstring.
+ */
+
+#define cs_dotsection 0
+/*      |cs_vstem3|     1 : TYPE1 */
+/*      |cs_hstem3|     2 : TYPE1 */
+#define cs_and        3
+#define cs_or         4
+#define cs_not        5
+/*      |cs_seac|       6 : TYPE1 */
+/*      |cs_sbw|        7 : TYPE1 */
+/*      RESERVED      8  */
+#define cs_abs        9
+#define cs_add        10
+#define cs_sub        11
+#define cs_div        12
+/*      RESERVED      13 */
+#define cs_neg        14
+#define cs_eq         15
+/*      |cs_callothersubr| 16 : TYPE1 */
+/*      |cs_pop|           17 : TYPE1 */
+#define cs_drop       18
+/*      RESERVED      19 */
+#define cs_put        20
+#define cs_get        21
+#define cs_ifelse     22
+#define cs_random     23
+#define cs_mul        24
+/*      RESERVED      25 */
+#define cs_sqrt       26
+#define cs_dup        27
+#define cs_exch       28
+#define cs_index      29
+#define cs_roll       30
+/*      |cs_setcurrentpoint| 31 : TYPE1 */
+/*      RESERVED      32 */
+/*      RESERVED      33 */
+#define cs_hflex      34
+#define cs_flex       35
+#define cs_hflex1     36
+#define cs_flex1      37
+
+@ |clear_stack()| put all operands sotred in operand stack to dest.
+@c
+static void clear_stack(card8 ** dest, card8 * limit)
+{
+    int i;
+
+    for (i = 0; i < cs2_stack_top; i++) {
+        double value;
+        long ivalue;
+        value = cs2_arg_stack[i];
+        /* Nearest integer value */
+        ivalue = (long) floor(value + 0.5);
+        if (value >= 0x8000L || value <= (-0x8000L - 1)) {
+            /*
+                This number cannot be represented as a single operand. We must
+                use `a b mul ...' or `a c div' to represent large values.
+            */
+            normal_error("cff","argument value too large (this is bug)");
+        } else if (fabs(value - (double) ivalue) > 3.0e-5) {
+            /* 16.16-bit signed fixed value  */
+            DST_NEED(limit, *dest + 5);
+            *(*dest)++ = 255;
+            ivalue = (long) floor(value); /* mantissa */
+            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
+            *(*dest)++ = (card8) (ivalue & 0xff);
+            ivalue = (long) ((value - (double) ivalue) * 0x10000l); /* fraction */
+            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
+            *(*dest)++ = (card8) (ivalue & 0xff);
+            /* Everything else are integers. */
+        } else if (ivalue >= -107 && ivalue <= 107) {
+            DST_NEED(limit, *dest + 1);
+            *(*dest)++ = (card8) (ivalue + 139);
+        } else if (ivalue >= 108 && ivalue <= 1131) {
+            DST_NEED(limit, *dest + 2);
+            ivalue = (long) 0xf700u + ivalue - 108;
+            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
+            *(*dest)++ = (card8) (ivalue & 0xff);
+        } else if (ivalue >= -1131 && ivalue <= -108) {
+            DST_NEED(limit, *dest + 2);
+            ivalue = (long) 0xfb00u - ivalue - 108;
+            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
+            *(*dest)++ = (card8) (ivalue & 0xff);
+        } else if (ivalue >= -32768 && ivalue <= 32767) {
+            /* shortint */
+            DST_NEED(limit, *dest + 3);
+            *(*dest)++ = 28;
+            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
+            *(*dest)++ = (card8) ((ivalue) & 0xff);
+        } else {                /* Shouldn't come here */
+            normal_error("cff","unexpected error");
+        }
+    }
+    cs2_stack_top = 0; /* clear stack */
+    return;
+}
+
+@ Single byte operators: Path construction, Operator for finishing a
+path, Hint operators. Phases:
+
+\item 0: inital state
+\item 1: hint declaration, first stack-clearing operator appeared
+\item 2: in path construction
+
+@c
+/*
+static int vsindex = 0;
+static int blends  = 0;
+static int regions = 5;
+*/
+
+static void do_operator1(card8 ** dest, card8 * limit, card8 ** data, card8 * endptr)
+{
+    card8 op = **data;
+
+    *data += 1;
+
+    switch (op) {
+    case cs_hstemhm:
+    case cs_vstemhm:
+        /* charstring may have hintmask if above operator have seen */
+    case cs_hstem:
+    case cs_vstem:
+        if (phase == 0 && (cs2_stack_top % 2)) {
+            have_width = 1;
+            width = cs2_arg_stack[0];
+        }
+        num_stems += cs2_stack_top / 2;
+        clear_stack(dest, limit);
+        DST_NEED(limit, *dest + 1);
+        *(*dest)++ = op;
+        phase = 1;
+        break;
+    case cs_hintmask:
+    case cs_cntrmask:
+        if (phase < 2) {
+            if (phase == 0 && (cs2_stack_top % 2)) {
+                have_width = 1;
+                width = cs2_arg_stack[0];
+            }
+            num_stems += cs2_stack_top / 2;
+        }
+        clear_stack(dest, limit);
+        DST_NEED(limit, *dest + 1);
+        *(*dest)++ = op;
+        if (num_stems > 0) {
+            int masklen = (num_stems + 7) / 8;
+            DST_NEED(limit, *dest + masklen);
+            SRC_NEED(endptr, *data + masklen);
+            memmove(*dest, *data, (size_t) masklen);
+            *data += masklen;
+            *dest += masklen;
+        }
+        phase = 2;
+        break;
+    case cs_rmoveto:
+        if (phase == 0 && (cs2_stack_top % 2)) {
+            have_width = 1;
+            width = cs2_arg_stack[0];
+        }
+        clear_stack(dest, limit);
+        DST_NEED(limit, *dest + 1);
+        *(*dest)++ = op;
+        phase = 2;
+        break;
+    case cs_hmoveto:
+    case cs_vmoveto:
+        if (phase == 0 && (cs2_stack_top % 2) == 0) {
+            have_width = 1;
+            width = cs2_arg_stack[0];
+        }
+        clear_stack(dest, limit);
+        DST_NEED(limit, *dest + 1);
+        *(*dest)++ = op;
+        phase = 2;
+        break;
+    case cs_endchar:
+        if (cs2_stack_top == 1) {
+            have_width = 1;
+            width = cs2_arg_stack[0];
+            clear_stack(dest, limit);
+        } else if (cs2_stack_top == 4 || cs2_stack_top == 5) {
+            normal_warning("cff","'seac' character deprecated in type 2 charstring");
+            status = CS_PARSE_CFF_ERROR;
+            return;
+        } else if (cs2_stack_top > 0) {
+            normal_warning("cff","operand stack not empty");
+        }
+        DST_NEED(limit, *dest + 1);
+        *(*dest)++ = op;
+        status = CS_CHAR_END;
+        break;
+        /* above operators are candidate for first stack-clearing operator */
+    case cs_setvsindex:
+        /*
+        vsindex = cs2_arg_stack[cs2_stack_top-1];
+        cs2_stack_top -= 1;
+        */
+        normal_warning("cff2","unsupported setvindex operator");
+        status = CS_PARSE_CFF_ERROR;
+        break;
+    case cs_blend:
+        /*
+        blends = cs2_arg_stack[cs2_stack_top-1];
+        cs2_stack_top -= 1;
+        cs2_stack_top -= blends * regions ;
+        */
+        normal_warning("cff2","unsupported blend operator");
+        status = CS_PARSE_CFF_ERROR;
+        break;
+    case cs_rlineto:
+    case cs_hlineto:
+    case cs_vlineto:
+    case cs_rrcurveto:
+    case cs_rcurveline:
+    case cs_rlinecurve:
+    case cs_vvcurveto:
+    case cs_hhcurveto:
+    case cs_vhcurveto:
+    case cs_hvcurveto:
+        if (phase < 2) {
+            normal_warning("cff","broken type 2 charstring");
+            status = CS_PARSE_CFF_ERROR;
+            return;
+        }
+        clear_stack(dest, limit);
+        DST_NEED(limit, *dest + 1);
+        *(*dest)++ = op;
+        break;
+        /* all operotors above are stack-clearing operator */
+        /* no output */
+    case cs_return:
+    case cs_callgsubr:
+    case cs_callsubr:
+        normal_error("cff","unexpected call(g)subr/return");
+        break;
+    default:
+        /* no-op ? */
+        formatted_warning("cff","%s: unknown charstring operator: 0x%02x", CS_TYPE2_DEBUG_STR, op);
+        status = CS_PARSE_CFF_ERROR;
+        break;
+    }
+    return;
+}
+
+@ Double byte operators: Flex, arithmetic, conditional, and storage operators.
+The following operators are not supported: random: How random ?
+
+@c
+static void do_operator2(card8 ** dest, card8 * limit, card8 ** data, card8 * endptr)
+{
+    card8 op;
+
+    *data += 1;
+
+    SRC_NEED(endptr, *data + 1);
+
+    op = **data;
+    *data += 1;
+
+    switch (op) {
+    case cs_dotsection:        /* deprecated */
+        normal_warning("cff","Operator 'dotsection' deprecated in type 2 charstring");
+        status = CS_PARSE_CFF_ERROR;
+        return;
+        break;
+    case cs_hflex:
+    case cs_flex:
+    case cs_hflex1:
+    case cs_flex1:
+        if (phase < 2) {
+            formatted_warning("cff","%s: broken type 2 charstring", CS_TYPE2_DEBUG_STR);
+            status = CS_PARSE_CFF_ERROR;
+            return;
+        }
+        clear_stack(dest, limit);
+        DST_NEED(limit, *dest + 2);
+        *(*dest)++ = cs_escape;
+        *(*dest)++ = op;
+        break;
+        /* all operator above are stack-clearing */
+        /* no output */
+    case cs_and:
+        NEED(cs2_stack_top, 2);
+        cs2_stack_top--;
+        if (cs2_arg_stack[cs2_stack_top] && cs2_arg_stack[cs2_stack_top - 1]) {
+            cs2_arg_stack[cs2_stack_top - 1] = 1.0;
+        } else {
+            cs2_arg_stack[cs2_stack_top - 1] = 0.0;
+        }
+        break;
+    case cs_or:
+        NEED(cs2_stack_top, 2);
+        cs2_stack_top--;
+        if (cs2_arg_stack[cs2_stack_top] || cs2_arg_stack[cs2_stack_top - 1]) {
+            cs2_arg_stack[cs2_stack_top - 1] = 1.0;
+        } else {
+            cs2_arg_stack[cs2_stack_top - 1] = 0.0;
+        }
+        break;
+    case cs_not:
+        NEED(cs2_stack_top, 1);
+        if (cs2_arg_stack[cs2_stack_top - 1]) {
+            cs2_arg_stack[cs2_stack_top - 1] = 0.0;
+        } else {
+            cs2_arg_stack[cs2_stack_top - 1] = 1.0;
+        }
+        break;
+    case cs_abs:
+        NEED(cs2_stack_top, 1);
+        cs2_arg_stack[cs2_stack_top - 1] =
+            fabs(cs2_arg_stack[cs2_stack_top - 1]);
+        break;
+    case cs_add:
+        NEED(cs2_stack_top, 2);
+        cs2_arg_stack[cs2_stack_top - 2] += cs2_arg_stack[cs2_stack_top - 1];
+        cs2_stack_top--;
+        break;
+    case cs_sub:
+        NEED(cs2_stack_top, 2);
+        cs2_arg_stack[cs2_stack_top - 2] -= cs2_arg_stack[cs2_stack_top - 1];
+        cs2_stack_top--;
+        break;
+    case cs_div:               /* doesn't check overflow */
+        NEED(cs2_stack_top, 2);
+        cs2_arg_stack[cs2_stack_top - 2] /= cs2_arg_stack[cs2_stack_top - 1];
+        cs2_stack_top--;
+        break;
+    case cs_neg:
+        NEED(cs2_stack_top, 1);
+        cs2_arg_stack[cs2_stack_top - 1] *= -1.0;
+        break;
+    case cs_eq:
+        NEED(cs2_stack_top, 2);
+        cs2_stack_top--;
+        if (cs2_arg_stack[cs2_stack_top] == cs2_arg_stack[cs2_stack_top - 1]) {
+            cs2_arg_stack[cs2_stack_top - 1] = 1.0;
+        } else {
+            cs2_arg_stack[cs2_stack_top - 1] = 0.0;
+        }
+        break;
+    case cs_drop:
+        NEED(cs2_stack_top, 1);
+        cs2_stack_top--;
+        break;
+    case cs_put:
+        NEED(cs2_stack_top, 2);
+        {
+            int idx = (int) cs2_arg_stack[--cs2_stack_top];
+            NEED(CS_TRANS_ARRAY_MAX, idx);
+            trn_array[idx] = cs2_arg_stack[--cs2_stack_top];
+        }
+        break;
+    case cs_get:
+        NEED(cs2_stack_top, 1);
+        {
+            int idx = (int) cs2_arg_stack[cs2_stack_top - 1];
+            NEED(CS_TRANS_ARRAY_MAX, idx);
+            cs2_arg_stack[cs2_stack_top - 1] = trn_array[idx];
+        }
+        break;
+    case cs_ifelse:
+        NEED(cs2_stack_top, 4);
+        cs2_stack_top -= 3;
+        if (cs2_arg_stack[cs2_stack_top + 1] > cs2_arg_stack[cs2_stack_top + 2]) {
+            cs2_arg_stack[cs2_stack_top - 1] = cs2_arg_stack[cs2_stack_top];
+        }
+        break;
+    case cs_mul:
+        NEED(cs2_stack_top, 2);
+        cs2_arg_stack[cs2_stack_top - 2] =
+            cs2_arg_stack[cs2_stack_top - 2] * cs2_arg_stack[cs2_stack_top - 1];
+        cs2_stack_top--;
+        break;
+    case cs_sqrt:
+        NEED(cs2_stack_top, 1);
+        cs2_arg_stack[cs2_stack_top - 1] =
+            sqrt(cs2_arg_stack[cs2_stack_top - 1]);
+        break;
+    case cs_dup:
+        NEED(cs2_stack_top, 1);
+        NEED(CS_ARG_STACK_MAX, cs2_stack_top + 1);
+        cs2_arg_stack[cs2_stack_top] = cs2_arg_stack[cs2_stack_top - 1];
+        cs2_stack_top++;
+        break;
+    case cs_exch:
+        NEED(cs2_stack_top, 2);
+        {
+            double save = cs2_arg_stack[cs2_stack_top - 2];
+            cs2_arg_stack[cs2_stack_top - 2] = cs2_arg_stack[cs2_stack_top - 1];
+            cs2_arg_stack[cs2_stack_top - 1] = save;
+        }
+        break;
+    case cs_index:
+        NEED(cs2_stack_top, 2); /* need two arguments at least */
+        {
+            int idx = (int) cs2_arg_stack[cs2_stack_top - 1];
+            if (idx < 0) {
+                cs2_arg_stack[cs2_stack_top - 1] =
+                    cs2_arg_stack[cs2_stack_top - 2];
+            } else {
+                NEED(cs2_stack_top, idx + 2);
+                cs2_arg_stack[cs2_stack_top - 1] =
+                    cs2_arg_stack[cs2_stack_top - idx - 2];
+            }
+        }
+        break;
+    case cs_roll:
+        NEED(cs2_stack_top, 2);
+        {
+            int N, J;
+            J = (int) cs2_arg_stack[--cs2_stack_top];
+            N = (int) cs2_arg_stack[--cs2_stack_top];
+            NEED(cs2_stack_top, N);
+            if (J > 0) {
+                J = J % N;
+                while (J-- > 0) {
+                    double save = cs2_arg_stack[cs2_stack_top - 1];
+                    int i = cs2_stack_top - 1;
+                    while (i > cs2_stack_top - N) {
+                        cs2_arg_stack[i] = cs2_arg_stack[i - 1];
+                        i--;
+                    }
+                    cs2_arg_stack[i] = save;
+                }
+            } else {
+                J = (-J) % N;
+                while (J-- > 0) {
+                    double save = cs2_arg_stack[cs2_stack_top - N];
+                    int i = cs2_stack_top - N;
+                    while (i < cs2_stack_top - 1) {
+                        cs2_arg_stack[i] = cs2_arg_stack[i + 1];
+                        i++;
+                    }
+                    cs2_arg_stack[i] = save;
+                }
+            }
+        }
+        break;
+    case cs_random:
+        formatted_warning("cff","%s: Charstring operator 'random' found.", CS_TYPE2_DEBUG_STR);
+        NEED(CS_ARG_STACK_MAX, cs2_stack_top + 1);
+        cs2_arg_stack[cs2_stack_top++] = 1.0;
+        break;
+    default:
+        /* no-op ? */
+        formatted_warning("cff","%s: unknown charstring operator: 0x0c%02x", CS_TYPE2_DEBUG_STR, op);
+        status = CS_PARSE_CFF_ERROR;
+        break;
+    }
+    return;
+}
+
+@ integer: exactly the same as the DICT encoding (except 29)
+@c
+static void cs2_get_integer(card8 ** data, card8 * endptr)
+{
+    long result = 0;
+    card8 b0 = **data, b1, b2;
+
+    *data += 1;
+
+    if (b0 == 28) {             /* shortint */
+        SRC_NEED(endptr, *data + 2);
+        b1 = **data;
+        b2 = *(*data + 1);
+        result = b1 * 256 + b2;
+        if (result > 0x7fff)
+            result -= 0x10000L;
+        *data += 2;
+    } else if (b0 >= 32 && b0 <= 246) { /* int (1) */
+        result = b0 - 139;
+    } else if (b0 >= 247 && b0 <= 250) {        /* int (2) */
+        SRC_NEED(endptr, *data + 1);
+        b1 = **data;
+        result = (b0 - 247) * 256 + b1 + 108;
+        *data += 1;
+    } else if (b0 >= 251 && b0 <= 254) {
+        SRC_NEED(endptr, *data + 1);
+        b1 = **data;
+        result = -(b0 - 251) * 256 - b1 - 108;
+        *data += 1;
+    } else {
+        status = CS_PARSE_CFF_ERROR;
+        return;
+    }
+    NEED(CS_ARG_STACK_MAX, cs2_stack_top + 1);
+    cs2_arg_stack[cs2_stack_top++] = (double) result;
+    return;
+}
+
+@ Signed 16.16-bits fixed number for Type 2 charstring encoding
+@c
+static void get_fixed(card8 ** data, card8 * endptr)
+{
+    long ivalue;
+    double rvalue;
+
+    *data += 1;
+
+    SRC_NEED(endptr, *data + 4);
+
+    ivalue = *(*data) * 0x100 + *(*data + 1);
+    rvalue = (double) ((ivalue > 0x7fffL) ? (ivalue - 0x10000L) : ivalue);
+    ivalue = *(*data + 2) * 0x100 + *(*data + 3);
+    rvalue += ((double) ivalue) / 0x10000L;
+
+    NEED(CS_ARG_STACK_MAX, cs2_stack_top + 1);
+    cs2_arg_stack[cs2_stack_top++] = rvalue;
+    *data += 4;
+
+    return;
+}
+
+@ Subroutines: the bias for subroutine number is introduced in type 2
+charstrings.
+
+\item subr:       set to a pointer to the subroutine charstring.
+\item len:        set to the length of subroutine charstring.
+\item |subr_idx|: CFF INDEX data that contains subroutines.
+\item id:         biased subroutine number.
+
+@c
+static void get_subr(card8 ** subr, long *len, cff_index * subr_idx, long id)
+{
+    card16 count;
+
+    if (subr_idx == NULL)
+        formatted_error("cff","%s: subroutine called but no subroutine found",CS_TYPE2_DEBUG_STR);
+
+    count = subr_idx->count;
+
+    /* Adding bias number */
+    if (count < 1240) {
+        id += 107;
+    } else if (count < 33900) {
+        id += 1131;
+    } else {
+        id += 32768;
+    }
+
+    if (id > count)
+        formatted_error("cff","%s: invalid subroutine index: %ld (max=%u)", CS_TYPE2_DEBUG_STR, id, count);
+
+    *len = (long) ((subr_idx->offset)[id + 1] - (subr_idx->offset)[id]);
+    *subr = subr_idx->data + (subr_idx->offset)[id] - 1;
+
+    return;
+}
+
+@ The Type 2 interpretation of a number encoded in five-bytes (those with
+an initial byte value of 255) differs from how it is interpreted in the
+Type 1 format.
+
+@c
+static void do_charstring(card8 ** dest, card8 * limit,
+      card8 ** data, card8 * endptr,
+      cff_index * gsubr_idx, cff_index * subr_idx, int cff2)
+{
+    card8 b0 = 0, *subr;
+    long len;
+
+    if (cs2_nest > CS_SUBR_NEST_MAX)
+        formatted_error("cff","%s: subroutine nested too deeply", CS_TYPE2_DEBUG_STR);
+
+    cs2_nest++;
+
+    while (*data < endptr && status == CS_PARSE_OK) {
+        b0 = **data;
+        if (b0 == 255) {        /* 16-bit.16-bit fixed signed number */
+            get_fixed(data, endptr);
+        } else if (b0 == cs_return) {
+            status = CS_SUBR_RETURN;
+        } else if (b0 == cs_callgsubr) {
+            if (cs2_stack_top < 1) {
+                status = CS_STACK_CFF_ERROR;
+            } else {
+                cs2_stack_top--;
+                get_subr(&subr, &len, gsubr_idx,
+                         (long) cs2_arg_stack[cs2_stack_top]);
+                if (*dest + len > limit)
+                    formatted_error("cff","%s: possible buffer overflow (1)", CS_TYPE2_DEBUG_STR);
+                do_charstring(dest, limit, &subr, subr + len, gsubr_idx, subr_idx, cff2);
+                *data += 1;
+            }
+        } else if (b0 == cs_callsubr) {
+            if (cs2_stack_top < 1) {
+                status = CS_STACK_CFF_ERROR;
+            } else {
+                cs2_stack_top--;
+                get_subr(&subr, &len, subr_idx,
+                         (long) cs2_arg_stack[cs2_stack_top]);
+                if (limit < *dest + len)
+                    formatted_error("cff","%s: possible buffer overflow (2)", CS_TYPE2_DEBUG_STR);
+                do_charstring(dest, limit, &subr, subr + len, gsubr_idx, subr_idx, cff2);
+                *data += 1;
+            }
+        } else if (b0 == cs_escape) {
+            do_operator2(dest, limit, data, endptr);
+        } else if (b0 < 32 && b0 != 28) {       /* 19, 20 need mask */
+            do_operator1(dest, limit, data, endptr);
+        } else if ((b0 <= 22 && b0 >= 27) || b0 == 31) {        /* reserved */
+            status = CS_PARSE_CFF_ERROR;        /* not an error ? */
+        } else {                /* integer */
+            cs2_get_integer(data, endptr);
+        }
+    }
+
+    if (cff2) {
+        DST_NEED(limit, *dest + 1);
+        ++endptr;
+        *(*dest)++ = cs_endchar;
+        /* no return and endchar */
+    } else if (status == CS_SUBR_RETURN) {
+        status = CS_PARSE_OK;
+    } else if (status == CS_CHAR_END && *data < endptr) {
+        formatted_warning("cff","%s: garbage after endchar", CS_TYPE2_DEBUG_STR);
+    } else if (status < CS_PARSE_OK) {  /* error */
+        formatted_error("cff","%s: parsing charstring failed: (status=%d, stack=%d)", CS_TYPE2_DEBUG_STR, status, cs2_stack_top);
+    }
+
+    cs2_nest--;
+
+    return;
+}
+
+@ @c
+static void cs_parse_init(void)
+{
+    status = CS_PARSE_OK;
+    cs2_nest = 0;
+    phase = 0;
+    num_stems = 0;
+    cs2_stack_top = 0;
+}
+
+@ Not just copying...
+@c
+static long cs_copy_charstring(card8 * dst, long dstlen, card8 * src, long srclen,
+       cff_index * gsubr, cff_index * subr, double default_width,
+       double nominal_width, cs_ginfo * ginfo, int cff2)
+{
+    card8 *save = dst;
+
+    cs_parse_init();
+
+    width = 0.0;
+    have_width = 0;
+
+    /* expand call(g)subrs */
+    do_charstring(&dst, dst + dstlen, &src, src + srclen, gsubr, subr, cff2);
+
+    if (ginfo) {
+        ginfo->flags = 0;       /* not used */
+        if (have_width) {
+            ginfo->wx = nominal_width + width;
+        } else {
+            ginfo->wx = default_width;
+        }
+    }
+
+    return (long) (dst - save);
+}
+
+@ CID-Keyed font specific
+@c
+long cff_read_fdselect(cff_font * cff)
+{
+    cff_fdselect *fdsel;
+    long offset, length;
+    card16 i;
+
+    if (cff->topdict == NULL)
+        normal_error("cff","top DICT not available");
+
+    if (!(cff->flag & FONTTYPE_CIDFONT))
+        return 0;
+
+    offset = (long) cff_dict_get(cff->topdict, "FDSelect", 0);
+    cff->offset = (l_offset) offset;
+    cff->fdselect = fdsel = xcalloc(1, sizeof(cff_fdselect));
+    fdsel->format = get_card8(cff);
+
+    length = 1;
+
+    switch (fdsel->format) {
+    case 0:
+        fdsel->num_entries = cff->num_glyphs;
+        (fdsel->data).fds = xmalloc(fdsel->num_entries * sizeof(card8));
+        for (i = 0; i < (fdsel->num_entries); i++) {
+            (fdsel->data).fds[i] = get_card8(cff);
+        }
+        length += fdsel->num_entries;
+        break;
+    case 3:
+        {
+            cff_range3 *ranges;
+            fdsel->num_entries = get_card16(cff);
+            fdsel->data.ranges = ranges =
+                xcalloc(fdsel->num_entries, sizeof(cff_range3));
+            for (i = 0; i < (fdsel->num_entries); i++) {
+                ranges[i].first = get_card16(cff);
+                ranges[i].fd = get_card8(cff);
+            }
+            if (ranges[0].first != 0)
+                normal_error("cff","range not starting with 0");
+            if (cff->num_glyphs != get_card16(cff))
+                normal_error("cff","sentinel value mismatched with number of glyphs");
+            length += (fdsel->num_entries) * 3 + 4;
+        }
+        break;
+    default:
+        xfree(fdsel);
+        normal_error("cff","unknown FDSelect format");
+        break;
+    }
+
+    return length;
+}
+
+@ @c
+long cff_pack_fdselect(cff_font * cff, card8 * dest, long destlen)
+{
+    cff_fdselect *fdsel;
+    long len = 0;
+    card16 i;
+
+    if (cff->fdselect == NULL)
+        return 0;
+
+    if (destlen < 1)
+        normal_error("cff","buffer overflow (23)");
+
+    fdsel = cff->fdselect;
+
+    dest[len++] = fdsel->format;
+    switch (fdsel->format) {
+    case 0:
+        if (fdsel->num_entries != cff->num_glyphs)
+            normal_error("cff","invalid data");
+        if (destlen < len + fdsel->num_entries)
+            normal_error("cff","buffer overflow (24)");
+        for (i = 0; i < fdsel->num_entries; i++) {
+            dest[len++] = (fdsel->data).fds[i];
+        }
+        break;
+    case 3:
+        {
+            if (destlen < len + 2)
+                normal_error("cff","buffer overflow (25)");
+            len += 2;
+            for (i = 0; i < (fdsel->num_entries); i++) {
+                if (destlen < len + 3)
+                    normal_error("cff","buffer overflow (26)");
+                dest[len++] =
+                    (card8) (((fdsel->data).ranges[i].first >> 8) & 0xff);
+                dest[len++] = (card8) ((fdsel->data).ranges[i].first & 0xff);
+                dest[len++] = (card8) ((fdsel->data).ranges[i].fd);
+            }
+            if (destlen < len + 2)
+                normal_error("cff","buffer overflow (27)");
+            dest[len++] = (card8) ((cff->num_glyphs >> 8) & 0xff);
+            dest[len++] = (card8) (cff->num_glyphs & 0xff);
+            dest[1] = (card8) (((len / 3 - 1) >> 8) & 0xff);
+            dest[2] = (card8) ((len / 3 - 1) & 0xff);
+        }
+        break;
+    default:
+        normal_error("cff","unknown FDSelect format");
+        break;
+    }
+
+    return len;
+}
+
+@ Create an instance of embeddable font.
+
+@c
+static void write_fontfile(PDF pdf, cff_font * cffont, char *fullname)
+{
+    cff_index *topdict, *fdarray, *private;
+    unsigned char *dest;
+    long destlen = 0, i, size;
+    long offset, topdict_offset, fdarray_offset;
+
+    /*  DICT sizes (offset set to long int) */
+    topdict = cff_new_index(1);
+    fdarray = cff_new_index(cffont->num_fds);
+    private = cff_new_index(cffont->num_fds);
+
+    cff_dict_remove(cffont->topdict, "UniqueID");
+    cff_dict_remove(cffont->topdict, "XUID");
+    cff_dict_remove(cffont->topdict, "Private");        /* some bad font may have */
+    cff_dict_remove(cffont->topdict, "Encoding");       /* some bad font may have */
+    cff_dict_remove(cffont->topdict, "vstore");         /* cff2 */
+    cff_dict_remove(cffont->topdict, "maxstack");       /* cff2 */
+
+    topdict->offset[1] = (l_offset) cff_dict_pack(cffont->topdict, (card8 *) work_buffer, WORK_BUFFER_SIZE) + 1;
+    for (i = 0; i < cffont->num_fds; i++) {
+        size = 0;
+        if (cffont->private && cffont->private[i]) {
+            size = cff_dict_pack(cffont->private[i], (card8 *) work_buffer, WORK_BUFFER_SIZE);
+            if (size < 1) {
+                /* Private had contained only Subr. */
+                cff_dict_remove(cffont->fdarray[i], "Private");
+            }
+        }
+        (private->offset)[i + 1] = (unsigned long) ((private->offset)[i]
+            + (unsigned) size);
+        (fdarray->offset)[i + 1] = (unsigned long) ((fdarray->offset)[i]
+            + (unsigned) cff_dict_pack(cffont->fdarray[i], (card8 *) work_buffer, WORK_BUFFER_SIZE));
+    }
+
+    destlen = 4;                /* header size */
+    destlen += cff_set_name(cffont, fullname);
+    destlen += cff_index_size(topdict);
+    destlen += cff_index_size(cffont->string);
+    destlen += cff_index_size(cffont->gsubr);
+    destlen += (cffont->charsets->num_entries) * 2 + 1; /* charset format 0 */
+    destlen += (cffont->fdselect->num_entries) * 3 + 5; /* fdselect format 3 */
+    destlen += cff_index_size(cffont->cstrings);
+    destlen += cff_index_size(fdarray);
+    destlen = (long) (destlen + (long) private->offset[private->count] - 1);    /* Private is not INDEX */
+    dest = xcalloc((unsigned) destlen, sizeof(card8));
+
+    offset = 0;
+    /* Header */
+    offset += cff_put_header(cffont, dest + offset, destlen - offset);
+    /* Name */
+    offset += cff_pack_index(cffont->name, dest + offset, destlen - offset);
+    /* Top DICT */
+    topdict_offset = offset;
+    offset += cff_index_size(topdict);
+    /* Strings */
+    offset += cff_pack_index(cffont->string, dest + offset, destlen - offset);
+    /* Global Subrs */
+    offset += cff_pack_index(cffont->gsubr, dest + offset, destlen - offset);
+    /* charset */
+    cff_dict_set(cffont->topdict, "charset", 0, (double) offset);
+    offset += cff_pack_charsets(cffont, dest + offset, destlen - offset);
+    /* FDSelect */
+    cff_dict_set(cffont->topdict, "FDSelect", 0, (double) offset);
+    offset += cff_pack_fdselect(cffont, dest + offset, destlen - offset);
+    /* CharStrings */
+    cff_dict_set(cffont->topdict, "CharStrings", 0, (double) offset);
+    offset += cff_pack_index(cffont->cstrings, dest + offset, cff_index_size(cffont->cstrings));
+    cff_release_index(cffont->cstrings);
+    cffont->cstrings = NULL;    /* Charstrings cosumes huge memory */
+    /* FDArray and Private */
+    cff_dict_set(cffont->topdict, "FDArray", 0, (double) offset);
+    fdarray_offset = offset;
+    offset += cff_index_size(fdarray);
+
+    fdarray->data = xcalloc((unsigned) (fdarray->offset[fdarray->count] - 1), sizeof(card8));
+    for (i = 0; i < cffont->num_fds; i++) {
+        size = (long) (private->offset[i + 1] - private->offset[i]);
+        if (cffont->private[i] && size > 0) {
+            cff_dict_pack(cffont->private[i], dest + offset, size);
+            cff_dict_set(cffont->fdarray[i], "Private", 0, (double) size);
+            cff_dict_set(cffont->fdarray[i], "Private", 1, (double) offset);
+        }
+        cff_dict_pack(cffont->fdarray[i], fdarray->data + (fdarray->offset)[i] - 1, (long) (fdarray->offset[fdarray->count] - 1));
+        offset += size;
+    }
+    cff_pack_index(fdarray, dest + fdarray_offset, cff_index_size(fdarray));
+    cff_release_index(fdarray);
+    cff_release_index(private);
+
+    /* Finally Top DICT */
+    topdict->data = xcalloc((unsigned) (topdict->offset[topdict->count] - 1), sizeof(card8));
+    cff_dict_pack(cffont->topdict, topdict->data, (long) (topdict->offset[topdict->count] - 1));
+    cff_pack_index(topdict, dest + topdict_offset, cff_index_size(topdict));
+    cff_release_index(topdict);
+
+    for (i = 0; i < offset; i++) {
+// printf("%i\n",(unsigned char)dest[i]);
+        strbuf_putchar(pdf->fb, dest[i]);
+    }
+    xfree(dest);
+    return;
+}
+
+@ @c
+void write_cff(PDF pdf, cff_font * cffont, fd_entry * fd)
+{
+    cff_index *charstrings, *cs_idx;
+
+    long charstring_len, max_len;
+    long size, offset = 0;
+
+    card8 *data;
+    card16 num_glyphs, cs_count1, code, gid, last_cid;
+
+    double nominal_width, default_width;
+
+    char *fontname;
+    char *fullname;
+
+    glw_entry *glyph, *found;
+    struct avl_traverser t;
+
+cffont->_string = NULL;
+
+    fontname = xcalloc((unsigned) (1 + strlen(fd->fontname)), 1);
+    sprintf(fontname, "%s", fd->fontname);
+
+    fullname = xcalloc((unsigned) (8 + strlen(fd->fontname)), 1);
+    sprintf(fullname, "%s+%s", fd->subset_tag, fd->fontname);
+
+    /* finish parsing the CFF */
+    cff_read_private(cffont);
+    cff_read_subrs(cffont);
+
+    /* Widths */
+    if (cffont->private[0] && cff_dict_known(cffont->private[0], "defaultWidthX")) {
+        default_width = (double) cff_dict_get(cffont->private[0], "defaultWidthX", 0);
+    } else {
+        default_width = CFF_DEFAULTWIDTHX_DEFAULT;
+    }
+    if (cffont->private[0] && cff_dict_known(cffont->private[0], "nominalWidthX")) {
+        nominal_width = (double) cff_dict_get(cffont->private[0], "nominalWidthX", 0);
+    } else {
+        nominal_width = CFF_NOMINALWIDTHX_DEFAULT;
+    }
+
+    num_glyphs = 0;
+    last_cid = 0;
+    glyph = xtalloc(1, glw_entry);
+
+    /* insert notdef */
+    glyph->id = 0;
+    if (avl_find(fd->gl_tree, glyph) == NULL) {
+        avl_insert(fd->gl_tree, glyph);
+        glyph = xtalloc(1, glw_entry);
+    }
+
+    avl_t_init(&t, fd->gl_tree);
+    for (found = (glw_entry *) avl_t_first(&t, fd->gl_tree);
+         found != NULL; found = (glw_entry *) avl_t_next(&t)) {
+        if (found->id > last_cid)
+            last_cid = (card16) found->id;
+        num_glyphs++;
+    }
+
+    {
+        cff_fdselect *fdselect;
+
+        fdselect = xcalloc(1, sizeof(cff_fdselect));
+        fdselect->format = 3;
+        fdselect->num_entries = 1;
+        fdselect->data.ranges = xcalloc(1, sizeof(cff_range3));
+        fdselect->data.ranges[0].first = 0;
+        fdselect->data.ranges[0].fd = 0;
+        cffont->fdselect = fdselect;
+    }
+
+    {
+        cff_charsets *charset;
+
+        charset = xcalloc(1, sizeof(cff_charsets));
+        charset->format = 0;
+        charset->num_entries = (card16) (num_glyphs - 1);
+        charset->data.glyphs = xcalloc(num_glyphs, sizeof(s_SID));
+
+        gid = 0;
+
+        avl_t_init(&t, fd->gl_tree);
+        for (found = (glw_entry *) avl_t_first(&t, fd->gl_tree);
+             found != NULL; found = (glw_entry *) avl_t_next(&t)) {
+            if (found->id != 0) {
+                charset->data.glyphs[gid] = (s_SID) found->id;
+                gid++;
+            }
+        }
+        cffont->charsets = charset;
+
+        if (cffont->header_major == 2) {
+            cff_dict_add(cffont->topdict, "charset", 1);
+        }
+    }
+    cff_dict_add(cffont->topdict, "CIDCount", 1);
+    cff_dict_set(cffont->topdict, "CIDCount", 0, last_cid + 1);
+
+    if (cffont->header_major == 2) {
+
+        cff_dict_add(cffont->topdict, "FullName", 1);
+        cff_dict_set(cffont->topdict, "FullName", 0, (double) cff_add_string(cffont, fontname));
+
+        cff_dict_add(cffont->topdict, "FontBBox", 4);
+        cff_dict_set(cffont->topdict, "FontBBox", 0, fd->font_dim[FONTBBOX1_CODE].val);
+        cff_dict_set(cffont->topdict, "FontBBox", 1, fd->font_dim[FONTBBOX2_CODE].val);
+        cff_dict_set(cffont->topdict, "FontBBox", 2, fd->font_dim[FONTBBOX3_CODE].val);
+        cff_dict_set(cffont->topdict, "FontBBox", 3, fd->font_dim[FONTBBOX4_CODE].val);
+    }
+
+    cffont->fdarray = xcalloc(1, sizeof(cff_dict *));
+    cffont->fdarray[0] = cff_new_dict();
+    cff_dict_add(cffont->fdarray[0], "FontName", 1);
+    /* FIXME: Skip XXXXXX+ */
+    cff_dict_set(cffont->fdarray[0], "FontName", 0, (double) cff_add_string(cffont, fullname));
+    cff_dict_add(cffont->fdarray[0], "Private", 2);
+    cff_dict_set(cffont->fdarray[0], "Private", 0, 0.0);
+    cff_dict_set(cffont->fdarray[0], "Private", 0, 0.0);
+    /* FDArray  - index offset, not known yet */
+    cff_dict_add(cffont->topdict, "FDArray", 1);
+    cff_dict_set(cffont->topdict, "FDArray", 0, 0.0);
+    /* FDSelect - offset, not known yet */
+    cff_dict_add(cffont->topdict, "FDSelect", 1);
+    cff_dict_set(cffont->topdict, "FDSelect", 0, 0.0);
+
+    cff_dict_remove(cffont->topdict, "UniqueID");
+    cff_dict_remove(cffont->topdict, "XUID");
+    cff_dict_remove(cffont->topdict, "Private");
+    cff_dict_remove(cffont->topdict, "Encoding");
+
+    cffont->offset = (l_offset) cff_dict_get(cffont->topdict, "CharStrings", 0);
+    cs_idx = cff_get_index_header(cffont);
+
+    offset = (long) cffont->offset;
+    cs_count1 = cs_idx->count;
+    if (cs_count1 < 2) {
+        normal_error("cff","no valid charstring data found");
+    }
+
+    /* build the new charstrings entry */
+    charstrings = cff_new_index((card16) (cs_count1==USHRT_MAX?cs_count1: cs_count1 + 1));
+    max_len = 2 * CS_STR_LEN_MAX;
+    charstrings->data = xcalloc((unsigned) max_len, sizeof(card8));
+    charstring_len = 0;
+
+    gid = 0;
+    data = xcalloc(CS_STR_LEN_MAX, sizeof(card8));
+
+    {
+        int i;
+        int tex_font = fd->tex_font;
+        int streamprovider = 0;
+        int callback_id = 0 ;
+        if ((tex_font > 0) && (font_streamprovider(tex_font) == 1)) {
+            streamprovider = font_streamprovider(tex_font);
+            callback_id = callback_defined(glyph_stream_provider_callback);
+        }
+        for (i = 0; i < cs_count1; i++) {
+            code = (card16) i;
+            glyph->id = code;
+            if ((avl_find(fd->gl_tree,glyph) != NULL)) {
+                /* this code is the same as below, apart from small details */
+                if (callback_id > 0) {
+                    lstring * result;
+                    run_callback(callback_id, "ddd->L", tex_font, i, streamprovider, &result); /* this call can be sped up */
+                    size = (size_t) result->l ;
+                    if (size > 0) {
+                        if (charstring_len + CS_STR_LEN_MAX >= max_len) {
+                            max_len = (long)(charstring_len + 2 * CS_STR_LEN_MAX);
+                            charstrings->data = xrealloc(charstrings->data, (unsigned)((unsigned)max_len*sizeof(card8)));
+                        }
+                        (charstrings->offset)[gid] = (unsigned)(charstring_len + 1);
+                cffont->offset = (l_offset)((unsigned)offset + (cs_idx->offset)[code] - 1);
+                        memcpy(charstrings->data+charstring_len,(const char *) result->s,(size_t) size);
+                        charstring_len += size;
+                        xfree(result);
+                    }
+                } else {
+                    size = (long)(cs_idx->offset[code+1] - cs_idx->offset[code]);
+                    if (size > CS_STR_LEN_MAX) {
+                        formatted_error("cff","charstring too long: gid=%u, %ld bytes", code, size);
+                    }
+                    if (charstring_len + CS_STR_LEN_MAX >= max_len) {
+                        max_len = (long)(charstring_len + 2 * CS_STR_LEN_MAX);
+                        charstrings->data = xrealloc(charstrings->data, (unsigned)((unsigned)max_len*sizeof(card8)));
+                    }
+                    (charstrings->offset)[gid] = (unsigned)(charstring_len + 1);
+                    cffont->offset = (l_offset)((unsigned)offset + (cs_idx->offset)[code] - 1);
+                    memcpy(data,&cffont->stream[cffont->offset],(size_t)size);
+                    charstring_len += cs_copy_charstring(
+                        charstrings->data + charstring_len,
+                        max_len - charstring_len,
+                        data, size,
+                        cffont->gsubr, (cffont->subrs)[0],
+                        default_width, nominal_width, NULL,
+                        cffont->header_major == 2
+                    );
+                }
+                gid++;
+            }
+        }
+    }
+    /*
+        CIDSet: a table of bits indexed by cid, bytes with high order bit first,
+        each (set) bit is a (present) CID.
+    */
+    if (1) {
+        int cid;
+        cidset = pdf_create_obj(pdf, obj_type_others, 0);
+        if (cidset != 0) {
+            size_t l = (last_cid/8)+1;
+            char *stream = xmalloc(l);
+            memset(stream, 0, l);
+            for (cid = 1; cid <= (long) last_cid; cid++) {
+                glyph->id = cid;
+                if (avl_find(fd->gl_tree,glyph) != NULL) {
+                    stream[(cid / 8)] |= (1 << (7 - (cid % 8)));
+                }
+            }
+            pdf_begin_obj(pdf, cidset, OBJSTM_NEVER);
+            pdf_begin_dict(pdf);
+            pdf_dict_add_streaminfo(pdf);
+            pdf_end_dict(pdf);
+            pdf_begin_stream(pdf);
+            pdf_out_block(pdf, stream, l);
+            pdf_end_stream(pdf);
+            pdf_end_obj(pdf);
+        }
+    }
+    /*
+        This happens if the internal metrics do not agree with the actual
+        disk font.
+    */
+    if (gid < num_glyphs) {
+        formatted_warning("cff","embedded subset is smaller than expected: %d instead of %d glyphs", gid, num_glyphs);
+        num_glyphs = gid;
+    }
+
+    xfree(data);
+    cff_release_index(cs_idx);
+
+    (charstrings->offset)[num_glyphs] = (l_offset) (charstring_len + 1);
+    charstrings->count = num_glyphs;
+    cffont->num_glyphs = num_glyphs;
+    cffont->cstrings = charstrings;
+    /*
+        We don't use subroutines at all.
+    */
+    if (cffont->gsubr)
+        cff_release_index(cffont->gsubr);
+    cffont->gsubr = cff_new_index(0);
+
+    if (cffont->subrs && cffont->subrs[0])
+        cff_release_index(cffont->subrs[0]);
+    cffont->subrs[0] = NULL;
+
+    if (cffont->private && (cffont->private)[0]) {
+        cff_dict_remove((cffont->private)[0], "Subrs"); /* no Subrs */
+    }
+
+    cff_dict_update(cffont->topdict, cffont);
+
+    cff_add_string(cffont, "Adobe");
+    cff_add_string(cffont, "Identity");
+    if (cffont->header_major == 2) {
+        /* crash */
+    } else {
+        cff_dict_update(cffont->private[0], cffont);
+    }
+    cff_update_string(cffont);
+
+    /* CFF code need to be rewritten */
+    cff_dict_add(cffont->topdict, "ROS", 3);
+    cff_dict_set(cffont->topdict, "ROS", 0, (double) cff_get_sid(cffont, "Adobe"));
+    cff_dict_set(cffont->topdict, "ROS", 1, (double) cff_get_sid(cffont, "Identity"));
+    cff_dict_set(cffont->topdict, "ROS", 2, 0.0);
+
+    write_fontfile(pdf, cffont, fullname);
+    xfree(fontname);
+    xfree(fullname);
+    cff_close(cffont);
+}
+
+@ @c
+#define is_cidfont(a) ((a)->flag & FONTTYPE_CIDFONT)
+#define CID_MAX 65535
+
+void write_cid_cff(PDF pdf, cff_font * cffont, fd_entry * fd)
+{
+    cff_index *charstrings, *cs_idx;
+
+    long charstring_len, max_len;
+    long size, offset = 0;
+    int tex_font = fd->tex_font;
+    int streamprovider = 0;
+    int callback_id = 0 ;
+
+    card8 *data;
+    card16 num_glyphs, cs_count1, gid, last_cid;
+
+    int fdsel, prev_fd, cid_count, cid ;
+    char *fullname;
+
+    glw_entry *glyph;
+
+    unsigned char *CIDToGIDMap = NULL;
+
+    cff_fdselect *fdselect = NULL;
+    cff_charsets *charset = NULL;
+
+    if (!is_cidfont(cffont)) {
+        normal_error("cff","invalid CIDfont");
+        return;
+    }
+
+    if ((tex_font > 0) && (font_streamprovider(tex_font) == 1)) {
+        streamprovider = font_streamprovider(tex_font);
+        callback_id = callback_defined(glyph_stream_provider_callback);
+    }
+
+    fullname = xcalloc((unsigned) (8 + strlen(fd->fontname)), 1);
+    sprintf(fullname, "%s+%s", fd->subset_tag, fd->fontname);
+
+    /* finish parsing the CFF */
+
+    if (cff_dict_known(cffont->topdict, "CIDCount")) {
+        cid_count = (card16) cff_dict_get(cffont->topdict, "CIDCount", 0);
+    } else {
+        cid_count = CFF_CIDCOUNT_DEFAULT;
+    }
+    if (cffont->header_major == 2) {
+        /* hm */
+    } else {
+        cff_read_charsets(cffont);
+    }
+    CIDToGIDMap = xmalloc((unsigned) ((2 * (unsigned) cid_count) * sizeof(unsigned char)));
+    memset(CIDToGIDMap, 0, (size_t) (2 * cid_count));
+
+    glyph = xtalloc(1, glw_entry);
+    /* insert notdef */
+    glyph->id = 0;
+    if (avl_find(fd->gl_tree, glyph) == NULL) {
+        avl_insert(fd->gl_tree, glyph);
+        glyph = xtalloc(1, glw_entry);
+    }
+
+    last_cid = 0;
+    num_glyphs = 0;
+    for (cid = 0; cid <= CID_MAX; cid++) {
+        glyph->id = (unsigned) cid;
+        if (avl_find(fd->gl_tree, glyph) != NULL) {
+            gid = (card16) cid;
+            CIDToGIDMap[2 * cid] = (unsigned char) ((gid >> 8) & 0xff);
+            CIDToGIDMap[2 * cid + 1] = (unsigned char) (gid & 0xff);
+            last_cid = (card16) cid;
+            num_glyphs++;
+        }
+    }
+    if (cffont->header_major == 2) {
+        /* hm */
+    } else if (last_cid >= cffont->num_glyphs) {
+        formatted_error("cff font","bad glyph index %i",last_cid);
+    }
+    /*
+        CIDSet: a table of bits indexed by cid, bytes with high order bit
+        first, each (set) bit is a (present) CID.
+    */
+    if (1) {
+        cidset = pdf_create_obj(pdf, obj_type_others, 0);
+        if (cidset != 0) {
+            size_t l = (last_cid / 8) + 1;
+            char *stream = xmalloc(l);
+            memset(stream, 0, l);
+            for (cid = 1; cid <= (long) last_cid; cid++) {
+                if (CIDToGIDMap[2 * cid] || CIDToGIDMap[2 * cid + 1]) {
+                    stream[(cid / 8)] |= (1 << (7 - (cid % 8)));
+                }
+            }
+            pdf_begin_obj(pdf, cidset, OBJSTM_NEVER);
+            pdf_begin_dict(pdf);
+            pdf_dict_add_streaminfo(pdf);
+            pdf_end_dict(pdf);
+            pdf_begin_stream(pdf);
+            pdf_out_block(pdf, stream, l);
+            pdf_end_stream(pdf);
+            pdf_end_obj(pdf);
+	    xfree(stream);
+        }
+    }
+
+    cff_read_fdselect(cffont);
+    cff_read_fdarray(cffont);
+    cff_read_private(cffont);
+
+    cff_read_subrs(cffont);
+
+    cffont->offset = (l_offset) cff_dict_get(cffont->topdict, "CharStrings", 0);
+    cs_idx = cff_get_index_header(cffont);
+
+    offset = (long) cffont->offset;
+    cs_count1 = cs_idx->count;
+    if (cs_count1 < 2) {
+        normal_error("cff","no valid charstring data found");
+    }
+
+    charset = xcalloc(1, sizeof(cff_charsets));
+    charset->format = 0;
+    charset->num_entries = 0;
+    charset->data.glyphs = xcalloc(num_glyphs, sizeof(s_SID));
+
+    fdselect = xcalloc(1, sizeof(cff_fdselect));
+    fdselect->format = 3;
+    fdselect->num_entries = 0;
+    fdselect->data.ranges = xcalloc(num_glyphs, sizeof(cff_range3));
+
+    charstrings = cff_new_index((card16) (cs_count1==USHRT_MAX?cs_count1: cs_count1 + 1));
+    max_len = 2 * CS_STR_LEN_MAX;
+    charstrings->data = xcalloc((unsigned) max_len, sizeof(card8));
+    charstring_len = 0;
+
+    prev_fd = -1;
+    gid = 0;
+    data = xcalloc(CS_STR_LEN_MAX, sizeof(card8));
+    for (cid = 0; cid <= last_cid; cid++) {
+        unsigned short gid_org;
+
+        glyph->id = (unsigned) cid;
+        if (avl_find(fd->gl_tree, glyph) == NULL)
+            continue;
+
+        gid_org = (short unsigned) ((CIDToGIDMap[2 * cid] << 8) | (CIDToGIDMap[2 * cid + 1]));
+        fdsel = cff_fdselect_lookup(cffont, gid_org);
+
+        if (callback_id > 0) {
+            /* the next blob is not yet tested ... i need a font */
+            lstring * result;
+            run_callback(callback_id, "ddd->L", tex_font, gid_org, streamprovider, &result); /* this call can be sped up */
+            size = (size_t) result->l ;
+            if (size > 0) {
+                if (charstring_len + CS_STR_LEN_MAX >= max_len) {
+                    max_len = (long)(charstring_len + 2 * CS_STR_LEN_MAX);
+                    charstrings->data = xrealloc(charstrings->data, (unsigned)((unsigned)max_len*sizeof(card8)));
+                }
+                (charstrings->offset)[gid] = (unsigned)(charstring_len + 1);
+        cffont->offset = (l_offset)((unsigned)offset + (cs_idx->offset)[gid_org] - 1);
+                memcpy(charstrings->data+charstring_len,(const char *) result->s,(size_t)size);
+                charstring_len += size;
+                xfree(result);
+            }
+        } else {
+            size = (long) (cs_idx->offset[gid_org + 1] - cs_idx->offset[gid_org]);
+            if (size > CS_STR_LEN_MAX) {
+                formatted_error("cff","charstring too long: gid=%u, %ld bytes", cid, size);
+            }
+            if (charstring_len + CS_STR_LEN_MAX >= max_len) {
+                max_len = charstring_len + 2 * CS_STR_LEN_MAX;
+                charstrings->data = xrealloc(charstrings->data, (unsigned) ((unsigned) max_len * sizeof(card8)));
+            }
+            (charstrings->offset)[gid] = (l_offset) (charstring_len + 1);
+            cffont->offset = (l_offset) ((unsigned) offset + (cs_idx->offset)[gid_org] - 1);
+            memcpy(data, &cffont->stream[cffont->offset], (size_t) size);
+            charstring_len += cs_copy_charstring(
+                charstrings->data + charstring_len,
+                max_len - charstring_len,
+                data, size,
+                cffont->gsubr, (cffont->subrs)[fdsel],
+                0, 0, NULL,
+                cffont->header_major == 2
+            );
+        }
+        if (cid > 0 && gid_org > 0) {
+            charset->data.glyphs[charset->num_entries] = (s_SID) cid;
+            charset->num_entries++;
+        }
+        if (fdsel != prev_fd) {
+            fdselect->data.ranges[fdselect->num_entries].first = gid;
+            fdselect->data.ranges[fdselect->num_entries].fd = (card8) fdsel;
+            fdselect->num_entries++;
+            prev_fd = fdsel;
+        }
+        gid++;
+    }
+
+    if (gid != num_glyphs)
+        formatted_error("cff","unexpected error: %i != %i", gid, num_glyphs);
+    xfree(data);
+    cff_release_index(cs_idx);
+
+    xfree(CIDToGIDMap);
+
+    (charstrings->offset)[num_glyphs] = (l_offset) (charstring_len + 1);
+    charstrings->count = num_glyphs;
+    cffont->num_glyphs = num_glyphs;
+    cffont->cstrings = charstrings;
+    cff_release_charsets(cffont->charsets);
+    cffont->charsets = charset;
+    cff_release_fdselect(cffont->fdselect);
+    cffont->fdselect = fdselect;
+    /*
+        We don't use subroutines at all.
+    */
+    if (cffont->gsubr)
+        cff_release_index(cffont->gsubr);
+    cffont->gsubr = cff_new_index(0);
+
+    for (fdsel = 0; fdsel < cffont->num_fds; fdsel++) {
+        if (cffont->subrs && cffont->subrs[fdsel]) {
+            cff_release_index(cffont->subrs[fdsel]);
+            cffont->subrs[fdsel] = NULL;
+        }
+        if (cffont->private && (cffont->private)[fdsel]) {
+            cff_dict_remove((cffont->private)[fdsel], "Subrs"); /* no Subrs */
+        }
+    }
+    write_fontfile(pdf, cffont, fullname);
+    xfree(fullname);
+    cff_close(cffont);
+}
+
+@ Here is a sneaky trick: fontforge knows how to convert Type1 to CFF, so
+I have defined a utility function in luafflib.c that does exactly that.
+If it works out ok, I will clean up this code.
+
+@c
+void writetype1w(PDF pdf, fd_entry * fd)
+{
+    cff_font *cff;
+    int i;
+    FILE *fp;
+    ff_entry *ff;
+    unsigned char *tfm_buffer = NULL;
+    int tfm_size = 0;
+
+    ff = check_ff_exist(fd->fm->ff_name, 0);
+
+    fp = fopen(ff->ff_path, "rb");
+    cur_file_name = ff->ff_path;
+
+    if (!fp) {
+        formatted_error("cff","could not open Type1 font: %s", cur_file_name);
+    }
+    fclose(fp);
+
+    if (is_subsetted(fd->fm)) {
+        report_start_file(filetype_subset,cur_file_name);
+    } else {
+        report_start_file(filetype_font,cur_file_name);
+    }
+    (void) ff_createcff(ff->ff_path, &tfm_buffer, &tfm_size);
+
+    if (tfm_size > 0) {
+        cff = read_cff(tfm_buffer, tfm_size, 0);
+        if (cff != NULL) {
+            write_cff(pdf, cff, fd);
+        } else {
+            for (i = 0; i < tfm_size; i++)
+                strbuf_putchar(pdf->fb, tfm_buffer[i]);
+        }
+        fd->ff_found = 1;
+    } else {
+        formatted_error("cff","could not understand Type1 font: %s",cur_file_name);
+    }
+    if (is_subsetted(fd->fm)) {
+        report_stop_file(filetype_subset);
+    } else {
+        report_stop_file(filetype_font);
+    }
+    cur_file_name = NULL;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/writecff.c
+++ /dev/null
@@ -1,3156 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-#include "font/writecff.h"
-
-extern int cidset;
-
-#define get_offset(s,n) get_unsigned(s, (n))
-#define get_card8(a)  (card8)(a->stream[a->offset++])
-#define get_card16(a) (card16)(get_unsigned(a,2))
-#define get_card32(a) (get_unsigned(a,4))
-
-#undef b0
-#undef b1
-#undef b2
-#undef b3
-
-#define WORK_BUFFER_SIZE 1024
-
-static char work_buffer[WORK_BUFFER_SIZE];
-
-static unsigned long get_unsigned(cff_font * cff, int n)
-{
-    unsigned long v = 0;
-    while (n-- > 0)
-        v = v * 256 + get_card8(cff);
-    return v;
-}
-
-const char *const cff_stdstr[CFF_STDSTR_MAX] = {
-    ".notdef", "space", "exclam", "quotedbl", "numbersign",
-    "dollar", "percent", "ampersand", "quoteright", "parenleft",
-    "parenright", "asterisk", "plus", "comma", "hyphen",
-    "period", "slash", "zero", "one", "two",
-    "three", "four", "five", "six", "seven",
-    "eight", "nine", "colon", "semicolon", "less",
-    "equal", "greater", "question", "at", "A",
-    "B", "C", "D", "E", "F",
-    "G", "H", "I", "J", "K",
-    "L", "M", "N", "O", "P",
-    "Q", "R", "S", "T", "U",
-    "V", "W", "X", "Y", "Z",
-    "bracketleft", "backslash", "bracketright", "asciicircum", "underscore",
-    "quoteleft", "a", "b", "c", "d",
-    "e", "f", "g", "h", "i",
-    "j", "k", "l", "m", "n",
-    "o", "p", "q", "r", "s",
-    "t", "u", "v", "w", "x",
-    "y", "z", "braceleft", "bar", "braceright",
-    "asciitilde", "exclamdown", "cent", "sterling", "fraction",
-    "yen", "florin", "section", "currency", "quotesingle",
-    "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi",
-    "fl", "endash", "dagger", "daggerdbl", "periodcentered",
-    "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright",
-    "guillemotright", "ellipsis", "perthousand", "questiondown", "grave",
-    "acute", "circumflex", "tilde", "macron", "breve",
-    "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
-    "ogonek", "caron", "emdash", "AE", "ordfeminine",
-    "Lslash", "Oslash", "OE", "ordmasculine", "ae",
-    "dotlessi", "lslash", "oslash", "oe", "germandbls",
-    "onesuperior", "logicalnot", "mu", "trademark", "Eth",
-    "onehalf", "plusminus", "Thorn", "onequarter", "divide",
-    "brokenbar", "degree", "thorn", "threequarters", "twosuperior",
-    "registered", "minus", "eth", "multiply", "threesuperior",
-    "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
-    "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex",
-    "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis",
-    "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis",
-    "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex",
-    "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron",
-    "aacute", "acircumflex", "adieresis", "agrave", "aring",
-    "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis",
-    "egrave", "iacute", "icircumflex", "idieresis", "igrave",
-    "ntilde", "oacute", "ocircumflex", "odieresis", "ograve",
-    "otilde", "scaron", "uacute", "ucircumflex", "udieresis",
-    "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall",
-    "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall",
-    "Acutesmall",
-    "parenleftsuperior", "parenrightsuperior", "twodotenleader",
-    "onedotenleader", "zerooldstyle",
-    "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle",
-    "fiveoldstyle",
-    "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle",
-    "commasuperior",
-    "threequartersemdash", "periodsuperior", "questionsmall", "asuperior",
-    "bsuperior",
-    "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior",
-    "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
-    "tsuperior", "ff", "ffi", "ffl", "parenleftinferior",
-    "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
-    "Asmall",
-    "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall",
-    "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall",
-    "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
-    "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall",
-    "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall",
-    "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall",
-    "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
-    "Dieresissmall",
-    "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash",
-    "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
-    "questiondownsmall",
-    "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird",
-    "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior",
-    "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior",
-    "oneinferior",
-    "twoinferior", "threeinferior", "fourinferior", "fiveinferior",
-    "sixinferior",
-    "seveninferior", "eightinferior", "nineinferior", "centinferior",
-    "dollarinferior",
-    "periodinferior", "commainferior", "Agravesmall", "Aacutesmall",
-    "Acircumflexsmall",
-    "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall",
-    "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall",
-    "Igravesmall",
-    "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall",
-    "Ntildesmall",
-    "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
-    "Odieresissmall",
-    "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall",
-    "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall",
-    "001.000", "001.001", "001.002", "001.003",
-    "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold"
-};
-
-/*tex Only read the header part and forget about the body */
-
-cff_index *cff_get_index_header(cff_font * cff)
-{
-    cff_index *idx;
-    card16 i, count;
-    idx = xcalloc(1, sizeof(cff_index));
-    if (cff->header_major == 2) {
-        idx->count = count = get_card32(cff);
-    } else {
-        idx->count = count = get_card16(cff);
-    }
-    if (count > 0) {
-        idx->offsize = get_card8(cff);
-        if (idx->offsize < 1 || idx->offsize > 4)
-            normal_error("cff","invalid offsize data (1)");
-        idx->offset = xmalloc((unsigned) (((unsigned) count + 1) * sizeof(l_offset)));
-        for (i = 0; i <count + 1 ; i++) {
-            (idx->offset)[i] = get_offset(cff, idx->offsize);
-            if (i == USHRT_MAX)
-                break;
-        }
-        if (idx->offset[0] != 1)
-            normal_error("cff","invalid index data");
-        idx->data = NULL;
-    } else {
-        idx->offsize = 0;
-        idx->offset = NULL;
-        idx->data = NULL;
-    }
-    return idx;
-}
-
-cff_index *cff_get_index(cff_font * cff)
-{
-    cff_index *idx;
-    card16 i, count;
-    size_t length;
-    idx = xcalloc(1, sizeof(cff_index));
-    idx->count = count = get_card16(cff);
-    if (count > 0) {
-        idx->offsize = get_card8(cff);
-        if (idx->offsize < 1 || idx->offsize > 4)
-            normal_error("cff","invalid offsize data (2)");
-        idx->offset = xmalloc((unsigned) (((unsigned) count + 1) * sizeof(l_offset)));
-        for (i = 0; i < count + 1; i++) {
-            idx->offset[i] = get_offset(cff, idx->offsize);
-        }
-        if (idx->offset[0] != 1)
-            normal_error("cff","invalid index offset data");
-        length = (size_t) (idx->offset[count] - idx->offset[0]);
-        idx->data = xmalloc((unsigned) length * sizeof(card8));
-        memcpy(idx->data, &cff->stream[cff->offset], length);
-        cff->offset += length;
-    } else {
-        idx->offsize = 0;
-        idx->offset = NULL;
-        idx->data = NULL;
-    }
-    return idx;
-}
-
-static cff_index *cff_empty_index(cff_font * cff)
-{
-    cff_index *idx;
-    idx = xcalloc(1, sizeof(cff_index));
-    idx->count = 0;
-    idx->offsize = 0;
-    idx->offset = NULL;
-    idx->data = NULL;
-    return idx;
-}
-
-static cff_index *cff_get_index2(cff_font * cff)
-{
-    /*tex We fake a dict array. */
-    cff_index *idx;
-    size_t length;
-    idx = xcalloc(1, sizeof(cff_index));
-    length = (size_t) cff->header_offsize;
-    idx->offsize = 2;
-    idx->count = 1;
-    idx->offset = xmalloc((unsigned) (((unsigned) 2) * sizeof(l_offset)));
-    idx->offset[0] = 1;
-    idx->offset[1] = length + 1;
-    idx->data = xmalloc((unsigned) length * sizeof(card8));
-    memcpy(idx->data, &cff->stream[cff->offset], length );
-    cff->offset += length ;
-    return idx;
-}
-
-long cff_pack_index(cff_index * idx, card8 * dest, long destlen)
-{
-    long len = 0;
-    unsigned long datalen;
-    card16 i;
-    if (idx->count < 1) {
-        if (destlen < 2)
-            normal_error("cff","not enough space available");
-        memset(dest, 0, 2);
-        return 2;
-    }
-    len = cff_index_size(idx);
-    datalen = idx->offset[idx->count] - 1;
-    if (destlen < len)
-        normal_error("cff","not enough space available");
-    *(dest++) = (card8) ((idx->count >> 8) & 0xff);
-    *(dest++) = (card8) (idx->count & 0xff);
-    if (datalen < 0xffUL) {
-        idx->offsize = 1;
-        *(dest++) = 1;
-        for (i = 0; i <= idx->count; i++) {
-            *(dest++) = (card8) (idx->offset[i] & 0xff);
-        }
-    } else if (datalen < 0xffffUL) {
-        idx->offsize = 2;
-        *(dest++) = 2;
-        for (i = 0; i <= idx->count; i++) {
-            *(dest++) = (card8) ((idx->offset[i] >> 8) & 0xff);
-            *(dest++) = (card8) (idx->offset[i] & 0xff);
-        }
-    } else if (datalen < 0xffffffUL) {
-        idx->offsize = 3;
-        *(dest++) = 3;
-        for (i = 0; i <= idx->count; i++) {
-            *(dest++) = (card8) ((idx->offset[i] >> 16) & 0xff);
-            *(dest++) = (card8) ((idx->offset[i] >> 8) & 0xff);
-            *(dest++) = (card8) (idx->offset[i] & 0xff);
-        }
-    } else {
-        idx->offsize = 4;
-        *(dest++) = 4;
-        for (i = 0; i <= idx->count; i++) {
-            *(dest++) = (card8) ((idx->offset[i] >> 24) & 0xff);
-            *(dest++) = (card8) ((idx->offset[i] >> 16) & 0xff);
-            *(dest++) = (card8) ((idx->offset[i] >> 8) & 0xff);
-            *(dest++) = (card8) (idx->offset[i] & 0xff);
-        }
-    }
-    memmove(dest, idx->data, idx->offset[idx->count] - 1);
-    return len;
-}
-
-long cff_index_size(cff_index * idx)
-{
-    if (idx->count > 0) {
-        l_offset datalen;
-        datalen = idx->offset[idx->count] - 1;
-        if (datalen < 0xffUL) {
-            idx->offsize = 1;
-        } else if (datalen < 0xffffUL) {
-            idx->offsize = 2;
-        } else if (datalen < 0xffffffUL) {
-            idx->offsize = 3;
-        } else {
-            idx->offsize = 4;
-        }
-        return (3 + (idx->offsize) * (idx->count + 1) + (long) datalen);
-    } else {
-        return 2;
-    }
-}
-
-cff_index *cff_new_index(card16 count)
-{
-    cff_index *idx;
-    idx = xcalloc(1, sizeof(cff_index));
-    idx->count = count;
-    idx->offsize = 0;
-    if (count > 0) {
-        idx->offset = xcalloc((unsigned) (count + 1), sizeof(l_offset));
-        (idx->offset)[0] = 1;
-    } else {
-        idx->offset = NULL;
-    }
-    idx->data = NULL;
-    return idx;
-}
-
-void cff_release_index(cff_index * idx)
-{
-    if (idx) {
-        xfree(idx->data);
-        xfree(idx->offset);
-        xfree(idx);
-    }
-}
-
-void cff_release_dict(cff_dict * dict)
-{
-    if (dict) {
-        if (dict->entries) {
-            int i;
-            for (i = 0; i < dict->count; i++) {
-                xfree((dict->entries)[i].values);
-            }
-            xfree(dict->entries);
-        }
-        xfree(dict);
-    }
-}
-
-void cff_release_encoding(cff_encoding * encoding)
-{
-    if (encoding) {
-        switch (encoding->format & (~0x80)) {
-        case 0:
-            xfree(encoding->data.codes);
-            break;
-        case 1:
-            xfree(encoding->data.range1);
-            break;
-        default:
-            normal_error("cff","unknown encoding format");
-        }
-        if (encoding->format & 0x80)
-            xfree(encoding->supp);
-        xfree(encoding);
-    }
-}
-
-void cff_release_charsets(cff_charsets * charset)
-{
-    if (charset) {
-        switch (charset->format) {
-        case 0:
-            xfree(charset->data.glyphs);
-            break;
-        case 1:
-            xfree(charset->data.range1);
-            break;
-        case 2:
-            xfree(charset->data.range2);
-            break;
-        default:
-            break;
-        }
-        xfree(charset);
-    }
-}
-
-void cff_release_fdselect(cff_fdselect * fdselect)
-{
-    if (fdselect) {
-        if (fdselect->format == 0) {
-            xfree(fdselect->data.fds);
-        } else if (fdselect->format == 3) {
-            xfree(fdselect->data.ranges);
-        }
-        xfree(fdselect);
-    }
-}
-
-void cff_close(cff_font * cff)
-{
-    card16 i;
-    if (cff) {
-        xfree(cff->fontname);
-        if (cff->name)
-            cff_release_index(cff->name);
-        if (cff->topdict)
-            cff_release_dict(cff->topdict);
-        if (cff->string)
-            cff_release_index(cff->string);
-        if (cff->gsubr)
-            cff_release_index(cff->gsubr);
-        if (cff->encoding)
-            cff_release_encoding(cff->encoding);
-        if (cff->charsets)
-            cff_release_charsets(cff->charsets);
-        if (cff->fdselect)
-            cff_release_fdselect(cff->fdselect);
-        if (cff->cstrings)
-            cff_release_index(cff->cstrings);
-        if (cff->fdarray) {
-            for (i = 0; i < cff->num_fds; i++) {
-                if (cff->fdarray[i])
-                    cff_release_dict(cff->fdarray[i]);
-            }
-            xfree(cff->fdarray);
-        }
-        if (cff->private) {
-            for (i = 0; i < cff->num_fds; i++) {
-                if (cff->private[i])
-                    cff_release_dict(cff->private[i]);
-            }
-            xfree(cff->private);
-        }
-        if (cff->subrs) {
-            for (i = 0; i < cff->num_fds; i++) {
-                if (cff->subrs[i])
-                    cff_release_index(cff->subrs[i]);
-            }
-            xfree(cff->subrs);
-        }
-        if (cff->_string)
-            cff_release_index(cff->_string);
-        xfree(cff);
-    }
-    return;
-}
-
-char *cff_get_name(cff_font * cff)
-{
-    char *fontname;
-    l_offset len;
-    cff_index *idx;
-    idx = cff->name;
-    len = idx->offset[cff->index + 1] - idx->offset[cff->index];
-    fontname = xmalloc((unsigned) (len + 1) * sizeof(char));
-    memcpy(fontname, idx->data + idx->offset[cff->index] - 1, len);
-    fontname[len] = '\0';
-    return fontname;
-}
-
-long cff_set_name(cff_font * cff, char *name)
-{
-    cff_index *idx;
-    if (strlen(name) > 127)
-        normal_error("cff","FontName string length too large");
-    if (cff->name)
-        cff_release_index(cff->name);
-    cff->name = idx = xcalloc(1, sizeof(cff_index));
-    idx->count = 1;
-    idx->offsize = 1;
-    idx->offset = xmalloc(2 * sizeof(l_offset));
-    (idx->offset)[0] = 1;
-    (idx->offset)[1] = strlen(name) + 1;
-    idx->data = xmalloc((unsigned) strlen(name) * sizeof(card8));
-    /*tex No trailing |\0| */
-    memmove(idx->data, name, strlen(name));
-    return (long) (5 + strlen(name));
-}
-
-long cff_put_header(cff_font * cff, card8 * dest, long destlen)
-{
-    if (destlen < 4)
-        normal_error("cff","not enough space available");
-    /*tex cff->header_major */
-    *(dest++) = 1;
-    *(dest++) = cff->header_minor;
-    *(dest++) = 4;
-    /*tex
-        Additional data in between header and Name INDEX is ignored. We will set
-        all offset (0) to a four-byte integer.
-    */
-    *(dest++) = 4;
-    cff->header_offsize = 4;
-    return 4;
-}
-
-#define CFF_PARSE_OK                    0
-#define CFF_CFF_ERROR_PARSE_CFF_ERROR  -1
-#define CFF_CFF_ERROR_STACK_OVERFLOW   -2
-#define CFF_CFF_ERROR_STACK_UNDERFLOW  -3
-#define CFF_CFF_ERROR_STACK_RANGECHECK -4
-
-#define DICT_ENTRY_MAX 16
-
-cff_dict *cff_new_dict(void)
-{
-    cff_dict *dict;
-    dict = xcalloc(1, sizeof(cff_dict));
-    dict->max = DICT_ENTRY_MAX;
-    dict->count = 0;
-    dict->entries = xcalloc((unsigned) dict->max, sizeof(cff_dict_entry));
-    return dict;
-}
-
-/*tex
-
-    Operand stack: only numbers are stored (as double). Operand types are:
-
-    \startitemize
-    \startitem number:  double (integer or real) \stopitem
-    \startitem boolean: stored as a number       \stopitem
-    \startitem SID:     stored as a number       \stopitem
-    \startitem array:   array of numbers         \stopitem
-    \startitem delta:   array of numbers         \stopitem
-    \stopitemize
-
-*/
-
-#define CFF_DICT_STACK_LIMIT 64
-static int stack_top = 0;
-static double arg_stack[CFF_DICT_STACK_LIMIT];
-
-/* The CFF DICT encoding: */
-
-#define CFF_LAST_DICT_OP1 26
-#define CFF_LAST_DICT_OP2 39
-#define CFF_LAST_DICT_OP (CFF_LAST_DICT_OP1 + CFF_LAST_DICT_OP2)
-
-static struct {
-    const char *opname;
-    int argtype;
-} dict_operator[CFF_LAST_DICT_OP] = {
-    { "version", CFF_TYPE_SID },
-    { "Notice", CFF_TYPE_SID },
-    { "FullName", CFF_TYPE_SID },
-    { "FamilyName", CFF_TYPE_SID },
-    { "Weight", CFF_TYPE_SID },
-    { "FontBBox", CFF_TYPE_ARRAY },
-    { "BlueValues", CFF_TYPE_DELTA },
-    { "OtherBlues", CFF_TYPE_DELTA },
-    { "FamilyBlues", CFF_TYPE_DELTA },
-    { "FamilyOtherBlues", CFF_TYPE_DELTA },
-    { "StdHW", CFF_TYPE_NUMBER },
-    { "StdVW", CFF_TYPE_NUMBER },
-    { NULL, -1 },
-    { "UniqueID", CFF_TYPE_NUMBER },
-    { "XUID", CFF_TYPE_ARRAY },
-    { "charset", CFF_TYPE_OFFSET },
-    { "Encoding", CFF_TYPE_OFFSET },
-    { "CharStrings", CFF_TYPE_OFFSET },
-    { "Private", CFF_TYPE_SZOFF },
-    { "Subrs", CFF_TYPE_OFFSET },
-    { "defaultWidthX", CFF_TYPE_NUMBER },
-    { "nominalWidthX", CFF_TYPE_NUMBER },
-    { NULL, -1 },
-    { NULL, -1 },
-    /*tex two CFF2 instructions */
-    { "vstore", CFF_TYPE_OFFSET },
-    { "maxstack", CFF_TYPE_NUMBER },
-    /*tex Here we start with operator 2 of 12. */
-    { "Copyright", CFF_TYPE_SID },
-    { "IsFixedPitch", CFF_TYPE_BOOLEAN },
-    { "ItalicAngle", CFF_TYPE_NUMBER },
-    { "UnderlinePosition", CFF_TYPE_NUMBER },
-    { "UnderlineThickness", CFF_TYPE_NUMBER },
-    { "PaintType", CFF_TYPE_NUMBER },
-    { "CharstringType", CFF_TYPE_NUMBER },
-    { "FontMatrix", CFF_TYPE_ARRAY },
-    { "StrokeWidth", CFF_TYPE_NUMBER },
-    { "BlueScale", CFF_TYPE_NUMBER },
-    { "BlueShift", CFF_TYPE_NUMBER },
-    { "BlueFuzz", CFF_TYPE_NUMBER },
-    { "StemSnapH", CFF_TYPE_DELTA },
-    { "StemSnapV", CFF_TYPE_DELTA },
-    { "ForceBold", CFF_TYPE_BOOLEAN },
-    { NULL, -1 },
-    { NULL, -1 },
-    { "LanguageGroup", CFF_TYPE_NUMBER },
-    { "ExpansionFactor", CFF_TYPE_NUMBER },
-    { "InitialRandomSeed", CFF_TYPE_NUMBER },
-    { "SyntheticBase", CFF_TYPE_NUMBER },
-    { "PostScript", CFF_TYPE_SID },
-    { "BaseFontName", CFF_TYPE_SID },
-    { "BaseFontBlend", CFF_TYPE_DELTA },
-    { NULL, -1 },
-    { NULL, -1 },
-    { NULL, -1 },
-    { NULL, -1 },
-    { NULL, -1 },
-    { NULL, -1 },
-    { "ROS", CFF_TYPE_ROS },
-    { "CIDFontVersion", CFF_TYPE_NUMBER },
-    { "CIDFontRevision", CFF_TYPE_NUMBER },
-    { "CIDFontType", CFF_TYPE_NUMBER },
-    { "CIDCount", CFF_TYPE_NUMBER },
-    { "UIDBase", CFF_TYPE_NUMBER },
-    { "FDArray", CFF_TYPE_OFFSET },
-    { "FDSelect", CFF_TYPE_OFFSET },
-    { "FontName", CFF_TYPE_SID }
-};
-
-/*tex Parse DICT data */
-
-static double get_integer(card8 ** data, card8 * endptr, int *status)
-{
-    long result = 0;
-    card8 b0, b1, b2;
-
-    b0 = *(*data)++;
-    if (b0 == 28 && *data < endptr - 2) {
-        /*tex shortint */
-        b1 = *(*data)++;
-        b2 = *(*data)++;
-        result = b1 * 256 + b2;
-        if (result > 0x7fffL)
-            result -= 0x10000L;
-    } else if (b0 == 29 && *data < endptr - 4) {
-        /*tex longint */
-        int i;
-        result = *(*data)++;
-        if (result > 0x7f)
-            result -= 0x100;
-        for (i = 0; i < 3; i++) {
-            result = result * 256 + (**data);
-            *data += 1;
-        }
-    } else if (b0 >= 32 && b0 <= 246) {
-        /*tex int (1) */
-        result = b0 - 139;
-    } else if (b0 >= 247 && b0 <= 250) {
-        /*tex int (2) */
-        b1 = *(*data)++;
-        result = (b0 - 247) * 256 + b1 + 108;
-    } else if (b0 >= 251 && b0 <= 254) {
-        b1 = *(*data)++;
-        result = -(b0 - 251) * 256 - b1 - 108;
-    } else {
-        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
-    }
-    return (double) result;
-}
-
-/*tex Simply uses |strtod|: */
-
-static double get_real(card8 ** data, card8 * endptr, int *status)
-{
-    double result = 0.0;
-    int nibble = 0, pos = 0;
-    int len = 0, fail = 0;
-
-    if (**data != 30 || *data >= endptr - 1) {
-        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
-        return 0.0;
-    }
-    /*tex Skip the first byte (30): */
-    *data += 1;
-    pos = 0;
-    while ((!fail) && len < WORK_BUFFER_SIZE - 2 && *data < endptr) {
-        /*tex Get a nibble. */
-        if (pos % 2) {
-            nibble = **data & 0x0f;
-            *data += 1;
-        } else {
-            nibble = (**data >> 4) & 0x0f;
-        }
-        if (nibble >= 0x00 && nibble <= 0x09) {
-            work_buffer[len++] = (char) (nibble + '0');
-        } else if (nibble == 0x0a) {    /* . */
-            work_buffer[len++] = '.';
-        } else if (nibble == 0x0b || nibble == 0x0c) {
-            /*tex E, E- */
-            work_buffer[len++] = 'e';
-            if (nibble == 0x0c)
-                work_buffer[len++] = '-';
-        } else if (nibble == 0x0e) {
-            /*tex the minus */
-            work_buffer[len++] = '-';
-        } else if (nibble == 0x0d) {
-            /*tex do nothing */
-        } else if (nibble == 0x0f) {
-            /*tex we're done */
-            work_buffer[len++] = '\0';
-            if (((pos % 2) == 0) && (**data != 0xff)) {
-                fail = 1;
-            }
-            break;
-        } else {
-            /*tex invalid */
-            fail = 1;
-        }
-        pos++;
-    }
-    /*tex the returned values */
-    if (fail || nibble != 0x0f) {
-        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
-    } else {
-        char *s;
-        errno=0;
-        result = strtod(work_buffer, &s);
-        if ((result==0.0 && work_buffer==s) || errno) {
-              /*tex Conversion is not possible. */
-             *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
-         }
-    }
-    return result;
-}
-
-/*tex Operators */
-
-static void add_dict(cff_dict * dict, card8 ** data, card8 * endptr, int *status)
-{
-    int id, argtype, t;
-    id = **data;
-    if (id == 0x0c) {
-        *data += 1;
-        if (*data >= endptr ||
-            (id = **data + CFF_LAST_DICT_OP1) >= CFF_LAST_DICT_OP) {
-            *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
-            return;
-        }
-    } else if (id >= CFF_LAST_DICT_OP1) {
-        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
-        return;
-    }
-    argtype = dict_operator[id].argtype;
-    if (dict_operator[id].opname == NULL || argtype < 0) {
-        *status = CFF_CFF_ERROR_PARSE_CFF_ERROR;
-        return;
-    }
-    if (dict->count >= dict->max) {
-        dict->max += DICT_ENTRY_MAX;
-        /*tex Not zeroed! */
-        dict->entries = xrealloc(dict->entries, (unsigned) ((unsigned) dict->max * sizeof(cff_dict_entry)));
-    }
-    (dict->entries)[dict->count].id = id;
-    (dict->entries)[dict->count].key = dict_operator[id].opname;
-    if (argtype == CFF_TYPE_NUMBER ||
-        argtype == CFF_TYPE_BOOLEAN ||
-        argtype == CFF_TYPE_SID || argtype == CFF_TYPE_OFFSET) {
-        /*tex Check for underflow here, as exactly one operand is expected. */
-        if (stack_top < 1) {
-            *status = CFF_CFF_ERROR_STACK_UNDERFLOW;
-            return;
-        }
-        stack_top--;
-        (dict->entries)[dict->count].count = 1;
-        (dict->entries)[dict->count].values = xcalloc(1, sizeof(double));
-        (dict->entries)[dict->count].values[0] = arg_stack[stack_top];
-        dict->count += 1;
-    } else {
-        /*tex
-            Just ignore operator if there were no operands provided. Don't treat
-            this as underflow, e.g. |StemSnapV| in |TemporaLGCUni-Italic.otf|.
-        */
-        if ((t = stack_top) > 0) {
-            (dict->entries)[dict->count].count = stack_top;
-            (dict->entries)[dict->count].values =
-                xmalloc((unsigned) ((unsigned) stack_top * sizeof(double)));
-            while (stack_top > 0) {
-                stack_top--;
-                (dict->entries)[dict->count].values[stack_top] =
-                    arg_stack[stack_top];
-            }
-            if (t > 3 && strcmp(dict_operator[id].opname, "FontMatrix") == 0) {
-                (dict->entries)[dict->count].values[0] = 0.001;
-                (dict->entries)[dict->count].values[3] = 0.001;
-            }
-            dict->count += 1;
-        }
-    }
-    *data += 1;
-    return;
-}
-
-/*tex
-
-    All operands are treated as number or array of numbers.
-
-    \startitemize
-    \startitem |Private|: two numbers, size and offset     \stopitem
-    \startitem |ROS|: hree numbers, SID, SID, and a number \stopitem
-    \stopitemize
-
-*/
-
-cff_dict *cff_dict_unpack(card8 * data, card8 * endptr)
-{
-    cff_dict *dict;
-    int status = CFF_PARSE_OK;
-    stack_top = 0;
-    dict = cff_new_dict();
-    while (data < endptr && status == CFF_PARSE_OK) {
-        if (*data < CFF_LAST_DICT_OP1) {
-            /*tex Some operator. */
-            add_dict(dict, &data, endptr, &status);
-        } else if (*data == 30) {
-            /*tex First byte of a sequence (variable). */
-            if (stack_top < CFF_DICT_STACK_LIMIT) {
-                arg_stack[stack_top] = get_real(&data, endptr, &status);
-                stack_top++;
-            } else {
-                status = CFF_CFF_ERROR_STACK_OVERFLOW;
-            }
-        } else if (*data == 255 || (*data >= CFF_LAST_DICT_OP1 && *data <= 27)) {
-            /*tex Reserved. */
-            data++;
-        } else {
-            /*tex Everything else is an integer. */
-            if (stack_top < CFF_DICT_STACK_LIMIT) {
-                arg_stack[stack_top] = get_integer(&data, endptr, &status);
-                stack_top++;
-            } else {
-                status = CFF_CFF_ERROR_STACK_OVERFLOW;
-            }
-        }
-    }
-    if (status != CFF_PARSE_OK) {
-        formatted_error("cff","parsing DICT failed (error=%d)", status);
-    } else if (stack_top != 0) {
-        normal_warning("cff","garbage in DICT data");
-        stack_top = 0;
-    }
-    return dict;
-}
-
-int cff_dict_known(cff_dict * dict, const char *key)
-{
-    int i;
-    for (i = 0; i < dict->count; i++) {
-        if (key && strcmp(key, (dict->entries)[i].key) == 0
-            && (dict->entries)[i].count > 0)
-            return 1;
-    }
-    return 0;
-}
-
-double cff_dict_get(cff_dict * dict, const char *key, int idx)
-{
-    double value = 0.0;
-    int i;
-    assert(key && dict);
-    for (i = 0; i < dict->count; i++) {
-        if (strcmp(key, (dict->entries)[i].key) == 0) {
-           if ((dict->entries)[i].count > idx)
-                value = (dict->entries)[i].values[idx];
-            else
-                normal_error("cff","invalid index number");
-            break;
-        }
-    }
-    if (i == dict->count)
-        formatted_error("cff","DICT entry '%s' not found", key);
-    return value;
-}
-
-card8 cff_fdselect_lookup(cff_font * cff, card16 gid)
-{
-    card8 fd = 0xff;
-    cff_fdselect *fdsel;
-    if (cff->fdselect == NULL)
-        normal_error("cff","FDSelect not available");
-    fdsel = cff->fdselect;
-    if (gid >= cff->num_glyphs)
-        normal_error("cff","invalid glyph index");
-    switch (fdsel->format) {
-        case 0:
-            fd = fdsel->data.fds[gid];
-            break;
-        case 3:
-            {
-                if (gid == 0) {
-                    fd = (fdsel->data).ranges[0].fd;
-                } else {
-                    card16 i;
-                    for (i = 1; i < (fdsel->num_entries); i++) {
-                        if (gid < (fdsel->data).ranges[i].first)
-                            break;
-                    }
-                    fd = (fdsel->data).ranges[i - 1].fd;
-                }
-            }
-            break;
-        default:
-            normal_error("cff","invalid FDSelect format");
-            break;
-    }
-    if (fd >= cff->num_fds)
-        normal_error("cff","invalid Font DICT index");
-    return fd;
-}
-
-long cff_read_subrs(cff_font * cff)
-{
-    long len = 0;
-    long offset;
-    int i;
-    if ((cff->flag & FONTTYPE_CIDFONT) && cff->fdselect == NULL) {
-        cff_read_fdselect(cff);
-    }
-    if ((cff->flag & FONTTYPE_CIDFONT) && cff->fdarray == NULL) {
-        cff_read_fdarray(cff);
-    }
-    if (cff->private == NULL)
-        cff_read_private(cff);
-    if (cff->gsubr == NULL) {
-        cff->offset = cff->gsubr_offset;
-        cff->gsubr = cff_get_index(cff);
-    }
-    cff->subrs = xcalloc(cff->num_fds, sizeof(cff_index *));
-    if (cff->flag & FONTTYPE_CIDFONT) {
-        for (i = 0; i < cff->num_fds; i++) {
-            if (cff->private[i] == NULL ||
-                !cff_dict_known(cff->private[i], "Subrs")) {
-                (cff->subrs)[i] = NULL;
-            } else {
-                offset = (long) cff_dict_get(cff->fdarray[i], "Private", 1);
-                offset += (long) cff_dict_get(cff->private[i], "Subrs", 0);
-                cff->offset = (l_offset) offset;
-                (cff->subrs)[i] = cff_get_index(cff);
-                len += cff_index_size((cff->subrs)[i]);
-            }
-        }
-    } else if (cff->private[0] == NULL || !cff_dict_known(cff->private[0], "Subrs")) {
-        (cff->subrs)[0] = NULL;
-    } else {
-        offset = (long) cff_dict_get(cff->topdict, "Private", 1);
-        offset += (long) cff_dict_get(cff->private[0], "Subrs", 0);
-        cff->offset = (l_offset) offset;
-        (cff->subrs)[0] = cff_get_index(cff);
-        len += cff_index_size((cff->subrs)[0]);
-    }
-    return len;
-}
-
-long cff_read_fdarray(cff_font * cff)
-{
-    long len = 0;
-    cff_index *idx;
-    long offset, size;
-    card16 i;
-    if (cff->topdict == NULL)
-        normal_error("cff","top DICT not found");
-    if (!(cff->flag & FONTTYPE_CIDFONT))
-        return 0;
-    offset = (long) cff_dict_get(cff->topdict, "FDArray", 0);
-    cff->offset = (l_offset) offset;
-    idx = cff_get_index(cff);
-    cff->num_fds = (card8) idx->count;
-    cff->fdarray = xmalloc((unsigned) (idx->count * sizeof(cff_dict *)));
-    for (i = 0; i < idx->count; i++) {
-        card8 *data = idx->data + (idx->offset)[i] - 1;
-        size = (long) ((idx->offset)[i + 1] - (idx->offset)[i]);
-        if (size > 0) {
-            (cff->fdarray)[i] = cff_dict_unpack(data, data + size);
-        } else {
-            (cff->fdarray)[i] = NULL;
-        }
-    }
-    len = cff_index_size(idx);
-    cff_release_index(idx);
-    return len;
-}
-
-long cff_read_private(cff_font * cff)
-{
-    long len = 0;
-    card8 *data;
-    long offset, size;
-    if (cff->flag & FONTTYPE_CIDFONT) {
-        int i;
-        if (cff->fdarray == NULL)
-            cff_read_fdarray(cff);
-        cff->private = xmalloc((unsigned) (cff->num_fds * sizeof(cff_dict *)));
-        for (i = 0; i < cff->num_fds; i++) {
-            if (cff->fdarray[i] != NULL &&
-                    cff_dict_known(cff->fdarray[i], "Private") &&
-                    (size = (long) cff_dict_get(cff->fdarray[i], "Private", 0)) > 0) {
-                offset = (long) cff_dict_get(cff->fdarray[i], "Private", 1);
-                cff->offset = (l_offset) offset;
-                data = xmalloc((unsigned) size * sizeof(card8));
-                memcpy(data, &cff->stream[cff->offset], (size_t) size);
-                cff->offset = (l_offset) size;
-                (cff->private)[i] = cff_dict_unpack(data, data + size);
-                xfree(data);
-                len += size;
-            } else {
-                (cff->private)[i] = NULL;
-            }
-        }
-    } else {
-        cff->num_fds = 1;
-        cff->private = xmalloc(sizeof(cff_dict *));
-        if (cff_dict_known(cff->topdict, "Private") &&
-                (size = (long) cff_dict_get(cff->topdict, "Private", 0)) > 0) {
-            offset = (long) cff_dict_get(cff->topdict, "Private", 1);
-            cff->offset = (l_offset) offset;
-            data = xmalloc((unsigned) size * sizeof(card8));
-            memcpy(data, &cff->stream[cff->offset], (size_t) size);
-            cff->offset = (l_offset) size;
-            cff->private[0] = cff_dict_unpack(data, data + size);
-            xfree(data);
-            len += size;
-        } else {
-            (cff->private)[0] = NULL;
-            len = 0;
-        }
-    }
-    return len;
-}
-
-cff_font *read_cff(unsigned char *buf, long buflength, int n)
-{
-    cff_font *cff;
-    cff_index *idx;
-    long offset;
-    cff = xcalloc(1, sizeof(cff_font));
-    cff->stream = buf;
-    cff->stream_size = (l_offset) buflength;
-    cff->index = n;
-    cff->header_major = get_card8(cff);
-    cff->header_minor = get_card8(cff);
-    cff->header_hdr_size = get_card8(cff);
-    if (cff->header_major == 2) {
-        /*tex We have only one top dictionary. */
-        cff->header_offsize = get_card16(cff);
-    } else {
-        cff->header_offsize = get_card8(cff);
-        if (cff->header_offsize < 1 || cff->header_offsize > 4) {
-            normal_warning("cff","invalid offsize data (4)");
-            cff_close(cff);
-            return NULL;
-        }
-    }
-    if (cff->header_major > 2) {
-        formatted_warning("cff","major version %u not supported", cff->header_major);
-        cff_close(cff);
-        return NULL;
-    }
-    cff->offset = cff->header_hdr_size;
-    /*tex The name index. */
-    if (cff->header_major == 2) {
-        cff->name = cff_empty_index(cff);
-    } else {
-        idx = cff_get_index(cff);
-        if (n > idx->count - 1) {
-            normal_warning("cff","invalid fontset index number");
-            cff_close(cff);
-            return NULL;
-        }
-        cff->name = idx;
-        cff->fontname = cff_get_name(cff);
-    }
-    /*tex The top dict index. */
-    if (cff->header_major == 2) {
-        /*tex we fake an index (just one entry) */
-        idx = cff_get_index2(cff);
-    } else {
-        idx = cff_get_index(cff);
-    }
-    if (n > idx->count - 1) {
-        normal_warning("cff","top DICT not exist");
-        cff_close(cff);
-        return NULL;
-    }
-    cff->topdict = cff_dict_unpack(idx->data + idx->offset[n] - 1, idx->data + idx->offset[n + 1] - 1);
-    if (!cff->topdict) {
-        normal_warning("cff","parsing top DICT data failed");
-        cff_close(cff);
-        return NULL;
-    }
-    cff_release_index(idx);
-    if (cff_dict_known(cff->topdict, "CharstringType") &&
-        cff_dict_get(cff->topdict, "CharstringType", 0) != 2) {
-        normal_warning("cff","only type 2 charstrings supported");
-        cff_close(cff);
-        return NULL;
-    }
-    if (cff_dict_known(cff->topdict, "SyntheticBase")) {
-        normal_warning("cff","synthetic font not supported");
-        cff_close(cff);
-        return NULL;
-    }
-    /*tex The string index. */
-    if (cff->header_major == 2) {
-        /*tex do nothing */
-    } else {
-        cff->string = cff_get_index(cff);
-    }
-    /*tex The offset to subroutines. */
-    cff->gsubr_offset = cff->offset;
-    /*tex The number of glyphs. */
-    offset = (long) cff_dict_get(cff->topdict, "CharStrings", 0);
-    cff->offset = (l_offset) offset;
-    cff->num_glyphs = get_card16(cff);
-    /*tex Check for font type. */
-    if (cff_dict_known(cff->topdict, "ROS")) {
-        cff->flag |= FONTTYPE_CIDFONT;
-    } else {
-        cff->flag |= FONTTYPE_FONT;
-    }
-    /*tex Check for the encoding. */
-    if (cff_dict_known(cff->topdict, "Encoding")) {
-        offset = (long) cff_dict_get(cff->topdict, "Encoding", 0);
-        if (offset == 0) {      /* predefined */
-            cff->flag |= ENCODING_STANDARD;
-        } else if (offset == 1) {
-            cff->flag |= ENCODING_EXPERT;
-        }
-    } else {
-        cff->flag |= ENCODING_STANDARD;
-    }
-    cff->offset = cff->gsubr_offset;
-    return cff;
-}
-
-/*tex Write CFF data for an \OPENTYPE\ font. We need to pack dictionary data. */
-
-static long pack_integer(card8 * dest, long destlen, long value)
-{
-    long len = 0;
-    if (value >= -107 && value <= 107) {
-        if (destlen < 1)
-            normal_error("cff","buffer overflow (1)");
-        dest[0] = (card8) ((value + 139) & 0xff);
-        len = 1;
-    } else if (value >= 108 && value <= 1131) {
-        if (destlen < 2)
-            normal_error("cff","buffer overflow (2)");
-        value = (long) 0xf700u + value - 108;
-        dest[0] = (card8) ((value >> 8) & 0xff);
-        dest[1] = (card8) (value & 0xff);
-        len = 2;
-    } else if (value >= -1131 && value <= -108) {
-        if (destlen < 2)
-            normal_error("cff","buffer overflow (3)");
-        value = (long) 0xfb00u - value - 108;
-        dest[0] = (card8) ((value >> 8) & 0xff);
-        dest[1] = (card8) (value & 0xff);
-        len = 2;
-    } else if (value >= -32768 && value <= 32767) {
-        /*tex shortint */
-        if (destlen < 3)
-            normal_error("cff","buffer overflow (4)");
-        dest[0] = 28;
-        dest[1] = (card8) ((value >> 8) & 0xff);
-        dest[2] = (card8) (value & 0xff);
-        len = 3;
-    } else {
-        /*tex longint */
-        if (destlen < 5)
-            normal_error("cff","buffer overflow (5)");
-        dest[0] = 29;
-        dest[1] = (card8) ((value >> 24) & 0xff);
-        dest[2] = (card8) ((value >> 16) & 0xff);
-        dest[3] = (card8) ((value >> 8) & 0xff);
-        dest[4] = (card8) (value & 0xff);
-        len = 5;
-    }
-    return len;
-}
-
-static long pack_real(card8 * dest, long destlen, double value)
-{
-    long e;
-    int i = 0, pos = 2;
-    int res;
-#define CFF_REAL_MAX_LEN 17
-    if (destlen < 2)
-        normal_error("cff","buffer overflow (6)");
-    dest[0] = 30;
-    if (value == 0.0) {
-        dest[1] = 0x0f;
-        return 2;
-    }
-    if (value < 0.0) {
-        dest[1] = 0xe0;
-        value *= -1.0;
-        pos++;
-    }
-    e = 0;
-    if (value >= 10.0) {
-        while (value >= 10.0) {
-            value /= 10.0;
-            e++;
-        }
-    } else if (value < 1.0) {
-        while (value < 1.0) {
-            value *= 10.0;
-            e--;
-        }
-    }
-    res = sprintf(work_buffer, "%1.14g", value);
-    if (res<0)
-        normal_error("cff","invalid conversion");
-    if (res>CFF_REAL_MAX_LEN)
-        res=CFF_REAL_MAX_LEN;
-    for (i = 0; i < res; i++) {
-        unsigned char ch = 0;
-        if (work_buffer[i] == '\0') {
-            /*tex In fact |res| should prevent this. */
-            break;
-        } else if (work_buffer[i] == '.') {
-            ch = 0x0a;
-        } else if (work_buffer[i] >= '0' && work_buffer[i] <= '9') {
-            ch = (unsigned char) (work_buffer[i] - '0');
-        } else {
-            normal_error("cff","invalid character");
-        }
-        if (destlen < pos / 2 + 1)
-            normal_error("cff","buffer overflow (7)");
-
-        if (pos % 2) {
-            dest[pos / 2] = (card8) (dest[pos / 2] + ch);
-        } else {
-            dest[pos / 2] = (card8) (ch << 4);
-        }
-        pos++;
-    }
-    if (e > 0) {
-        if (pos % 2) {
-            dest[pos / 2] = (card8) (dest[pos / 2] + 0x0b);
-        } else {
-            if (destlen < pos / 2 + 1)
-                normal_error("cff","buffer overflow (8)");
-            dest[pos / 2] = (card8) (0xb0);
-        }
-        pos++;
-    } else if (e < 0) {
-        if (pos % 2) {
-            dest[pos / 2] = (card8) (dest[pos / 2] + 0x0c);
-        } else {
-            if (destlen < pos / 2 + 1)
-                normal_error("cff","buffer overflow (9)");
-            dest[pos / 2] = (card8) (0xc0);
-        }
-        e *= -1;
-        pos++;
-    }
-    if (e != 0) {
-        sprintf(work_buffer, "%ld", e);
-        for (i = 0; i < CFF_REAL_MAX_LEN; i++) {
-            unsigned char ch = 0;
-            if (work_buffer[i] == '\0') {
-                break;
-            } else if (work_buffer[i] == '.') {
-                ch = 0x0a;
-            } else if (work_buffer[i] >= '0' && work_buffer[i] <= '9') {
-                ch = (unsigned char) (work_buffer[i] - '0');
-            } else {
-                normal_error("cff","invalid character");
-            }
-            if (destlen < pos / 2 + 1)
-                normal_error("cff","buffer overflow (10)");
-            if (pos % 2) {
-                dest[pos / 2] = (card8) (dest[pos / 2] + ch);
-            } else {
-                dest[pos / 2] = (card8) (ch << 4);
-            }
-            pos++;
-        }
-    }
-    if (pos % 2) {
-        dest[pos / 2] = (card8) (dest[pos / 2] + 0x0f);
-        pos++;
-    } else {
-        if (destlen < pos / 2 + 1)
-            normal_error("cff","buffer overflow (11)");
-        dest[pos / 2] = (card8) (0xff);
-        pos += 2;
-    }
-    return pos / 2;
-}
-
-static long cff_dict_put_number(double value, card8 * dest, long destlen, int type)
-{
-    long len = 0;
-    double nearint;
-    nearint = floor(value + 0.5);
-    if (type == CFF_TYPE_OFFSET) {
-        long lvalue;
-        lvalue = (long) value;
-        if (destlen < 5)
-            normal_error("cff","buffer overflow (12)");
-        dest[0] = 29;
-        dest[1] = (card8) ((lvalue >> 24) & 0xff);
-        dest[2] = (card8) ((lvalue >> 16) & 0xff);
-        dest[3] = (card8) ((lvalue >> 8) & 0xff);
-        dest[4] = (card8) (lvalue & 0xff);
-        len = 5;
-    } else if (value > CFF_INT_MAX || value < CFF_INT_MIN || (fabs(value - nearint) > 1.0e-5)) {
-        /*tex A real */
-        len = pack_real(dest, destlen, value);
-    } else {
-        /*tex An integer */
-        len = pack_integer(dest, destlen, (long) nearint);
-    }
-    return len;
-}
-
-static long put_dict_entry(cff_dict_entry * de, card8 * dest, long destlen)
-{
-    long len = 0;
-    int i, type, id;
-    if (de->count > 0) {
-        id = de->id;
-        if (dict_operator[id].argtype == CFF_TYPE_OFFSET ||
-            dict_operator[id].argtype == CFF_TYPE_SZOFF) {
-            type = CFF_TYPE_OFFSET;
-        } else {
-            type = CFF_TYPE_NUMBER;
-        }
-        for (i = 0; i < de->count; i++) {
-            len += cff_dict_put_number(de->values[i], dest + len, destlen - len, type);
-        }
-        if (id >= 0 && id < CFF_LAST_DICT_OP1) {
-            if (len + 1 > destlen)
-                normal_error("cff","buffer overflow (13)");
-            dest[len++] = (card8) id;
-        } else if (id >= 0 && id < CFF_LAST_DICT_OP) {
-            if (len + 2 > destlen)
-                normal_error("cff","buffer overflow (14)");
-            dest[len++] = 12;
-            dest[len++] = (card8) (id - CFF_LAST_DICT_OP1);
-        } else {
-            normal_error("cff","invalid DICT operator ID");
-        }
-    }
-    return len;
-}
-
-long cff_dict_pack(cff_dict * dict, card8 * dest, long destlen)
-{
-    long len = 0;
-    int i;
-    for (i = 0; i < dict->count; i++) {
-        if (!strcmp(dict->entries[i].key, "ROS")) {
-            len += put_dict_entry(&dict->entries[i], dest, destlen);
-            break;
-        }
-    }
-    for (i = 0; i < dict->count; i++) {
-        if (strcmp(dict->entries[i].key, "ROS")) {
-            len += put_dict_entry(&dict->entries[i], dest + len, destlen - len);
-        }
-    }
-    return len;
-}
-
-void cff_dict_add(cff_dict * dict, const char *key, int count)
-{
-    int id, i;
-    for (id = 0; id < CFF_LAST_DICT_OP; id++) {
-        if (key && dict_operator[id].opname &&
-            strcmp(dict_operator[id].opname, key) == 0)
-            break;
-    }
-    if (id == CFF_LAST_DICT_OP)
-        normal_error("cff","unknown DICT operator");
-    for (i = 0; i < dict->count; i++) {
-        if ((dict->entries)[i].id == id) {
-            if ((dict->entries)[i].count != count)
-                normal_error("cff","inconsistent DICT argument number");
-            return;
-        }
-    }
-    if (dict->count + 1 >= dict->max) {
-        dict->max += 8;
-        dict->entries =
-            xrealloc(dict->entries, (unsigned) ((unsigned) dict->max * sizeof(cff_dict_entry)));
-    }
-    (dict->entries)[dict->count].id = id;
-    (dict->entries)[dict->count].key = dict_operator[id].opname;
-    (dict->entries)[dict->count].count = count;
-    if (count > 0) {
-        (dict->entries)[dict->count].values = xcalloc((unsigned) count, sizeof(double));
-    } else {
-        (dict->entries)[dict->count].values = NULL;
-    }
-    dict->count += 1;
-    return;
-}
-
-void cff_dict_remove(cff_dict * dict, const char *key)
-{
-    int i;
-    for (i = 0; i < dict->count; i++) {
-        if (key && strcmp(key, (dict->entries)[i].key) == 0) {
-            (dict->entries)[i].count = 0;
-            xfree((dict->entries)[i].values);
-        }
-    }
-}
-
-void cff_dict_set(cff_dict * dict, const char *key, int idx, double value)
-{
-    int i;
-    for (i = 0; i < dict->count; i++) {
-        if (strcmp(key, (dict->entries)[i].key) == 0) {
-            if ((dict->entries)[i].count > idx)
-                (dict->entries)[i].values[idx] = value;
-            else
-                normal_error("cff","invalid index number");
-            break;
-        }
-    }
-    if (i == dict->count)
-        formatted_error("cff","DICT entry '%s' not found", key);
-}
-
-
-/*tex Strings */
-
-char *cff_get_string(cff_font * cff, s_SID id)
-{
-    char *result = NULL;
-    size_t len;
-    if (id < CFF_STDSTR_MAX) {
-        len = strlen(cff_stdstr[id]);
-        result = xmalloc((unsigned) (len + 1) * sizeof(char));
-        memcpy(result, cff_stdstr[id], len);
-        result[len] = '\0';
-    } else if (cff && cff->string) {
-        cff_index *strings = cff->string;
-        id = (s_SID) (id - CFF_STDSTR_MAX);
-        if (id < strings->count) {
-            len = (strings->offset)[id + 1] - (strings->offset)[id];
-            result = xmalloc((unsigned) (len + 1) * sizeof(char));
-            memmove(result, strings->data + (strings->offset)[id] - 1, len);
-            result[len] = '\0';
-        }
-    }
-    return result;
-}
-
-long cff_get_sid(cff_font * cff, const char *str)
-{
-    card16 i;
-    if (!cff || !str)
-        return -1;
-    /*tex We search the string index first. */
-    if (cff && cff->string) {
-        cff_index *idx = cff->string;
-        for (i = 0; i < idx->count; i++) {
-            if (strlen(str) == (idx->offset)[i + 1] - (idx->offset)[i] &&
-                !memcmp(str, (idx->data) + (idx->offset)[i] - 1, strlen(str)))
-                return (i + CFF_STDSTR_MAX);
-        }
-    }
-    for (i = 0; i < CFF_STDSTR_MAX; i++) {
-        if (!strcmp(str, cff_stdstr[i]))
-            return i;
-    }
-    return -1;
-}
-
-void cff_update_string(cff_font * cff)
-{
-    if (cff == NULL)
-        normal_error("cff","CFF font not opened");
-    if (cff->string)
-        cff_release_index(cff->string);
-    cff->string = cff->_string;
-    cff->_string = NULL;
-}
-
-s_SID cff_add_string(cff_font * cff, const char *str)
-{
-    card16 idx;
-    cff_index *strings;
-    l_offset offset, size;
-    if (cff == NULL) {
-        normal_error("cff","CFF font not opened");
-    }
-    if (cff->_string == NULL) {
-        cff->_string = cff_new_index(0);
-    }
-    strings = cff->_string;
-    for (idx = 0; idx < strings->count; idx++) {
-        size = strings->offset[idx + 1] - strings->offset[idx];
-        offset = strings->offset[idx];
-        if (size == strlen(str) && !memcmp(strings->data + offset - 1, str, strlen(str))) {
-            return (s_SID) (idx + CFF_STDSTR_MAX);
-        }
-    }
-    for (idx = 0; idx < CFF_STDSTR_MAX; idx++) {
-        if (cff_stdstr[idx] && !strcmp(cff_stdstr[idx], str)) {
-            return idx;
-        }
-    }
-    offset = (strings->count > 0) ? strings->offset[strings->count] : 1;
-    strings->offset = xrealloc(strings->offset, (unsigned) (((unsigned) strings->count + 2) * sizeof(l_offset)));
-    if (strings->count == 0)
-        strings->offset[0] = 1;
-    idx = strings->count;
-    strings->count = (card16) (strings->count + 1);
-    strings->offset[strings->count] = offset + strlen(str);
-    strings->data = xrealloc(strings->data, (unsigned) ((offset + strlen(str) - 1) * sizeof(card8)));
-    memcpy(strings->data + offset - 1, str, strlen(str));
-    return (s_SID) (idx + CFF_STDSTR_MAX);
-}
-
-void cff_dict_update(cff_dict * dict, cff_font * cff)
-{
-    int i;
-    for (i = 0; i < dict->count; i++) {
-        if ((dict->entries)[i].count > 0) {
-            char *str;
-            int id;
-            id = (dict->entries)[i].id;
-            if (dict_operator[id].argtype == CFF_TYPE_SID) {
-                str = cff_get_string(cff, (s_SID) (dict->entries)[i].values[0]);
-                if (str != NULL) {
-                    (dict->entries)[i].values[0] = cff_add_string(cff, str);
-                    xfree(str);
-                }
-            } else if (dict_operator[id].argtype == CFF_TYPE_ROS) {
-                str = cff_get_string(cff, (s_SID) (dict->entries)[i].values[0]);
-                if (str != NULL) {
-                    (dict->entries)[i].values[0] = cff_add_string(cff, str);
-                    xfree(str);
-                }
-                str = cff_get_string(cff, (s_SID) (dict->entries)[i].values[1]);
-                if (str != NULL) {
-                    (dict->entries)[i].values[1] = cff_add_string(cff, str);
-                    xfree(str);
-                }
-            }
-        }
-    }
-}
-
-/*tex The charsets. */
-
-long cff_read_charsets(cff_font * cff)
-{
-    cff_charsets *charset;
-    long offset, length;
-    card16 count, i;
-    if (cff->topdict == NULL)
-        normal_error("cff","top DICT not available");
-    if (!cff_dict_known(cff->topdict, "charset")) {
-        cff->flag |= CHARSETS_ISOADOBE;
-        cff->charsets = NULL;
-        return 0;
-    }
-    offset = (long) cff_dict_get(cff->topdict, "charset", 0);
-    if (offset == 0) {
-        /*tex predefined */
-        cff->flag |= CHARSETS_ISOADOBE;
-        cff->charsets = NULL;
-        return 0;
-    } else if (offset == 1) {
-        cff->flag |= CHARSETS_EXPERT;
-        cff->charsets = NULL;
-        return 0;
-    } else if (offset == 2) {
-        cff->flag |= CHARSETS_EXPSUB;
-        cff->charsets = NULL;
-        return 0;
-    }
-    cff->offset = (l_offset) offset;
-    cff->charsets = charset = xcalloc(1, sizeof(cff_charsets));
-    charset->format = get_card8(cff);
-    charset->num_entries = 0;
-    count = (card16) (cff->num_glyphs - 1);
-    length = 1;
-    /*tex Not well documented. */
-    switch (charset->format) {
-        case 0:
-            charset->num_entries = (card16) (cff->num_glyphs - 1);  /* no .notdef */
-            charset->data.glyphs =
-                xmalloc((unsigned) (charset->num_entries * sizeof(s_SID)));
-            length += (charset->num_entries) * 2;
-            for (i = 0; i < (charset->num_entries); i++) {
-                charset->data.glyphs[i] = get_card16(cff);
-            }
-            count = 0;
-            break;
-        case 1:
-            {
-                cff_range1 *ranges = NULL;
-                while (count > 0 && charset->num_entries < cff->num_glyphs) {
-                    ranges =
-                        xrealloc(ranges,
-                                 (unsigned) (((unsigned) charset->num_entries +
-                                              1) * sizeof(cff_range1)));
-                    ranges[charset->num_entries].first = get_card16(cff);
-                    ranges[charset->num_entries].n_left = get_card8(cff);
-                    count = (card16) (count - ranges[charset->num_entries].n_left + 1);     /* no-overrap */
-                    charset->num_entries++;
-                    charset->data.range1 = ranges;
-                }
-                length += (charset->num_entries) * 3;
-            }
-            break;
-        case 2:
-            {
-                cff_range2 *ranges = NULL;
-                while (count > 0 && charset->num_entries < cff->num_glyphs) {
-                    ranges =
-                        xrealloc(ranges,
-                                 (unsigned) (((unsigned) charset->num_entries +
-                                              1) * sizeof(cff_range2)));
-                    ranges[charset->num_entries].first = get_card16(cff);
-                    ranges[charset->num_entries].n_left = get_card16(cff);
-                    count = (card16) (count - (ranges[charset->num_entries].n_left + 1));   /* non-overrapping */
-                    charset->num_entries++;
-                }
-                charset->data.range2 = ranges;
-                length += (charset->num_entries) * 4;
-            }
-            break;
-        default:
-            xfree(charset);
-            normal_error("cff","unknown charset format");
-            break;
-    }
-    if (count > 0) {
-        normal_warning("cff","charset data possibly broken (too many glyphs)");
-    }
-    return length;
-}
-
-long cff_pack_charsets(cff_font * cff, card8 * dest, long destlen)
-{
-    long len = 0;
-    card16 i;
-    cff_charsets *charset;
-    if (cff->flag & HAVE_STANDARD_CHARSETS || cff->charsets == NULL)
-        return 0;
-    if (destlen < 1)
-        normal_error("cff","buffer overflow (15)");
-    charset = cff->charsets;
-    dest[len++] = charset->format;
-    switch (charset->format) {
-        case 0:
-            if (destlen < len + (charset->num_entries) * 2)
-                normal_error("cff","buffer overflow (16)");
-            for (i = 0; i < (charset->num_entries); i++) {
-                s_SID sid = (charset->data).glyphs[i];      /* or CID */
-                dest[len++] = (card8) ((sid >> 8) & 0xff);
-                dest[len++] = (card8) (sid & 0xff);
-            }
-            break;
-        case 1:
-            {
-                if (destlen < len + (charset->num_entries) * 3)
-                    normal_error("cff","buffer overflow (17)");
-                for (i = 0; i < (charset->num_entries); i++) {
-                    dest[len++] = (card8) (((charset->data).range1[i].first >> 8) & 0xff);
-                    dest[len++] = (card8) ((charset->data).range1[i].first & 0xff);
-                    dest[len++] = (card8) ((charset->data).range1[i].n_left);
-                }
-            }
-            break;
-        case 2:
-            {
-                if (destlen < len + (charset->num_entries) * 4)
-                    normal_error("cff","buffer overflow (18)");
-                for (i = 0; i < (charset->num_entries); i++) {
-                    dest[len++] = (card8) (((charset->data).range2[i].first >> 8) & 0xff);
-                    dest[len++] = (card8) ((charset->data).range2[i].first & 0xff);
-                    dest[len++] = (card8) (((charset->data).range2[i].n_left >> 8) & 0xff);
-                    dest[len++] = (card8) ((charset->data).range2[i].n_left & 0xff);
-                }
-            }
-            break;
-        default:
-            normal_error("cff","unknown charset format");
-            break;
-    }
-    return len;
-}
-
-/*tex
-
-    Here we decode and encode Type 2 charstring. All local/global subroutine
-    calls in a given charstring is replace by the content of subroutine
-    charstrings. We do this because some PostScript RIP may have problems with
-    sparse subroutine array. Workaround for this is to re-order subroutine array
-    so that no gap appears in the subroutine array, or put dummy charstrings that
-    contains only `return' in the gap. However, re-ordering of subroutine is
-    rather difficult for Type 2 charstrings due to the bias which depends on the
-    total number of subroutines. Replacing callgsubr/callsubr calls with the
-    content of the corresponding subroutine charstring may be more efficient than
-    putting dummy subroutines in the case of subsetted font. Adobe distiller
-    seems doing same thing.
-
-    And also note that subroutine numbers within subroutines can depend on the
-    content of operand stack as follows:
-
-    \startyping
-    \.{  ... l m callsubr << subr \#(m+bias): n add callsubr >> ...}
-    \stoptyping
-
-    I've not implemented the `random' operator which generates a pseudo-random
-    number in the range (0, 1] and push them into argument stack. How
-    pseudo-random sequences are generated is not documented in the Type 2
-    charstring spec.
-
-*/
-
-#define CS_TYPE2_DEBUG_STR   "Type2 Charstring Parser"
-#define CS_TYPE2_DEBUG       5
-
-#define CS_BUFFER_CFF_ERROR -3
-#define CS_STACK_CFF_ERROR  -2
-#define CS_PARSE_CFF_ERROR  -1
-#define CS_PARSE_OK          0
-#define CS_PARSE_END         1
-#define CS_SUBR_RETURN       2
-#define CS_CHAR_END          3
-
-static int status = CS_PARSE_CFF_ERROR;
-
-#define DST_NEED(a,b) {if ((a) < (b)) { status = CS_BUFFER_CFF_ERROR ; return ; }}
-#define SRC_NEED(a,b) {if ((a) < (b)) { status = CS_PARSE_CFF_ERROR  ; return ; }}
-#define NEED(a,b)     {if ((a) < (b)) { status = CS_STACK_CFF_ERROR  ; return ; }}
-
-/*tex The hintmask and cntrmask need the number of stem zones. */
-
-static int num_stems = 0;
-static int phase = 0;
-
-/*tex Subroutine nesting.
-*/
-static int cs2_nest = 0;
-
-/*tex The advance width. */
-
-static int have_width = 0;
-static double width = 0.0;
-
-/*tex
-
-    Standard Encoding Accented Characters: Optional four arguments for endchar.
-    See, CFF spec., p.35. This is obsolete feature and is no longer supported.
-
-    \starttyping
-    static double seac[4] = { 0.0, 0.0, 0.0, 0.0 }; // gone
-    \stoptyping
-
-*/
-
-/*tex Operand stack and Transient array */
-
-static int cs2_stack_top = 0;
-static double cs2_arg_stack[CS_ARG_STACK_MAX];
-static double trn_array[CS_TRANS_ARRAY_MAX];
-
-/*tex
-
-    Type 2 CharString encoding, first the 1 byte operators:
-
-*/
-
-/*      RESERVED       0 */
-#define cs_hstem       1
-/*      RESERVED       2 */
-#define cs_vstem       3
-#define cs_vmoveto     4
-#define cs_rlineto     5
-#define cs_hlineto     6
-#define cs_vlineto     7
-#define cs_rrcurveto   8
-/*      cs_closepath   9  */
-#define cs_callsubr   10
-#define cs_return     11
-#define cs_escape     12
-/*      cs_hsbw       13 */
-#define cs_endchar    14
-#define cs_setvsindex 15
-#define cs_blend      16
-/*      RESERVED      17 */
-#define cs_hstemhm    18
-#define cs_hintmask   19
-#define cs_cntrmask   20
-#define cs_rmoveto    21
-#define cs_hmoveto    22
-#define cs_vstemhm    23
-#define cs_rcurveline 24
-#define cs_rlinecurve 25
-#define cs_vvcurveto  26
-#define cs_hhcurveto  27
-/*      SHORTINT      28 */
-#define cs_callgsubr  29
-#define cs_vhcurveto  30
-#define cs_hvcurveto  31
-
-/*tex
-
-    Next the two byte CharString operators:
-
-*/
-
-#define cs_dotsection       0
-/*      cs_vstem3           1 */
-/*      cs_hstem3           2 */
-#define cs_and              3
-#define cs_or               4
-#define cs_not              5
-/*      cs_seac             6 */
-/*      cs_sbw              7 */
-/*      RESERVED            8 */
-#define cs_abs              9
-#define cs_add             10
-#define cs_sub             11
-#define cs_div             12
-/*      RESERVED           13 */
-#define cs_neg             14
-#define cs_eq              15
-/*      cs_callothersubr   16 */
-/*      cs_pop|            17 */
-#define cs_drop            18
-/*      RESERVED           19 */
-#define cs_put             20
-#define cs_get             21
-#define cs_ifelse          22
-#define cs_random          23
-#define cs_mul             24
-/*      RESERVED           25 */
-#define cs_sqrt            26
-#define cs_dup             27
-#define cs_exch            28
-#define cs_index           29
-#define cs_roll            30
-/*      cs_setcurrentpoint 31 */
-/*      RESERVED           32 */
-/*      RESERVED           33 */
-#define cs_hflex           34
-#define cs_flex            35
-#define cs_hflex1          36
-#define cs_flex1           37
-
-/*tex  |clear_stack| put all operands sotred in operand stack to dest. */
-
-static void clear_stack(card8 ** dest, card8 * limit)
-{
-    int i;
-    for (i = 0; i < cs2_stack_top; i++) {
-        double value;
-        long ivalue;
-        value = cs2_arg_stack[i];
-        /*tex The nearest integer value. */
-        ivalue = (long) floor(value + 0.5);
-        if (value >= 0x8000L || value <= (-0x8000L - 1)) {
-            /*tex
-                This number cannot be represented as a single operand. We must
-                use |a b mul ...| or |a c div| to represent large values.
-            */
-            normal_error("cff","argument value too large (this is bug)");
-        } else if (fabs(value - (double) ivalue) > 3.0e-5) {
-            /*tex A 16.16-bit signed fixed value  */
-            DST_NEED(limit, *dest + 5);
-            *(*dest)++ = 255;
-            /*tex The mantissa. */
-            ivalue = (long) floor(value);
-            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
-            *(*dest)++ = (card8) (ivalue & 0xff);
-            /*tex The fraction. */
-            ivalue = (long) ((value - (double) ivalue) * 0x10000l);
-            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
-            *(*dest)++ = (card8) (ivalue & 0xff);
-            /*tex Everything else is integer. */
-        } else if (ivalue >= -107 && ivalue <= 107) {
-            DST_NEED(limit, *dest + 1);
-            *(*dest)++ = (card8) (ivalue + 139);
-        } else if (ivalue >= 108 && ivalue <= 1131) {
-            DST_NEED(limit, *dest + 2);
-            ivalue = (long) 0xf700u + ivalue - 108;
-            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
-            *(*dest)++ = (card8) (ivalue & 0xff);
-        } else if (ivalue >= -1131 && ivalue <= -108) {
-            DST_NEED(limit, *dest + 2);
-            ivalue = (long) 0xfb00u - ivalue - 108;
-            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
-            *(*dest)++ = (card8) (ivalue & 0xff);
-        } else if (ivalue >= -32768 && ivalue <= 32767) {
-            /*tex A shortint. */
-            DST_NEED(limit, *dest + 3);
-            *(*dest)++ = 28;
-            *(*dest)++ = (card8) ((ivalue >> 8) & 0xff);
-            *(*dest)++ = (card8) ((ivalue) & 0xff);
-        } else {
-            normal_error("cff","unexpected error");
-        }
-    }
-    /*tex Clear the stack. */
-    cs2_stack_top = 0;
-    return;
-}
-
-/*tex
-    Single byte operators: Path construction, Operator for finishing a path, Hint
-    operators. Phases:
-
-    \starttabulate
-    \NC \type{0} \NC inital state \NC \NR
-    \NC \type{1} \NC hint declaration, first stack-clearing operator appeared \NC \NR
-    \NC \type{2} \NC in path construction \NC \NR
-    \stoptabulate
-
-*/
-
-static void do_operator1(card8 ** dest, card8 * limit, card8 ** data, card8 * endptr)
-{
-    card8 op = **data;
-
-    *data += 1;
-
-    switch (op) {
-        case cs_hstemhm:
-        case cs_vstemhm:
-            /*tex  A charstring may have a hintmask if the above operator has been seen. */
-        case cs_hstem:
-        case cs_vstem:
-            if (phase == 0 && (cs2_stack_top % 2)) {
-                have_width = 1;
-                width = cs2_arg_stack[0];
-            }
-            num_stems += cs2_stack_top / 2;
-            clear_stack(dest, limit);
-            DST_NEED(limit, *dest + 1);
-            *(*dest)++ = op;
-            phase = 1;
-            break;
-        case cs_hintmask:
-        case cs_cntrmask:
-            if (phase < 2) {
-                if (phase == 0 && (cs2_stack_top % 2)) {
-                    have_width = 1;
-                    width = cs2_arg_stack[0];
-                }
-                num_stems += cs2_stack_top / 2;
-            }
-            clear_stack(dest, limit);
-            DST_NEED(limit, *dest + 1);
-            *(*dest)++ = op;
-            if (num_stems > 0) {
-                int masklen = (num_stems + 7) / 8;
-                DST_NEED(limit, *dest + masklen);
-                SRC_NEED(endptr, *data + masklen);
-                memmove(*dest, *data, (size_t) masklen);
-                *data += masklen;
-                *dest += masklen;
-            }
-            phase = 2;
-            break;
-        case cs_rmoveto:
-            if (phase == 0 && (cs2_stack_top % 2)) {
-                have_width = 1;
-                width = cs2_arg_stack[0];
-            }
-            clear_stack(dest, limit);
-            DST_NEED(limit, *dest + 1);
-            *(*dest)++ = op;
-            phase = 2;
-            break;
-        case cs_hmoveto:
-        case cs_vmoveto:
-            if (phase == 0 && (cs2_stack_top % 2) == 0) {
-                have_width = 1;
-                width = cs2_arg_stack[0];
-            }
-            clear_stack(dest, limit);
-            DST_NEED(limit, *dest + 1);
-            *(*dest)++ = op;
-            phase = 2;
-            break;
-        case cs_endchar:
-            if (cs2_stack_top == 1) {
-                have_width = 1;
-                width = cs2_arg_stack[0];
-                clear_stack(dest, limit);
-            } else if (cs2_stack_top == 4 || cs2_stack_top == 5) {
-                normal_warning("cff","'seac' character deprecated in type 2 charstring");
-                status = CS_PARSE_CFF_ERROR;
-                return;
-            } else if (cs2_stack_top > 0) {
-                normal_warning("cff","operand stack not empty");
-            }
-            DST_NEED(limit, *dest + 1);
-            *(*dest)++ = op;
-            status = CS_CHAR_END;
-            break;
-            /*tex The above operators are candidate for first stack clearing operator. */
-        case cs_setvsindex:
-            /*
-                vsindex = cs2_arg_stack[cs2_stack_top-1];
-                cs2_stack_top -= 1;
-            */
-            normal_warning("cff2","unsupported setvindex operator");
-            status = CS_PARSE_CFF_ERROR;
-            break;
-        case cs_blend:
-            /*
-                blends = cs2_arg_stack[cs2_stack_top-1];
-                cs2_stack_top -= 1;
-                cs2_stack_top -= blends * regions ;
-            */
-            normal_warning("cff2","unsupported blend operator");
-            status = CS_PARSE_CFF_ERROR;
-            break;
-        case cs_rlineto:
-        case cs_hlineto:
-        case cs_vlineto:
-        case cs_rrcurveto:
-        case cs_rcurveline:
-        case cs_rlinecurve:
-        case cs_vvcurveto:
-        case cs_hhcurveto:
-        case cs_vhcurveto:
-        case cs_hvcurveto:
-            if (phase < 2) {
-                normal_warning("cff","broken type 2 charstring");
-                status = CS_PARSE_CFF_ERROR;
-                return;
-            }
-            clear_stack(dest, limit);
-            DST_NEED(limit, *dest + 1);
-            *(*dest)++ = op;
-            break;
-            /*tex All the operotors above are stack clearing. */
-        case cs_return:
-            normal_error("cff","unexpected return");
-        case cs_callgsubr:
-            normal_error("cff","unexpected callgsubr");
-        case cs_callsubr:
-            normal_error("cff","unexpected callsubr");
-            break;
-        default:
-            formatted_warning("cff","%s: unknown charstring operator: 0x%02x", CS_TYPE2_DEBUG_STR, op);
-            status = CS_PARSE_CFF_ERROR;
-            break;
-    }
-    return;
-}
-
-/*tex
-
-    Double byte operators: Flex, arithmetic, conditional, and storage operators.
-    The following operators are not supported: random but How random ?
-
-*/
-
-static void do_operator2(card8 ** dest, card8 * limit, card8 ** data, card8 * endptr)
-{
-    card8 op;
-    *data += 1;
-    SRC_NEED(endptr, *data + 1);
-    op = **data;
-    *data += 1;
-    switch (op) {
-        case cs_dotsection:
-            normal_warning("cff","Operator 'dotsection' deprecated in type 2 charstring");
-            status = CS_PARSE_CFF_ERROR;
-            return;
-            break;
-        case cs_hflex:
-        case cs_flex:
-        case cs_hflex1:
-        case cs_flex1:
-            if (phase < 2) {
-                formatted_warning("cff","%s: broken type 2 charstring", CS_TYPE2_DEBUG_STR);
-                status = CS_PARSE_CFF_ERROR;
-                return;
-            }
-            clear_stack(dest, limit);
-            DST_NEED(limit, *dest + 2);
-            *(*dest)++ = cs_escape;
-            *(*dest)++ = op;
-            break;
-            /*tex All operators above are stack clearing. */
-        case cs_and:
-            NEED(cs2_stack_top, 2);
-            cs2_stack_top--;
-            if (cs2_arg_stack[cs2_stack_top] && cs2_arg_stack[cs2_stack_top - 1]) {
-                cs2_arg_stack[cs2_stack_top - 1] = 1.0;
-            } else {
-                cs2_arg_stack[cs2_stack_top - 1] = 0.0;
-            }
-            break;
-        case cs_or:
-            NEED(cs2_stack_top, 2);
-            cs2_stack_top--;
-            if (cs2_arg_stack[cs2_stack_top] || cs2_arg_stack[cs2_stack_top - 1]) {
-                cs2_arg_stack[cs2_stack_top - 1] = 1.0;
-            } else {
-                cs2_arg_stack[cs2_stack_top - 1] = 0.0;
-            }
-            break;
-        case cs_not:
-            NEED(cs2_stack_top, 1);
-            if (cs2_arg_stack[cs2_stack_top - 1]) {
-                cs2_arg_stack[cs2_stack_top - 1] = 0.0;
-            } else {
-                cs2_arg_stack[cs2_stack_top - 1] = 1.0;
-            }
-            break;
-        case cs_abs:
-            NEED(cs2_stack_top, 1);
-            cs2_arg_stack[cs2_stack_top - 1] =
-                fabs(cs2_arg_stack[cs2_stack_top - 1]);
-            break;
-        case cs_add:
-            NEED(cs2_stack_top, 2);
-            cs2_arg_stack[cs2_stack_top - 2] += cs2_arg_stack[cs2_stack_top - 1];
-            cs2_stack_top--;
-            break;
-        case cs_sub:
-            NEED(cs2_stack_top, 2);
-            cs2_arg_stack[cs2_stack_top - 2] -= cs2_arg_stack[cs2_stack_top - 1];
-            cs2_stack_top--;
-            break;
-        case cs_div:
-            NEED(cs2_stack_top, 2);
-            cs2_arg_stack[cs2_stack_top - 2] /= cs2_arg_stack[cs2_stack_top - 1];
-            cs2_stack_top--;
-            break;
-        case cs_neg:
-            NEED(cs2_stack_top, 1);
-            cs2_arg_stack[cs2_stack_top - 1] *= -1.0;
-            break;
-        case cs_eq:
-            NEED(cs2_stack_top, 2);
-            cs2_stack_top--;
-            if (cs2_arg_stack[cs2_stack_top] == cs2_arg_stack[cs2_stack_top - 1]) {
-                cs2_arg_stack[cs2_stack_top - 1] = 1.0;
-            } else {
-                cs2_arg_stack[cs2_stack_top - 1] = 0.0;
-            }
-            break;
-        case cs_drop:
-            NEED(cs2_stack_top, 1);
-            cs2_stack_top--;
-            break;
-        case cs_put:
-            NEED(cs2_stack_top, 2);
-            {
-                int idx = (int) cs2_arg_stack[--cs2_stack_top];
-                NEED(CS_TRANS_ARRAY_MAX, idx);
-                trn_array[idx] = cs2_arg_stack[--cs2_stack_top];
-            }
-            break;
-        case cs_get:
-            NEED(cs2_stack_top, 1);
-            {
-                int idx = (int) cs2_arg_stack[cs2_stack_top - 1];
-                NEED(CS_TRANS_ARRAY_MAX, idx);
-                cs2_arg_stack[cs2_stack_top - 1] = trn_array[idx];
-            }
-            break;
-        case cs_ifelse:
-            NEED(cs2_stack_top, 4);
-            cs2_stack_top -= 3;
-            if (cs2_arg_stack[cs2_stack_top + 1] > cs2_arg_stack[cs2_stack_top + 2]) {
-                cs2_arg_stack[cs2_stack_top - 1] = cs2_arg_stack[cs2_stack_top];
-            }
-            break;
-        case cs_mul:
-            NEED(cs2_stack_top, 2);
-            cs2_arg_stack[cs2_stack_top - 2] =
-                cs2_arg_stack[cs2_stack_top - 2] * cs2_arg_stack[cs2_stack_top - 1];
-            cs2_stack_top--;
-            break;
-        case cs_sqrt:
-            NEED(cs2_stack_top, 1);
-            cs2_arg_stack[cs2_stack_top - 1] =
-                sqrt(cs2_arg_stack[cs2_stack_top - 1]);
-            break;
-        case cs_dup:
-            NEED(cs2_stack_top, 1);
-            NEED(CS_ARG_STACK_MAX, cs2_stack_top + 1);
-            cs2_arg_stack[cs2_stack_top] = cs2_arg_stack[cs2_stack_top - 1];
-            cs2_stack_top++;
-            break;
-        case cs_exch:
-            NEED(cs2_stack_top, 2);
-            {
-                double save = cs2_arg_stack[cs2_stack_top - 2];
-                cs2_arg_stack[cs2_stack_top - 2] = cs2_arg_stack[cs2_stack_top - 1];
-                cs2_arg_stack[cs2_stack_top - 1] = save;
-            }
-            break;
-        case cs_index:
-            NEED(cs2_stack_top, 2);
-            {
-                int idx = (int) cs2_arg_stack[cs2_stack_top - 1];
-                if (idx < 0) {
-                    cs2_arg_stack[cs2_stack_top - 1] =
-                        cs2_arg_stack[cs2_stack_top - 2];
-                } else {
-                    NEED(cs2_stack_top, idx + 2);
-                    cs2_arg_stack[cs2_stack_top - 1] =
-                        cs2_arg_stack[cs2_stack_top - idx - 2];
-                }
-            }
-            break;
-        case cs_roll:
-            NEED(cs2_stack_top, 2);
-            {
-                int N, J;
-                J = (int) cs2_arg_stack[--cs2_stack_top];
-                N = (int) cs2_arg_stack[--cs2_stack_top];
-                NEED(cs2_stack_top, N);
-                if (J > 0) {
-                    J = J % N;
-                    while (J-- > 0) {
-                        double save = cs2_arg_stack[cs2_stack_top - 1];
-                        int i = cs2_stack_top - 1;
-                        while (i > cs2_stack_top - N) {
-                            cs2_arg_stack[i] = cs2_arg_stack[i - 1];
-                            i--;
-                        }
-                        cs2_arg_stack[i] = save;
-                    }
-                } else {
-                    J = (-J) % N;
-                    while (J-- > 0) {
-                        double save = cs2_arg_stack[cs2_stack_top - N];
-                        int i = cs2_stack_top - N;
-                        while (i < cs2_stack_top - 1) {
-                            cs2_arg_stack[i] = cs2_arg_stack[i + 1];
-                            i++;
-                        }
-                        cs2_arg_stack[i] = save;
-                    }
-                }
-            }
-            break;
-        case cs_random:
-            formatted_warning("cff","%s: Charstring operator 'random' found.", CS_TYPE2_DEBUG_STR);
-            NEED(CS_ARG_STACK_MAX, cs2_stack_top + 1);
-            cs2_arg_stack[cs2_stack_top++] = 1.0;
-            break;
-        default:
-            formatted_warning("cff","%s: unknown charstring operator: 0x0c%02x", CS_TYPE2_DEBUG_STR, op);
-            status = CS_PARSE_CFF_ERROR;
-            break;
-    }
-    return;
-}
-
-/*tex integer: exactly the same as the DICT encoding (except 29) */
-
-static void cs2_get_integer(card8 ** data, card8 * endptr)
-{
-    long result = 0;
-    card8 b0 = **data, b1, b2;
-    *data += 1;
-    if (b0 == 28) {
-        /*tex shortint */
-        SRC_NEED(endptr, *data + 2);
-        b1 = **data;
-        b2 = *(*data + 1);
-        result = b1 * 256 + b2;
-        if (result > 0x7fff)
-            result -= 0x10000L;
-        *data += 2;
-    } else if (b0 >= 32 && b0 <= 246) {
-        /*tex int (1) */
-        result = b0 - 139;
-    } else if (b0 >= 247 && b0 <= 250) {
-        /*tex int (2) */
-        SRC_NEED(endptr, *data + 1);
-        b1 = **data;
-        result = (b0 - 247) * 256 + b1 + 108;
-        *data += 1;
-    } else if (b0 >= 251 && b0 <= 254) {
-        SRC_NEED(endptr, *data + 1);
-        b1 = **data;
-        result = -(b0 - 251) * 256 - b1 - 108;
-        *data += 1;
-    } else {
-        status = CS_PARSE_CFF_ERROR;
-        return;
-    }
-    NEED(CS_ARG_STACK_MAX, cs2_stack_top + 1);
-    cs2_arg_stack[cs2_stack_top++] = (double) result;
-    return;
-}
-
-/*tex Signed 16.16-bits fixed number for Type 2 charstring encoding. */
-
-static void get_fixed(card8 ** data, card8 * endptr)
-{
-    long ivalue;
-    double rvalue;
-    *data += 1;
-    SRC_NEED(endptr, *data + 4);
-    ivalue = *(*data) * 0x100 + *(*data + 1);
-    rvalue = (double) ((ivalue > 0x7fffL) ? (ivalue - 0x10000L) : ivalue);
-    ivalue = *(*data + 2) * 0x100 + *(*data + 3);
-    rvalue += ((double) ivalue) / 0x10000L;
-    NEED(CS_ARG_STACK_MAX, cs2_stack_top + 1);
-    cs2_arg_stack[cs2_stack_top++] = rvalue;
-    *data += 4;
-    return;
-}
-
-/*tex
-
-Subroutines: the bias for subroutine number is introduced in type 2
-charstrings.
-
-\starttabulate
-\NC \type {subr}     \NC set to a pointer to the subroutine charstring \NC \NR
-\NC \type {len}      \NC set to the length of subroutine charstring \NC \NR
-\NC \type {subr_idx} \NC CFF INDEX data that contains subroutines \NC \NR
-\NC \type {id}       \NC biased subroutine number \NC \NR
-\stoptabulate
-
-*/
-
-static void get_subr(card8 ** subr, long *len, cff_index * subr_idx, long id)
-{
-    card16 count;
-    if (subr_idx == NULL)
-        formatted_error("cff","%s: subroutine called but no subroutine found",CS_TYPE2_DEBUG_STR);
-    count = subr_idx->count;
-    /*tex addi the bias number */
-    if (count < 1240) {
-        id += 107;
-    } else if (count < 33900) {
-        id += 1131;
-    } else {
-        id += 32768;
-    }
-    if (id > count)
-        formatted_error("cff","%s: invalid subroutine index: %ld (max=%u)", CS_TYPE2_DEBUG_STR, id, count);
-    *len = (long) ((subr_idx->offset)[id + 1] - (subr_idx->offset)[id]);
-    *subr = subr_idx->data + (subr_idx->offset)[id] - 1;
-    return;
-}
-
-/*tex
-
-    The Type 2 interpretation of a number encoded in five-bytes (those with an
-    initial byte value of 255) differs from how it is interpreted in the Type 1
-    format.
-
-*/
-
-static void do_charstring(card8 ** dest, card8 * limit, card8 ** data, card8 * endptr,
-    cff_index * gsubr_idx, cff_index * subr_idx, int cff2)
-{
-    card8 b0 = 0, *subr;
-    long len;
-    if (cs2_nest > CS_SUBR_NEST_MAX)
-        formatted_error("cff","%s: subroutine nested too deeply", CS_TYPE2_DEBUG_STR);
-    cs2_nest++;
-    while (*data < endptr && status == CS_PARSE_OK) {
-        b0 = **data;
-        if (b0 == 255) {
-            /*tex A 16-bit.16-bit fixed signed number. */
-            get_fixed(data, endptr);
-        } else if (b0 == cs_return) {
-            status = CS_SUBR_RETURN;
-        } else if (b0 == cs_callgsubr) {
-            if (cs2_stack_top < 1) {
-                status = CS_STACK_CFF_ERROR;
-            } else {
-                cs2_stack_top--;
-                get_subr(&subr, &len, gsubr_idx, (long) cs2_arg_stack[cs2_stack_top]);
-                if (*dest + len > limit)
-                    formatted_error("cff","%s: possible buffer overflow (1)", CS_TYPE2_DEBUG_STR);
-                do_charstring(dest, limit, &subr, subr + len, gsubr_idx, subr_idx, cff2);
-                *data += 1;
-            }
-        } else if (b0 == cs_callsubr) {
-            if (cs2_stack_top < 1) {
-                status = CS_STACK_CFF_ERROR;
-            } else {
-                cs2_stack_top--;
-                get_subr(&subr, &len, subr_idx, (long) cs2_arg_stack[cs2_stack_top]);
-                if (limit < *dest + len)
-                    formatted_error("cff","%s: possible buffer overflow (2)", CS_TYPE2_DEBUG_STR);
-                do_charstring(dest, limit, &subr, subr + len, gsubr_idx, subr_idx, cff2);
-                *data += 1;
-            }
-        } else if (b0 == cs_escape) {
-            do_operator2(dest, limit, data, endptr);
-        } else if (b0 < 32 && b0 != 28) {
-            do_operator1(dest, limit, data, endptr);
-        } else if ((b0 <= 22 && b0 >= 27) || b0 == 31) {
-            status = CS_PARSE_CFF_ERROR;
-        } else {
-            cs2_get_integer(data, endptr);
-        }
-    }
-    if (cff2) {
-        DST_NEED(limit, *dest + 1);
-        ++endptr;
-        *(*dest)++ = cs_endchar;
-    } else if (status == CS_SUBR_RETURN) {
-        status = CS_PARSE_OK;
-    } else if (status == CS_CHAR_END && *data < endptr) {
-        formatted_warning("cff","%s: garbage after endchar", CS_TYPE2_DEBUG_STR);
-    } else if (status < CS_PARSE_OK) {
-        formatted_error("cff","%s: parsing charstring failed: (status=%d, stack=%d)", CS_TYPE2_DEBUG_STR, status, cs2_stack_top);
-    }
-    cs2_nest--;
-    return;
-}
-
-static void cs_parse_init(void)
-{
-    status = CS_PARSE_OK;
-    cs2_nest = 0;
-    phase = 0;
-    num_stems = 0;
-    cs2_stack_top = 0;
-}
-
-/*tex Not just copying \unknown */
-
-static long cs_copy_charstring(card8 * dst, long dstlen, card8 * src, long srclen, cff_index * gsubr,
-    cff_index * subr, double default_width, double nominal_width, cs_ginfo * ginfo, int cff2)
-{
-    card8 *save = dst;
-
-    cs_parse_init();
-
-    width = 0.0;
-    have_width = 0;
-
-    /* expand call(g)subrs */
-    do_charstring(&dst, dst + dstlen, &src, src + srclen, gsubr, subr, cff2);
-
-    if (ginfo) {
-        ginfo->flags = 0;       /* not used */
-        if (have_width) {
-            ginfo->wx = nominal_width + width;
-        } else {
-            ginfo->wx = default_width;
-        }
-    }
-
-    return (long) (dst - save);
-}
-
-/*tex CID-Keyed font specific. */
-
-long cff_read_fdselect(cff_font * cff)
-{
-    cff_fdselect *fdsel;
-    long offset, length;
-    card16 i;
-    if (cff->topdict == NULL)
-        normal_error("cff","top DICT not available");
-    if (!(cff->flag & FONTTYPE_CIDFONT))
-        return 0;
-    offset = (long) cff_dict_get(cff->topdict, "FDSelect", 0);
-    cff->offset = (l_offset) offset;
-    cff->fdselect = fdsel = xcalloc(1, sizeof(cff_fdselect));
-    fdsel->format = get_card8(cff);
-    length = 1;
-    switch (fdsel->format) {
-        case 0:
-            fdsel->num_entries = cff->num_glyphs;
-            (fdsel->data).fds = xmalloc(fdsel->num_entries * sizeof(card8));
-            for (i = 0; i < (fdsel->num_entries); i++) {
-                (fdsel->data).fds[i] = get_card8(cff);
-            }
-            length += fdsel->num_entries;
-            break;
-        case 3:
-            {
-                cff_range3 *ranges;
-                fdsel->num_entries = get_card16(cff);
-                fdsel->data.ranges = ranges =
-                    xcalloc(fdsel->num_entries, sizeof(cff_range3));
-                for (i = 0; i < (fdsel->num_entries); i++) {
-                    ranges[i].first = get_card16(cff);
-                    ranges[i].fd = get_card8(cff);
-                }
-                if (ranges[0].first != 0)
-                    normal_error("cff","range not starting with 0");
-                if (cff->num_glyphs != get_card16(cff))
-                    normal_error("cff","sentinel value mismatched with number of glyphs");
-                length += (fdsel->num_entries) * 3 + 4;
-            }
-            break;
-        default:
-            xfree(fdsel);
-            normal_error("cff","unknown FDSelect format");
-            break;
-    }
-    return length;
-}
-
-long cff_pack_fdselect(cff_font * cff, card8 * dest, long destlen)
-{
-    cff_fdselect *fdsel;
-    long len = 0;
-    card16 i;
-    if (cff->fdselect == NULL)
-        return 0;
-    if (destlen < 1)
-        normal_error("cff","buffer overflow (23)");
-    fdsel = cff->fdselect;
-    dest[len++] = fdsel->format;
-    switch (fdsel->format) {
-        case 0:
-            if (fdsel->num_entries != cff->num_glyphs)
-                normal_error("cff","invalid data");
-            if (destlen < len + fdsel->num_entries)
-                normal_error("cff","buffer overflow (24)");
-            for (i = 0; i < fdsel->num_entries; i++) {
-                dest[len++] = (fdsel->data).fds[i];
-            }
-            break;
-        case 3:
-            {
-                if (destlen < len + 2)
-                    normal_error("cff","buffer overflow (25)");
-                len += 2;
-                for (i = 0; i < (fdsel->num_entries); i++) {
-                    if (destlen < len + 3)
-                        normal_error("cff","buffer overflow (26)");
-                    dest[len++] =
-                        (card8) (((fdsel->data).ranges[i].first >> 8) & 0xff);
-                    dest[len++] = (card8) ((fdsel->data).ranges[i].first & 0xff);
-                    dest[len++] = (card8) ((fdsel->data).ranges[i].fd);
-                }
-                if (destlen < len + 2)
-                    normal_error("cff","buffer overflow (27)");
-                dest[len++] = (card8) ((cff->num_glyphs >> 8) & 0xff);
-                dest[len++] = (card8) (cff->num_glyphs & 0xff);
-                dest[1] = (card8) (((len / 3 - 1) >> 8) & 0xff);
-                dest[2] = (card8) ((len / 3 - 1) & 0xff);
-            }
-            break;
-        default:
-            normal_error("cff","unknown FDSelect format");
-            break;
-    }
-    return len;
-}
-
-/*tex Create an instance of embeddable font. */
-
-static void write_fontfile(PDF pdf, cff_font * cffont, char *fullname)
-{
-    cff_index *topdict, *fdarray, *private;
-    unsigned char *dest;
-    long destlen = 0, i, size;
-    long offset, topdict_offset, fdarray_offset;
-    topdict = cff_new_index(1);
-    fdarray = cff_new_index(cffont->num_fds);
-    private = cff_new_index(cffont->num_fds);
-    cff_dict_remove(cffont->topdict, "UniqueID");
-    cff_dict_remove(cffont->topdict, "XUID");
-    /*tex A bad font may have this: */
-    cff_dict_remove(cffont->topdict, "Private");
-    /*tex A bad font may have this: */
-    cff_dict_remove(cffont->topdict, "Encoding");
-    /*tex This is CFF2 specific: */
-    cff_dict_remove(cffont->topdict, "vstore");
-    /*tex This is CFF2 specific: */
-    cff_dict_remove(cffont->topdict, "maxstack");
-    topdict->offset[1] = (l_offset) cff_dict_pack(cffont->topdict, (card8 *) work_buffer, WORK_BUFFER_SIZE) + 1;
-    for (i = 0; i < cffont->num_fds; i++) {
-        size = 0;
-        if (cffont->private && cffont->private[i]) {
-            size = cff_dict_pack(cffont->private[i], (card8 *) work_buffer, WORK_BUFFER_SIZE);
-            if (size < 1) {
-                /*tex |Private| contains only |Subr|: */
-                cff_dict_remove(cffont->fdarray[i], "Private");
-            }
-        }
-        (private->offset)[i + 1] = (unsigned long) ((private->offset)[i] + (unsigned) size);
-        (fdarray->offset)[i + 1] = (unsigned long) ((fdarray->offset)[i]
-            + (unsigned) cff_dict_pack(cffont->fdarray[i], (card8 *) work_buffer, WORK_BUFFER_SIZE));
-    }
-    /*tex The header size: */
-    destlen = 4;
-    destlen += cff_set_name(cffont, fullname);
-    destlen += cff_index_size(topdict);
-    destlen += cff_index_size(cffont->string);
-    destlen += cff_index_size(cffont->gsubr);
-    /*tex |charset| format 0 */
-    destlen += (cffont->charsets->num_entries) * 2 + 1;
-    /*tex |fdselect| format 3 */
-    destlen += (cffont->fdselect->num_entries) * 3 + 5;
-    destlen += cff_index_size(cffont->cstrings);
-    destlen += cff_index_size(fdarray);
-    /* |Private| is not indexed */
-    destlen = (long) (destlen + (long) private->offset[private->count] - 1);
-    dest = xcalloc((unsigned) destlen, sizeof(card8));
-    offset = 0;
-    /*tex |Header| */
-    offset += cff_put_header(cffont, dest + offset, destlen - offset);
-    /*tex |Name| */
-    offset += cff_pack_index(cffont->name, dest + offset, destlen - offset);
-    /*tex |Top DICT| */
-    topdict_offset = offset;
-    offset += cff_index_size(topdict);
-    /*tex |Strings| */
-    offset += cff_pack_index(cffont->string, dest + offset, destlen - offset);
-    /*tex |Global Subrs| */
-    offset += cff_pack_index(cffont->gsubr, dest + offset, destlen - offset);
-    /*tex |charset| */
-    cff_dict_set(cffont->topdict, "charset", 0, (double) offset);
-    offset += cff_pack_charsets(cffont, dest + offset, destlen - offset);
-    /*tex |FDSelect| */
-    cff_dict_set(cffont->topdict, "FDSelect", 0, (double) offset);
-    offset += cff_pack_fdselect(cffont, dest + offset, destlen - offset);
-    /*tex |CharStrings| */
-    cff_dict_set(cffont->topdict, "CharStrings", 0, (double) offset);
-    offset += cff_pack_index(cffont->cstrings, dest + offset, cff_index_size(cffont->cstrings));
-    cff_release_index(cffont->cstrings);
-    /*tex |Charstring|s can consume a lot of memory. */
-    cffont->cstrings = NULL;
-    /*tex |FDArray| and |Private| */
-    cff_dict_set(cffont->topdict, "FDArray", 0, (double) offset);
-    fdarray_offset = offset;
-    offset += cff_index_size(fdarray);
-    fdarray->data = xcalloc((unsigned) (fdarray->offset[fdarray->count] - 1), sizeof(card8));
-    for (i = 0; i < cffont->num_fds; i++) {
-        size = (long) (private->offset[i + 1] - private->offset[i]);
-        if (cffont->private[i] && size > 0) {
-            cff_dict_pack(cffont->private[i], dest + offset, size);
-            cff_dict_set(cffont->fdarray[i], "Private", 0, (double) size);
-            cff_dict_set(cffont->fdarray[i], "Private", 1, (double) offset);
-        }
-        cff_dict_pack(cffont->fdarray[i], fdarray->data + (fdarray->offset)[i] - 1, (long) (fdarray->offset[fdarray->count] - 1));
-        offset += size;
-    }
-    cff_pack_index(fdarray, dest + fdarray_offset, cff_index_size(fdarray));
-    cff_release_index(fdarray);
-    cff_release_index(private);
-    /*tex Finally the |Top DICT| */
-    topdict->data = xcalloc((unsigned) (topdict->offset[topdict->count] - 1), sizeof(card8));
-    cff_dict_pack(cffont->topdict, topdict->data, (long) (topdict->offset[topdict->count] - 1));
-    cff_pack_index(topdict, dest + topdict_offset, cff_index_size(topdict));
-    cff_release_index(topdict);
-    for (i = 0; i < offset; i++) {
-        strbuf_putchar(pdf->fb, dest[i]);
-    }
-    xfree(dest);
-    return;
-}
-
-void write_cff(PDF pdf, cff_font * cffont, fd_entry * fd)
-{
-    cff_index *charstrings, *cs_idx;
-    long charstring_len, max_len;
-    long size, offset = 0;
-    card8 *data;
-    card16 num_glyphs, cs_count1, code, gid, last_cid;
-    double nominal_width, default_width;
-    char *fontname;
-    char *fullname;
-    glw_entry *glyph, *found;
-    struct avl_traverser t;
-    cffont->_string = NULL;
-    fontname = xcalloc((unsigned) (1 + strlen(fd->fontname)), 1);
-    sprintf(fontname, "%s", fd->fontname);
-    fullname = xcalloc((unsigned) (8 + strlen(fd->fontname)), 1);
-    sprintf(fullname, "%s+%s", fd->subset_tag, fd->fontname);
-    /*tex Finish parsing the CFF. */
-    cff_read_private(cffont);
-    cff_read_subrs(cffont);
-    /*tex The |Width|s. */
-    if (cffont->private[0] && cff_dict_known(cffont->private[0], "defaultWidthX")) {
-        default_width = (double) cff_dict_get(cffont->private[0], "defaultWidthX", 0);
-    } else {
-        default_width = CFF_DEFAULTWIDTHX_DEFAULT;
-    }
-    if (cffont->private[0] && cff_dict_known(cffont->private[0], "nominalWidthX")) {
-        nominal_width = (double) cff_dict_get(cffont->private[0], "nominalWidthX", 0);
-    } else {
-        nominal_width = CFF_NOMINALWIDTHX_DEFAULT;
-    }
-    num_glyphs = 0;
-    last_cid = 0;
-    glyph = xtalloc(1, glw_entry);
-    /*tex insert |notdef| */
-    glyph->id = 0;
-    if (avl_find(fd->gl_tree, glyph) == NULL) {
-        avl_insert(fd->gl_tree, glyph);
-        glyph = xtalloc(1, glw_entry);
-    }
-    avl_t_init(&t, fd->gl_tree);
-    for (found = (glw_entry *) avl_t_first(&t, fd->gl_tree);
-         found != NULL; found = (glw_entry *) avl_t_next(&t)) {
-        if (found->id > last_cid)
-            last_cid = (card16) found->id;
-        num_glyphs++;
-    }
-    {
-        cff_fdselect *fdselect;
-        fdselect = xcalloc(1, sizeof(cff_fdselect));
-        fdselect->format = 3;
-        fdselect->num_entries = 1;
-        fdselect->data.ranges = xcalloc(1, sizeof(cff_range3));
-        fdselect->data.ranges[0].first = 0;
-        fdselect->data.ranges[0].fd = 0;
-        cffont->fdselect = fdselect;
-    }
-    {
-        cff_charsets *charset;
-        charset = xcalloc(1, sizeof(cff_charsets));
-        charset->format = 0;
-        charset->num_entries = (card16) (num_glyphs - 1);
-        charset->data.glyphs = xcalloc(num_glyphs, sizeof(s_SID));
-        gid = 0;
-        avl_t_init(&t, fd->gl_tree);
-        for (found = (glw_entry *) avl_t_first(&t, fd->gl_tree);
-             found != NULL; found = (glw_entry *) avl_t_next(&t)) {
-            if (found->id != 0) {
-                charset->data.glyphs[gid] = (s_SID) found->id;
-                gid++;
-            }
-        }
-        cffont->charsets = charset;
-        if (cffont->header_major == 2) {
-            cff_dict_add(cffont->topdict, "charset", 1);
-        }
-    }
-    cff_dict_add(cffont->topdict, "CIDCount", 1);
-    cff_dict_set(cffont->topdict, "CIDCount", 0, last_cid + 1);
-    if (cffont->header_major == 2) {
-        cff_dict_add(cffont->topdict, "FullName", 1);
-        cff_dict_set(cffont->topdict, "FullName", 0, (double) cff_add_string(cffont, fontname));
-        cff_dict_add(cffont->topdict, "FontBBox", 4);
-        cff_dict_set(cffont->topdict, "FontBBox", 0, fd->font_dim[FONTBBOX1_CODE].val);
-        cff_dict_set(cffont->topdict, "FontBBox", 1, fd->font_dim[FONTBBOX2_CODE].val);
-        cff_dict_set(cffont->topdict, "FontBBox", 2, fd->font_dim[FONTBBOX3_CODE].val);
-        cff_dict_set(cffont->topdict, "FontBBox", 3, fd->font_dim[FONTBBOX4_CODE].val);
-    }
-    cffont->fdarray = xcalloc(1, sizeof(cff_dict *));
-    cffont->fdarray[0] = cff_new_dict();
-    cff_dict_add(cffont->fdarray[0], "FontName", 1);
-    /*tex fix: skip XXXXXX+ */
-    cff_dict_set(cffont->fdarray[0], "FontName", 0, (double) cff_add_string(cffont, fullname));
-    cff_dict_add(cffont->fdarray[0], "Private", 2);
-    cff_dict_set(cffont->fdarray[0], "Private", 0, 0.0);
-    cff_dict_set(cffont->fdarray[0], "Private", 0, 0.0);
-    /*tex |FDArray| index offset, not known yet */
-    cff_dict_add(cffont->topdict, "FDArray", 1);
-    cff_dict_set(cffont->topdict, "FDArray", 0, 0.0);
-    /*tex |FDSelect| offset, not known yet */
-    cff_dict_add(cffont->topdict, "FDSelect", 1);
-    cff_dict_set(cffont->topdict, "FDSelect", 0, 0.0);
-    cff_dict_remove(cffont->topdict, "UniqueID");
-    cff_dict_remove(cffont->topdict, "XUID");
-    cff_dict_remove(cffont->topdict, "Private");
-    cff_dict_remove(cffont->topdict, "Encoding");
-    cffont->offset = (l_offset) cff_dict_get(cffont->topdict, "CharStrings", 0);
-    cs_idx = cff_get_index_header(cffont);
-    offset = (long) cffont->offset;
-    cs_count1 = cs_idx->count;
-    if (cs_count1 < 2) {
-        normal_error("cff","no valid charstring data found");
-    }
-    /*tex Build the new charstrings entry. */
-    charstrings = cff_new_index((card16) (cs_count1==USHRT_MAX?cs_count1: cs_count1 + 1));
-    max_len = 2 * CS_STR_LEN_MAX;
-    charstrings->data = xcalloc((unsigned) max_len, sizeof(card8));
-    charstring_len = 0;
-    gid = 0;
-    data = xcalloc(CS_STR_LEN_MAX, sizeof(card8));
-    {
-        int i;
-        int tex_font = fd->tex_font;
-        int streamprovider = 0;
-        int callback_id = 0 ;
-        if ((tex_font > 0) && (font_streamprovider(tex_font) == 1)) {
-            streamprovider = font_streamprovider(tex_font);
-            callback_id = callback_defined(glyph_stream_provider_callback);
-        }
-        for (i = 0; i < cs_count1; i++) {
-            code = (card16) i;
-            glyph->id = code;
-            if ((avl_find(fd->gl_tree,glyph) != NULL)) {
-                /*tex This code is the same as below, apart from small details */
-                if (callback_id > 0) {
-                    lstring * result;
-                    run_callback(callback_id, "ddd->L", tex_font, i, streamprovider, &result); /* this call can be sped up */
-                    size = (size_t) result->l ;
-                    if (size > 0) {
-                        if (charstring_len + CS_STR_LEN_MAX >= max_len) {
-                            max_len = (long)(charstring_len + 2 * CS_STR_LEN_MAX);
-                            charstrings->data = xrealloc(charstrings->data, (unsigned)((unsigned)max_len*sizeof(card8)));
-                        }
-                        (charstrings->offset)[gid] = (unsigned)(charstring_len + 1);
-                        cffont->offset = (l_offset)((unsigned)offset + (cs_idx->offset)[code] - 1);
-                        memcpy(charstrings->data+charstring_len,(const char *) result->s,(size_t) size);
-                        charstring_len += size;
-                        xfree(result);
-                    }
-                } else {
-                    size = (long)(cs_idx->offset[code+1] - cs_idx->offset[code]);
-                    if (size > CS_STR_LEN_MAX) {
-                        formatted_error("cff","charstring too long: gid=%u, %ld bytes", code, size);
-                    }
-                    if (charstring_len + CS_STR_LEN_MAX >= max_len) {
-                        max_len = (long)(charstring_len + 2 * CS_STR_LEN_MAX);
-                        charstrings->data = xrealloc(charstrings->data, (unsigned)((unsigned)max_len*sizeof(card8)));
-                    }
-                    (charstrings->offset)[gid] = (unsigned)(charstring_len + 1);
-                    cffont->offset = (l_offset)((unsigned)offset + (cs_idx->offset)[code] - 1);
-                    memcpy(data,&cffont->stream[cffont->offset],(size_t)size);
-                    charstring_len += cs_copy_charstring(
-                        charstrings->data + charstring_len,
-                        max_len - charstring_len,
-                        data, size,
-                        cffont->gsubr, (cffont->subrs)[0],
-                        default_width, nominal_width, NULL,
-                        cffont->header_major == 2
-                    );
-                }
-                gid++;
-            }
-        }
-    }
-    /*tex
-        The |CIDSet| is a table of bits indexed by cid, bytes with high order bit
-        first, each (set) bit is a (present) CID.
-    */
-    if (1) {
-        int cid;
-        cidset = pdf_create_obj(pdf, obj_type_others, 0);
-        if (cidset != 0) {
-            size_t l = (last_cid/8)+1;
-            char *stream = xmalloc(l);
-            memset(stream, 0, l);
-            for (cid = 1; cid <= (long) last_cid; cid++) {
-                glyph->id = cid;
-                if (avl_find(fd->gl_tree,glyph) != NULL) {
-                    stream[(cid / 8)] |= (1 << (7 - (cid % 8)));
-                }
-            }
-            pdf_begin_obj(pdf, cidset, OBJSTM_NEVER);
-            pdf_begin_dict(pdf);
-            pdf_dict_add_streaminfo(pdf);
-            pdf_end_dict(pdf);
-            pdf_begin_stream(pdf);
-            pdf_out_block(pdf, stream, l);
-            pdf_end_stream(pdf);
-            pdf_end_obj(pdf);
-        }
-    }
-    /*tex
-        This happens if the internal metrics do not agree with the actual disk
-        font.
-    */
-    if (gid < num_glyphs) {
-        formatted_warning("cff","embedded subset is smaller than expected: %d instead of %d glyphs", gid, num_glyphs);
-        num_glyphs = gid;
-    }
-    xfree(data);
-    cff_release_index(cs_idx);
-    (charstrings->offset)[num_glyphs] = (l_offset) (charstring_len + 1);
-    charstrings->count = num_glyphs;
-    cffont->num_glyphs = num_glyphs;
-    cffont->cstrings = charstrings;
-    /*tex
-        We don't use subroutines at all.
-    */
-    if (cffont->gsubr)
-        cff_release_index(cffont->gsubr);
-    cffont->gsubr = cff_new_index(0);
-    if (cffont->subrs && cffont->subrs[0])
-        cff_release_index(cffont->subrs[0]);
-    cffont->subrs[0] = NULL;
-    if (cffont->private && (cffont->private)[0]) {
-        cff_dict_remove((cffont->private)[0], "Subrs"); /* no Subrs */
-    }
-    cff_dict_update(cffont->topdict, cffont);
-    cff_add_string(cffont, "Adobe");
-    cff_add_string(cffont, "Identity");
-    if (cffont->header_major == 2) {
-        /*tex A crash. */
-    } else {
-        cff_dict_update(cffont->private[0], cffont);
-    }
-    cff_update_string(cffont);
-    /* CFF code need to be rewritten */
-    cff_dict_add(cffont->topdict, "ROS", 3);
-    cff_dict_set(cffont->topdict, "ROS", 0, (double) cff_get_sid(cffont, "Adobe"));
-    cff_dict_set(cffont->topdict, "ROS", 1, (double) cff_get_sid(cffont, "Identity"));
-    cff_dict_set(cffont->topdict, "ROS", 2, 0.0);
-    write_fontfile(pdf, cffont, fullname);
-    xfree(fontname);
-    xfree(fullname);
-    cff_close(cffont);
-}
-
-#define is_cidfont(a) ((a)->flag & FONTTYPE_CIDFONT)
-#define CID_MAX 65535
-
-void write_cid_cff(PDF pdf, cff_font * cffont, fd_entry * fd)
-{
-    cff_index *charstrings, *cs_idx;
-    long charstring_len, max_len;
-    long size, offset = 0;
-    int tex_font = fd->tex_font;
-    int streamprovider = 0;
-    int callback_id = 0 ;
-    card8 *data;
-    card16 num_glyphs, cs_count1, gid, last_cid;
-    int fdsel, prev_fd, cid_count, cid ;
-    char *fullname;
-    glw_entry *glyph;
-    unsigned char *CIDToGIDMap = NULL;
-    cff_fdselect *fdselect = NULL;
-    cff_charsets *charset = NULL;
-    if (!is_cidfont(cffont)) {
-        normal_error("cff","invalid CIDfont");
-        return;
-    }
-    if ((tex_font > 0) && (font_streamprovider(tex_font) == 1)) {
-        streamprovider = font_streamprovider(tex_font);
-        callback_id = callback_defined(glyph_stream_provider_callback);
-    }
-    fullname = xcalloc((unsigned) (8 + strlen(fd->fontname)), 1);
-    sprintf(fullname, "%s+%s", fd->subset_tag, fd->fontname);
-    /*tex Finish parsing the CFF. */
-    if (cff_dict_known(cffont->topdict, "CIDCount")) {
-        cid_count = (card16) cff_dict_get(cffont->topdict, "CIDCount", 0);
-    } else {
-        cid_count = CFF_CIDCOUNT_DEFAULT;
-    }
-    if (cffont->header_major == 2) {
-        /*tex hm */
-    } else {
-        cff_read_charsets(cffont);
-    }
-    CIDToGIDMap = xmalloc((unsigned) ((2 * (unsigned) cid_count) * sizeof(unsigned char)));
-    memset(CIDToGIDMap, 0, (size_t) (2 * cid_count));
-    glyph = xtalloc(1, glw_entry);
-    /*tex insert |notdef| */
-    glyph->id = 0;
-    if (avl_find(fd->gl_tree, glyph) == NULL) {
-        avl_insert(fd->gl_tree, glyph);
-        glyph = xtalloc(1, glw_entry);
-    }
-    last_cid = 0;
-    num_glyphs = 0;
-    for (cid = 0; cid <= CID_MAX; cid++) {
-        glyph->id = (unsigned) cid;
-        if (avl_find(fd->gl_tree, glyph) != NULL) {
-            gid = (card16) cid;
-            CIDToGIDMap[2 * cid] = (unsigned char) ((gid >> 8) & 0xff);
-            CIDToGIDMap[2 * cid + 1] = (unsigned char) (gid & 0xff);
-            last_cid = (card16) cid;
-            num_glyphs++;
-        }
-    }
-    if (cffont->header_major == 2) {
-        /*tex hm */
-    } else if (last_cid >= cffont->num_glyphs) {
-        formatted_error("cff font","bad glyph index %i",last_cid);
-    }
-    /*tex
-        The |CIDSet| table is a table of bits indexed by cid, bytes with high
-        order bit first, each (set) bit is a (present) CID.
-    */
-    if (1) {
-        cidset = pdf_create_obj(pdf, obj_type_others, 0);
-        if (cidset != 0) {
-            size_t l = (last_cid / 8) + 1;
-            char *stream = xmalloc(l);
-            memset(stream, 0, l);
-            for (cid = 1; cid <= (long) last_cid; cid++) {
-                if (CIDToGIDMap[2 * cid] || CIDToGIDMap[2 * cid + 1]) {
-                    stream[(cid / 8)] |= (1 << (7 - (cid % 8)));
-                }
-            }
-            pdf_begin_obj(pdf, cidset, OBJSTM_NEVER);
-            pdf_begin_dict(pdf);
-            pdf_dict_add_streaminfo(pdf);
-            pdf_end_dict(pdf);
-            pdf_begin_stream(pdf);
-            pdf_out_block(pdf, stream, l);
-            pdf_end_stream(pdf);
-            pdf_end_obj(pdf);
-            xfree(stream);
-        }
-    }
-    cff_read_fdselect(cffont);
-    cff_read_fdarray(cffont);
-    cff_read_private(cffont);
-    cff_read_subrs(cffont);
-    cffont->offset = (l_offset) cff_dict_get(cffont->topdict, "CharStrings", 0);
-    cs_idx = cff_get_index_header(cffont);
-    offset = (long) cffont->offset;
-    cs_count1 = cs_idx->count;
-    if (cs_count1 < 2) {
-        normal_error("cff","no valid charstring data found");
-    }
-    charset = xcalloc(1, sizeof(cff_charsets));
-    charset->format = 0;
-    charset->num_entries = 0;
-    charset->data.glyphs = xcalloc(num_glyphs, sizeof(s_SID));
-    fdselect = xcalloc(1, sizeof(cff_fdselect));
-    fdselect->format = 3;
-    fdselect->num_entries = 0;
-    fdselect->data.ranges = xcalloc(num_glyphs, sizeof(cff_range3));
-    charstrings = cff_new_index((card16) (cs_count1==USHRT_MAX?cs_count1: cs_count1 + 1));
-    max_len = 2 * CS_STR_LEN_MAX;
-    charstrings->data = xcalloc((unsigned) max_len, sizeof(card8));
-    charstring_len = 0;
-    prev_fd = -1;
-    gid = 0;
-    data = xcalloc(CS_STR_LEN_MAX, sizeof(card8));
-    for (cid = 0; cid <= last_cid; cid++) {
-        unsigned short gid_org;
-        glyph->id = (unsigned) cid;
-        if (avl_find(fd->gl_tree, glyph) == NULL)
-            continue;
-        gid_org = (short unsigned) ((CIDToGIDMap[2 * cid] << 8) | (CIDToGIDMap[2 * cid + 1]));
-        fdsel = cff_fdselect_lookup(cffont, gid_org);
-        if (callback_id > 0) {
-            /*tex The next blob is not yet tested \unknown\ I need a font. */
-            lstring * result;
-            run_callback(callback_id, "ddd->L", tex_font, gid_org, streamprovider, &result);
-            size = (size_t) result->l ;
-            if (size > 0) {
-                if (charstring_len + CS_STR_LEN_MAX >= max_len) {
-                    max_len = (long)(charstring_len + 2 * CS_STR_LEN_MAX);
-                    charstrings->data = xrealloc(charstrings->data, (unsigned)((unsigned)max_len*sizeof(card8)));
-                }
-                (charstrings->offset)[gid] = (unsigned)(charstring_len + 1);
-                cffont->offset = (l_offset)((unsigned)offset + (cs_idx->offset)[gid_org] - 1);
-                memcpy(charstrings->data+charstring_len,(const char *) result->s,(size_t)size);
-                charstring_len += size;
-                xfree(result);
-            }
-        } else {
-            size = (long) (cs_idx->offset[gid_org + 1] - cs_idx->offset[gid_org]);
-            if (size > CS_STR_LEN_MAX) {
-                formatted_error("cff","charstring too long: gid=%u, %ld bytes", cid, size);
-            }
-            if (charstring_len + CS_STR_LEN_MAX >= max_len) {
-                max_len = charstring_len + 2 * CS_STR_LEN_MAX;
-                charstrings->data = xrealloc(charstrings->data, (unsigned) ((unsigned) max_len * sizeof(card8)));
-            }
-            (charstrings->offset)[gid] = (l_offset) (charstring_len + 1);
-            cffont->offset = (l_offset) ((unsigned) offset + (cs_idx->offset)[gid_org] - 1);
-            memcpy(data, &cffont->stream[cffont->offset], (size_t) size);
-            charstring_len += cs_copy_charstring(
-                charstrings->data + charstring_len,
-                max_len - charstring_len,
-                data, size,
-                cffont->gsubr, (cffont->subrs)[fdsel],
-                0, 0, NULL,
-                cffont->header_major == 2
-            );
-        }
-        if (cid > 0 && gid_org > 0) {
-            charset->data.glyphs[charset->num_entries] = (s_SID) cid;
-            charset->num_entries++;
-        }
-        if (fdsel != prev_fd) {
-            fdselect->data.ranges[fdselect->num_entries].first = gid;
-            fdselect->data.ranges[fdselect->num_entries].fd = (card8) fdsel;
-            fdselect->num_entries++;
-            prev_fd = fdsel;
-        }
-        gid++;
-    }
-    if (gid != num_glyphs)
-        formatted_error("cff","unexpected error: %i != %i", gid, num_glyphs);
-    xfree(data);
-    cff_release_index(cs_idx);
-    xfree(CIDToGIDMap);
-    (charstrings->offset)[num_glyphs] = (l_offset) (charstring_len + 1);
-    charstrings->count = num_glyphs;
-    cffont->num_glyphs = num_glyphs;
-    cffont->cstrings = charstrings;
-    cff_release_charsets(cffont->charsets);
-    cffont->charsets = charset;
-    cff_release_fdselect(cffont->fdselect);
-    cffont->fdselect = fdselect;
-    /*tex
-        We don't use subroutines at all.
-    */
-    if (cffont->gsubr)
-        cff_release_index(cffont->gsubr);
-    cffont->gsubr = cff_new_index(0);
-    for (fdsel = 0; fdsel < cffont->num_fds; fdsel++) {
-        if (cffont->subrs && cffont->subrs[fdsel]) {
-            cff_release_index(cffont->subrs[fdsel]);
-            cffont->subrs[fdsel] = NULL;
-        }
-        if (cffont->private && (cffont->private)[fdsel]) {
-            cff_dict_remove((cffont->private)[fdsel], "Subrs"); /* no Subrs */
-        }
-    }
-    write_fontfile(pdf, cffont, fullname);
-    xfree(fullname);
-    cff_close(cffont);
-}
-
-/*tex
-
-    Here is a sneaky trick: fontforge knows how to convert Type1 to CFF, so I
-    have defined a utility function in luafflib.c that does exactly that. If it
-    works out ok, I will clean up this code.
-
-*/
-
-void writetype1w(PDF pdf, fd_entry * fd)
-{
-    cff_font *cff;
-    int i;
-    FILE *fp;
-    ff_entry *ff;
-    unsigned char *tfm_buffer = NULL;
-    int tfm_size = 0;
-    ff = check_ff_exist(fd->fm->ff_name, 0);
-    fp = fopen(ff->ff_path, "rb");
-    cur_file_name = ff->ff_path;
-    if (!fp) {
-        formatted_error("cff","could not open Type1 font: %s", cur_file_name);
-    }
-    fclose(fp);
-    if (is_subsetted(fd->fm)) {
-        report_start_file(filetype_subset,cur_file_name);
-    } else {
-        report_start_file(filetype_font,cur_file_name);
-    }
-    (void) ff_createcff(ff->ff_path, &tfm_buffer, &tfm_size);
-    if (tfm_size > 0) {
-        cff = read_cff(tfm_buffer, tfm_size, 0);
-        if (cff != NULL) {
-            write_cff(pdf, cff, fd);
-        } else {
-            for (i = 0; i < tfm_size; i++)
-                strbuf_putchar(pdf->fb, tfm_buffer[i]);
-        }
-        fd->ff_found = 1;
-    } else {
-        formatted_error("cff","could not understand Type1 font: %s",cur_file_name);
-    }
-    if (is_subsetted(fd->fm)) {
-        report_stop_file(filetype_subset);
-    } else {
-        report_stop_file(filetype_font);
-    }
-    cur_file_name = NULL;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/writeenc.w
@@ -0,0 +1,162 @@
+% writeenc.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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"
+
+@ All encoding entries go into AVL tree for fast search by name.
+@c
+struct avl_table *fe_tree = NULL;
+
+@ AVL sort |fe_entry| into |fe_tree| by name
+@c
+static int comp_fe_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return strcmp(((const fe_entry *) pa)->name, ((const fe_entry *) pb)->name);
+}
+
+static fe_entry *new_fe_entry(void)
+{
+    fe_entry *fe;
+    fe = xtalloc(1, fe_entry);
+    fe->name = NULL;
+    fe->fe_objnum = 0;
+    fe->glyph_names = NULL;     /* encoding file not yet read in */
+    fe->tx_tree = NULL;
+    return fe;
+}
+
+static fe_entry *lookup_fe_entry(char *s)
+{
+    fe_entry fe;
+    assert(s != NULL);
+    fe.name = s;
+    if (fe_tree == NULL) {
+        fe_tree = avl_create(comp_fe_entry, NULL, &avl_xallocator);
+        assert(fe_tree != NULL);
+    }
+    return (fe_entry *) avl_find(fe_tree, &fe);
+}
+
+static void register_fe_entry(fe_entry * fe)
+{
+    void **aa;
+    if (fe_tree == NULL) {
+        fe_tree = avl_create(comp_fe_entry, NULL, &avl_xallocator);
+        assert(fe_tree != NULL);
+    }
+    assert(fe != NULL);
+    assert(fe->name != NULL);
+    assert(lookup_fe_entry(fe->name) == NULL);  /* encoding not yet registered */
+    aa = avl_probe(fe_tree, fe);
+    assert(aa != NULL);
+}
+
+fe_entry *get_fe_entry(char *s)
+{
+    fe_entry *fe;
+    char **gl;
+    if ((fe = lookup_fe_entry(s)) == NULL && (gl = load_enc_file(s)) != NULL) {
+        fe = new_fe_entry();
+        fe->name = s;
+        fe->glyph_names = gl;
+        register_fe_entry(fe);
+    }
+    return fe;
+}
+
+@ @c
+static void write_enc(PDF pdf, char **glyph_names, struct avl_table *tx_tree,
+                      int fe_objnum)
+{
+    int i_old, *p;
+    struct avl_traverser t;
+    assert(glyph_names != NULL);
+    assert(tx_tree != NULL);
+    assert(fe_objnum != 0);
+    pdf_begin_obj(pdf, fe_objnum, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "Encoding");
+    pdf_add_name(pdf, "Differences");
+    pdf_begin_array(pdf);
+    avl_t_init(&t, tx_tree);
+    for (i_old = -2, p = (int *) avl_t_first(&t, tx_tree); p != NULL;
+         p = (int *) avl_t_next(&t)) {
+        if (*p == i_old + 1)    /* consecutive */
+            pdf_add_name(pdf, glyph_names[*p]);
+        else {
+            pdf_add_int(pdf, *p);
+            pdf_add_name(pdf, glyph_names[*p]);
+        }
+        i_old = *p;
+    }
+    pdf_end_array(pdf);
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+}
+
+static void write_fontencoding(PDF pdf, fe_entry * fe)
+{
+    assert(fe != NULL);
+    write_enc(pdf, fe->glyph_names, fe->tx_tree, fe->fe_objnum);
+}
+
+void write_fontencodings(PDF pdf)
+{
+    fe_entry *fe;
+    struct avl_traverser t;
+    if (fe_tree == NULL)
+        return;
+    avl_t_init(&t, fe_tree);
+    for (fe = (fe_entry *) avl_t_first(&t, fe_tree); fe != NULL;
+         fe = (fe_entry *) avl_t_next(&t))
+        if (fe->fe_objnum != 0)
+            write_fontencoding(pdf, fe);
+}
+
+@ cleaning up...
+@c
+
+static void destroy_fe_entry(void *pa, void *pb)
+{
+    fe_entry *p;
+    int i;
+    (void) pb;
+    p = (fe_entry *) pa;
+    xfree(p->name);
+    if (p->glyph_names != NULL)
+        for (i = 0; i < 256; i++)
+            if (p->glyph_names[i] != notdef)
+                xfree(p->glyph_names[i]);
+    xfree(p->glyph_names);
+    if (p->tx_tree != NULL)
+        avl_destroy(p->tx_tree,NULL);
+    xfree(p);
+}
+
+void enc_free(void)
+{
+    if (fe_tree != NULL)
+        avl_destroy(fe_tree, destroy_fe_entry);
+    fe_tree = NULL;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/writeenc.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    All encoding entries go into AVL tree for fast search by name.
-
-*/
-
-struct avl_table *fe_tree = NULL;
-
-/* The AVL sort |fe_entry| into |fe_tree| by name. */
-
-static int comp_fe_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return strcmp(((const fe_entry *) pa)->name, ((const fe_entry *) pb)->name);
-}
-
-static fe_entry *new_fe_entry(void)
-{
-    fe_entry *fe;
-    fe = xtalloc(1, fe_entry);
-    fe->name = NULL;
-    fe->fe_objnum = 0;
-    /*tex The encoding file is not yet read in. */
-    fe->glyph_names = NULL;
-    fe->tx_tree = NULL;
-    return fe;
-}
-
-static fe_entry *lookup_fe_entry(char *s)
-{
-    fe_entry fe;
-    assert(s != NULL);
-    fe.name = s;
-    if (fe_tree == NULL) {
-        fe_tree = avl_create(comp_fe_entry, NULL, &avl_xallocator);
-        assert(fe_tree != NULL);
-    }
-    return (fe_entry *) avl_find(fe_tree, &fe);
-}
-
-static void register_fe_entry(fe_entry * fe)
-{
-    void **aa;
-    if (fe_tree == NULL) {
-        fe_tree = avl_create(comp_fe_entry, NULL, &avl_xallocator);
-        assert(fe_tree != NULL);
-    }
-    assert(fe != NULL);
-    assert(fe->name != NULL);
-    /*tex The encoding is not yet registered. */
-    assert(lookup_fe_entry(fe->name) == NULL);
-    aa = avl_probe(fe_tree, fe);
-    assert(aa != NULL);
-}
-
-fe_entry *get_fe_entry(char *s)
-{
-    fe_entry *fe;
-    char **gl;
-    if ((fe = lookup_fe_entry(s)) == NULL && (gl = load_enc_file(s)) != NULL) {
-        fe = new_fe_entry();
-        fe->name = s;
-        fe->glyph_names = gl;
-        register_fe_entry(fe);
-    }
-    return fe;
-}
-
-static void write_enc(PDF pdf, char **glyph_names, struct avl_table *tx_tree, int fe_objnum)
-{
-    int i_old, *p;
-    struct avl_traverser t;
-    assert(glyph_names != NULL);
-    assert(tx_tree != NULL);
-    assert(fe_objnum != 0);
-    pdf_begin_obj(pdf, fe_objnum, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "Encoding");
-    pdf_add_name(pdf, "Differences");
-    pdf_begin_array(pdf);
-    avl_t_init(&t, tx_tree);
-    for (i_old = -2, p = (int *) avl_t_first(&t, tx_tree); p != NULL;
-         p = (int *) avl_t_next(&t)) {
-        if (*p == i_old + 1) {
-            pdf_add_name(pdf, glyph_names[*p]);
-        } else {
-            pdf_add_int(pdf, *p);
-            pdf_add_name(pdf, glyph_names[*p]);
-        }
-        i_old = *p;
-    }
-    pdf_end_array(pdf);
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-}
-
-static void write_fontencoding(PDF pdf, fe_entry * fe)
-{
-    assert(fe != NULL);
-    write_enc(pdf, fe->glyph_names, fe->tx_tree, fe->fe_objnum);
-}
-
-void write_fontencodings(PDF pdf)
-{
-    fe_entry *fe;
-    struct avl_traverser t;
-    if (fe_tree == NULL)
-        return;
-    avl_t_init(&t, fe_tree);
-    for (fe = (fe_entry *) avl_t_first(&t, fe_tree); fe != NULL;
-         fe = (fe_entry *) avl_t_next(&t))
-        if (fe->fe_objnum != 0)
-            write_fontencoding(pdf, fe);
-}
-
-/*tex Cleaning up \unknown */
-
-static void destroy_fe_entry(void *pa, void *pb)
-{
-    fe_entry *p;
-    int i;
-    (void) pb;
-    p = (fe_entry *) pa;
-    xfree(p->name);
-    if (p->glyph_names != NULL)
-        for (i = 0; i < 256; i++)
-            if (p->glyph_names[i] != notdef)
-                xfree(p->glyph_names[i]);
-    xfree(p->glyph_names);
-    if (p->tx_tree != NULL)
-        avl_destroy(p->tx_tree,NULL);
-    xfree(p);
-}
-
-void enc_free(void)
-{
-    if (fe_tree != NULL)
-        avl_destroy(fe_tree, destroy_fe_entry);
-    fe_tree = NULL;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/writefont.w
@@ -0,0 +1,1116 @@
+% writefont.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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"
+
+void write_cid_fontdictionary(PDF pdf, fo_entry * fo, internal_font_number f);
+static void create_cid_fontdictionary(PDF pdf, internal_font_number f);
+
+const key_entry font_key[FONT_KEYS_NUM] = {
+    { "Ascent", "Ascender", 1 },
+    { "CapHeight", "CapHeight", 1 },
+    { "Descent", "Descender", 1 },
+    { "ItalicAngle", "ItalicAngle", 1 },
+    { "StemV", "StdVW", 1 },
+    { "XHeight", "XHeight", 1 },
+    { "FontBBox", "FontBBox", 1 },
+    { "", "", 0 },
+    { "", "", 0 },
+    { "", "", 0 },
+    { "FontName", "FontName", 1 }
+};
+
+@
+@c
+struct avl_table *fo_tree = NULL; /* tree of font dictionaries */
+struct avl_table *fd_tree = NULL; /* tree of font descriptor objects */
+
+static int comp_fo_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return strcmp(((const fo_entry *) pa)->fm->tfm_name,
+                  ((const fo_entry *) pb)->fm->tfm_name);
+}
+
+static int comp_fd_entry(const void *pa, const void *pb, void *p)
+{
+    const fd_entry *p1 = (const fd_entry *) pa, *p2 = (const fd_entry *) pb;
+    (void) p;
+    assert(p1->fm != NULL && is_fontfile(p1->fm) &&
+           p2->fm != NULL && is_fontfile(p2->fm));
+    return strcmp(p1->fm->ff_name, p2->fm->ff_name);
+}
+
+@ initialize data structure for /Type /Font
+@c
+static fo_entry *new_fo_entry(void)
+{
+    fo_entry *fo;
+    fo = xtalloc(1, fo_entry);
+    fo->fo_objnum = 0;
+    fo->tex_font = 0;
+    fo->fm = NULL;
+    fo->fd = NULL;
+    fo->fe = NULL;
+    fo->cw_objnum = 0;
+    fo->first_char = 1;
+    fo->last_char = 0;
+    fo->tx_tree = NULL;
+    fo->tounicode_objnum = 0;
+    return fo;
+}
+
+@ initialize data structure for /Type /FontDescriptor
+@c
+fd_entry *new_fd_entry(internal_font_number f)
+{
+    fd_entry *fd;
+    int i;
+    fd = xtalloc(1, fd_entry);
+    fd->fd_objnum = 0;
+    fd->fontname = NULL;
+    fd->subset_tag = NULL;
+    fd->ff_found = false;
+    fd->ff_objnum = 0;
+    fd->all_glyphs = false;
+    fd->write_ttf_glyph_names = false;
+    for (i = 0; i < FONT_KEYS_NUM; i++) {
+        fd->font_dim[i].val = 0;
+        fd->font_dim[i].set = false;
+    }
+    fd->fe = NULL;
+    fd->builtin_glyph_names = NULL;
+    fd->fm = NULL;
+    fd->tx_tree = NULL;
+    fd->gl_tree = NULL;
+    fd->tex_font = f;
+    return fd;
+}
+
+@
+Only fallback values of font metrics are taken from the TFM info
+of |f| by |preset_fontmetrics|. During reading of the font file,
+these values are replaced by metrics from the font, if available.
+
+@c
+static void preset_fontmetrics(fd_entry * fd, internal_font_number f)
+{
+    int i;
+    fd->font_dim[ITALIC_ANGLE_CODE].val = 0;
+    fd->font_dim[ASCENT_CODE].val =
+        divide_scaled(char_height(f, 'h'), font_size(f), 3);
+    fd->font_dim[CAPHEIGHT_CODE].val =
+        divide_scaled(char_height(f, 'H'), font_size(f), 3);
+    i = -divide_scaled(char_depth(f, 'y'), font_size(f), 3);
+    fd->font_dim[DESCENT_CODE].val = i < 0 ? i : 0;
+    fd->font_dim[STEMV_CODE].val =
+        divide_scaled(char_width(f, '.') / 3, font_size(f), 3);
+    fd->font_dim[XHEIGHT_CODE].val =
+        divide_scaled(get_x_height(f), font_size(f), 3);
+    fd->font_dim[FONTBBOX1_CODE].val = 0;
+    fd->font_dim[FONTBBOX2_CODE].val = fd->font_dim[DESCENT_CODE].val;
+    fd->font_dim[FONTBBOX3_CODE].val =
+        divide_scaled(get_quad(f), font_size(f), 3);
+    fd->font_dim[FONTBBOX4_CODE].val =
+        fd->font_dim[CAPHEIGHT_CODE].val > fd->font_dim[ASCENT_CODE].val ?
+        fd->font_dim[CAPHEIGHT_CODE].val : fd->font_dim[ASCENT_CODE].val;
+    for (i = 0; i < INT_KEYS_NUM; i++)
+        fd->font_dim[i].set = true;
+}
+
+static void fix_fontmetrics(fd_entry * fd)
+{
+    int i;
+    intparm *p = (intparm *) fd->font_dim;
+    assert(p[FONTBBOX1_CODE].set && p[FONTBBOX2_CODE].set
+           && p[FONTBBOX3_CODE].set && p[FONTBBOX4_CODE].set);
+    /* make sure there is a rectangle */
+    if (p[FONTBBOX3_CODE].val < p[FONTBBOX1_CODE].val) {
+        i = p[FONTBBOX3_CODE].val;
+        p[FONTBBOX3_CODE].val = p[FONTBBOX1_CODE].val;
+        p[FONTBBOX1_CODE].val = i;
+    } else if (p[FONTBBOX3_CODE].val == p[FONTBBOX1_CODE].val)
+        p[FONTBBOX3_CODE].val = p[FONTBBOX1_CODE].val + 1;
+    if (p[FONTBBOX4_CODE].val < p[FONTBBOX2_CODE].val) {
+        i = p[FONTBBOX4_CODE].val;
+        p[FONTBBOX4_CODE].val = p[FONTBBOX2_CODE].val;
+        p[FONTBBOX2_CODE].val = i;
+    } else if (p[FONTBBOX4_CODE].val == p[FONTBBOX2_CODE].val)
+        p[FONTBBOX4_CODE].val = p[FONTBBOX2_CODE].val + 1;
+    if (!p[ASCENT_CODE].set) {
+        p[ASCENT_CODE].val = p[FONTBBOX4_CODE].val;
+        p[ASCENT_CODE].set = true;
+    }
+    if (!p[DESCENT_CODE].set) {
+        p[DESCENT_CODE].val = p[FONTBBOX2_CODE].val;
+        p[DESCENT_CODE].set = true;
+    }
+    if (!p[CAPHEIGHT_CODE].set) {
+        p[CAPHEIGHT_CODE].val = p[FONTBBOX4_CODE].val;
+        p[CAPHEIGHT_CODE].set = true;
+    }
+}
+
+static void write_fontmetrics(PDF pdf, fd_entry * fd)
+{
+    int i;
+    fix_fontmetrics(fd);
+    pdf_add_name(pdf, font_key[FONTBBOX1_CODE].pdfname);
+    pdf_begin_array(pdf);
+    pdf_printf(pdf, "%i %i %i %i", (int) fd->font_dim[FONTBBOX1_CODE].val,
+               (int) fd->font_dim[FONTBBOX2_CODE].val,
+               (int) fd->font_dim[FONTBBOX3_CODE].val,
+               (int) fd->font_dim[FONTBBOX4_CODE].val);
+    pdf_end_array(pdf);
+    for (i = 0; i < GEN_KEY_NUM; i++)
+        if (fd->font_dim[i].set)
+            pdf_dict_add_int(pdf, font_key[i].pdfname, fd->font_dim[i].val);
+}
+
+@
+@c
+static void preset_fontname(fo_entry * fo, internal_font_number f)
+{
+    if (fo->fm->ps_name != NULL)
+        fo->fd->fontname = xstrdup(fo->fm->ps_name);    /* just fallback */
+    else if (font_fullname(f) != NULL)
+        fo->fd->fontname = xstrdup(font_fullname(f));
+    else
+        fo->fd->fontname = xstrdup(fo->fm->tfm_name);
+}
+
+static void pdf_dict_add_fontname(PDF pdf, const char *key, fd_entry * fd)
+{
+    char *s;
+    size_t l1 = 0, l2;
+    assert(fd->fontname != NULL);
+    assert(key != NULL);
+    if (fd->subset_tag != NULL)
+        l1 = strlen(fd->subset_tag);
+    l2 = strlen(fd->fontname);
+    s = xmalloc(l1 + l2 + 2);
+    if (l1 > 0)
+        snprintf(s, l1 + l2 + 2, "%s+%s", fd->subset_tag, fd->fontname);
+    else
+        snprintf(s, l2 + 1, "%s", fd->fontname);
+    pdf_dict_add_name(pdf, key, s);
+    xfree(s);
+}
+
+@
+@c
+fd_entry *lookup_fd_entry(char *s)
+{
+    fd_entry fd;
+    fm_entry fm;
+    assert(s != NULL);
+    fm.ff_name = s;
+    fd.fm = &fm;
+    if (fd_tree == NULL) {
+        fd_tree = avl_create(comp_fd_entry, NULL, &avl_xallocator);
+        assert(fd_tree != NULL);
+    }
+    return (fd_entry *) avl_find(fd_tree, &fd);
+}
+
+static fd_entry *lookup_fontdescriptor(fo_entry * fo)
+{
+    assert(fo != NULL);
+    assert(fo->fm != NULL);
+    assert(is_fontfile(fo->fm));
+    return lookup_fd_entry(fo->fm->ff_name);
+}
+
+void register_fd_entry(fd_entry * fd)
+{
+    void **aa;
+    if (fd_tree == NULL) {
+        fd_tree = avl_create(comp_fd_entry, NULL, &avl_xallocator);
+        assert(fd_tree != NULL);
+    }
+    assert(fd != NULL && fd->fm != NULL && is_fontfile(fd->fm));
+    /* font descriptor not yet registered: */
+    assert(lookup_fd_entry(fd->fm->ff_name) == NULL);
+    aa = avl_probe(fd_tree, fd);
+    assert(aa != NULL);
+}
+
+static void create_fontdescriptor(fo_entry * fo, internal_font_number f)
+{
+    assert(fo != NULL);
+    assert(fo->fm != NULL);
+    assert(fo->fd == NULL);
+    fo->fd = new_fd_entry(f);
+    preset_fontname(fo, f);
+    preset_fontmetrics(fo->fd, f);
+    /* encoding needed by TrueType writing: */
+    fo->fd->fe = fo->fe;
+    /* map entry needed by TrueType writing: */
+    fo->fd->fm = fo->fm;
+    fo->fd->gl_tree = avl_create(comp_string_entry, NULL, &avl_xallocator);
+    assert(fo->fd->gl_tree != NULL);
+}
+
+@
+For all used characters of \TeX font |f|, get corresponding glyph names
+from external reencoding (.enc) file and collect these in the glyph
+tree |gl_tree| of font descriptor |fd| referenced by font dictionary |fo|.
+
+@c
+static void mark_reenc_glyphs(fo_entry * fo, internal_font_number f)
+{
+    int i;
+    char **g;
+    void **aa;
+    assert(fo->fe != NULL);
+    if (is_subsetted(fo->fm)) {
+        assert(is_included(fo->fm));
+        /* mark glyphs from TeX (externally reencoded characters) */
+        g = fo->fe->glyph_names;
+        for (i = fo->first_char; i <= fo->last_char; i++) {
+            if (pdf_char_marked(f, i)
+                && g[i] != notdef
+                && (char *) avl_find(fo->fd->gl_tree, g[i]) == NULL) {
+                aa = avl_probe(fo->fd->gl_tree, xstrdup(g[i]));
+                assert(aa != NULL);
+            }
+        }
+    }
+}
+
+@
+Function |mark_chars| has 2 uses:
+\item 1. Mark characters as chars on \TeX\ level.
+\item 2. Mark encoding pairs used by \TeX\ to optimize encoding vector.
+
+@c
+static struct avl_table *mark_chars(fo_entry * fo, struct avl_table *tx_tree,
+     internal_font_number f)
+{
+    int i, *j;
+    void **aa;
+    if (tx_tree == NULL) {
+        tx_tree = avl_create(comp_int_entry, NULL, &avl_xallocator);
+        assert(tx_tree != NULL);
+    }
+    for (i = fo->first_char; i <= fo->last_char; i++) {
+        if (pdf_char_marked(f, i) && (int *) avl_find(tx_tree, &i) == NULL) {
+            j = xtalloc(1, int);
+            *j = i;
+            aa = avl_probe(tx_tree, j);
+            assert(aa != NULL);
+        }
+    }
+    return tx_tree;
+}
+
+@
+@c
+static void get_char_range(fo_entry * fo, internal_font_number f)
+{
+    int i;
+    assert(fo != NULL);
+    /* search for |first_char| and |last_char| */
+    for (i = font_bc(f); i <= font_ec(f); i++)
+        if (pdf_char_marked(f, i))
+            break;
+    fo->first_char = i;
+    for (i = font_ec(f); i >= font_bc(f); i--)
+        if (pdf_char_marked(f, i))
+            break;
+    fo->last_char = i;
+    if ((fo->first_char > fo->last_char) || !pdf_char_marked(f, fo->first_char)) {
+        /* no character used from this font */
+        fo->last_char = 0;
+        fo->first_char = fo->last_char + 1;
+    }
+}
+
+static int font_has_subset(internal_font_number f)
+{
+    int i, s;
+    /* search for |first_char| and |last_char| */
+    for (i = font_bc(f); i <= font_ec(f); i++)
+        if (pdf_char_marked(f, i))
+            break;
+    s = i;
+    for (i = font_ec(f); i >= font_bc(f); i--)
+        if (pdf_char_marked(f, i))
+            break;
+    if (s > i)
+        return 0;
+    else
+        return 1;
+}
+
+@
+@c
+static void write_charwidth_array(PDF pdf, fo_entry * fo, internal_font_number f)
+{
+    int i, j, *ip, *fip;
+    struct avl_traverser t;
+    assert(fo->tx_tree != NULL);
+    assert(fo->cw_objnum == 0);
+    fo->cw_objnum = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_begin_obj(pdf, fo->cw_objnum, OBJSTM_ALWAYS);
+    avl_t_init(&t, fo->tx_tree);
+    fip = (int *) avl_t_first(&t, fo->tx_tree);
+    assert(fip != NULL);
+    pdf_begin_array(pdf);
+    for (ip = fip, j = *ip; ip != NULL; ip = (int *) avl_t_next(&t)) {
+        if (ip != fip)
+            pdf_out(pdf, ' ');
+        i = *ip;
+        while (j < i - 1) {
+            pdf_puts(pdf, "0 ");
+            j++;
+        }
+        j = i;
+        pdf_print_charwidth(pdf, f, i);
+    }
+    pdf_end_array(pdf);
+    pdf_end_obj(pdf);
+}
+
+@ Remark: Font objects from embedded PDF files are never registered
+into |fo_tree|; they are individually written out.
+@c
+static fo_entry *lookup_fo_entry(char *s)
+{
+    fo_entry fo;
+    fm_entry fm;
+    assert(s != NULL);
+    fm.tfm_name = s;
+    fo.fm = &fm;
+    if (fo_tree == NULL) {
+        fo_tree = avl_create(comp_fo_entry, NULL, &avl_xallocator);
+        assert(fo_tree != NULL);
+    }
+    return (fo_entry *) avl_find(fo_tree, &fo);
+}
+
+static void register_fo_entry(fo_entry * fo)
+{
+    void **aa;
+    if (fo_tree == NULL) {
+        fo_tree = avl_create(comp_fo_entry, NULL, &avl_xallocator);
+        assert(fo_tree != NULL);
+    }
+    assert(fo != NULL);
+    assert(fo->fm != NULL);
+    assert(fo->fm->tfm_name != NULL);
+    assert(lookup_fo_entry(fo->fm->tfm_name) == NULL);
+    aa = avl_probe(fo_tree, fo);
+    assert(aa != NULL);
+}
+
+@
+In principle we could replace the pdftex derived ttf.otf inclusion part
+by using the regular code for this and assigning indices and tounicodes
+to the character blobs, but for the moment we keep the current approach.
+@c
+static void write_fontfile(PDF pdf, fd_entry * fd)
+{
+    assert(is_included(fd->fm));
+    if (is_cidkeyed(fd->fm)) {
+        if (is_opentype(fd->fm)) {
+            writetype0(pdf, fd);
+	} else if (is_truetype(fd->fm)) {
+            if (!writetype2(pdf, fd)) {
+                writetype0(pdf,fd);
+	        fd->fm->type |= F_OTF; fd->fm->type ^= F_TRUETYPE;
+            }
+	} else if (is_type1(fd->fm))
+	    writetype1w(pdf, fd);
+	else
+	    assert(0);
+    } else {
+	if (is_type1(fd->fm))
+	    writet1(pdf, fd);
+        else if (is_truetype(fd->fm))
+            writettf(pdf, fd);
+        else if (is_opentype(fd->fm))
+            writeotf(pdf, fd);
+        else
+            assert(0);
+    }
+    if (!fd->ff_found)
+        return;
+    assert(fd->ff_objnum == 0);
+    fd->ff_objnum = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_begin_obj(pdf, fd->ff_objnum, OBJSTM_NEVER); /* font file stream */
+    pdf_begin_dict(pdf);
+    if (is_cidkeyed(fd->fm)) {
+        /* No subtype is used for TrueType-based OpenType fonts */
+        if (is_opentype(fd->fm) || is_type1(fd->fm))
+            pdf_dict_add_name(pdf, "Subtype", "CIDFontType0C");
+#if 0
+        else
+            pdf_dict_add_name(pdf, "Subtype", "OpenType");
+#endif
+    } else if (is_type1(fd->fm)) {
+        pdf_dict_add_int(pdf, "Length1", (int) t1_length1);
+        pdf_dict_add_int(pdf, "Length2", (int) t1_length2);
+        pdf_dict_add_int(pdf, "Length3", (int) t1_length3);
+    } else if (is_truetype(fd->fm)) {
+        pdf_dict_add_int(pdf, "Length1", (int) ttf_length);
+    } else if (is_opentype(fd->fm)) {
+        pdf_dict_add_name(pdf, "Subtype", "Type1C");
+    } else {
+        assert(0); /* todo: error messages */
+    }
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    strbuf_flush(pdf, pdf->fb);
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+}
+
+@
+@c
+int cidset = 0;
+static void write_fontdescriptor(PDF pdf, fd_entry * fd)
+{
+    static const int std_flags[] = {
+        /*
+            The indices for << start with 0, but bits start with 1, so the
+            numbers for << are 1 lower than the bits in table 5.20.
+        */
+        /* *INDENT-OFF* */
+        1 + 2 + (1 << 5),                        /* Courier */
+        1 + 2 + (1 << 5)            + (1 << 18), /* Courier-Bold */
+        1 + 2 + (1 << 5) + (1 << 6),             /* Courier-Oblique */
+        1 + 2 + (1 << 5) + (1 << 6) + (1 << 18), /* Courier-BoldOblique */
+                (1 << 5),                        /* Helvetica */
+                (1 << 5)            + (1 << 18), /* Helvetica-Bold */
+                (1 << 5) + (1 << 6),             /* Helvetica-Oblique */
+                (1 << 5) + (1 << 6) + (1 << 18), /* Helvetica-BoldOblique */
+              4,                                 /* Symbol */
+            2 + (1 << 5),                        /* Times-Roman */
+            2 + (1 << 5)            + (1 << 18), /* Times-Bold */
+            2 + (1 << 5) + (1 << 6),             /* Times-Italic */
+            2 + (1 << 5) + (1 << 6) + (1 << 18), /* Times-BoldItalic */
+              4                                  /* ZapfDingbats */
+        /* *INDENT-ON* */
+    };
+    char *glyph;
+    struct avl_traverser t;
+    int fd_flags;
+    assert(fd != NULL && fd->fm != NULL);
+    cidset = 0; /* possibly updated by |write_fontfile| */
+    if (is_fontfile(fd->fm) && is_included(fd->fm)) {
+        /* this will set |fd->ff_found| if font file is found */
+        write_fontfile(pdf, fd);
+    }
+    if (fd->fd_objnum == 0)
+        fd->fd_objnum = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_begin_obj(pdf, fd->fd_objnum, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "FontDescriptor");
+    pdf_dict_add_fontname(pdf, "FontName", fd);
+    if (fd->fm->fd_flags != FD_FLAGS_NOT_SET_IN_MAPLINE)
+        fd_flags = (int) fd->fm->fd_flags;
+    else if (fd->ff_found)
+        fd_flags = FD_FLAGS_DEFAULT_EMBED;
+    else {
+        fd_flags = is_std_t1font(fd->fm)
+            ? std_flags[check_std_t1font(fd->fm->ps_name)]
+            : FD_FLAGS_DEFAULT_NON_EMBED;
+        formatted_warning("map file",
+             "No flags specified for non-embedded font '%s' (%s), I'm using %i, fix your map entry",
+             fd->fm->ps_name != NULL ? fd->fm->ps_name : "No name given",
+             fd->fm->tfm_name, fd_flags);
+    }
+    pdf_dict_add_int(pdf, "Flags", fd_flags);
+    write_fontmetrics(pdf, fd);
+    if (fd->ff_found) {
+        if (is_cidkeyed(fd->fm)) {
+            if (is_type1(fd->fm))
+                pdf_dict_add_ref(pdf, "FontFile3", (int) fd->ff_objnum);
+            else if (is_truetype(fd->fm))
+                pdf_dict_add_ref(pdf, "FontFile2", (int) fd->ff_objnum);
+            else if (is_opentype(fd->fm))
+                pdf_dict_add_ref(pdf, "FontFile3", (int) fd->ff_objnum);
+            else
+                assert(0);
+        } else {
+            if (is_subsetted(fd->fm) && is_type1(fd->fm)) {
+                /* /CharSet is optional; names may appear in any order */
+                assert(fd->gl_tree != NULL);
+                avl_t_init(&t, fd->gl_tree);
+                pdf_add_name(pdf, "CharSet");
+                pdf_out(pdf, '(');
+                for (glyph = (char *) avl_t_first(&t, fd->gl_tree);
+                     glyph != NULL; glyph = (char *) avl_t_next(&t))
+                    pdf_add_name(pdf, glyph);
+                pdf_out(pdf, ')');
+                pdf->cave = 0;
+            }
+            if (is_type1(fd->fm))
+                pdf_dict_add_ref(pdf, "FontFile", (int) fd->ff_objnum);
+            else if (is_truetype(fd->fm))
+                pdf_dict_add_ref(pdf, "FontFile2", (int) fd->ff_objnum);
+            else if (is_opentype(fd->fm))
+                pdf_dict_add_ref(pdf, "FontFile3", (int) fd->ff_objnum);
+            else
+                assert(0);
+        }
+    }
+    if ((! pdf->omit_cidset) && (pdf->major_version == 1) && (cidset != 0) ) {
+        pdf_dict_add_ref(pdf, "CIDSet", cidset);
+    }
+    /*
+        Currently we don't export the optional keys for CID fonts like
+        \.{/Style << /Panose <12-byte string> >>} and we probably never
+        will.
+    */
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+}
+
+static void write_fontdescriptors(PDF pdf)
+{
+    fd_entry *fd;
+    struct avl_traverser t;
+    if (fd_tree == NULL)
+        return;
+    avl_t_init(&t, fd_tree);
+    for (fd = (fd_entry *) avl_t_first(&t, fd_tree); fd != NULL; fd = (fd_entry *) avl_t_next(&t))
+        write_fontdescriptor(pdf, fd);
+}
+
+@
+@c
+static void write_fontdictionary(PDF pdf, fo_entry * fo)
+{
+    assert(fo != NULL);
+    assert(fo->fm != NULL);
+    /* reserved as |pdf_font_num(f)| elsewhere: */
+    assert(fo->fo_objnum != 0);
+
+    /* write ToUnicode entry if needed */
+    if (pdf->gen_tounicode > 0 && fo->fd != NULL) {
+        if (fo->fe != NULL) {
+            fo->tounicode_objnum = write_tounicode(pdf, fo->fe->glyph_names, fo->fe->name);
+        } else if (is_type1(fo->fm)) {
+            assert(fo->fd->builtin_glyph_names != NULL);
+            fo->tounicode_objnum = write_tounicode(pdf, fo->fd->builtin_glyph_names, fo->fm->tfm_name);
+        }
+    }
+    pdf_begin_obj(pdf, fo->fo_objnum, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "Font");
+    if (is_type1(fo->fm))
+        pdf_dict_add_name(pdf, "Subtype", "Type1");
+    else if (is_truetype(fo->fm))
+        pdf_dict_add_name(pdf, "Subtype", "TrueType");
+    else if (is_opentype(fo->fm))
+        pdf_dict_add_name(pdf, "Subtype", "Type1");
+    else
+        assert(0);
+    assert(fo->fd != NULL && fo->fd->fd_objnum != 0);
+    pdf_dict_add_fontname(pdf, "BaseFont", fo->fd);
+    pdf_dict_add_ref(pdf, "FontDescriptor", (int) fo->fd->fd_objnum);
+    assert(fo->cw_objnum != 0);
+    pdf_dict_add_int(pdf, "FirstChar", (int) fo->first_char);
+    pdf_dict_add_int(pdf, "LastChar", (int) fo->last_char);
+    pdf_dict_add_ref(pdf, "Widths", (int) fo->cw_objnum);
+    if ((is_type1(fo->fm) || is_opentype(fo->fm)) && fo->fe != NULL && fo->fe->fe_objnum != 0)
+        pdf_dict_add_ref(pdf, "Encoding", (int) fo->fe->fe_objnum);
+    if (fo->tounicode_objnum != 0)
+        pdf_dict_add_ref(pdf, "ToUnicode", (int) fo->tounicode_objnum);
+    if (pdf_font_attr(fo->tex_font) != get_nullstr() && pdf_font_attr(fo->tex_font) != 0) {
+        pdf_print(pdf, pdf_font_attr(fo->tex_font));
+        pdf_out(pdf, '\n');
+    }
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+}
+
+static void write_fontdictionaries(PDF pdf)
+{
+    fo_entry *fo;
+    struct avl_traverser t;
+    if (fo_tree == NULL)
+        return;
+    avl_t_init(&t, fo_tree);
+    for (fo = (fo_entry *) avl_t_first(&t, fo_tree); fo != NULL; fo = (fo_entry *) avl_t_next(&t))
+        write_fontdictionary(pdf, fo);
+}
+
+@ Final flush of all font related stuff by call from \.{Output fonts
+definitions} elsewhere
+@c
+void write_fontstuff(PDF pdf)
+{
+    write_fontdescriptors(pdf);
+    write_fontencodings(pdf);   /* see \.{writeenc.w} */
+    write_fontdictionaries(pdf);
+}
+
+@
+@c
+static void create_fontdictionary(PDF pdf, internal_font_number f)
+{
+    fo_entry *fo = new_fo_entry();
+    fm_entry *fm = font_map(f);
+    /* set |fo->first_char| and |fo->last_char| from |f| */
+    get_char_range(fo, f);
+    if (fo->last_char > 255)
+        fo->last_char = 255;
+    assert(fo->last_char >= fo->first_char);
+    fo->fm = fm;
+    fo->fo_objnum = pdf_font_num(f);
+    fo->tex_font = f;
+    if (is_reencoded(fo->fm)) {
+        /*
+            At least the map entry tells so but it returns |NULL| if the .enc
+            file couldn't be opened.
+        */
+        fo->fe = get_fe_entry(fo->fm->encname);
+        if (fo->fe != NULL && (is_type1(fo->fm) || is_opentype(fo->fm))) {
+            /* We don't end up here for truetype fonts. */
+            if (fo->fe->fe_objnum == 0)
+                fo->fe->fe_objnum = pdf_create_obj(pdf, obj_type_others, 0);    /* then it will be written out */
+            /* Mark encoding pairs used by TeX to optimize encoding vector. */
+            fo->fe->tx_tree = mark_chars(fo, fo->fe->tx_tree, f);
+        }
+    }
+    /* for |write_charwidth_array|: */
+    fo->tx_tree = mark_chars(fo, fo->tx_tree, f);
+    write_charwidth_array(pdf, fo, f);
+    if (!is_builtin(fo->fm)) {
+        if (is_type1(fo->fm)) {
+            if ((fo->fd = lookup_fontdescriptor(fo)) == NULL) {
+                create_fontdescriptor(fo, f);
+                register_fd_entry(fo->fd);
+            }
+        } else {
+            create_fontdescriptor(fo, f);
+        }
+        if (fo->fe != NULL) {
+            mark_reenc_glyphs(fo, f);
+            if (!is_type1(fo->fm)) {
+                /* mark reencoded characters as chars on TeX level */
+                assert(fo->fd->tx_tree == NULL);
+                fo->fd->tx_tree = mark_chars(fo, fo->fd->tx_tree, f);
+                if (is_truetype(fo->fm)) {
+                    fo->fd->write_ttf_glyph_names = true;
+                }
+            }
+        } else {
+            /* mark non-reencoded characters as chars on TeX level */
+            fo->fd->tx_tree = mark_chars(fo, fo->fd->tx_tree, f);
+        }
+        if (!is_type1(fo->fm)) {
+            write_fontdescriptor(pdf, fo->fd);
+        }
+    } else {
+        /*
+            Builtin fonts still need the /Widths array and /FontDescriptor
+            (to avoid error 'font FOO contains bad /BBox').
+        */
+        create_fontdescriptor(fo, f);
+        write_fontdescriptor(pdf, fo->fd);
+        if (!is_std_t1font(fo->fm)) {
+            formatted_warning("map file", "font '%s' is not a standard font; I suppose it is available to your PDF viewer then", fo->fm->ps_name);
+        }
+    }
+    if (is_type1(fo->fm)) {
+        register_fo_entry(fo);
+    } else {
+        write_fontdictionary(pdf, fo);
+    }
+}
+
+@
+@c
+static int has_ttf_outlines(fm_entry * fm)
+{
+    FILE *f = fopen(fm->ff_name, "rb");
+    if (f != NULL) {
+        int ch1 = getc(f);
+        int ch2 = getc(f);
+        int ch3 = getc(f);
+        int ch4 = getc(f);
+        fclose(f);
+        if (ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O')
+            return 0;
+        return 1;
+    }
+    return 0;
+}
+
+void do_pdf_font(PDF pdf, internal_font_number f)
+{
+    int del_file = 0;
+    fm_entry *fm;
+    /*
+        This is not 100\% true: CID is actually needed whenever (and
+        only) there are more than 256 separate glyphs used. But for
+        now, we just assume the user knows what he is doing. In practice
+        this seems to be the case.
+    */
+    if (!font_has_subset(f))
+        return;
+
+    if (font_encodingbytes(f) == 2) {
+        /*
+            Create a virtual font map entry, as this is needed by the
+            rest of the font inclusion mechanism.
+        */
+        fm = font_map(f) = new_fm_entry();
+        fm->tfm_name = font_name(f);    /* or whatever, not a real tfm */
+        fm->ff_name = font_filename(f); /* the actual file */
+        if (font_psname(f) != NULL)
+            fm->ps_name = font_psname(f);   /* the true name */
+        else
+            fm->ps_name = font_fullname(f); /* the true name */
+        if (fm->ff_name
+            && strlen(fm->ff_name) >= 6
+            && strstr(fm->ff_name,".dfont") == (fm->ff_name + strlen(fm->ff_name) - 6)) {
+            /*
+                In case of a .dfont, we will extract the correct ttf here,
+                and adjust |fm->ff_name| to point to the temporary file.
+                This file will be deleted later. Todo: keep a nicer name
+                somewhere for the terminal message.
+
+                Support for dfonts will be removed at some point anyhow.
+             */
+            char *s = FindResourceTtfFont(fm->ff_name, fm->ps_name);
+            if (s != NULL) {
+                fm->ff_name = s;
+                del_file = 1;
+            } else {
+                formatted_error("font","file '%s' does not contain font '%s'",fm->ff_name, fm->ps_name);
+            }
+        }
+        /* Needed for the CIDSystemInfo: */
+        fm->encname = font_encodingname(f);
+        fm->slant = font_slant(f);
+        set_slantset(fm);
+        fm->extend = font_extend(f);
+        set_extendset(fm);
+        /* Flags can perhaps be done better. */
+        fm->fd_flags = 4;
+        set_inuse(fm);
+
+        switch (font_format(f)) {
+        case opentype_format:
+            if (has_ttf_outlines(fm)) {
+                set_truetype(fm);
+            } else {
+                set_opentype(fm);
+            }
+            break;
+        case truetype_format:
+            set_truetype(fm);
+            break;
+        case type1_format:
+            set_type1(fm);
+            break;
+        default:
+            formatted_error("font","file format '%s' for '%s' is incompatible with wide characters",
+                font_format_name(f), font_name(f));
+        }
+        /* This makes "unknown" default to subsetted inclusion */
+        if (font_embedding(f) != no_embedding) {
+            set_included(fm);
+            if (font_embedding(f) != full_embedding) {
+                set_subsetted(fm);
+            }
+        }
+        set_cidkeyed(fm);
+        create_cid_fontdictionary(pdf, f);
+
+        if (del_file)
+            unlink(fm->ff_name);
+
+    } else {
+        /*
+            By now |font_map(f)|, if any, should have been set via
+            |pdf_init_font()|.
+        */
+        if ((fm = font_map(f)) == NULL
+            || (fm->ps_name == NULL && fm->ff_name == NULL))
+            writet3(pdf, f);
+        else
+            create_fontdictionary(pdf, f);
+    }
+}
+
+@ The glyph width is included in |glw_entry|, because that width
+depends on the value it has in the font where it is actually
+typeset from, not the font that is the 'owner' of the fd entry.
+
+TODO: It is possible that the user messes with the metric width,
+but handling that properly would require access to the 'hmtx' table
+at this point in the program.
+
+@c
+static int comp_glw_entry(const void *pa, const void *pb, void *p
+                   __attribute__ ((unused)))
+{
+    unsigned short i, j;
+
+    i = (unsigned short) (*(const glw_entry *) pa).id;
+    j = (unsigned short) (*(const glw_entry *) pb).id;
+    cmp_return(i, j);
+    return 0;
+}
+
+static void create_cid_fontdescriptor(fo_entry * fo, internal_font_number f)
+{
+    assert(fo != NULL);
+    assert(fo->fm != NULL);
+    assert(fo->fd == NULL);
+    fo->fd = new_fd_entry(f);
+    preset_fontname(fo, f);
+    preset_fontmetrics(fo->fd, f);
+    fo->fd->fe = fo->fe; /* encoding needed by TrueType writing */
+    fo->fd->fm = fo->fm; /* map entry needed by TrueType writing */
+    fo->fd->gl_tree = avl_create(comp_glw_entry, NULL, &avl_xallocator);
+    assert(fo->fd->gl_tree != NULL);
+}
+
+
+@ The values |font_bc()| and |font_ec()| are potentially large character
+ids, but the strings that are written out use CID indexes, and those are
+limited to 16-bit values.
+
+@c
+/*
+    This is old code ... it fails when the order of using the same font at
+    different extends changes. Probably because widths get overwritten or
+    set wrong. The loop also looks kind of weird (why a loop).
+*/
+
+/*
+static void mark_cid_subset_glyphs(fo_entry * fo, internal_font_number f)
+{
+    int i, k, l;
+    glw_entry *j;
+    void *aa;
+    for (k = 1; k <= max_font_id(); k++) {
+        if (k == f || -f == pdf_font_num(k)) {
+            l = font_size(k);
+            for (i = font_bc(k); i <= font_ec(k); i++) {
+                if (quick_char_exists(k, i) && char_used(k, i)) {
+                    j = xtalloc(1, glw_entry);
+                    j->id = (unsigned) char_index(k, i);
+                    j->wd = divide_scaled_n(char_width(k, i), l, 10000.0);
+                    if ((glw_entry *) avl_find(fo->fd->gl_tree, j) == NULL) {
+                        aa = avl_probe(fo->fd->gl_tree, j);
+                        assert(aa != NULL);
+                    } else {
+                        xfree(j);
+                    }
+                }
+            }
+        }
+    }
+}
+*/
+
+/*
+    So, let's try the following.
+*/
+
+static void mark_cid_subset_glyphs(fo_entry * fo, internal_font_number f)
+{
+    glw_entry *j;
+    void *aa;
+    int l = font_size(f);
+    int i;
+    for (i = font_bc(f); i <= font_ec(f); i++) {
+        if (quick_char_exists(f, i) && char_used(f, i)) {
+            j = xtalloc(1, glw_entry);
+            j->id = (unsigned) char_index(f, i);
+            j->wd = divide_scaled_n(char_width(f, i), l, 10000.0);
+            if ((glw_entry *) avl_find(fo->fd->gl_tree, j) == NULL) {
+                aa = avl_probe(fo->fd->gl_tree, j);
+                assert(aa != NULL);
+            } else {
+                xfree(j);
+            }
+        }
+    }
+}
+
+@ It is possible to compress the widths array even better, by using the
+alternate 'range' syntax and possibly even using /DW to set a default
+value.
+
+There is a some optimization here already: glyphs that are not used do
+not appear in the widths array at all.
+
+We have to make sure that we do not output an (incorrect!) width for a
+character that exists in the font, but is not used in typesetting. An
+enormous negative width is used as sentinel value
+
+@c
+static void write_cid_charwidth_array(PDF pdf, fo_entry * fo)
+{
+    int i, j;
+    glw_entry *glyph;
+    struct avl_traverser t;
+
+    assert(fo->cw_objnum == 0);
+    fo->cw_objnum = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_begin_obj(pdf, fo->cw_objnum, OBJSTM_ALWAYS);
+    avl_t_init(&t, fo->fd->gl_tree);
+    glyph = (glw_entry *) avl_t_first(&t, fo->fd->gl_tree);
+    assert(glyph != NULL);
+    i = (int) glyph->id;
+    pdf_begin_array(pdf);
+    pdf_add_int(pdf, i);
+    pdf_begin_array(pdf);
+    for (; glyph != NULL; glyph = (glw_entry *) avl_t_next(&t)) {
+        j = glyph->wd;
+        if (glyph->id > (unsigned) (i + 1)) {
+            pdf_end_array(pdf);
+            pdf_add_int(pdf, glyph->id);
+            pdf_begin_array(pdf);
+            j = glyph->wd;
+        }
+        if (glyph->id == (unsigned) (i + 1))
+            pdf_out(pdf, ' ');
+
+        if (j < 0) {
+            pdf_out(pdf, '-');
+            j = -j;
+        }
+
+        pdf_printf(pdf, "%i", (j / 10));
+        if ((j % 10) != 0)
+            pdf_printf(pdf, ".%i", (j % 10));
+
+        i = (int) glyph->id;
+    }
+    pdf_end_array(pdf);
+    pdf_end_array(pdf);
+    pdf_end_obj(pdf);
+}
+
+static void destroy_glw_cid_entry(void *pa, void *pb)
+{
+    glw_entry *e = (glw_entry *) pa;
+    (void) pb;
+    xfree(e);
+}
+
+
+static void create_cid_fontdictionary(PDF pdf, internal_font_number f)
+{
+    fm_entry *fm = font_map(f);
+    fo_entry *fo = new_fo_entry();
+    get_char_range(fo, f);      /* set |fo->first_char| and |fo->last_char| from |f| */
+    assert(fo->last_char >= fo->first_char);
+    fo->fm = fm;
+    fo->fo_objnum = pdf_font_num(f);
+    fo->tex_font = f;
+    create_cid_fontdescriptor(fo, f);
+    mark_cid_subset_glyphs(fo, f);
+    if (is_subsetted(fo->fm)) {
+        /*
+           This is a bit sneaky. |make_subset_tag()| actually expects the glyph
+           tree to contain strings instead of |glw_entry| items. However, all
+           calculations are done using explicit typecasts, so it works out ok.
+        */
+        make_subset_tag(fo->fd);
+    }
+    write_cid_charwidth_array(pdf, fo);
+    write_fontdescriptor(pdf, fo->fd);
+
+    write_cid_fontdictionary(pdf, fo, f);
+    if (fo->fd) {
+      if (fo->fd->gl_tree){
+	avl_destroy(fo->fd->gl_tree,destroy_glw_cid_entry);
+      }
+      xfree(fo->fd);
+    }
+    xfree(fo);
+}
+
+@ @c
+void write_cid_fontdictionary(PDF pdf, fo_entry * fo, internal_font_number f)
+{
+    int i;
+
+    fo->tounicode_objnum = write_cid_tounicode(pdf, fo, f);
+
+    pdf_begin_obj(pdf, fo->fo_objnum, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "Font");
+    pdf_dict_add_name(pdf, "Subtype", "Type0");
+    if (font_identity(f) == vertical_identity) {
+        pdf_dict_add_name(pdf, "Encoding", "Identity-V");
+    } else {
+        pdf_dict_add_name(pdf, "Encoding", "Identity-H");
+    }
+    pdf_dict_add_fontname(pdf, "BaseFont", fo->fd);
+    i = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_add_name(pdf, "DescendantFonts");
+    pdf_begin_array(pdf);
+    pdf_add_ref(pdf, i);
+    pdf_end_array(pdf);
+    /* todo: the ToUnicode CMap */
+    if (fo->tounicode_objnum != 0)
+        pdf_dict_add_ref(pdf, "ToUnicode", (int) fo->tounicode_objnum);
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+
+    pdf_begin_obj(pdf, i, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "Font");
+    if (is_opentype(fo->fm) || is_type1(fo->fm)) {
+        pdf_dict_add_name(pdf, "Subtype", "CIDFontType0");
+    } else {
+        pdf_dict_add_name(pdf, "Subtype", "CIDFontType2");
+        pdf_dict_add_name(pdf, "CIDToGIDMap", "Identity");
+    }
+    pdf_dict_add_fontname(pdf, "BaseFont", fo->fd);
+    pdf_dict_add_ref(pdf, "FontDescriptor", (int) fo->fd->fd_objnum);
+    pdf_dict_add_ref(pdf, "W", (int) fo->cw_objnum);
+    pdf_add_name(pdf, "CIDSystemInfo");
+    pdf_begin_dict(pdf);
+    pdf_dict_add_string(pdf, "Registry", (font_cidregistry(f) ? font_cidregistry(f) : "Adobe"));
+    pdf_dict_add_string(pdf, "Ordering", (font_cidordering(f) ? font_cidordering(f) : "Identity"));
+    pdf_dict_add_int(pdf, "Supplement", (int) font_cidsupplement(f));
+    pdf_end_dict(pdf);
+
+    /* I doubt there is anything useful that could be written here */
+
+#if 0
+    if (pdf_font_attr(fo->tex_font) != get_nullstr()) {
+        pdf_out(pdf, '\n');
+        pdf_print(pdf_font_attr(fo->tex_font));
+        pdf_out(pdf, '\n');
+    }
+#endif
+
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/writefont.c
+++ /dev/null
@@ -1,1069 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-void write_cid_fontdictionary(PDF pdf, fo_entry * fo, internal_font_number f);
-
-static void create_cid_fontdictionary(PDF pdf, internal_font_number f);
-
-const key_entry font_key[FONT_KEYS_NUM] = {
-    { "Ascent",      "Ascender",    1 },
-    { "CapHeight",   "CapHeight",   1 },
-    { "Descent",     "Descender",   1 },
-    { "ItalicAngle", "ItalicAngle", 1 },
-    { "StemV",       "StdVW",       1 },
-    { "XHeight",     "XHeight",     1 },
-    { "FontBBox",    "FontBBox",    1 },
-    { "",            "",            0 },
-    { "",            "",            0 },
-    { "",            "",            0 },
-    { "FontName",    "FontName",    1 }
-};
-
-/*tex A tree of font dictionaries: */
-
-struct avl_table *fo_tree = NULL;
-
-/*tex A tree of font descriptor objects: */
-
-struct avl_table *fd_tree = NULL;
-
-static int comp_fo_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return strcmp(((const fo_entry *) pa)->fm->tfm_name, ((const fo_entry *) pb)->fm->tfm_name);
-}
-
-static int comp_fd_entry(const void *pa, const void *pb, void *p)
-{
-    const fd_entry *p1 = (const fd_entry *) pa, *p2 = (const fd_entry *) pb;
-    (void) p;
-    return strcmp(p1->fm->ff_name, p2->fm->ff_name);
-}
-
-/*tex We initialize data structure for |/Type| |/Font|: */
-
-static fo_entry *new_fo_entry(void)
-{
-    fo_entry *fo;
-    fo = xtalloc(1, fo_entry);
-    fo->fo_objnum = 0;
-    fo->tex_font = 0;
-    fo->fm = NULL;
-    fo->fd = NULL;
-    fo->fe = NULL;
-    fo->cw_objnum = 0;
-    fo->first_char = 1;
-    fo->last_char = 0;
-    fo->tx_tree = NULL;
-    fo->tounicode_objnum = 0;
-    return fo;
-}
-
-/*tex We initialize data structure for |/Type| |/FontDescriptor|: */
-
-fd_entry *new_fd_entry(internal_font_number f)
-{
-    fd_entry *fd;
-    int i;
-    fd = xtalloc(1, fd_entry);
-    fd->fd_objnum = 0;
-    fd->fontname = NULL;
-    fd->subset_tag = NULL;
-    fd->ff_found = false;
-    fd->ff_objnum = 0;
-    fd->all_glyphs = false;
-    fd->write_ttf_glyph_names = false;
-    for (i = 0; i < FONT_KEYS_NUM; i++) {
-        fd->font_dim[i].val = 0;
-        fd->font_dim[i].set = false;
-    }
-    fd->fe = NULL;
-    fd->builtin_glyph_names = NULL;
-    fd->fm = NULL;
-    fd->tx_tree = NULL;
-    fd->gl_tree = NULL;
-    fd->tex_font = f;
-    return fd;
-}
-
-/*tex
-
-    Only fallback values of font metrics are taken from the TFM info of |f| by
-    |preset_fontmetrics|. During reading of the font file, these values are
-    replaced by metrics from the font, if available.
-
-*/
-
-static void preset_fontmetrics(fd_entry * fd, internal_font_number f)
-{
-    int i;
-    fd->font_dim[ITALIC_ANGLE_CODE].val = 0;
-    fd->font_dim[ASCENT_CODE].val =
-        divide_scaled(char_height(f, 'h'), font_size(f), 3);
-    fd->font_dim[CAPHEIGHT_CODE].val =
-        divide_scaled(char_height(f, 'H'), font_size(f), 3);
-    i = -divide_scaled(char_depth(f, 'y'), font_size(f), 3);
-    fd->font_dim[DESCENT_CODE].val = i < 0 ? i : 0;
-    fd->font_dim[STEMV_CODE].val =
-        divide_scaled(char_width(f, '.') / 3, font_size(f), 3);
-    fd->font_dim[XHEIGHT_CODE].val =
-        divide_scaled(get_x_height(f), font_size(f), 3);
-    fd->font_dim[FONTBBOX1_CODE].val = 0;
-    fd->font_dim[FONTBBOX2_CODE].val = fd->font_dim[DESCENT_CODE].val;
-    fd->font_dim[FONTBBOX3_CODE].val =
-        divide_scaled(get_quad(f), font_size(f), 3);
-    fd->font_dim[FONTBBOX4_CODE].val =
-        fd->font_dim[CAPHEIGHT_CODE].val > fd->font_dim[ASCENT_CODE].val ?
-        fd->font_dim[CAPHEIGHT_CODE].val : fd->font_dim[ASCENT_CODE].val;
-    for (i = 0; i < INT_KEYS_NUM; i++)
-        fd->font_dim[i].set = true;
-}
-
-static void fix_fontmetrics(fd_entry * fd)
-{
-    int i;
-    intparm *p = (intparm *) fd->font_dim;
-    /*tex Make sure there is a rectangle. */
-    if (p[FONTBBOX3_CODE].val < p[FONTBBOX1_CODE].val) {
-        i = p[FONTBBOX3_CODE].val;
-        p[FONTBBOX3_CODE].val = p[FONTBBOX1_CODE].val;
-        p[FONTBBOX1_CODE].val = i;
-    } else if (p[FONTBBOX3_CODE].val == p[FONTBBOX1_CODE].val)
-        p[FONTBBOX3_CODE].val = p[FONTBBOX1_CODE].val + 1;
-    if (p[FONTBBOX4_CODE].val < p[FONTBBOX2_CODE].val) {
-        i = p[FONTBBOX4_CODE].val;
-        p[FONTBBOX4_CODE].val = p[FONTBBOX2_CODE].val;
-        p[FONTBBOX2_CODE].val = i;
-    } else if (p[FONTBBOX4_CODE].val == p[FONTBBOX2_CODE].val)
-        p[FONTBBOX4_CODE].val = p[FONTBBOX2_CODE].val + 1;
-    if (!p[ASCENT_CODE].set) {
-        p[ASCENT_CODE].val = p[FONTBBOX4_CODE].val;
-        p[ASCENT_CODE].set = true;
-    }
-    if (!p[DESCENT_CODE].set) {
-        p[DESCENT_CODE].val = p[FONTBBOX2_CODE].val;
-        p[DESCENT_CODE].set = true;
-    }
-    if (!p[CAPHEIGHT_CODE].set) {
-        p[CAPHEIGHT_CODE].val = p[FONTBBOX4_CODE].val;
-        p[CAPHEIGHT_CODE].set = true;
-    }
-}
-
-static void write_fontmetrics(PDF pdf, fd_entry * fd)
-{
-    int i;
-    fix_fontmetrics(fd);
-    pdf_add_name(pdf, font_key[FONTBBOX1_CODE].pdfname);
-    pdf_begin_array(pdf);
-    /*
-    pdf_check_space;
-    pdf_printf(pdf, "%i %i %i %i",
-        (int) fd->font_dim[FONTBBOX1_CODE].val,
-        (int) fd->font_dim[FONTBBOX2_CODE].val,
-        (int) fd->font_dim[FONTBBOX3_CODE].val,
-        (int) fd->font_dim[FONTBBOX4_CODE].val);
-    */
-    pdf_add_int(pdf,(int) fd->font_dim[FONTBBOX1_CODE].val);
-    pdf_add_int(pdf,(int) fd->font_dim[FONTBBOX2_CODE].val);
-    pdf_add_int(pdf,(int) fd->font_dim[FONTBBOX3_CODE].val);
-    pdf_add_int(pdf,(int) fd->font_dim[FONTBBOX4_CODE].val);
-    /* */
-    pdf_end_array(pdf);
-    for (i = 0; i < GEN_KEY_NUM; i++)
-        if (fd->font_dim[i].set)
-            pdf_dict_add_int(pdf, font_key[i].pdfname, fd->font_dim[i].val);
-}
-
-static void preset_fontname(fo_entry * fo, internal_font_number f)
-{
-    if (fo->fm->ps_name != NULL) {
-        /*tex We just fallback. */
-        fo->fd->fontname = xstrdup(fo->fm->ps_name);
-    } else if (font_fullname(f) != NULL) {
-        fo->fd->fontname = xstrdup(font_fullname(f));
-    } else {
-        fo->fd->fontname = xstrdup(fo->fm->tfm_name);
-    }
-}
-
-static void pdf_dict_add_fontname(PDF pdf, const char *key, fd_entry * fd)
-{
-    char *s;
-    size_t l1 = 0, l2;
-    if (fd->subset_tag != NULL)
-        l1 = strlen(fd->subset_tag);
-    l2 = strlen(fd->fontname);
-    s = xmalloc(l1 + l2 + 2);
-    if (l1 > 0)
-        snprintf(s, l1 + l2 + 2, "%s+%s", fd->subset_tag, fd->fontname);
-    else
-        snprintf(s, l2 + 1, "%s", fd->fontname);
-    pdf_dict_add_name(pdf, key, s);
-    xfree(s);
-}
-
-fd_entry *lookup_fd_entry(char *s)
-{
-    fd_entry fd;
-    fm_entry fm;
-    fm.ff_name = s;
-    fd.fm = &fm;
-    if (fd_tree == NULL) {
-        fd_tree = avl_create(comp_fd_entry, NULL, &avl_xallocator);
-    }
-    return (fd_entry *) avl_find(fd_tree, &fd);
-}
-
-static fd_entry *lookup_fontdescriptor(fo_entry * fo)
-{
-    return lookup_fd_entry(fo->fm->ff_name);
-}
-
-void register_fd_entry(fd_entry * fd)
-{
-    void **aa;
-    if (fd_tree == NULL) {
-        fd_tree = avl_create(comp_fd_entry, NULL, &avl_xallocator);
-    }
-    /*tex The font descriptor is not yet registered: */
-    if (lookup_fd_entry(fd->fm->ff_name) == NULL) {
-        /*tex Is this a problem? */
-    } else {
-        /*tex The lookup also can create */
-    }
-    aa = avl_probe(fd_tree, fd);
-    if (aa == NULL) {
-        /*tex Is this a problem? */
-    }
-}
-
-static void create_fontdescriptor(fo_entry * fo, internal_font_number f)
-{
-    fo->fd = new_fd_entry(f);
-    preset_fontname(fo, f);
-    preset_fontmetrics(fo->fd, f);
-    /*tex An encoding is needed for \TRUETYPE\ writing: */
-    fo->fd->fe = fo->fe;
-    /*tex A map entry is needed for \TRUETYPE\ writing: */
-    fo->fd->fm = fo->fm;
-    fo->fd->gl_tree = avl_create(comp_string_entry, NULL, &avl_xallocator);
-}
-
-/*tex
-
-    For all used characters of \TeX font |f|, get corresponding glyph names from
-    external reencoding (.enc) file and collect these in the glyph tree |gl_tree|
-    of font descriptor |fd| referenced by font dictionary |fo|.
-
-*/
-
-static void mark_reenc_glyphs(fo_entry * fo, internal_font_number f)
-{
-    int i;
-    char **g;
-    void **aa;
-    if (is_subsetted(fo->fm)) {
-        /*tex mark glyphs from TeX (externally reencoded characters) */
-        g = fo->fe->glyph_names;
-        for (i = fo->first_char; i <= fo->last_char; i++) {
-            if (pdf_char_marked(f, i)
-                && g[i] != notdef
-                && (char *) avl_find(fo->fd->gl_tree, g[i]) == NULL) {
-                aa = avl_probe(fo->fd->gl_tree, xstrdup(g[i]));
-                if (aa == NULL) {
-                    /*tex Is this a problem? */
-                }
-            }
-        }
-    }
-}
-
-/*tex
-
-    Function |mark_chars| has 2 uses:
-
-    \startitemize[n]
-        \startitem Mark characters as chars on \TeX\ level. \stopitem
-        \startitem Mark encoding pairs used by \TeX\ to optimize encoding vector. \stopitem
-    \stopitemize
-
-*/
-
-static struct avl_table *mark_chars(fo_entry * fo, struct avl_table *tx_tree, internal_font_number f)
-{
-    int i, *j;
-    void **aa;
-    if (tx_tree == NULL) {
-        tx_tree = avl_create(comp_int_entry, NULL, &avl_xallocator);
-        assert(tx_tree != NULL);
-    }
-    for (i = fo->first_char; i <= fo->last_char; i++) {
-        if (pdf_char_marked(f, i) && (int *) avl_find(tx_tree, &i) == NULL) {
-            j = xtalloc(1, int);
-            *j = i;
-            aa = avl_probe(tx_tree, j);
-            if (aa == NULL) {
-                /*tex Is this a problem? */
-            }
-        }
-    }
-    return tx_tree;
-}
-
-static void get_char_range(fo_entry * fo, internal_font_number f)
-{
-    int i;
-    assert(fo != NULL);
-    /*tex Search for |first_char| and |last_char|. */
-    for (i = font_bc(f); i <= font_ec(f); i++)
-        if (pdf_char_marked(f, i))
-            break;
-    fo->first_char = i;
-    for (i = font_ec(f); i >= font_bc(f); i--)
-        if (pdf_char_marked(f, i))
-            break;
-    fo->last_char = i;
-    if ((fo->first_char > fo->last_char) || !pdf_char_marked(f, fo->first_char)) {
-        /*tex No character has been used from this font. */
-        fo->last_char = 0;
-        fo->first_char = fo->last_char + 1;
-    }
-}
-
-static int font_has_subset(internal_font_number f)
-{
-    int i, s;
-    /*tex Search for |first_char| and |last_char|. */
-    for (i = font_bc(f); i <= font_ec(f); i++)
-        if (pdf_char_marked(f, i))
-            break;
-    s = i;
-    for (i = font_ec(f); i >= font_bc(f); i--)
-        if (pdf_char_marked(f, i))
-            break;
-    if (s > i)
-        return 0;
-    else
-        return 1;
-}
-
-static void write_charwidth_array(PDF pdf, fo_entry * fo, internal_font_number f)
-{
-    int i, j, *ip, *fip;
-    struct avl_traverser t;
-    fo->cw_objnum = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_begin_obj(pdf, fo->cw_objnum, OBJSTM_ALWAYS);
-    avl_t_init(&t, fo->tx_tree);
-    fip = (int *) avl_t_first(&t, fo->tx_tree);
-    pdf_begin_array(pdf);
-    for (ip = fip, j = *ip; ip != NULL; ip = (int *) avl_t_next(&t)) {
-        if (ip != fip)
-            pdf_out(pdf, ' ');
-        i = *ip;
-        while (j < i - 1) {
-            pdf_puts(pdf, "0 ");
-            j++;
-        }
-        j = i;
-        pdf_print_charwidth(pdf, f, i);
-    }
-    pdf_end_array(pdf);
-    pdf_end_obj(pdf);
-}
-
-/*tex
-
-    Remark: Font objects from embedded PDF files are never registered into
-    |fo_tree|; they are individually written out.
-
-*/
-
-static fo_entry *lookup_fo_entry(char *s)
-{
-    fo_entry fo;
-    fm_entry fm;
-    fm.tfm_name = s;
-    fo.fm = &fm;
-    if (fo_tree == NULL) {
-        fo_tree = avl_create(comp_fo_entry, NULL, &avl_xallocator);
-    }
-    return (fo_entry *) avl_find(fo_tree, &fo);
-}
-
-static void register_fo_entry(fo_entry * fo)
-{
-    void **aa;
-    if (fo_tree == NULL) {
-        fo_tree = avl_create(comp_fo_entry, NULL, &avl_xallocator);
-    }
-    if (lookup_fo_entry(fo->fm->tfm_name) == NULL) {
-        /*tex Is this a problem? */
-    } else {
-        /*tex The lookup also can create */
-    }
-    aa = avl_probe(fo_tree, fo);
-    if (aa == NULL) {
-        /*tex Is this a problem? */
-    }
-}
-
-/*tex
-
-In principle we could replace the pdftex derived ttf.otf inclusion part by using
-the regular code for this and assigning indices and tounicodes to the character
-blobs, but for the moment we keep the current approach.
-
-*/
-
-static void write_fontfile(PDF pdf, fd_entry * fd)
-{
-    if (is_cidkeyed(fd->fm)) {
-        if (is_opentype(fd->fm)) {
-            writetype0(pdf, fd);
-        } else if (is_truetype(fd->fm)) {
-            if (!writetype2(pdf, fd)) {
-                writetype0(pdf,fd);
-                fd->fm->type |= F_OTF; fd->fm->type ^= F_TRUETYPE;
-            }
-        } else if (is_type1(fd->fm)) {
-            writetype1w(pdf, fd);
-        } else {
-            normal_error("fonts","there is a problem writing the font file (1)");
-        }
-    } else {
-        if (is_type1(fd->fm)) {
-            writet1(pdf, fd);
-        } else if (is_truetype(fd->fm)) {
-            writettf(pdf, fd);
-        } else if (is_opentype(fd->fm)) {
-            writeotf(pdf, fd);
-        } else {
-            normal_error("fonts","there is a problem writing the font file (2)");
-        }
-    }
-    if (!fd->ff_found)
-        return;
-    fd->ff_objnum = pdf_create_obj(pdf, obj_type_others, 0);
-    /*tex The font file stream: */
-    pdf_begin_obj(pdf, fd->ff_objnum, OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    if (is_cidkeyed(fd->fm)) {
-        /*tex No subtype is used for |TRUETYPE\ based \OPENTYPE\ fonts. */
-        if (is_opentype(fd->fm) || is_type1(fd->fm)) {
-            pdf_dict_add_name(pdf, "Subtype", "CIDFontType0C");
-        }
-    } else if (is_type1(fd->fm)) {
-        pdf_dict_add_int(pdf, "Length1", (int) t1_length1);
-        pdf_dict_add_int(pdf, "Length2", (int) t1_length2);
-        pdf_dict_add_int(pdf, "Length3", (int) t1_length3);
-    } else if (is_truetype(fd->fm)) {
-        pdf_dict_add_int(pdf, "Length1", (int) ttf_length);
-    } else if (is_opentype(fd->fm)) {
-        pdf_dict_add_name(pdf, "Subtype", "Type1C");
-    } else {
-        normal_error("fonts","there is a problem writing the font file (3)");
-    }
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    strbuf_flush(pdf, pdf->fb);
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-}
-
-int cidset = 0;
-
-static void write_fontdescriptor(PDF pdf, fd_entry * fd)
-{
-    static const int std_flags[] = {
-        1 + 2 + (1 << 5),                        /* Courier */
-        1 + 2 + (1 << 5)            + (1 << 18), /* Courier-Bold */
-        1 + 2 + (1 << 5) + (1 << 6),             /* Courier-Oblique */
-        1 + 2 + (1 << 5) + (1 << 6) + (1 << 18), /* Courier-BoldOblique */
-                (1 << 5),                        /* Helvetica */
-                (1 << 5)            + (1 << 18), /* Helvetica-Bold */
-                (1 << 5) + (1 << 6),             /* Helvetica-Oblique */
-                (1 << 5) + (1 << 6) + (1 << 18), /* Helvetica-BoldOblique */
-              4,                                 /* Symbol */
-            2 + (1 << 5),                        /* Times-Roman */
-            2 + (1 << 5)            + (1 << 18), /* Times-Bold */
-            2 + (1 << 5) + (1 << 6),             /* Times-Italic */
-            2 + (1 << 5) + (1 << 6) + (1 << 18), /* Times-BoldItalic */
-              4                                  /* ZapfDingbats */
-    };
-    char *glyph;
-    struct avl_traverser t;
-    int fd_flags;
-    /*tex Possibly updated by |write_fontfile|: */
-    cidset = 0;
-    if (fd->fd_objnum == 0) {
-        int n = 0;
-        int callback_id = callback_defined(font_descriptor_objnum_provider_callback);
-        if (callback_id) {
-            run_callback(callback_id, "S->d", fd->fontname, &n);
-        }
-        if (!n) {
-           n = pdf_create_obj(pdf, obj_type_others, 0);
-        }
-        fd->fd_objnum = n;
-    }
-    if (is_fontfile(fd->fm) && is_included(fd->fm)) {
-        /*tex This will set |fd->ff_found| if font file is found: */
-        write_fontfile(pdf, fd);
-    }
-    pdf_begin_obj(pdf, fd->fd_objnum, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "FontDescriptor");
-    pdf_dict_add_fontname(pdf, "FontName", fd);
-    if (fd->fm->fd_flags != FD_FLAGS_NOT_SET_IN_MAPLINE) {
-        fd_flags = (int) fd->fm->fd_flags;
-    } else if (fd->ff_found) {
-        fd_flags = FD_FLAGS_DEFAULT_EMBED;
-    } else {
-        fd_flags = is_std_t1font(fd->fm) ? std_flags[check_std_t1font(fd->fm->ps_name)] : FD_FLAGS_DEFAULT_NON_EMBED;
-        formatted_warning("map file",
-             "No flags specified for non-embedded font '%s' (%s), I'm using %i, fix your map entry",
-             fd->fm->ps_name != NULL ? fd->fm->ps_name : "No name given",
-             fd->fm->tfm_name, fd_flags);
-    }
-    pdf_dict_add_int(pdf, "Flags", fd_flags);
-    write_fontmetrics(pdf, fd);
-    if (fd->ff_found) {
-        if (is_cidkeyed(fd->fm)) {
-            if (is_type1(fd->fm))
-                pdf_dict_add_ref(pdf, "FontFile3", (int) fd->ff_objnum);
-            else if (is_truetype(fd->fm))
-                pdf_dict_add_ref(pdf, "FontFile2", (int) fd->ff_objnum);
-            else if (is_opentype(fd->fm))
-                pdf_dict_add_ref(pdf, "FontFile3", (int) fd->ff_objnum);
-            else
-                normal_error("fonts","there is a problem writing the font file (4)");
-        } else {
-            if (is_subsetted(fd->fm) && is_type1(fd->fm)) {
-                /*tex |/CharSet| is optional; names may appear in any order */
-                if ((! pdf->omit_charset) && (pdf->major_version == 1)) {
-                    avl_t_init(&t, fd->gl_tree);
-                    pdf_add_name(pdf, "CharSet");
-                    pdf_out(pdf, '(');
-                    for (glyph = (char *) avl_t_first(&t, fd->gl_tree); glyph != NULL; glyph = (char *) avl_t_next(&t)) {
-                        pdf_add_name(pdf, glyph);
-                    }
-                    pdf_out(pdf, ')');
-                    pdf_set_space(pdf);
-                }
-            }
-            if (is_type1(fd->fm))
-                pdf_dict_add_ref(pdf, "FontFile", (int) fd->ff_objnum);
-            else if (is_truetype(fd->fm))
-                pdf_dict_add_ref(pdf, "FontFile2", (int) fd->ff_objnum);
-            else if (is_opentype(fd->fm))
-                pdf_dict_add_ref(pdf, "FontFile3", (int) fd->ff_objnum);
-            else
-                normal_error("fonts","there is a problem writing the font file (5)");
-        }
-    }
-    if ((! pdf->omit_cidset) && (pdf->major_version == 1) && (cidset != 0) ) {
-        pdf_dict_add_ref(pdf, "CIDSet", cidset);
-    }
-    /*tex
-        Currently we don't export the optional keys for CID fonts like |/Style <<
-        /Panose <12-byte string> >>| and we probably never will.
-    */
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-}
-
-static void write_fontdescriptors(PDF pdf)
-{
-    fd_entry *fd;
-    struct avl_traverser t;
-    if (fd_tree == NULL)
-        return;
-    avl_t_init(&t, fd_tree);
-    for (fd = (fd_entry *) avl_t_first(&t, fd_tree); fd != NULL; fd = (fd_entry *) avl_t_next(&t))
-        write_fontdescriptor(pdf, fd);
-}
-
-static void write_fontdictionary(PDF pdf, fo_entry * fo)
-{
-    /*tex Write the |/ToUnicode| entry if needed. */
-    if (pdf->gen_tounicode > 0 && fo->fd != NULL) {
-        if (fo->fe != NULL) {
-            fo->tounicode_objnum = write_tounicode(pdf, fo->fe->glyph_names, fo->fe->name);
-        } else if (is_type1(fo->fm)) {
-            fo->tounicode_objnum = write_tounicode(pdf, fo->fd->builtin_glyph_names, fo->fm->tfm_name);
-        }
-    }
-    pdf_begin_obj(pdf, fo->fo_objnum, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "Font");
-    if (is_type1(fo->fm))
-        pdf_dict_add_name(pdf, "Subtype", "Type1");
-    else if (is_truetype(fo->fm))
-        pdf_dict_add_name(pdf, "Subtype", "TrueType");
-    else if (is_opentype(fo->fm))
-        pdf_dict_add_name(pdf, "Subtype", "Type1");
-    else
-        normal_error("fonts","there is a problem writing the font file (6)");
-    pdf_dict_add_fontname(pdf, "BaseFont", fo->fd);
-    pdf_dict_add_ref(pdf, "FontDescriptor", (int) fo->fd->fd_objnum);
-    pdf_dict_add_int(pdf, "FirstChar", (int) fo->first_char);
-    pdf_dict_add_int(pdf, "LastChar", (int) fo->last_char);
-    pdf_dict_add_ref(pdf, "Widths", (int) fo->cw_objnum);
-    if ((is_type1(fo->fm) || is_opentype(fo->fm)) && fo->fe != NULL && fo->fe->fe_objnum != 0)
-        pdf_dict_add_ref(pdf, "Encoding", (int) fo->fe->fe_objnum);
-    if (fo->tounicode_objnum != 0)
-        pdf_dict_add_ref(pdf, "ToUnicode", (int) fo->tounicode_objnum);
-    if (pdf_font_attr(fo->tex_font) != get_nullstr() && pdf_font_attr(fo->tex_font) != 0) {
-        pdf_check_space(pdf);
-        pdf_print(pdf, pdf_font_attr(fo->tex_font));
-        pdf_set_space(pdf);
-    }
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-}
-
-static void write_fontdictionaries(PDF pdf)
-{
-    fo_entry *fo;
-    struct avl_traverser t;
-    if (fo_tree == NULL)
-        return;
-    avl_t_init(&t, fo_tree);
-    for (fo = (fo_entry *) avl_t_first(&t, fo_tree); fo != NULL; fo = (fo_entry *) avl_t_next(&t)) {
-        write_fontdictionary(pdf, fo);
-    }
-}
-
-/*tex
-
-    Final flush of all font related stuff by call from \.{Output fonts
-    definitions} elsewhere.
-
-*/
-
-void write_fontstuff(PDF pdf)
-{
-    write_fontdescriptors(pdf);
-    write_fontencodings(pdf);
-    write_fontdictionaries(pdf);
-}
-
-static void create_fontdictionary(PDF pdf, internal_font_number f)
-{
-    fo_entry *fo = new_fo_entry();
-    fm_entry *fm = font_map(f);
-    /*tex set |fo->first_char| and |fo->last_char| from |f| */
-    get_char_range(fo, f);
-    if (fo->last_char > 255)
-        fo->last_char = 255;
-    fo->fm = fm;
-    fo->fo_objnum = pdf_font_num(f);
-    fo->tex_font = f;
-    if (is_reencoded(fo->fm)) {
-        /*tex
-            At least the map entry tells so but it returns |NULL| if the .enc
-            file couldn't be opened.
-        */
-        fo->fe = get_fe_entry(fo->fm->encname);
-        if (fo->fe != NULL && (is_type1(fo->fm) || is_opentype(fo->fm))) {
-            /*tex We don't end up here for truetype fonts. */
-            if (fo->fe->fe_objnum == 0) {
-                /*tex It will be written out */
-                fo->fe->fe_objnum = pdf_create_obj(pdf, obj_type_others, 0);
-            }
-            /*tex Mark encoding pairs used by TeX to optimize encoding vector. */
-            fo->fe->tx_tree = mark_chars(fo, fo->fe->tx_tree, f);
-        }
-    }
-    /*tex For |write_charwidth_array|: */
-    fo->tx_tree = mark_chars(fo, fo->tx_tree, f);
-    write_charwidth_array(pdf, fo, f);
-    if (!is_builtin(fo->fm)) {
-        if (is_type1(fo->fm)) {
-            if ((fo->fd = lookup_fontdescriptor(fo)) == NULL) {
-                create_fontdescriptor(fo, f);
-                register_fd_entry(fo->fd);
-            }
-        } else {
-            create_fontdescriptor(fo, f);
-        }
-        if (fo->fe != NULL) {
-            mark_reenc_glyphs(fo, f);
-            if (!is_type1(fo->fm)) {
-                /*tex Mark reencoded characters as chars on TeX level. */
-                assert(fo->fd->tx_tree == NULL);
-                fo->fd->tx_tree = mark_chars(fo, fo->fd->tx_tree, f);
-                if (is_truetype(fo->fm)) {
-                    fo->fd->write_ttf_glyph_names = true;
-                }
-            }
-        } else {
-            /*tex Mark non-reencoded characters as chars on TeX level. */
-            fo->fd->tx_tree = mark_chars(fo, fo->fd->tx_tree, f);
-        }
-        if (!is_type1(fo->fm)) {
-            write_fontdescriptor(pdf, fo->fd);
-        }
-    } else {
-        /*tex
-            Builtin fonts still need the \type {/Widths} array and \type
-            {/FontDescriptor} (to avoid error \quotation {font FOO contains bad
-            \type {/BBox}}).
-        */
-        create_fontdescriptor(fo, f);
-        write_fontdescriptor(pdf, fo->fd);
-        if (!is_std_t1font(fo->fm)) {
-            formatted_warning("map file", "font '%s' is not a standard font; I suppose it is available to your PDF viewer then", fo->fm->ps_name);
-        }
-    }
-    if (is_type1(fo->fm)) {
-        register_fo_entry(fo);
-    } else {
-        write_fontdictionary(pdf, fo);
-    }
-}
-
-static int has_ttf_outlines(fm_entry * fm)
-{
-    FILE *f = fopen(fm->ff_name, "rb");
-    if (f != NULL) {
-        int ch1 = getc(f);
-        int ch2 = getc(f);
-        int ch3 = getc(f);
-        int ch4 = getc(f);
-        fclose(f);
-        if (ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O')
-            return 0;
-        return 1;
-    }
-    return 0;
-}
-
-void do_pdf_font(PDF pdf, internal_font_number f)
-{
-    int del_file = 0;
-    fm_entry *fm;
-    /*tex
-        This is not 100\% true: CID is actually needed whenever (and only) there
-        are more than 256 separate glyphs used. But for now, we just assume the
-        user knows what he is doing. In practice this seems to be the case.
-    */
-    if (!font_has_subset(f))
-        return;
-    if (font_encodingbytes(f) == 2) {
-        /*tex
-            Create a virtual font map entry, as this is needed by the rest of the
-            font inclusion mechanism.
-        */
-        fm = font_map(f) = new_fm_entry();
-        /*tex Set this to a name or whatever, not a real \TFM\ anyway: */
-        fm->tfm_name = font_name(f);
-        /*tex The actual file: */
-        fm->ff_name = font_filename(f);
-        /*tex The true (used) name: */
-        if (font_psname(f) != NULL) {
-            fm->ps_name = font_psname(f);
-        } else {
-            fm->ps_name = font_fullname(f);
-        }
-        if (fm->ff_name
-            && strlen(fm->ff_name) >= 6
-            && strstr(fm->ff_name,".dfont") == (fm->ff_name + strlen(fm->ff_name) - 6)) {
-            /*tex
-
-                In case of a .dfont (an obsolete format), we will extract the
-                correct ttf here, and adjust |fm->ff_name| to point to the
-                temporary file. This file will be deleted later. Todo: keep a
-                nicer name somewhere for the terminal message.
-            */
-            char *s = FindResourceTtfFont(fm->ff_name, fm->ps_name);
-            if (s != NULL) {
-                fm->ff_name = s;
-                del_file = 1;
-            } else {
-                formatted_error("font","file '%s' does not contain font '%s'",fm->ff_name, fm->ps_name);
-            }
-        }
-        /*tex Needed for the CIDSystemInfo: */
-        fm->encname = font_encodingname(f);
-        fm->slant = font_slant(f);
-        set_slantset(fm);
-        fm->extend = font_extend(f);
-        set_extendset(fm);
-        /*tex Flags can perhaps be done better. */
-        fm->fd_flags = 4;
-        set_inuse(fm);
-        switch (font_format(f)) {
-            case opentype_format:
-                if (has_ttf_outlines(fm)) {
-                    set_truetype(fm);
-                } else {
-                    set_opentype(fm);
-                }
-                break;
-            case truetype_format:
-                set_truetype(fm);
-                break;
-            case type1_format:
-                set_type1(fm);
-                break;
-            default:
-                formatted_error("font","file format '%s' for '%s' is incompatible with wide characters",
-                    font_format_name(f), font_name(f));
-        }
-        /*tex This makes \quotation {unknown} default to subsetted inclusion. */
-        if (font_embedding(f) != no_embedding) {
-            set_included(fm);
-            if (font_embedding(f) != full_embedding) {
-                set_subsetted(fm);
-            }
-        }
-        set_cidkeyed(fm);
-        create_cid_fontdictionary(pdf, f);
-        if (del_file)
-            unlink(fm->ff_name);
-    } else {
-        /*tex By now |font_map(f)|, if any, should have been set via |pdf_init_font|. */
-        if ((fm = font_map(f)) == NULL || (fm->ps_name == NULL && fm->ff_name == NULL))
-            writet3(pdf, f);
-        else
-            create_fontdictionary(pdf, f);
-    }
-}
-
-/*tex
-
-    The glyph width is included in |glw_entry|, because that width depends on the
-    value it has in the font where it is actually typeset from, not the font that
-    is the owner of the fd entry.
-
-    It is possible that the user messes with the metric width, but handling that
-    properly would require access to the |hmtx| table at this point in the
-    program.
-
-*/
-
-static int comp_glw_entry(const void *pa, const void *pb, void *p
-    __attribute__ ((unused)))
-{
-    unsigned short i, j;
-    i = (unsigned short) (*(const glw_entry *) pa).id;
-    j = (unsigned short) (*(const glw_entry *) pb).id;
-    cmp_return(i, j);
-    return 0;
-}
-
-static void create_cid_fontdescriptor(fo_entry * fo, internal_font_number f)
-{
-    fo->fd = new_fd_entry(f);
-    preset_fontname(fo, f);
-    preset_fontmetrics(fo->fd, f);
-    /*tex Encoding needed by \TRUETYPE\ writing: */
-    fo->fd->fe = fo->fe;
-    /*tex Map entry needed by \TRUETYPE\ writing */
-    fo->fd->fm = fo->fm;
-    fo->fd->gl_tree = avl_create(comp_glw_entry, NULL, &avl_xallocator);
-    assert(fo->fd->gl_tree != NULL);
-}
-
-
-/*tex
-
-    The values |font_bc()| and |font_ec()| are potentially large character ids,
-    but the strings that are written out use CID indexes, and those are limited
-    to 16-bit values.
-
-*/
-
-static void mark_cid_subset_glyphs(fo_entry * fo, internal_font_number f)
-{
-    glw_entry *j;
-    void *aa;
-    int l = font_size(f);
-    int i;
-    for (i = font_bc(f); i <= font_ec(f); i++) {
-        if (quick_char_exists(f, i) && char_used(f, i)) {
-            j = xtalloc(1, glw_entry);
-            j->id = (unsigned) char_index(f, i);
-            j->wd = divide_scaled_n(char_width(f, i), l, 10000.0);
-            if ((glw_entry *) avl_find(fo->fd->gl_tree, j) == NULL) {
-                aa = avl_probe(fo->fd->gl_tree, j);
-                if (aa == NULL) {
-                    /*tex Is this a problem? */
-                }
-            } else {
-                xfree(j);
-            }
-        }
-    }
-}
-
-/*tex
-
-    It is possible to compress the widths array even better, by using the
-    alternate 'range' syntax and possibly even using /DW to set a default value.
-
-    There is a some optimization here already: glyphs that are not used do not
-    appear in the widths array at all.
-
-    We have to make sure that we do not output an (incorrect!) width for a
-    character that exists in the font, but is not used in typesetting. An
-    enormous negative width is used as sentinel value
-
-*/
-
-static void write_cid_charwidth_array(PDF pdf, fo_entry * fo)
-{
-    int i, j;
-    glw_entry *glyph;
-    struct avl_traverser t;
-    fo->cw_objnum = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_begin_obj(pdf, fo->cw_objnum, OBJSTM_ALWAYS);
-    avl_t_init(&t, fo->fd->gl_tree);
-    glyph = (glw_entry *) avl_t_first(&t, fo->fd->gl_tree);
-    i = (int) glyph->id;
-    pdf_begin_array(pdf);
-    pdf_add_int(pdf, i);
-    pdf_begin_array(pdf);
-    for (; glyph != NULL; glyph = (glw_entry *) avl_t_next(&t)) {
-        j = glyph->wd;
-        if (glyph->id > (unsigned) (i + 1)) {
-            pdf_end_array(pdf);
-            pdf_add_int(pdf, glyph->id);
-            pdf_begin_array(pdf);
-            j = glyph->wd;
-        }
-        pdf_check_space(pdf);
-        if (j < 0) {
-            pdf_out(pdf, '-');
-            j = -j;
-        }
-        pdf_printf(pdf, "%i", (j / 10));
-        if ((j % 10) != 0)
-            pdf_printf(pdf, ".%i", (j % 10));
-        i = (int) glyph->id;
-        pdf_set_space(pdf);
-    }
-    pdf_end_array(pdf);
-    pdf_end_array(pdf);
-    pdf_end_obj(pdf);
-}
-
-static void destroy_glw_cid_entry(void *pa, void *pb)
-{
-    glw_entry *e = (glw_entry *) pa;
-    (void) pb;
-    xfree(e);
-}
-
-
-static void create_cid_fontdictionary(PDF pdf, internal_font_number f)
-{
-    fm_entry *fm = font_map(f);
-    fo_entry *fo = new_fo_entry();
-    /*tex set |fo->first_char| and |fo->last_char| from |f| */
-    get_char_range(fo, f);
-    fo->fm = fm;
-    fo->fo_objnum = pdf_font_num(f);
-    fo->tex_font = f;
-    create_cid_fontdescriptor(fo, f);
-    mark_cid_subset_glyphs(fo, f);
-    if (is_subsetted(fo->fm)) {
-        /*tex
-           This is a bit sneaky. |make_subset_tag()| actually expects the glyph
-           tree to contain strings instead of |glw_entry| items. However, all
-           calculations are done using explicit typecasts, so it works out ok.
-        */
-        make_subset_tag(fo->fd);
-    }
-    write_cid_charwidth_array(pdf, fo);
-    write_fontdescriptor(pdf, fo->fd);
-    write_cid_fontdictionary(pdf, fo, f);
-    if (fo->fd) {
-        if (fo->fd->gl_tree) {
-            avl_destroy(fo->fd->gl_tree,destroy_glw_cid_entry);
-        }
-        xfree(fo->fd);
-    }
-    xfree(fo);
-}
-
-void write_cid_fontdictionary(PDF pdf, fo_entry * fo, internal_font_number f)
-{
-    int i;
-    fo->tounicode_objnum = write_cid_tounicode(pdf, fo, f);
-    pdf_begin_obj(pdf, fo->fo_objnum, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "Font");
-    pdf_dict_add_name(pdf, "Subtype", "Type0");
-    if (font_identity(f) == vertical_identity) {
-        pdf_dict_add_name(pdf, "Encoding", "Identity-V");
-    } else {
-        pdf_dict_add_name(pdf, "Encoding", "Identity-H");
-    }
-    pdf_dict_add_fontname(pdf, "BaseFont", fo->fd);
-    i = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_add_name(pdf, "DescendantFonts");
-    pdf_begin_array(pdf);
-    pdf_add_ref(pdf, i);
-    pdf_end_array(pdf);
-    if (fo->tounicode_objnum != 0)
-        pdf_dict_add_ref(pdf, "ToUnicode", (int) fo->tounicode_objnum);
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-    pdf_begin_obj(pdf, i, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "Font");
-    if (is_opentype(fo->fm) || is_type1(fo->fm)) {
-        pdf_dict_add_name(pdf, "Subtype", "CIDFontType0");
-    } else {
-        pdf_dict_add_name(pdf, "Subtype", "CIDFontType2");
-        pdf_dict_add_name(pdf, "CIDToGIDMap", "Identity");
-    }
-    pdf_dict_add_fontname(pdf, "BaseFont", fo->fd);
-    pdf_dict_add_ref(pdf, "FontDescriptor", (int) fo->fd->fd_objnum);
-    pdf_dict_add_ref(pdf, "W", (int) fo->cw_objnum);
-    pdf_add_name(pdf, "CIDSystemInfo");
-    pdf_begin_dict(pdf);
-    pdf_dict_add_string(pdf, "Registry", (font_cidregistry(f) ? font_cidregistry(f) : "Adobe"));
-    pdf_dict_add_string(pdf, "Ordering", (font_cidordering(f) ? font_cidordering(f) : "Identity"));
-    pdf_dict_add_int(pdf, "Supplement", (int) font_cidsupplement(f));
-    pdf_end_dict(pdf);
-    /*tex
-        I doubt there is anything useful that could be written here so for now we
-        comment this.
-    */
-    /*
-        if (pdf_font_attr(fo->tex_font) != get_nullstr()) {
-            pdf_out(pdf, '\n');
-            pdf_print(pdf_font_attr(fo->tex_font));
-            pdf_out(pdf, '\n');
-        }
-    */
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/writet1.w
@@ -0,0 +1,1741 @@
+% writet1.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% Copyright 2006-2009 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 <string.h>
+
+#define get_length1()    t1_length1 = t1_offset() - t1_save_offset
+#define get_length2()    t1_length2 = t1_offset() - t1_save_offset
+#define get_length3()    t1_length3 = fixedcontent? t1_offset() - t1_save_offset : 0
+#define save_offset()    t1_save_offset = t1_offset()
+
+#define t1_putchar(A)       strbuf_putchar(pdf->fb, (A))
+#define t1_offset()         strbuf_offset(pdf->fb)
+#define out_eexec_char      t1_putchar
+
+#define end_last_eexec_line() \
+    t1_eexec_encrypt = false
+#define t1_char(c)          c
+#define embed_all_glyphs(tex_font)  fm_cur->all_glyphs
+#define extra_charset()     fm_cur->charset
+#define fixedcontent        false
+
+int t1_length1, t1_length2, t1_length3;
+static int t1_save_offset;
+static int t1_fontname_offset;
+
+static unsigned char *t1_buffer = NULL;
+static int t1_size = 0;
+static int t1_curbyte = 0;
+@ @c
+#define t1_read_file()  \
+    readbinfile(t1_file,&t1_buffer,&t1_size)
+#define t1_close()      xfclose(t1_file,cur_file_name)
+#define t1_getchar()    t1_buffer[t1_curbyte++]
+#define t1_ungetchar(c) t1_curbyte--
+#define t1_eof()        (t1_curbyte>t1_size)
+
+#define t1_prefix(s)        str_prefix(t1_line_array, s)
+#define t1_buf_prefix(s)    str_prefix(t1_buf_array, s)
+#define t1_suffix(s)        str_suffix(t1_line_array, t1_line_ptr, s)
+#define t1_buf_suffix(s)    str_suffix(t1_buf_array, t1_buf_ptr, s)
+#define t1_charstrings()    strstr(t1_line_array, charstringname)
+#define t1_subrs()          t1_prefix("/Subrs")
+#define t1_end_eexec()      t1_suffix("mark currentfile closefile")
+#define t1_cleartomark()    t1_prefix("cleartomark")
+
+static unsigned char *enc_buffer = NULL;
+static int enc_size = 0;
+static int enc_curbyte = 0;
+
+@ @c
+#define enc_open(a)          \
+    (enc_file = fopen((char *)(a), FOPEN_RBIN_MODE))
+#define enc_read_file()  \
+    readbinfile(enc_file,&enc_buffer,&enc_size)
+#define enc_close()       xfclose(enc_file,cur_file_name)
+#define enc_getchar()    enc_buffer[enc_curbyte++]
+#define enc_eof()        (enc_curbyte>enc_size)
+
+#define valid_code(c)   (c >= 0 && c < 256)
+#define fixedcontent     false
+
+@ @c
+static const char *standard_glyph_names[256] = {
+    /* 0x00 */
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    /* 0x10 */
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    /* 0x20 */
+    "space", "exclam", "quotedbl", "numbersign", "dollar", "percent",
+    "ampersand", "quoteright", "parenleft", "parenright", "asterisk",
+    "plus", "comma", "hyphen", "period", "slash",
+    /* 0x30 */
+    "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
+    "nine", "colon", "semicolon", "less", "equal", "greater", "question",
+    /* 0x40 */
+    "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
+    "O",
+    /* 0x50 */
+    "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
+    "backslash", "bracketright", "asciicircum", "underscore",
+    /* 0x60 */
+    "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
+    "m", "n", "o",
+    /* 0x70 */
+    "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar",
+    "braceright", "asciitilde", notdef,
+    /* 0x80 */
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    /* 0x90 */
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    /* 0xa0 */
+    notdef, "exclamdown", "cent", "sterling", "fraction", "yen", "florin",
+    "section", "currency", "quotesingle", "quotedblleft", "guillemotleft",
+    "guilsinglleft", "guilsinglright", "fi", "fl",
+    /* 0xb0 */
+    notdef, "endash", "dagger", "daggerdbl", "periodcentered", notdef,
+    "paragraph", "bullet", "quotesinglbase", "quotedblbase",
+    "quotedblright", "guillemotright", "ellipsis", "perthousand", notdef,
+    "questiondown",
+    /* 0xc0 */
+    notdef, "grave", "acute", "circumflex", "tilde", "macron", "breve",
+    "dotaccent", "dieresis", notdef,
+    "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron",
+    /* 0xd0 */
+    "emdash", notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+    /* 0xe0 */
+    notdef, "AE", notdef, "ordfeminine", notdef, notdef, notdef, notdef,
+    "Lslash", "Oslash", "OE", "ordmasculine", notdef, notdef, notdef,
+    notdef,
+    /* 0xf0 */
+    notdef, "ae", notdef, notdef, notdef, "dotlessi", notdef, notdef, "lslash",
+    "oslash", "oe", "germandbls", notdef, notdef, notdef, notdef
+};
+
+@ @c
+static fd_entry *fd_cur;
+
+static char charstringname[] = "/CharStrings";
+
+enum { ENC_STANDARD, ENC_BUILTIN } t1_encoding;
+
+#define T1_BUF_SIZE   0x10
+#define ENC_BUF_SIZE  0x1000
+
+@ @c
+#define CS_HSTEM            1
+#define CS_VSTEM            3
+#define CS_VMOVETO          4
+#define CS_RLINETO          5
+#define CS_HLINETO          6
+#define CS_VLINETO          7
+#define CS_RRCURVETO        8
+#define CS_CLOSEPATH        9
+#define CS_CALLSUBR         10
+#define CS_RETURN           11
+#define CS_ESCAPE           12
+#define CS_HSBW             13
+#define CS_ENDCHAR          14
+#define CS_RMOVETO          21
+#define CS_HMOVETO          22
+#define CS_VHCURVETO        30
+#define CS_HVCURVETO        31
+#define CS_1BYTE_MAX        (CS_HVCURVETO + 1)
+
+#define CS_DOTSECTION       CS_1BYTE_MAX + 0
+#define CS_VSTEM3           CS_1BYTE_MAX + 1
+#define CS_HSTEM3           CS_1BYTE_MAX + 2
+#define CS_SEAC             CS_1BYTE_MAX + 6
+#define CS_SBW              CS_1BYTE_MAX + 7
+#define CS_DIV              CS_1BYTE_MAX + 12
+#define CS_CALLOTHERSUBR    CS_1BYTE_MAX + 16
+#define CS_POP              CS_1BYTE_MAX + 17
+#define CS_SETCURRENTPOINT  CS_1BYTE_MAX + 33
+#define CS_2BYTE_MAX        (CS_SETCURRENTPOINT + 1)
+#define CS_MAX              CS_2BYTE_MAX
+
+@ @c
+typedef unsigned char byte;
+
+typedef struct {
+    byte nargs;                 /* number of arguments */
+    boolean bottom;             /* take arguments from bottom of stack? */
+    boolean clear;              /* clear stack? */
+    boolean valid;
+} cc_entry;                     /* CharString Command */
+
+typedef struct {
+    char *name;                 /* glyph name (or notdef for Subrs entry) */
+    byte *data;
+    unsigned short len;         /* length of the whole string */
+    unsigned short cslen;       /* length of the encoded part of the string */
+    boolean used;
+    boolean valid;
+} cs_entry;
+
+static unsigned short t1_dr, t1_er;
+static const unsigned short t1_c1 = 52845, t1_c2 = 22719;
+static unsigned short t1_cslen;
+static short t1_lenIV;
+static char enc_line[ENC_BUF_SIZE];
+
+@ define |t1_line_ptr|, |t1_line_array|, and |t1_line_limit|
+@c
+#define t1_line_entry char
+define_array(t1_line);
+
+@ define |t1_buf_ptr|, |t1_buf_array|, and |t1_buf_limit|
+@c
+#define t1_buf_entry char
+define_array(t1_buf);
+
+static int cs_start;
+
+static cs_entry *cs_tab, *cs_ptr, *cs_notdef;
+static char *cs_dict_start, *cs_dict_end;
+static int cs_counter, cs_size, cs_size_pos;
+
+static cs_entry *subr_tab;
+static char *subr_array_start, *subr_array_end;
+static int subr_max, subr_size, subr_size_pos;
+
+@ This list contains the begin/end tokens commonly used in the
+/Subrs array of a Type 1 font.
+
+@c
+static const char *cs_token_pairs_list[][2] = {
+    {" RD", "NP"},
+    {" -|", "|"},
+    {" RD", "noaccess put"},
+    {" -|", "noaccess put"},
+    {NULL, NULL}
+};
+
+@ @c
+static const char **cs_token_pair;
+
+static boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
+static int t1_in_eexec;         /* 0 before eexec-encrypted, 1 during, 2 after */
+static long t1_block_length;
+static int last_hexbyte;
+static FILE *t1_file;
+static FILE *enc_file;
+
+@ @c
+static void enc_getline(void)
+{
+    char *p;
+    char c;
+  restart:
+    if (enc_eof())
+        normal_error("type 1","unexpected end of file");
+    p = enc_line;
+    do {
+        c = (char) enc_getchar();
+        append_char_to_buf(c, p, enc_line, ENC_BUF_SIZE);
+    }
+    while (c != 10 && !enc_eof());
+    append_eol(p, enc_line, ENC_BUF_SIZE);
+    if (p - enc_line < 2 || *enc_line == '%')
+        goto restart;
+}
+
+@ read encoding from .enc file, return |glyph_names array|, or |pdffail()|
+@c
+char **load_enc_file(char *enc_name)
+{
+    int callback_id = 0;
+    int file_opened = 0;
+
+    char buf[ENC_BUF_SIZE], *p, *r;
+    int i, names_count;
+    char **glyph_names;
+
+    cur_file_name = luatex_find_file(enc_name, find_enc_file_callback);
+
+    if (cur_file_name == NULL) {
+        formatted_error("type 1","cannot find encoding file '%s' for reading", enc_name);
+    }
+    callback_id = callback_defined(read_enc_file_callback);
+    enc_curbyte = 0;
+    enc_size = 0;
+    if (callback_id > 0) {
+        if (run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &enc_buffer, &enc_size)) {
+            if ((!file_opened) || enc_size == 0) {
+                formatted_error("type 1","cannot open encoding file '%s' for reading", cur_file_name);
+            }
+        }
+    } else {
+        if (!enc_open(cur_file_name)) {
+            formatted_error("type 1","cannot open encoding file '%s' for reading", cur_file_name);
+        }
+        enc_read_file();
+        enc_close();
+    }
+    glyph_names = xtalloc(256, char *);
+    for (i = 0; i < 256; i++)
+        glyph_names[i] = (char *) notdef;
+    report_start_file(filetype_map,cur_file_name);
+    enc_getline();
+    if (*enc_line != '/' || (r = strchr(enc_line, '[')) == NULL) {
+        remove_eol(r, enc_line);
+        formatted_error("type 1","invalid encoding vector (a name or '[' missing): '%s'", enc_line);
+    }
+    names_count = 0;
+    r++;                        /* skip '[' */
+    skip_char(r, ' ');
+    for (;;) {
+        while (*r == '/') {
+            for (p = buf, r++;
+                 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
+            *p = 0;
+            skip_char(r, ' ');
+            if (names_count >= 256)
+                normal_error("type 1","encoding vector contains more than 256 names");
+            if (strcmp(buf, notdef) != 0)
+                glyph_names[names_count] = xstrdup(buf);
+            names_count++;
+        }
+        if (*r != 10 && *r != '%') {
+            if (strncmp(r, "] def", strlen("] def")) == 0)
+                goto done;
+            else {
+                remove_eol(r, enc_line);
+                formatted_error("type 1","invalid encoding vector: a name or '] def' expected: `%s'",enc_line);
+            }
+        }
+        enc_getline();
+        r = enc_line;
+    }
+  done:
+    report_stop_file(filetype_map);
+    cur_file_name = NULL;
+    xfree(enc_buffer);
+    return glyph_names;
+}
+
+@ @c
+#if 0
+static void free_glyph_names(char **glyph_names)
+{
+    int i;
+    assert(glyph_names != NULL);
+    for (i = 0; i < 256; i++)
+        if (glyph_names[i] != notdef)
+            xfree(glyph_names[i]);
+    xfree(glyph_names);
+}
+#endif
+
+static void t1_check_pfa(void)
+{
+    const int c = t1_getchar();
+    t1_pfa = (c != 128) ? true : false;
+    t1_ungetchar(c);
+}
+
+static int t1_getbyte(void)
+{
+    int c = t1_getchar();
+    if (t1_pfa)
+        return c;
+    if (t1_block_length == 0) {
+        if (c != 128)
+            normal_error("type 1","invalid marker");
+        c = t1_getchar();
+        if (c == 3) {
+            while (!t1_eof())
+                (void) t1_getchar();
+            return EOF;
+        }
+        t1_block_length = t1_getchar() & 0xff;
+        t1_block_length |= (t1_getchar() & 0xff) << 8;
+        t1_block_length |= (t1_getchar() & 0xff) << 16;
+        t1_block_length |= (t1_getchar() & 0xff) << 24;
+        c = t1_getchar();
+    }
+    t1_block_length--;
+    return c;
+}
+
+static int hexval(int c)
+{
+    if (c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+    else if (c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+    else if (c >= '0' && c <= '9')
+        return c - '0';
+    else
+        return -1;
+}
+
+static byte edecrypt(byte cipher)
+{
+    byte plain;
+    if (t1_pfa) {
+        while (cipher == 10 || cipher == 13)
+            cipher = (byte) t1_getbyte();
+        last_hexbyte = cipher =
+            (byte) ((hexval(cipher) << 4) + hexval(t1_getbyte()));
+    }
+    plain = (byte) (cipher ^ (t1_dr >> 8));
+    t1_dr = (unsigned short) ((cipher + t1_dr) * t1_c1 + t1_c2);
+    return plain;
+}
+
+static byte cdecrypt(byte cipher, unsigned short *cr)
+{
+    const byte plain = (byte) (cipher ^ (*cr >> 8));
+    *cr = (unsigned short) ((cipher + *cr) * t1_c1 + t1_c2);
+    return plain;
+}
+
+static byte eencrypt(byte plain)
+{
+    const byte cipher = (byte) (plain ^ (t1_er >> 8));
+    t1_er = (unsigned short) ((cipher + t1_er) * t1_c1 + t1_c2);
+    return cipher;
+}
+
+static byte cencrypt(byte plain, unsigned short *cr)
+{
+    const byte cipher = (byte) (plain ^ (*cr >> 8));
+    *cr = (unsigned short) ((cipher + *cr) * t1_c1 + t1_c2);
+    return cipher;
+}
+
+static char *eol(char *s)
+{
+    char *p = strend(s);
+    if (p - s > 1 && p[-1] != 10) {
+        *p++ = 10;
+        *p = 0;
+    }
+    return p;
+}
+
+static float t1_scan_num(char *p, char **r)
+{
+    float f;
+    skip_char(p, ' ');
+    if (sscanf(p, "%g", &f) != 1) {
+        remove_eol(p, t1_line_array);
+        formatted_error("type 1","a number expected: '%s'", t1_line_array);
+    }
+    if (r != NULL) {
+        for (; isdigit((unsigned char)*p) || *p == '.' ||
+             *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
+        *r = p;
+    }
+    return f;
+}
+
+static boolean str_suffix(const char *begin_buf, const char *end_buf,
+                          const char *s)
+{
+    const char *s1 = end_buf - 1, *s2 = strend(s) - 1;
+    if (*s1 == 10)
+        s1--;
+    while (s1 >= begin_buf && s2 >= s) {
+        if (*s1-- != *s2--)
+            return false;
+    }
+    return s2 < s;
+}
+
+@ @c
+static void t1_getline(void)
+{
+    int c, l, eexec_scan;
+    char *p;
+    static const char eexec_str[] = "currentfile eexec";
+    static int eexec_len = 17;  /* |strlen(eexec_str)| */
+  restart:
+    if (t1_eof())
+        normal_error("type 1","unexpected end of file");
+    t1_line_ptr = t1_line_array;
+    alloc_array(t1_line, 1, T1_BUF_SIZE);
+    t1_cslen = 0;
+    eexec_scan = 0;
+    c = t1_getbyte();
+    if (c == EOF)
+        goto exit;
+    while (!t1_eof()) {
+        if (t1_in_eexec == 1)
+            c = edecrypt((byte) c);
+        alloc_array(t1_line, 1, T1_BUF_SIZE);
+        {
+            char cc = (char) c;
+            append_char_to_buf(cc, t1_line_ptr, t1_line_array, t1_line_limit);
+        }
+        if (t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
+            if (t1_line_array[eexec_scan] == eexec_str[eexec_scan])
+                eexec_scan++;
+            else
+                eexec_scan = -1;
+        }
+        if (c == 10 || c == 13
+            || (t1_pfa && eexec_scan == eexec_len && c == 32)) {
+            break;
+        }
+        if (t1_cs && t1_cslen == 0 && (t1_line_ptr - t1_line_array > 4) &&
+            (t1_suffix(" RD ") || t1_suffix(" -| "))) {
+            p = t1_line_ptr - 5;
+            while (*p != ' ')
+                p--;
+            l = (int) t1_scan_num(p + 1, 0);
+            t1_cslen = (unsigned short) l;
+            cs_start = (int) (t1_line_ptr - t1_line_array);     /* |cs_start| is an index now */
+            alloc_array(t1_line, l, T1_BUF_SIZE);
+            while (l-- > 0)
+                *t1_line_ptr++ = (t1_line_entry) edecrypt((byte) t1_getbyte());
+        }
+        c = t1_getbyte();
+    }
+    alloc_array(t1_line, 2, T1_BUF_SIZE);       /* |append_eol| can append 2 chars */
+    append_eol(t1_line_ptr, t1_line_array, t1_line_limit);
+    if (t1_line_ptr - t1_line_array < 2)
+        goto restart;
+    if (eexec_scan == eexec_len)
+        t1_in_eexec = 1;
+  exit:
+    /* ensure that |t1_buf_array| has as much room as |t1_line_array| */
+    t1_buf_ptr = t1_buf_array;
+    alloc_array(t1_buf, t1_line_limit, t1_line_limit);
+}
+
+@ @c
+static void t1_putline(PDF pdf)
+{
+    char *p = t1_line_array;
+    if (t1_line_ptr - t1_line_array <= 1)
+        return;
+    if (t1_eexec_encrypt) {
+        while (p < t1_line_ptr)
+            t1_putchar((eight_bits) eencrypt((byte) * p++));
+    } else
+        while (p < t1_line_ptr)
+            t1_putchar((eight_bits) * p++);
+}
+
+static void t1_puts(PDF pdf, const char *s)
+{
+    if (s != t1_line_array)
+        strcpy(t1_line_array, s);
+    t1_line_ptr = strend(t1_line_array);
+    t1_putline(pdf);
+}
+
+__attribute__ ((format(printf, 2, 3)))
+static void t1_printf(PDF pdf, const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    vsprintf(t1_line_array, fmt, args);
+    t1_puts(pdf, t1_line_array);
+    va_end(args);
+}
+
+@ @c
+static void t1_init_params(int open_name_prefix)
+{
+    report_start_file(open_name_prefix,cur_file_name);
+    t1_lenIV = 4;
+    t1_dr = 55665;
+    t1_er = 55665;
+    t1_in_eexec = 0;
+    t1_cs = false;
+    t1_scan = true;
+    t1_synthetic = false;
+    t1_eexec_encrypt = false;
+    t1_block_length = 0;
+    t1_check_pfa();
+}
+
+static void t1_close_font_file(int close_name_suffix)
+{
+    report_stop_file(close_name_suffix);
+    cur_file_name = NULL;
+}
+
+static void t1_check_block_len(boolean decrypt)
+{
+    int l, c;
+    if (t1_block_length == 0)
+        return;
+    c = t1_getbyte();
+    if (decrypt)
+        c = edecrypt((byte) c);
+    l = (int) t1_block_length;
+    if (!(l == 0 && (c == 10 || c == 13))) {
+        formatted_error("type 1","%i bytes more than expected were ignored", l + 1);
+    }
+}
+
+@ @c
+static void t1_start_eexec(PDF pdf)
+{
+    int i;
+    assert(is_included(fd_cur->fm));
+    get_length1();
+    save_offset();
+
+    if (!t1_pfa)
+        t1_check_block_len(false);
+    for (t1_line_ptr = t1_line_array, i = 0; i < 4; i++) {
+        edecrypt((byte) t1_getbyte());
+        *t1_line_ptr++ = 0;
+    }
+    t1_eexec_encrypt = true;
+    t1_putline(pdf);            /* to put the first four bytes */
+}
+
+static void t1_stop_eexec(PDF pdf)
+{
+    int c;
+    assert(is_included(fd_cur->fm));
+    get_length2();
+    save_offset();
+    t1_eexec_encrypt = false;
+    if (!t1_pfa)
+        t1_check_block_len(true);
+    else {
+        c = edecrypt((byte) t1_getbyte());
+        if (!(c == 10 || c == 13)) {
+            if (last_hexbyte == 0)
+                t1_puts(pdf, "00");
+            else
+                normal_error("type 1","unexpected data after eexec");
+        }
+    }
+    t1_cs = false;
+    t1_in_eexec = 2;
+}
+
+@ macros for various transforms; unused, left for reference
+
+@c
+#ifdef T1TRANSFORMMACROS
+#  define do_xshift(x,a) {x[4]+=a;}
+#  define do_yshift(x,a) {x[5]+=a;}
+#  define do_xscale(x,a) {x[0]*=a; x[2]*=a; x[4]*=a;}
+#  define do_yscale(x,a) {x[1]*=a; x[3]*=a; x[5]*=a;}
+#  define do_extend(x,a) {do_xscale(x,a);}
+#  define do_scale(x,a)  {do_xscale(x,a); do_yscale(x,a);}
+#  define do_slant(x,a)  {x[0]+=x[1]*(a); x[2]+=x[3]*(a); x[4]+=x[5]*(a);}
+#  define do_shear(x,a)  {x[1]+=x[0]*(a); x[3]+=x[2]*(a); x[5]+=x[4]*(a);}
+#  define do_rotate(x,a)          \
+  {float t, u=cos(a), v=sin(a); \
+  t    =x[0]*u+x[1]*-v;         \
+  x[1] =x[0]*v+x[1]* u; x[0]=t; \
+  t    =x[2]*u+x[3]*-v;         \
+  x[3] =x[2]*v+x[3]* u; x[2]=t; \
+  t    =x[4]*u+x[5]*-v;         \
+  x[5] =x[4]*v+x[5]* u; x[4]=t;}
+#endif
+
+@ @c
+static void t1_scan_keys(PDF pdf)
+{
+    int i, k;
+    char *p, *q, *r;
+    const key_entry *key;
+    if (t1_prefix("/FontType")) {
+        p = t1_line_array + strlen("FontType") + 1;
+        if ((i = (int) t1_scan_num(p, 0)) != 1)
+            formatted_error("type 1","Type%d fonts unsupported by backend", i);
+        return;
+    }
+    for (key = (const key_entry *) font_key; key - font_key < FONT_KEYS_NUM;
+         key++) {
+        if (key->t1name[0] != '\0'
+            && str_prefix(t1_line_array + 1, key->t1name))
+            break;
+    }
+    if (key - font_key == FONT_KEYS_NUM)
+        return;
+    p = t1_line_array + strlen(key->t1name) + 1;
+    skip_char(p, ' ');
+    if ((k = (int) (key - font_key)) == FONTNAME_CODE) {
+        if (*p != '/') {
+            remove_eol(p, t1_line_array);
+            formatted_error("type 1","a name expected: '%s'", t1_line_array);
+        }
+        r = ++p;                /* skip the slash */
+        for (q = t1_buf_array; *p != ' ' && *p != 10; *q++ = *p++);
+        *q = 0;
+        xfree(fd_cur->fontname);
+        fd_cur->fontname = xstrdup(t1_buf_array);
+        /* at this moment we cannot call |make_subset_tag()| yet, as the encoding
+         is not read; thus we mark the offset of the subset tag and write it
+         later */
+        if (is_subsetted(fd_cur->fm)) {
+            assert(is_included(fd_cur->fm));
+            t1_fontname_offset = (int) (t1_offset() + (r - t1_line_array));
+            strcpy(t1_buf_array, p);
+            sprintf(r, "ABCDEF+%s%s", fd_cur->fontname, t1_buf_array);
+            t1_line_ptr = eol(r);
+        }
+        return;
+    }
+    if ((k == STEMV_CODE || k == FONTBBOX1_CODE) && (*p == '[' || *p == '{'))
+        p++;
+    if (k == FONTBBOX1_CODE) {
+        for (i = 0; i < 4; i++, k++) {
+            fd_cur->font_dim[k].val = (int) t1_scan_num(p, &r);
+            fd_cur->font_dim[k].set = true;
+            p = r;
+        }
+        return;
+    }
+    fd_cur->font_dim[k].val = (int) t1_scan_num(p, 0);
+    fd_cur->font_dim[k].set = true;
+}
+
+@ @c
+static void t1_scan_param(PDF pdf)
+{
+    static const char *lenIV = "/lenIV";
+    if (!t1_scan || *t1_line_array != '/')
+        return;
+    if (t1_prefix(lenIV)) {
+        t1_lenIV = (short) t1_scan_num(t1_line_array + strlen(lenIV), 0);
+        if (t1_lenIV < 0)
+            normal_error("type 1","negative value of lenIV is not supported");
+        return;
+    }
+    t1_scan_keys(pdf);
+}
+
+static void copy_glyph_names(char **glyph_names, int a, int b)
+{
+    if (glyph_names[b] != notdef) {
+        xfree(glyph_names[b]);
+        glyph_names[b] = (char *) notdef;
+    }
+    if (glyph_names[a] != notdef) {
+        glyph_names[b] = xstrdup(glyph_names[a]);
+    }
+}
+
+@ read encoding from Type1 font file, return |glyph_names| array, or |pdffail()|
+
+@c
+static char **t1_builtin_enc(void)
+{
+    int i, a, b, c, counter = 0;
+    char *r, *p, **glyph_names;
+    /* At this moment \.{/Encoding} is the prefix of |t1_line_array| */
+    glyph_names = xtalloc(256, char *);
+    for (i = 0; i < 256; i++)
+        glyph_names[i] = (char *) notdef;
+    if (t1_suffix("def")) {     /* predefined encoding */
+        sscanf(t1_line_array + strlen("/Encoding"), "%255s", t1_buf_array);
+        if (strcmp(t1_buf_array, "StandardEncoding") == 0) {
+            t1_encoding = ENC_STANDARD;
+            for (i = 0; i < 256; i++) {
+                if (standard_glyph_names[i] != notdef)
+                    glyph_names[i] = xstrdup(standard_glyph_names[i]);
+            }
+            return glyph_names;
+        } else
+            formatted_error("type 1","cannot subset font (unknown predefined encoding '%s')",t1_buf_array);
+    }
+    /* At this moment \.{/Encoding} is the prefix of |t1_line_array|, and the encoding is
+     not a predefined encoding.
+
+      We have two possible forms of Encoding vector. The first case is
+
+          \.{/Encoding [/a /b /c...] readonly def}
+
+      and the second case can look like
+
+      {\obeylines
+          \.{/Encoding 256 array 0 1 255 {1 index exch /.notdef put} for}
+          \.{dup 0 /x put}
+          \.{dup 1 /y put}
+          \.{...}
+          \.{readonly def}}
+     */
+    t1_encoding = ENC_BUILTIN;
+    if (t1_prefix("/Encoding [") || t1_prefix("/Encoding[")) {  /* the first case */
+        r = strchr(t1_line_array, '[') + 1;
+        skip_char(r, ' ');
+        for (;;) {
+            while (*r == '/') {
+                for (p = t1_buf_array, r++;
+                     *r != 32 && *r != 10 && *r != ']' && *r != '/';
+                     *p++ = *r++);
+                *p = 0;
+                skip_char(r, ' ');
+                if (counter > 255)
+                    normal_error("type 1","encoding vector contains more than 256 names");
+                if (strcmp(t1_buf_array, notdef) != 0)
+                    glyph_names[counter] = xstrdup(t1_buf_array);
+                counter++;
+            }
+            if (*r != 10 && *r != '%') {
+                if (str_prefix(r, "] def") || str_prefix(r, "] readonly def"))
+                    break;
+                else {
+                    remove_eol(r, t1_line_array);
+                    formatted_error("type 1","a name or '] def' or '] readonly def' expected: '%s'", t1_line_array);
+                }
+            }
+            t1_getline();
+            r = t1_line_array;
+        }
+    } else {                    /* the second case */
+        p = strchr(t1_line_array, 10);
+        for (;;) {
+            if (*p == 10) {
+                t1_getline();
+                p = t1_line_array;
+            }
+            /*
+               check for \.{dup <index> <glyph> put}
+             */
+            if (sscanf(p, "dup %i%255s put", &i, t1_buf_array) == 2 &&
+                *t1_buf_array == '/' && valid_code(i)) {
+                if (strcmp(t1_buf_array + 1, notdef) != 0)
+                    glyph_names[i] = xstrdup(t1_buf_array + 1);
+                p = strstr(p, " put") + strlen(" put");
+                skip_char(p, ' ');
+            }
+            /*
+               check for \.{dup dup <to> exch <from> get put}
+             */
+            else if (sscanf(p, "dup dup %i exch %i get put", &b, &a) == 2
+                     && valid_code(a) && valid_code(b)) {
+                copy_glyph_names(glyph_names, a, b);
+                p = strstr(p, " get put") + strlen(" get put");
+                skip_char(p, ' ');
+            }
+            /*
+               check for \.{dup dup <from> <size> getinterval <to> exch putinterval}
+             */
+            else if (sscanf
+                     (p, "dup dup %i %i getinterval %i exch putinterval",
+                      &a, &c, &b) == 3 && valid_code(a) && valid_code(b)
+                     && valid_code(c)) {
+                for (i = 0; i < c; i++)
+                    copy_glyph_names(glyph_names, a + i, b + i);
+                p = strstr(p, " putinterval") + strlen(" putinterval");
+                skip_char(p, ' ');
+            }
+            /*
+               check for \.{def} or \.{readonly def}
+             */
+            else if ((p == t1_line_array || (p > t1_line_array && p[-1] == ' '))
+                     && strcmp(p, "def\n") == 0)
+                return glyph_names;
+            /*
+               skip an unrecognizable word
+             */
+            else {
+                while (*p != ' ' && *p != 10)
+                    p++;
+                skip_char(p, ' ');
+            }
+        }
+    }
+    return glyph_names;
+}
+
+
+@
+@c
+static void t1_check_end(PDF pdf)
+{
+    if (t1_eof())
+        return;
+    t1_getline();
+    if (t1_prefix("{restore}"))
+        t1_putline(pdf);
+}
+
+@
+@c
+static boolean t1_open_fontfile(int open_name_prefix)
+{
+    ff_entry *ff;
+    int callback_id = 0;
+    int file_opened = 0;
+    t1_curbyte = 0;
+    t1_size = 0;
+    ff = check_ff_exist(fd_cur->fm->ff_name, is_truetype(fd_cur->fm));
+    if (ff->ff_path == NULL) {
+        formatted_error("type 1","cannot open file for reading '%s'",fd_cur->fm->ff_name);
+        return false;
+    }
+    cur_file_name = luatex_find_file(ff->ff_path, find_type1_file_callback);
+    if (cur_file_name == NULL) {
+        formatted_error("type 1","cannot open file for reading '%s'", ff->ff_path);
+        return false;
+    }
+    callback_id = callback_defined(read_type1_file_callback);
+    if (callback_id > 0) {
+        if (!run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &t1_buffer, &t1_size)
+            && file_opened && t1_size > 0) {
+            formatted_warning("type 1","cannot open file for reading '%s'",cur_file_name);
+            return false;
+        }
+    } else {
+        t1_file = xfopen(cur_file_name, FOPEN_RBIN_MODE);
+        t1_read_file();
+        t1_close();
+    }
+    recorder_record_input(cur_file_name);
+    t1_init_params(open_name_prefix);
+    return true;
+}
+
+static void t1_include(PDF pdf)
+{
+    do {
+        t1_getline();
+        t1_scan_param(pdf);
+        t1_putline(pdf);
+    }
+    while (t1_in_eexec == 0);
+    t1_start_eexec(pdf);
+    do {
+        t1_getline();
+        t1_scan_param(pdf);
+        t1_putline(pdf);
+    }
+    while (!(t1_charstrings() || t1_subrs()));
+    t1_cs = true;
+    do {
+        t1_getline();
+        t1_putline(pdf);
+    }
+    while (!t1_end_eexec());
+    t1_stop_eexec(pdf);
+    if (fixedcontent) {         /* copy 512 zeros (not needed for PDF) */
+        do {
+            t1_getline();
+            t1_putline(pdf);
+        }
+        while (!t1_cleartomark());
+        t1_check_end(pdf);      /* write "{restore}if" if found */
+    }
+    get_length3();
+}
+
+@
+@c
+#define check_subr(subr) \
+    if (subr >= subr_size || subr < 0) \
+        formatted_error("type 1","Subrs array: entry index out of range '%i'", subr);
+
+static const char **check_cs_token_pair(void)
+{
+    const char **p = (const char **) cs_token_pairs_list;
+    for (; p[0] != NULL; ++p)
+        if (t1_buf_prefix(p[0]) && t1_buf_suffix(p[1]))
+            return p;
+    return NULL;
+}
+
+static void cs_store(boolean is_subr)
+{
+    char *p;
+    cs_entry *ptr;
+    int subr;
+    for (p = t1_line_array, t1_buf_ptr = t1_buf_array; *p != ' ';
+         *t1_buf_ptr++ = *p++);
+    *t1_buf_ptr = 0;
+    if (is_subr) {
+        subr = (int) t1_scan_num(p + 1, 0);
+        check_subr(subr);
+        ptr = subr_tab + subr;
+    } else {
+        ptr = cs_ptr++;
+        if (cs_ptr - cs_tab > cs_size)
+            formatted_error("type 1","CharStrings dict: more entries than dict size '%i'", cs_size);
+        if (strcmp(t1_buf_array + 1, notdef) == 0)      /* skip the slash */
+            ptr->name = (char *) notdef;
+        else
+            ptr->name = xstrdup(t1_buf_array + 1);
+    }
+    /* copy |" RD " + cs data| to |t1_buf_array| */
+    memcpy(t1_buf_array, t1_line_array + cs_start - 4,
+           (unsigned) (t1_cslen + 4));
+    /* copy the end of cs data to |t1_buf_array| */
+    for (p = t1_line_array + cs_start + t1_cslen, t1_buf_ptr =
+         t1_buf_array + t1_cslen + 4; *p != 10; *t1_buf_ptr++ = *p++);
+    *t1_buf_ptr++ = 10;
+    if (is_subr && cs_token_pair == NULL)
+        cs_token_pair = check_cs_token_pair();
+    ptr->len = (unsigned short) (t1_buf_ptr - t1_buf_array);
+    ptr->cslen = t1_cslen;
+    xfree(ptr->data);           /* mem leak? */
+    ptr->data = xtalloc(ptr->len, byte);
+    memcpy(ptr->data, t1_buf_array, ptr->len);
+    ptr->valid = true;
+}
+
+@
+@c
+#define store_subr() cs_store(true)
+#define store_cs()   cs_store(false)
+
+#define CC_STACK_SIZE 24
+
+static int cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
+static cc_entry cc_tab[CS_MAX];
+static boolean is_cc_init = false;
+
+#define cc_pop(N) \
+    if (stack_ptr - cc_stack < (N)) \
+        stack_error(N); \
+    stack_ptr -= N
+
+#define stack_error(N) { \
+    formatted_error("type 1","CharString: invalid access '%i' to stack, '%i' entries", (int) N, (int)(stack_ptr - cc_stack)); \
+    goto cs_error; \
+}
+
+#define cc_get(N)   ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
+
+#define cc_push(V)  *stack_ptr++ = V
+#define cc_clear()  stack_ptr = cc_stack
+
+#define set_cc(N, B, A, C) \
+    cc_tab[N].nargs = A;   \
+    cc_tab[N].bottom = B;  \
+    cc_tab[N].clear = C;   \
+    cc_tab[N].valid = true
+
+static void cc_init(void)
+{
+    int i;
+    if (is_cc_init)
+        return;
+    for (i = 0; i < CS_MAX; i++)
+        cc_tab[i].valid = false;
+    set_cc(CS_HSTEM, true, 2, true);
+    set_cc(CS_VSTEM, true, 2, true);
+    set_cc(CS_VMOVETO, true, 1, true);
+    set_cc(CS_RLINETO, true, 2, true);
+    set_cc(CS_HLINETO, true, 1, true);
+    set_cc(CS_VLINETO, true, 1, true);
+    set_cc(CS_RRCURVETO, true, 6, true);
+    set_cc(CS_CLOSEPATH, false, 0, true);
+    set_cc(CS_CALLSUBR, false, 1, false);
+    set_cc(CS_RETURN, false, 0, false);
+#if 0
+       set_cc(CS_ESCAPE,          false,  0, false);
+#endif
+    set_cc(CS_HSBW, true, 2, true);
+    set_cc(CS_ENDCHAR, false, 0, true);
+    set_cc(CS_RMOVETO, true, 2, true);
+    set_cc(CS_HMOVETO, true, 1, true);
+    set_cc(CS_VHCURVETO, true, 4, true);
+    set_cc(CS_HVCURVETO, true, 4, true);
+    set_cc(CS_DOTSECTION, false, 0, true);
+    set_cc(CS_VSTEM3, true, 6, true);
+    set_cc(CS_HSTEM3, true, 6, true);
+    set_cc(CS_SEAC, true, 5, true);
+    set_cc(CS_SBW, true, 4, true);
+    set_cc(CS_DIV, false, 2, false);
+    set_cc(CS_CALLOTHERSUBR, false, 0, false);
+    set_cc(CS_POP, false, 0, false);
+    set_cc(CS_SETCURRENTPOINT, true, 2, true);
+    is_cc_init = true;
+}
+
+@
+@c
+#define cs_getchar()    cdecrypt(*data++, &cr)
+
+#define mark_subr(n)    cs_mark(0, n)
+#define mark_cs(s)      cs_mark(s, 0)
+
+static void cs_fail(const char *cs_name, int subr, const char *fmt, ...)
+{
+    char buf[SMALL_BUF_SIZE];
+    va_list args;
+    va_start(args, fmt);
+    vsprintf(buf, fmt, args);
+    va_end(args);
+    if (cs_name == NULL)
+        formatted_error("type 1","Subr '%i': %s", (int) subr, buf);
+    else
+        formatted_error("type 1","CharString (/%s): %s", cs_name, buf);
+}
+
+@ fix a return-less subr by appending |CS_RETURN|
+@c
+static void append_cs_return(cs_entry * ptr)
+{
+    unsigned short cr;
+    int i;
+    byte *p, *q, *data, *new_data;
+    assert(ptr != NULL && ptr->valid && ptr->used);
+
+    /* decrypt the cs data to |t1_buf_array|, append |CS_RETURN| */
+    p = (byte *) t1_buf_array;
+    data = ptr->data + 4;
+    cr = 4330;
+    for (i = 0; i < ptr->cslen; i++)
+        *p++ = cs_getchar();
+    *p = CS_RETURN;
+
+    /* encrypt the new cs data to |new_data| */
+    new_data = xtalloc((unsigned) (ptr->len + 1), byte);
+    memcpy(new_data, ptr->data, 4);
+    p = new_data + 4;
+    q = (byte *) t1_buf_array;
+    cr = 4330;
+    for (i = 0; i < ptr->cslen + 1; i++)
+        *p++ = cencrypt(*q++, &cr);
+    memcpy(p, ptr->data + 4 + ptr->cslen, (size_t) (ptr->len - ptr->cslen - 4));
+
+    /* update |*ptr| */
+    xfree(ptr->data);
+    ptr->data = new_data;
+    ptr->len++;
+    ptr->cslen++;
+}
+
+@
+@c
+static void cs_mark(const char *cs_name, int subr)
+{
+    byte *data;
+    int i, b, cs_len;
+    int last_cmd = 0;
+    int a, a1, a2;
+    unsigned short cr;
+    static int lastargOtherSubr3 = 3;   /* the argument of last call to
+                                           OtherSubrs[3] */
+    cs_entry *ptr;
+    cc_entry *cc;
+    if (cs_name == NULL) {
+        check_subr(subr);
+        ptr = subr_tab + subr;
+        if (!ptr->valid)
+            return;
+    } else {
+        if (cs_notdef != NULL &&
+            (cs_name == notdef || strcmp(cs_name, notdef) == 0))
+            ptr = cs_notdef;
+        else {
+            for (ptr = cs_tab; ptr < cs_ptr; ptr++)
+                if (strcmp(ptr->name, cs_name) == 0)
+                    break;
+            if (ptr == cs_ptr) {
+                formatted_warning("type 1","glyph '%s' undefined", cs_name);
+                return;
+            }
+            if (ptr->name == notdef)
+                cs_notdef = ptr;
+        }
+    }
+    /* only marked CharString entries and invalid entries can be skipped;
+       valid marked subrs must be parsed to keep the stack in sync */
+    if (!ptr->valid || (ptr->used && cs_name != NULL))
+        return;
+    ptr->used = true;
+    cr = 4330;
+    cs_len = ptr->cslen;
+    data = ptr->data + 4;
+    for (i = 0; i < t1_lenIV; i++, cs_len--)
+        cs_getchar();
+    while (cs_len > 0) {
+        --cs_len;
+        b = cs_getchar();
+        if (b >= 32) {
+            if (b <= 246)
+                a = b - 139;
+            else if (b <= 250) {
+                --cs_len;
+                a = ((b - 247) << 8) + 108 + cs_getchar();
+            } else if (b <= 254) {
+                --cs_len;
+                a = -((b - 251) << 8) - 108 - cs_getchar();
+            } else {
+                cs_len -= 4;
+                a = (cs_getchar() & 0xff) << 24;
+                a |= (cs_getchar() & 0xff) << 16;
+                a |= (cs_getchar() & 0xff) << 8;
+                a |= (cs_getchar() & 0xff) << 0;
+                if (sizeof(int) > 4 && (a & 0x80000000))
+                    a |= ~0x7FFFFFFF;
+            }
+            cc_push(a);
+        } else {
+            if (b == CS_ESCAPE) {
+                b = cs_getchar() + CS_1BYTE_MAX;
+                cs_len--;
+            }
+            if (b >= CS_MAX) {
+                cs_fail(cs_name, subr, "command value out of range: %i", (int) b);
+                goto cs_error;
+            }
+            cc = cc_tab + b;
+            if (!cc->valid) {
+                cs_fail(cs_name, subr, "command not valid: %i", (int) b);
+                goto cs_error;
+            }
+            if (cc->bottom) {
+                if (stack_ptr - cc_stack < cc->nargs)
+                    cs_fail(cs_name, subr,
+                            "less arguments on stack '%i' than required '%i'",
+                            (int) (stack_ptr - cc_stack), (int) cc->nargs);
+                else if (stack_ptr - cc_stack > cc->nargs)
+                    cs_fail(cs_name, subr,
+                            "more arguments on stack '%i' than required '%i'",
+                            (int) (stack_ptr - cc_stack), (int) cc->nargs);
+            }
+            last_cmd = b;
+            switch (cc - cc_tab) {
+            case CS_CALLSUBR:
+                a1 = cc_get(-1);
+                cc_pop(1);
+                mark_subr(a1);
+                if (!subr_tab[a1].valid) {
+                    cs_fail(cs_name, subr, "cannot call subr '%i'", (int) a1);
+                    goto cs_error;
+                }
+                break;
+            case CS_DIV:
+                cc_pop(2);
+                cc_push(0);
+                break;
+            case CS_CALLOTHERSUBR:
+                if (cc_get(-1) == 3)
+                    lastargOtherSubr3 = cc_get(-3);
+                a1 = cc_get(-2) + 2;
+                cc_pop(a1);
+                break;
+            case CS_POP:
+                cc_push(lastargOtherSubr3);
+                /* the only case when we care about the value being pushed onto
+                   stack is when POP follows CALLOTHERSUBR (changing hints by
+                   OtherSubrs[3])
+                 */
+                break;
+            case CS_SEAC:
+                a1 = cc_get(3);
+                a2 = cc_get(4);
+                cc_clear();
+                mark_cs(standard_glyph_names[a1]);
+                mark_cs(standard_glyph_names[a2]);
+                break;
+            default:
+                if (cc->clear)
+                    cc_clear();
+            }
+        }
+    }
+    if (cs_name == NULL && last_cmd != CS_RETURN) {
+        formatted_warning("type 1",
+            "last command in subr '%i' is not a RETURN; I will add it now but please consider fixing the font",
+            (int) subr);
+        append_cs_return(ptr);
+    }
+    return;
+  cs_error:                    /* an error occured during parsing */
+    cc_clear();
+    ptr->valid = false;
+    ptr->used = false;
+}
+
+@ AVL search tree for glyph code by glyph name
+@c
+static int comp_t1_glyphs(const void *pa, const void *pb, void *p
+                          __attribute__ ((unused)))
+{
+    return strcmp(*(const char *const *) pa, *(const char *const *) pb);
+}
+
+static struct avl_table *create_t1_glyph_tree(char **glyph_names)
+{
+    int i;
+    void **aa;
+    static struct avl_table *gl_tree;
+    gl_tree = avl_create(comp_t1_glyphs, NULL, &avl_xallocator);
+    assert(gl_tree != NULL);
+    for (i = 0; i < 256; i++) {
+        if (glyph_names[i] != notdef &&
+            (char **) avl_find(gl_tree, &glyph_names[i]) == NULL) {
+            /* no |strdup| here, just point to the |glyph_names| array members */
+            aa = avl_probe(gl_tree, &glyph_names[i]);
+            assert(aa != NULL);
+        }
+    }
+    return gl_tree;
+}
+
+static void destroy_t1_glyph_tree(struct avl_table *gl_tree)
+{
+    assert(gl_tree != NULL);
+    avl_destroy(gl_tree, NULL);
+}
+
+@
+@c
+static void t1_subset_ascii_part(PDF pdf)
+{
+    int j, *p;
+    char *glyph, **gg, **glyph_names;
+    struct avl_table *gl_tree;
+    struct avl_traverser t;
+    void **aa;
+    assert(fd_cur != NULL);
+    assert(fd_cur->gl_tree != NULL);
+    t1_getline();
+    while (!t1_prefix("/Encoding")) {
+        t1_scan_param(pdf);
+        t1_putline(pdf);
+        t1_getline();
+    }
+    glyph_names = t1_builtin_enc();
+    fd_cur->builtin_glyph_names = glyph_names;
+    if (is_subsetted(fd_cur->fm)) {
+        assert(is_included(fd_cur->fm));
+        if (fd_cur->tx_tree != NULL) {
+            /* take over collected non-reencoded characters from \TeX */
+            avl_t_init(&t, fd_cur->tx_tree);
+            for (p = (int *) avl_t_first(&t, fd_cur->tx_tree); p != NULL;
+                 p = (int *) avl_t_next(&t)) {
+                if ((char *) avl_find(fd_cur->gl_tree, glyph_names[*p]) == NULL) {
+                    glyph = xstrdup(glyph_names[*p]);
+                    aa = avl_probe(fd_cur->gl_tree, glyph);
+                    assert(aa != NULL);
+                }
+            }
+        }
+        make_subset_tag(fd_cur);
+        assert(t1_fontname_offset != 0);
+        strncpy((char *) pdf->fb->data + t1_fontname_offset, fd_cur->subset_tag,6);
+    }
+    /* now really all glyphs needed from this font are in the |fd_cur->gl_tree| */
+    if (t1_encoding == ENC_STANDARD)
+        t1_puts(pdf, "/Encoding StandardEncoding def\n");
+    else {
+        t1_puts
+            (pdf,
+             "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
+        gl_tree = create_t1_glyph_tree(glyph_names);
+        avl_t_init(&t, fd_cur->gl_tree);
+        j = 0;
+        for (glyph = (char *) avl_t_first(&t, fd_cur->gl_tree); glyph != NULL;
+             glyph = (char *) avl_t_next(&t)) {
+            if ((gg = (char **) avl_find(gl_tree, &glyph)) != NULL) {
+                t1_printf(pdf, "dup %i /%s put\n", (int) (gg - glyph_names),
+                          *gg);
+                j++;
+            }
+        }
+        destroy_t1_glyph_tree(gl_tree);
+        if (j == 0)
+            /* We didn't mark anything for the Encoding array.
+               We add \.{dup 0 /.notdef put} for compatibility with Acrobat 5.0. */
+            t1_puts(pdf, "dup 0 /.notdef put\n");
+        t1_puts(pdf, "readonly def\n");
+    }
+    do {
+        t1_getline();
+        t1_scan_param(pdf);
+        if (!t1_prefix("/UniqueID"))    /* ignore UniqueID for subsetted fonts */
+            t1_putline(pdf);
+    }
+    while (t1_in_eexec == 0);
+}
+
+
+@
+@c
+static void cs_init(void)
+{
+    cs_ptr = cs_tab = NULL;
+    cs_dict_start = cs_dict_end = NULL;
+    cs_counter = cs_size = cs_size_pos = 0;
+    cs_token_pair = NULL;
+    subr_tab = NULL;
+    subr_array_start = subr_array_end = NULL;
+    subr_max = subr_size = subr_size_pos = 0;
+}
+
+static void init_cs_entry(cs_entry * cs)
+{
+    cs->data = NULL;
+    cs->name = NULL;
+    cs->len = 0;
+    cs->cslen = 0;
+    cs->used = false;
+    cs->valid = false;
+}
+
+@
+@c
+static void t1_read_subrs(PDF pdf)
+{
+    int i, s;
+    cs_entry *ptr;
+    t1_getline();
+    while (!(t1_charstrings() || t1_subrs())) {
+        t1_scan_param(pdf);
+        if (!t1_prefix("/UniqueID"))    /* ignore UniqueID for subsetted fonts */
+            t1_putline(pdf);
+        t1_getline();
+    }
+  found:
+    t1_cs = true;
+    t1_scan = false;
+    if (!t1_subrs())
+        return;
+    subr_size_pos = strlen("/Subrs") + 1;
+    /* |subr_size_pos| points to the number indicating dict size after |"Subrs"| */
+    subr_size = (int) t1_scan_num(t1_line_array + subr_size_pos, 0);
+    if (subr_size == 0) {
+        while (!t1_charstrings())
+            t1_getline();
+        return;
+    }
+    subr_tab = xtalloc((unsigned) subr_size, cs_entry);
+    for (ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
+        init_cs_entry(ptr);
+    subr_array_start = xstrdup(t1_line_array);
+    t1_getline();
+    while (t1_cslen) {
+        store_subr();
+        t1_getline();
+    }
+    /* mark the first four entries without parsing */
+    for (i = 0; i < subr_size && i < 4; i++)
+        subr_tab[i].used = true;
+    /* the end of the Subrs array might have more than one line so we need to
+       concatenate them to |subr_array_end|. Unfortunately some fonts don't have
+       the Subrs array followed by the CharStrings dict immediately (synthetic
+       fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
+       we will treat the font as synthetic and ignore everything until next
+       Subrs is found
+     */
+#define POST_SUBRS_SCAN     5
+    s = 0;
+    *t1_buf_array = 0;
+    for (i = 0; i < POST_SUBRS_SCAN; i++) {
+        if (t1_charstrings())
+            break;
+        s = (int) (s + t1_line_ptr - t1_line_array);
+        alloc_array(t1_buf, s, T1_BUF_SIZE);
+        strcat(t1_buf_array, t1_line_array);
+        t1_getline();
+    }
+    subr_array_end = xstrdup(t1_buf_array);
+    if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
+                                   suppose synthetic font */
+        for (ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
+            if (ptr->valid)
+                xfree(ptr->data);
+        xfree(subr_tab);
+        xfree(subr_array_start);
+        xfree(subr_array_end);
+        cs_init();
+        t1_cs = false;
+        t1_synthetic = true;
+        while (!(t1_charstrings() || t1_subrs()))
+            t1_getline();
+        goto found;
+    }
+}
+
+@
+@c
+#define t1_subr_flush()  t1_flush_cs(pdf, true)
+#define t1_cs_flush()    t1_flush_cs(pdf, false)
+
+static void t1_flush_cs(PDF pdf, boolean is_subr)
+{
+    char *p;
+    byte *r, *return_cs = NULL;
+    cs_entry *tab, *end_tab, *ptr;
+    char *start_line, *line_end;
+    int count, size_pos;
+    unsigned short cr, cs_len;
+    if (is_subr) {
+        start_line = subr_array_start;
+        line_end = subr_array_end;
+        size_pos = subr_size_pos;
+        tab = subr_tab;
+        count = subr_max + 1;
+        end_tab = subr_tab + count;
+    } else {
+        start_line = cs_dict_start;
+        line_end = cs_dict_end;
+        size_pos = cs_size_pos;
+        tab = cs_tab;
+        end_tab = cs_ptr;
+        count = cs_counter;
+    }
+    t1_line_ptr = t1_line_array;
+    for (p = start_line; p - start_line < size_pos;)
+        *t1_line_ptr++ = *p++;
+    while (isdigit((unsigned char)*p))
+        p++;
+    sprintf(t1_line_ptr, "%u", count);
+    strcat(t1_line_ptr, p);
+    t1_line_ptr = eol(t1_line_array);
+    t1_putline(pdf);
+
+    cs_len = 0;                 /* for -Wall */
+    /* create |return_cs| to replace unsused subr's */
+    if (is_subr) {
+        cr = 4330;
+        cs_len = 0;
+        /* at this point we have |t1_lenIV >= 0;|
+         a negative value would be caught in |t1_scan_param| */
+        return_cs = xtalloc((unsigned) (t1_lenIV + 1), byte);
+        for (cs_len = 0, r = return_cs; cs_len < t1_lenIV; cs_len++, r++)
+            *r = cencrypt(0x00, &cr);
+        *r = cencrypt(CS_RETURN, &cr);
+        cs_len++;
+    }
+
+    for (ptr = tab; ptr < end_tab; ptr++) {
+        if (ptr->used) {
+            if (is_subr)
+                sprintf(t1_line_array, "dup %li %u", (long int) (ptr - tab),
+                        ptr->cslen);
+            else
+                sprintf(t1_line_array, "/%s %u", ptr->name, ptr->cslen);
+            p = strend(t1_line_array);
+            memcpy(p, ptr->data, ptr->len);
+            t1_line_ptr = p + ptr->len;
+            t1_putline(pdf);
+        } else {
+            /* replace unsused subr's by |return_cs| */
+            if (is_subr) {
+                sprintf(t1_line_array, "dup %li %u%s ", (long int) (ptr - tab),
+                        cs_len, cs_token_pair[0]);
+                p = strend(t1_line_array);
+                memcpy(p, return_cs, cs_len);
+                t1_line_ptr = p + cs_len;
+                t1_putline(pdf);
+                sprintf(t1_line_array, " %s", cs_token_pair[1]);
+                t1_line_ptr = eol(t1_line_array);
+                t1_putline(pdf);
+            }
+        }
+        xfree(ptr->data);
+	if (is_subr)
+	  ptr->valid=false;
+        if (ptr->name != notdef)
+            xfree(ptr->name);
+    }
+    sprintf(t1_line_array, "%s", line_end);
+    t1_line_ptr = eol(t1_line_array);
+    t1_putline(pdf);
+    if (is_subr) {
+        end_tab = subr_tab + subr_size;
+	for (ptr = tab; ptr < end_tab; ptr++) {
+          if (ptr->valid) {
+            xfree(ptr->data);
+            if (ptr->name != notdef)
+              xfree(ptr->name);
+          }
+        }
+	xfree(return_cs);
+    }
+    xfree(tab);
+    xfree(start_line);
+    xfree(line_end);
+}
+
+@
+@c
+static void t1_mark_glyphs(void)
+{
+    char *glyph;
+    struct avl_traverser t;
+    cs_entry *ptr;
+    if (t1_synthetic || fd_cur->all_glyphs) {   /* mark everything */
+        if (cs_tab != NULL)
+            for (ptr = cs_tab; ptr < cs_ptr; ptr++)
+                if (ptr->valid)
+                    ptr->used = true;
+        if (subr_tab != NULL) {
+            for (ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
+                if (ptr->valid)
+                    ptr->used = true;
+            subr_max = subr_size - 1;
+        }
+        return;
+    }
+    mark_cs(notdef);
+    avl_t_init(&t, fd_cur->gl_tree);
+    for (glyph = (char *) avl_t_first(&t, fd_cur->gl_tree); glyph != NULL;
+         glyph = (char *) avl_t_next(&t)) {
+        mark_cs(glyph);
+    }
+    if (subr_tab != NULL)
+        for (subr_max = -1, ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
+            if (ptr->used && ptr - subr_tab > subr_max)
+                subr_max = (int) (ptr - subr_tab);
+}
+
+
+@ When |t1_subset_charstrings| is called, the |t1_line_array| contains \.{/CharStrings}.
+When we hit a case like this:
+{\obeylines \tt
+         dup/CharStrings
+         229 dict dup begin
+}
+we read the next line and concatenate to |t1_line_array| before moving on. That is
+what |t1_check_unusual_charstring| is for.
+
+@c
+static void t1_check_unusual_charstring(void)
+{
+    char *p = strstr(t1_line_array, charstringname) + strlen(charstringname);
+    int i;
+    /* if no number follows "/CharStrings", let's read the next line */
+    if (sscanf(p, "%i", &i) != 1) {
+        strcpy(t1_buf_array, t1_line_array);
+        t1_getline();
+        alloc_array(t1_buf, strlen(t1_line_array) + strlen(t1_buf_array) + 1, T1_BUF_SIZE);
+        strcat(t1_buf_array, t1_line_array);
+        alloc_array(t1_line, strlen(t1_buf_array) + 1, T1_BUF_SIZE);
+        strcpy(t1_line_array, t1_buf_array);
+        t1_line_ptr = eol(t1_line_array);
+    }
+}
+
+static void t1_subset_charstrings(PDF pdf)
+{
+    cs_entry *ptr;
+
+    t1_check_unusual_charstring();
+
+    cs_size_pos = (int) (strstr(t1_line_array,
+                                charstringname) + strlen(charstringname) -
+                         t1_line_array + 1);
+    /* |cs_size_pos| points to the number indicating
+       dict size after |"/CharStrings"| */
+    cs_size = (int) t1_scan_num(t1_line_array + cs_size_pos, 0);
+    cs_ptr = cs_tab = xtalloc((unsigned) cs_size, cs_entry);
+    for (ptr = cs_tab; ptr - cs_tab < cs_size; ptr++)
+        init_cs_entry(ptr);
+    cs_notdef = NULL;
+    cs_dict_start = xstrdup(t1_line_array);
+    t1_getline();
+    while (t1_cslen) {
+        store_cs();
+        t1_getline();
+    }
+    cs_dict_end = xstrdup(t1_line_array);
+    t1_mark_glyphs();
+    if (subr_tab != NULL) {
+
+
+        if (cs_token_pair == NULL)
+            formatted_error("type 1","mismatched subroutine begin/end token pairs");
+        t1_subr_flush();
+
+    }
+    for (cs_counter = 0, ptr = cs_tab; ptr < cs_ptr; ptr++)
+        if (ptr->used)
+            cs_counter++;
+    t1_cs_flush();
+}
+
+@
+@c
+static void t1_subset_end(PDF pdf)
+{
+    if (t1_synthetic) {         /* copy to \.{dup /FontName get exch definefont pop} */
+        while (!strstr(t1_line_array, "definefont")) {
+            t1_getline();
+            t1_putline(pdf);
+        }
+        while (!t1_end_eexec())
+            t1_getline();       /* ignore the rest */
+        t1_putline(pdf);        /* write \.{mark currentfile closefile} */
+    } else
+        while (!t1_end_eexec()) {       /* copy to \.{mark currentfile closefile} */
+            t1_getline();
+            t1_putline(pdf);
+        }
+    t1_stop_eexec(pdf);
+    if (fixedcontent) {         /* copy 512 zeros (not needed for PDF) */
+        while (!t1_cleartomark()) {
+            t1_getline();
+            t1_putline(pdf);
+        }
+        if (!t1_synthetic)      /* don't check \.{{restore}if} for synthetic fonts */
+            t1_check_end(pdf);  /* write \.{{restore}if} if found */
+    }
+    get_length3();
+}
+
+@
+@c
+void writet1(PDF pdf, fd_entry * fd)
+{
+    fd_cur = fd;                /* |fd_cur| is global inside \.{writet1.w} */
+    assert(fd_cur->fm != NULL);
+    assert(is_type1(fd->fm));
+    assert(is_included(fd->fm));
+
+    t1_save_offset = 0;
+    if (!is_subsetted(fd_cur->fm)) {    /* include entire font */
+        if (!(fd->ff_found = t1_open_fontfile(filetype_font)))
+            return;
+        t1_include(pdf);
+        t1_close_font_file(filetype_font);
+        xfree(t1_buffer);
+        return;
+    }
+    /* partial downloading */
+    if (!(fd->ff_found = t1_open_fontfile(filetype_subset)))
+        return;
+    t1_subset_ascii_part(pdf);
+    t1_start_eexec(pdf);
+    cc_init();
+    cs_init();
+    t1_read_subrs(pdf);
+    t1_subset_charstrings(pdf);
+    t1_subset_end(pdf);
+    t1_close_font_file(filetype_subset);
+    xfree(t1_buffer);
+}
+
+@
+@c
+void t1_free(void)
+{
+    xfree(t1_line_array);
+    xfree(t1_buf_array);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/writet1.c
+++ /dev/null
@@ -1,1695 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-Copyright 2006-2009 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <string.h>
-
-#define get_length1()              t1_length1 = t1_offset() - t1_save_offset
-#define get_length2()              t1_length2 = t1_offset() - t1_save_offset
-#define get_length3()              t1_length3 = fixedcontent? t1_offset() - t1_save_offset : 0
-#define save_offset()              t1_save_offset = t1_offset()
-#define t1_putchar(A)              strbuf_putchar(pdf->fb, (A))
-#define t1_offset()                strbuf_offset(pdf->fb)
-#define out_eexec_char             t1_putchar
-#define end_last_eexec_line()      t1_eexec_encrypt = false
-#define t1_char(c)                 c
-#define embed_all_glyphs(tex_font) fm_cur->all_glyphs
-#define extra_charset()            fm_cur->charset
-#define fixedcontent               false
-
-int t1_length1, t1_length2, t1_length3;
-static int t1_save_offset;
-static int t1_fontname_offset;
-
-static unsigned char *t1_buffer = NULL;
-static int t1_size = 0;
-static int t1_curbyte = 0;
-
-#define t1_read_file()   readbinfile(t1_file,&t1_buffer,&t1_size)
-#define t1_close()       xfclose(t1_file,cur_file_name)
-#define t1_getchar()     t1_buffer[t1_curbyte++]
-#define t1_ungetchar(c)  t1_curbyte--
-#define t1_eof()         (t1_curbyte>t1_size)
-
-#define t1_prefix(s)     str_prefix(t1_line_array, s)
-#define t1_buf_prefix(s) str_prefix(t1_buf_array, s)
-#define t1_suffix(s)     str_suffix(t1_line_array, t1_line_ptr, s)
-#define t1_buf_suffix(s) str_suffix(t1_buf_array, t1_buf_ptr, s)
-#define t1_charstrings() strstr(t1_line_array, charstringname)
-#define t1_subrs()       t1_prefix("/Subrs")
-#define t1_end_eexec()   t1_suffix("mark currentfile closefile")
-#define t1_cleartomark() t1_prefix("cleartomark")
-
-static unsigned char *enc_buffer = NULL;
-static int enc_size = 0;
-static int enc_curbyte = 0;
-
-#define enc_open(a)     (enc_file = fopen((char *)(a), FOPEN_RBIN_MODE))
-#define enc_read_file() readbinfile(enc_file,&enc_buffer,&enc_size)
-#define enc_close()     xfclose(enc_file,cur_file_name)
-#define enc_getchar()   enc_buffer[enc_curbyte++]
-#define enc_eof()       (enc_curbyte>enc_size)
-
-#define valid_code(c)  (c >= 0 && c < 256)
-#define fixedcontent    false
-
-static const char *standard_glyph_names[256] = {
-    /* 0x00 */
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    /* 0x10 */
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    /* 0x20 */
-    "space", "exclam", "quotedbl", "numbersign", "dollar", "percent",
-    "ampersand", "quoteright", "parenleft", "parenright", "asterisk",
-    "plus", "comma", "hyphen", "period", "slash",
-    /* 0x30 */
-    "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
-    "nine", "colon", "semicolon", "less", "equal", "greater", "question",
-    /* 0x40 */
-    "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
-    "O",
-    /* 0x50 */
-    "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
-    "backslash", "bracketright", "asciicircum", "underscore",
-    /* 0x60 */
-    "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
-    "m", "n", "o",
-    /* 0x70 */
-    "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar",
-    "braceright", "asciitilde", notdef,
-    /* 0x80 */
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    /* 0x90 */
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    /* 0xa0 */
-    notdef, "exclamdown", "cent", "sterling", "fraction", "yen", "florin",
-    "section", "currency", "quotesingle", "quotedblleft", "guillemotleft",
-    "guilsinglleft", "guilsinglright", "fi", "fl",
-    /* 0xb0 */
-    notdef, "endash", "dagger", "daggerdbl", "periodcentered", notdef,
-    "paragraph", "bullet", "quotesinglbase", "quotedblbase",
-    "quotedblright", "guillemotright", "ellipsis", "perthousand", notdef,
-    "questiondown",
-    /* 0xc0 */
-    notdef, "grave", "acute", "circumflex", "tilde", "macron", "breve",
-    "dotaccent", "dieresis", notdef,
-    "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron",
-    /* 0xd0 */
-    "emdash", notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    notdef, notdef, notdef, notdef, notdef, notdef, notdef,
-    /* 0xe0 */
-    notdef, "AE", notdef, "ordfeminine", notdef, notdef, notdef, notdef,
-    "Lslash", "Oslash", "OE", "ordmasculine", notdef, notdef, notdef,
-    notdef,
-    /* 0xf0 */
-    notdef, "ae", notdef, notdef, notdef, "dotlessi", notdef, notdef, "lslash",
-    "oslash", "oe", "germandbls", notdef, notdef, notdef, notdef
-};
-
-static fd_entry *fd_cur;
-
-static char charstringname[] = "/CharStrings";
-
-enum { ENC_STANDARD, ENC_BUILTIN } t1_encoding;
-
-#define T1_BUF_SIZE   0x0010
-#define ENC_BUF_SIZE  0x1000
-
-#define CS_HSTEM             1
-#define CS_VSTEM             3
-#define CS_VMOVETO           4
-#define CS_RLINETO           5
-#define CS_HLINETO           6
-#define CS_VLINETO           7
-#define CS_RRCURVETO         8
-#define CS_CLOSEPATH         9
-#define CS_CALLSUBR         10
-#define CS_RETURN           11
-#define CS_ESCAPE           12
-#define CS_HSBW             13
-#define CS_ENDCHAR          14
-#define CS_RMOVETO          21
-#define CS_HMOVETO          22
-#define CS_VHCURVETO        30
-#define CS_HVCURVETO        31
-#define CS_1BYTE_MAX        (CS_HVCURVETO + 1)
-
-#define CS_DOTSECTION       CS_1BYTE_MAX +  0
-#define CS_VSTEM3           CS_1BYTE_MAX +  1
-#define CS_HSTEM3           CS_1BYTE_MAX +  2
-#define CS_SEAC             CS_1BYTE_MAX +  6
-#define CS_SBW              CS_1BYTE_MAX +  7
-#define CS_DIV              CS_1BYTE_MAX + 12
-#define CS_CALLOTHERSUBR    CS_1BYTE_MAX + 16
-#define CS_POP              CS_1BYTE_MAX + 17
-#define CS_SETCURRENTPOINT  CS_1BYTE_MAX + 33
-#define CS_2BYTE_MAX        (CS_SETCURRENTPOINT + 1)
-#define CS_MAX              CS_2BYTE_MAX
-
-typedef unsigned char byte;
-
-/*tex A |CharString| command: */
-
-typedef struct {
-    /*tex number of arguments */
-    byte nargs;
-    /*tex take arguments from bottom of stack? */
-    boolean bottom;
-    /*tex clear stack? */
-    boolean clear;
-    boolean valid;
-} cc_entry;
-
-typedef struct {
-    /*tex glyph name (or |notdef| for |Subrs| entry) */
-    char *name;
-    byte *data;
-    /*tex length of the whole string */
-    unsigned short len;
-    /*tex length of the encoded part of the string */
-    unsigned short cslen;
-    boolean used;
-    boolean valid;
-} cs_entry;
-
-static unsigned short t1_dr, t1_er;
-static const unsigned short t1_c1 = 52845, t1_c2 = 22719;
-static unsigned short t1_cslen;
-static short t1_lenIV;
-static char enc_line[ENC_BUF_SIZE];
-
-#define t1_line_entry char
-define_array(t1_line);
-
-#define t1_buf_entry char
-define_array(t1_buf);
-
-static int cs_start;
-
-static cs_entry *cs_tab, *cs_ptr, *cs_notdef;
-static char *cs_dict_start, *cs_dict_end;
-static int cs_counter, cs_size, cs_size_pos;
-
-static cs_entry *subr_tab;
-static char *subr_array_start, *subr_array_end;
-static int subr_max, subr_size, subr_size_pos;
-
-/*tex
-
-    This list contains the begin/end tokens commonly used in the |/Subrs| array of
-    a Type 1 font.
-
-*/
-
-static const char *cs_token_pairs_list[][2] = {
-    { " RD", "NP" },
-    { " -|", "|" },
-    { " RD", "noaccess put" },
-    { " -|", "noaccess put" },
-    { NULL,  NULL }
-};
-
-static const char **cs_token_pair;
-
-static boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
-
-/*tex This one becomes 0 before 1 during and 2 after |eexec| encryption. */
-
-static int t1_in_eexec;
-
-static long t1_block_length;
-static int last_hexbyte;
-static FILE *t1_file;
-static FILE *enc_file;
-
-static void enc_getline(void)
-{
-    char *p;
-    char c;
-  restart:
-    if (enc_eof())
-        normal_error("type 1","unexpected end of file");
-    p = enc_line;
-    do {
-        c = (char) enc_getchar();
-        append_char_to_buf(c, p, enc_line, ENC_BUF_SIZE);
-    }
-    while (c != 10 && !enc_eof());
-    append_eol(p, enc_line, ENC_BUF_SIZE);
-    if (p - enc_line < 2 || *enc_line == '%')
-        goto restart;
-}
-
-/*tex
-
-    Read encoding from .enc file, return |glyph_names array|, or |pdffail|.
-
-*/
-
-char **load_enc_file(char *enc_name)
-{
-    int callback_id = 0;
-    int file_opened = 0;
-    char buf[ENC_BUF_SIZE], *p, *r;
-    int i, names_count;
-    char **glyph_names;
-    cur_file_name = luatex_find_file(enc_name, find_enc_file_callback);
-    if (cur_file_name == NULL) {
-        formatted_error("type 1","cannot find encoding file '%s' for reading", enc_name);
-    }
-    callback_id = callback_defined(read_enc_file_callback);
-    enc_curbyte = 0;
-    enc_size = 0;
-    if (callback_id > 0) {
-        if (run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &enc_buffer, &enc_size)) {
-            if ((!file_opened) || enc_size == 0) {
-                formatted_error("type 1","cannot open encoding file '%s' for reading", cur_file_name);
-            }
-        }
-    } else {
-        if (!enc_open(cur_file_name)) {
-            formatted_error("type 1","cannot open encoding file '%s' for reading", cur_file_name);
-        }
-        enc_read_file();
-        enc_close();
-    }
-    glyph_names = xtalloc(256, char *);
-    for (i = 0; i < 256; i++)
-        glyph_names[i] = (char *) notdef;
-    report_start_file(filetype_map,cur_file_name);
-    enc_getline();
-    if (*enc_line != '/' || (r = strchr(enc_line, '[')) == NULL) {
-        remove_eol(r, enc_line);
-        formatted_error("type 1","invalid encoding vector (a name or '[' missing): '%s'", enc_line);
-    }
-    names_count = 0;
-    /*tex Skip |[|: */
-    r++;
-    skip_char(r, ' ');
-    for (;;) {
-        while (*r == '/') {
-            for (p = buf, r++;
-                 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
-            *p = 0;
-            skip_char(r, ' ');
-            if (names_count >= 256)
-                normal_error("type 1","encoding vector contains more than 256 names");
-            if (strcmp(buf, notdef) != 0)
-                glyph_names[names_count] = xstrdup(buf);
-            names_count++;
-        }
-        if (*r != 10 && *r != '%') {
-            if (strncmp(r, "] def", strlen("] def")) == 0)
-                goto done;
-            else {
-                remove_eol(r, enc_line);
-                formatted_error("type 1","invalid encoding vector: a name or '] def' expected: `%s'",enc_line);
-            }
-        }
-        enc_getline();
-        r = enc_line;
-    }
-  done:
-    report_stop_file(filetype_map);
-    cur_file_name = NULL;
-    xfree(enc_buffer);
-    return glyph_names;
-}
-
-static void t1_check_pfa(void)
-{
-    const int c = t1_getchar();
-    t1_pfa = (c != 128) ? true : false;
-    t1_ungetchar(c);
-}
-
-static int t1_getbyte(void)
-{
-    int c = t1_getchar();
-    if (t1_pfa)
-        return c;
-    if (t1_block_length == 0) {
-        if (c != 128)
-            normal_error("type 1","invalid marker");
-        c = t1_getchar();
-        if (c == 3) {
-            while (!t1_eof())
-                (void) t1_getchar();
-            return EOF;
-        }
-        t1_block_length = t1_getchar() & 0xff;
-        t1_block_length |= (t1_getchar() & 0xff) << 8;
-        t1_block_length |= (t1_getchar() & 0xff) << 16;
-        t1_block_length |= (t1_getchar() & 0xff) << 24;
-        c = t1_getchar();
-    }
-    t1_block_length--;
-    return c;
-}
-
-static int hexval(int c)
-{
-    if (c >= 'A' && c <= 'F')
-        return c - 'A' + 10;
-    else if (c >= 'a' && c <= 'f')
-        return c - 'a' + 10;
-    else if (c >= '0' && c <= '9')
-        return c - '0';
-    else
-        return -1;
-}
-
-static byte edecrypt(byte cipher)
-{
-    byte plain;
-    if (t1_pfa) {
-        while (cipher == 10 || cipher == 13)
-            cipher = (byte) t1_getbyte();
-        last_hexbyte = cipher = (byte) ((hexval(cipher) << 4) + hexval(t1_getbyte()));
-    }
-    plain = (byte) (cipher ^ (t1_dr >> 8));
-    t1_dr = (unsigned short) ((cipher + t1_dr) * t1_c1 + t1_c2);
-    return plain;
-}
-
-static byte cdecrypt(byte cipher, unsigned short *cr)
-{
-    const byte plain = (byte) (cipher ^ (*cr >> 8));
-    *cr = (unsigned short) ((cipher + *cr) * t1_c1 + t1_c2);
-    return plain;
-}
-
-static byte eencrypt(byte plain)
-{
-    const byte cipher = (byte) (plain ^ (t1_er >> 8));
-    t1_er = (unsigned short) ((cipher + t1_er) * t1_c1 + t1_c2);
-    return cipher;
-}
-
-static byte cencrypt(byte plain, unsigned short *cr)
-{
-    const byte cipher = (byte) (plain ^ (*cr >> 8));
-    *cr = (unsigned short) ((cipher + *cr) * t1_c1 + t1_c2);
-    return cipher;
-}
-
-static char *eol(char *s)
-{
-    char *p = strend(s);
-    if (p - s > 1 && p[-1] != 10) {
-        *p++ = 10;
-        *p = 0;
-    }
-    return p;
-}
-
-static float t1_scan_num(char *p, char **r)
-{
-    float f;
-    skip_char(p, ' ');
-    if (sscanf(p, "%g", &f) != 1) {
-        remove_eol(p, t1_line_array);
-        formatted_error("type 1","a number expected: '%s'", t1_line_array);
-    }
-    if (r != NULL) {
-        for (; isdigit((unsigned char)*p) || *p == '.' ||
-             *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
-        *r = p;
-    }
-    return f;
-}
-
-static boolean str_suffix(const char *begin_buf, const char *end_buf, const char *s)
-{
-    const char *s1 = end_buf - 1, *s2 = strend(s) - 1;
-    if (*s1 == 10)
-        s1--;
-    while (s1 >= begin_buf && s2 >= s) {
-        if (*s1-- != *s2--)
-            return false;
-    }
-    return s2 < s;
-}
-
-static void t1_getline(void)
-{
-    int c, l, eexec_scan;
-    char *p;
-    static const char eexec_str[] = "currentfile eexec";
-    static int eexec_len = 17;
-  restart:
-    if (t1_eof())
-        normal_error("type 1","unexpected end of file");
-    t1_line_ptr = t1_line_array;
-    alloc_array(t1_line, 1, T1_BUF_SIZE);
-    t1_cslen = 0;
-    eexec_scan = 0;
-    c = t1_getbyte();
-    if (c == EOF)
-        goto exit;
-    while (!t1_eof()) {
-        if (t1_in_eexec == 1)
-            c = edecrypt((byte) c);
-        alloc_array(t1_line, 1, T1_BUF_SIZE);
-        {
-            char cc = (char) c;
-            append_char_to_buf(cc, t1_line_ptr, t1_line_array, t1_line_limit);
-        }
-        if (t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
-            if (t1_line_array[eexec_scan] == eexec_str[eexec_scan])
-                eexec_scan++;
-            else
-                eexec_scan = -1;
-        }
-        if (c == 10 || c == 13
-            || (t1_pfa && eexec_scan == eexec_len && c == 32)) {
-            break;
-        }
-        if (t1_cs && t1_cslen == 0 && (t1_line_ptr - t1_line_array > 4) &&
-            (t1_suffix(" RD ") || t1_suffix(" -| "))) {
-            p = t1_line_ptr - 5;
-            while (*p != ' ')
-                p--;
-            l = (int) t1_scan_num(p + 1, 0);
-            t1_cslen = (unsigned short) l;
-            /*tex |cs_start| is an index now */
-            cs_start = (int) (t1_line_ptr - t1_line_array);
-            alloc_array(t1_line, l, T1_BUF_SIZE);
-            while (l-- > 0)
-                *t1_line_ptr++ = (t1_line_entry) edecrypt((byte) t1_getbyte());
-        }
-        c = t1_getbyte();
-    }
-    /*tex |append_eol| can append 2 chars */
-    alloc_array(t1_line, 2, T1_BUF_SIZE);
-    append_eol(t1_line_ptr, t1_line_array, t1_line_limit);
-    if (t1_line_ptr - t1_line_array < 2)
-        goto restart;
-    if (eexec_scan == eexec_len)
-        t1_in_eexec = 1;
-  exit:
-    /*tex Ensure that |t1_buf_array| has as much room as |t1_line_array|. */
-    t1_buf_ptr = t1_buf_array;
-    alloc_array(t1_buf, t1_line_limit, t1_line_limit);
-}
-
-static void t1_putline(PDF pdf)
-{
-    char *p = t1_line_array;
-    if (t1_line_ptr - t1_line_array <= 1)
-        return;
-    if (t1_eexec_encrypt) {
-        while (p < t1_line_ptr)
-            t1_putchar((eight_bits) eencrypt((byte) * p++));
-    } else
-        while (p < t1_line_ptr)
-            t1_putchar((eight_bits) * p++);
-}
-
-static void t1_puts(PDF pdf, const char *s)
-{
-    if (s != t1_line_array)
-        strcpy(t1_line_array, s);
-    t1_line_ptr = strend(t1_line_array);
-    t1_putline(pdf);
-}
-
-__attribute__ ((format(printf, 2, 3)))
-static void t1_printf(PDF pdf, const char *fmt, ...)
-{
-    va_list args;
-    va_start(args, fmt);
-    vsprintf(t1_line_array, fmt, args);
-    t1_puts(pdf, t1_line_array);
-    va_end(args);
-}
-
-static void t1_init_params(int open_name_prefix)
-{
-    report_start_file(open_name_prefix,cur_file_name);
-    t1_lenIV = 4;
-    t1_dr = 55665;
-    t1_er = 55665;
-    t1_in_eexec = 0;
-    t1_cs = false;
-    t1_scan = true;
-    t1_synthetic = false;
-    t1_eexec_encrypt = false;
-    t1_block_length = 0;
-    t1_check_pfa();
-}
-
-static void t1_close_font_file(int close_name_suffix)
-{
-    report_stop_file(close_name_suffix);
-    cur_file_name = NULL;
-}
-
-static void t1_check_block_len(boolean decrypt)
-{
-    int l, c;
-    if (t1_block_length == 0)
-        return;
-    c = t1_getbyte();
-    if (decrypt)
-        c = edecrypt((byte) c);
-    l = (int) t1_block_length;
-    if (!(l == 0 && (c == 10 || c == 13))) {
-        formatted_error("type 1","%i bytes more than expected were ignored", l + 1);
-    }
-}
-
-static void t1_start_eexec(PDF pdf)
-{
-    int i;
-    get_length1();
-    save_offset();
-    if (!t1_pfa)
-        t1_check_block_len(false);
-    for (t1_line_ptr = t1_line_array, i = 0; i < 4; i++) {
-        edecrypt((byte) t1_getbyte());
-        *t1_line_ptr++ = 0;
-    }
-    t1_eexec_encrypt = true;
-    /*tex To put the first four bytes: */
-    t1_putline(pdf);
-}
-
-static void t1_stop_eexec(PDF pdf)
-{
-    int c;
-    get_length2();
-    save_offset();
-    t1_eexec_encrypt = false;
-    if (!t1_pfa)
-        t1_check_block_len(true);
-    else {
-        c = edecrypt((byte) t1_getbyte());
-        if (!(c == 10 || c == 13)) {
-            if (last_hexbyte == 0)
-                t1_puts(pdf, "00");
-            else
-                normal_error("type 1","unexpected data after eexec");
-        }
-    }
-    t1_cs = false;
-    t1_in_eexec = 2;
-}
-
-/*tex Macros for various transforms; unused, left for reference: */
-
-#ifdef T1TRANSFORMMACROS
-#  define do_xshift(x,a) {x[4]+=a;}
-#  define do_yshift(x,a) {x[5]+=a;}
-#  define do_xscale(x,a) {x[0]*=a; x[2]*=a; x[4]*=a;}
-#  define do_yscale(x,a) {x[1]*=a; x[3]*=a; x[5]*=a;}
-#  define do_extend(x,a) {do_xscale(x,a);}
-#  define do_scale(x,a)  {do_xscale(x,a); do_yscale(x,a);}
-#  define do_slant(x,a)  {x[0]+=x[1]*(a); x[2]+=x[3]*(a); x[4]+=x[5]*(a);}
-#  define do_shear(x,a)  {x[1]+=x[0]*(a); x[3]+=x[2]*(a); x[5]+=x[4]*(a);}
-
-#  define do_rotate(x,a) {        \
-    float t, u=cos(a), v=sin(a);  \
-    t    =x[0]*u+x[1]*-v;         \
-    x[1] =x[0]*v+x[1]* u; x[0]=t; \
-    t    =x[2]*u+x[3]*-v;         \
-    x[3] =x[2]*v+x[3]* u; x[2]=t; \
-    t    =x[4]*u+x[5]*-v;         \
-    x[5] =x[4]*v+x[5]* u; x[4]=t; \
-}
-#endif
-
-static void t1_scan_keys(PDF pdf)
-{
-    int i, k;
-    char *p, *q, *r;
-    const key_entry *key;
-    if (t1_prefix("/FontType")) {
-        p = t1_line_array + strlen("FontType") + 1;
-        if ((i = (int) t1_scan_num(p, 0)) != 1)
-            formatted_error("type 1","Type%d fonts unsupported by backend", i);
-        return;
-    }
-    for (key = (const key_entry *) font_key; key - font_key < FONT_KEYS_NUM;
-         key++) {
-        if (key->t1name[0] != '\0'
-            && str_prefix(t1_line_array + 1, key->t1name))
-            break;
-    }
-    if (key - font_key == FONT_KEYS_NUM)
-        return;
-    p = t1_line_array + strlen(key->t1name) + 1;
-    skip_char(p, ' ');
-    if ((k = (int) (key - font_key)) == FONTNAME_CODE) {
-        if (*p != '/') {
-            remove_eol(p, t1_line_array);
-            formatted_error("type 1","a name expected: '%s'", t1_line_array);
-        }
-        /*tex Skip the slash. */
-        r = ++p;
-        for (q = t1_buf_array; *p != ' ' && *p != 10; *q++ = *p++);
-        *q = 0;
-        xfree(fd_cur->fontname);
-        fd_cur->fontname = xstrdup(t1_buf_array);
-        /*tex
-
-            At this moment we cannot call |make_subset_tag| yet, as the encoding
-            is not read; thus we mark the offset of the subset tag and write it
-            later.
-
-        */
-        if (is_subsetted(fd_cur->fm)) {
-            t1_fontname_offset = (int) (t1_offset() + (r - t1_line_array));
-            strcpy(t1_buf_array, p);
-            sprintf(r, "ABCDEF+%s%s", fd_cur->fontname, t1_buf_array);
-            t1_line_ptr = eol(r);
-        }
-        return;
-    }
-    if ((k == STEMV_CODE || k == FONTBBOX1_CODE) && (*p == '[' || *p == '{'))
-        p++;
-    if (k == FONTBBOX1_CODE) {
-        for (i = 0; i < 4; i++, k++) {
-            fd_cur->font_dim[k].val = (int) t1_scan_num(p, &r);
-            fd_cur->font_dim[k].set = true;
-            p = r;
-        }
-        return;
-    }
-    fd_cur->font_dim[k].val = (int) t1_scan_num(p, 0);
-    fd_cur->font_dim[k].set = true;
-}
-
-static void t1_scan_param(PDF pdf)
-{
-    static const char *lenIV = "/lenIV";
-    if (!t1_scan || *t1_line_array != '/')
-        return;
-    if (t1_prefix(lenIV)) {
-        t1_lenIV = (short) t1_scan_num(t1_line_array + strlen(lenIV), 0);
-        if (t1_lenIV < 0)
-            normal_error("type 1","negative value of lenIV is not supported");
-        return;
-    }
-    t1_scan_keys(pdf);
-}
-
-static void copy_glyph_names(char **glyph_names, int a, int b)
-{
-    if (glyph_names[b] != notdef) {
-        xfree(glyph_names[b]);
-        glyph_names[b] = (char *) notdef;
-    }
-    if (glyph_names[a] != notdef) {
-        glyph_names[b] = xstrdup(glyph_names[a]);
-    }
-}
-
-/*tex Read encoding from Type1 font file, return |glyph_names| array, or |pdffail|. */
-
-static char **t1_builtin_enc(void)
-{
-    int i, a, b, c, counter = 0;
-    char *r, *p, **glyph_names;
-    /*tex At this moment |/Encoding| is the prefix of |t1_line_array|. */
-    glyph_names = xtalloc(256, char *);
-    for (i = 0; i < 256; i++)
-        glyph_names[i] = (char *) notdef;
-    if (t1_suffix("def")) {
-        /*tex A predefined encoding: */
-        sscanf(t1_line_array + strlen("/Encoding"), "%255s", t1_buf_array);
-        if (strcmp(t1_buf_array, "StandardEncoding") == 0) {
-            t1_encoding = ENC_STANDARD;
-            for (i = 0; i < 256; i++) {
-                if (standard_glyph_names[i] != notdef)
-                    glyph_names[i] = xstrdup(standard_glyph_names[i]);
-            }
-            return glyph_names;
-        } else
-            formatted_error("type 1","cannot subset font (unknown predefined encoding '%s')",t1_buf_array);
-    }
-    /*
-
-        At this moment |/Encoding| is the prefix of |t1_line_array|, and the
-        encoding is not a predefined encoding. We have two possible forms of
-        vector. The first case is
-
-        \starttyping
-        /Encoding [
-            /a /b /c ...
-        ] readonly def
-        \stoptyping
-
-        and the second case can look like
-
-        \starttyping
-        /Encoding 256 array 0 1 255 {
-            1 index exch /.notdef put} for
-            dup 0 /x put
-            dup 1 /y put
-            ...
-        } readonly def
-        \stoptyping
-
-    */
-    t1_encoding = ENC_BUILTIN;
-    if (t1_prefix("/Encoding [") || t1_prefix("/Encoding[")) {  /* the first case */
-        r = strchr(t1_line_array, '[') + 1;
-        skip_char(r, ' ');
-        for (;;) {
-            while (*r == '/') {
-                for (p = t1_buf_array, r++; *r != 32 && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
-                *p = 0;
-                skip_char(r, ' ');
-                if (counter > 255)
-                    normal_error("type 1","encoding vector contains more than 256 names");
-                if (strcmp(t1_buf_array, notdef) != 0)
-                    glyph_names[counter] = xstrdup(t1_buf_array);
-                counter++;
-            }
-            if (*r != 10 && *r != '%') {
-                if (str_prefix(r, "] def") || str_prefix(r, "] readonly def"))
-                    break;
-                else {
-                    remove_eol(r, t1_line_array);
-                    formatted_error("type 1","a name or '] def' or '] readonly def' expected: '%s'", t1_line_array);
-                }
-            }
-            t1_getline();
-            r = t1_line_array;
-        }
-    } else {
-        /*tex The second case. */
-        p = strchr(t1_line_array, 10);
-        for (;;) {
-            if (*p == 10) {
-                t1_getline();
-                p = t1_line_array;
-            }
-            /*tex Check for |dup <index> <glyph> put|. */
-            if (sscanf(p, "dup %i%255s put", &i, t1_buf_array) == 2 &&
-                *t1_buf_array == '/' && valid_code(i)) {
-                if (strcmp(t1_buf_array + 1, notdef) != 0)
-                    glyph_names[i] = xstrdup(t1_buf_array + 1);
-                p = strstr(p, " put") + strlen(" put");
-                skip_char(p, ' ');
-            }
-            /*tex Check for |dup dup <to> exch <from> get put|. */
-            else if (sscanf(p, "dup dup %i exch %i get put", &b, &a) == 2 && valid_code(a) && valid_code(b)) {
-                copy_glyph_names(glyph_names, a, b);
-                p = strstr(p, " get put") + strlen(" get put");
-                skip_char(p, ' ');
-            }
-            /*tex Check for |dup dup <from> <size> getinterval <to> exch putinterval|. */
-            else if (sscanf(p, "dup dup %i %i getinterval %i exch putinterval",
-                    &a, &c, &b) == 3 && valid_code(a) && valid_code(b) && valid_code(c)) {
-                for (i = 0; i < c; i++)
-                    copy_glyph_names(glyph_names, a + i, b + i);
-                p = strstr(p, " putinterval") + strlen(" putinterval");
-                skip_char(p, ' ');
-            }
-            /*tex Check for |def or |readonly def|. */
-            else if ((p == t1_line_array || (p > t1_line_array && p[-1] == ' ')) && strcmp(p, "def\n") == 0) {
-                return glyph_names;
-            } else {
-                /*tex Skip an unrecognizable word. */
-                while (*p != ' ' && *p != 10)
-                    p++;
-                skip_char(p, ' ');
-            }
-        }
-    }
-    return glyph_names;
-}
-
-static void t1_check_end(PDF pdf)
-{
-    if (t1_eof())
-        return;
-    t1_getline();
-    if (t1_prefix("{restore}"))
-        t1_putline(pdf);
-}
-
-static boolean t1_open_fontfile(int open_name_prefix)
-{
-    ff_entry *ff;
-    int callback_id = 0;
-    int file_opened = 0;
-    t1_curbyte = 0;
-    t1_size = 0;
-    ff = check_ff_exist(fd_cur->fm->ff_name, is_truetype(fd_cur->fm));
-    if (ff->ff_path == NULL) {
-        formatted_error("type 1","cannot open file for reading '%s'",fd_cur->fm->ff_name);
-        return false;
-    }
-    cur_file_name = luatex_find_file(ff->ff_path, find_type1_file_callback);
-    if (cur_file_name == NULL) {
-        formatted_error("type 1","cannot open file for reading '%s'", ff->ff_path);
-        return false;
-    }
-    callback_id = callback_defined(read_type1_file_callback);
-    if (callback_id > 0) {
-        if (!run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &t1_buffer, &t1_size)
-            && file_opened && t1_size > 0) {
-            formatted_warning("type 1","cannot open file for reading '%s'",cur_file_name);
-            return false;
-        }
-    } else {
-        t1_file = xfopen(cur_file_name, FOPEN_RBIN_MODE);
-        t1_read_file();
-        t1_close();
-    }
-    recorder_record_input(cur_file_name);
-    t1_init_params(open_name_prefix);
-    return true;
-}
-
-static void t1_include(PDF pdf)
-{
-    do {
-        t1_getline();
-        t1_scan_param(pdf);
-        t1_putline(pdf);
-    }
-    while (t1_in_eexec == 0);
-    t1_start_eexec(pdf);
-    do {
-        t1_getline();
-        t1_scan_param(pdf);
-        t1_putline(pdf);
-    }
-    while (!(t1_charstrings() || t1_subrs()));
-    t1_cs = true;
-    do {
-        t1_getline();
-        t1_putline(pdf);
-    }
-    while (!t1_end_eexec());
-    t1_stop_eexec(pdf);
-    if (fixedcontent) {
-        /*tex Copy 512 zeros (not needed for \PDF). */
-        do {
-            t1_getline();
-            t1_putline(pdf);
-        }
-        while (!t1_cleartomark());
-        /*tex Write |{restore} if| if found. */
-        t1_check_end(pdf);
-    }
-    get_length3();
-}
-
-#define check_subr(subr) \
-    if (subr >= subr_size || subr < 0) \
-        formatted_error("type 1","Subrs array: entry index out of range '%i'", subr);
-
-static const char **check_cs_token_pair(void)
-{
-    const char **p = (const char **) cs_token_pairs_list;
-    for (; p[0] != NULL; ++p)
-        if (t1_buf_prefix(p[0]) && t1_buf_suffix(p[1]))
-            return p;
-    return NULL;
-}
-
-static void cs_store(boolean is_subr)
-{
-    char *p;
-    cs_entry *ptr;
-    int subr;
-    for (p = t1_line_array, t1_buf_ptr = t1_buf_array; *p != ' ';
-         *t1_buf_ptr++ = *p++);
-    *t1_buf_ptr = 0;
-    if (is_subr) {
-        subr = (int) t1_scan_num(p + 1, 0);
-        check_subr(subr);
-        ptr = subr_tab + subr;
-    } else {
-        ptr = cs_ptr++;
-        if (cs_ptr - cs_tab > cs_size)
-            formatted_error("type 1","CharStrings dict: more entries than dict size '%i'", cs_size);
-        if (strcmp(t1_buf_array + 1, notdef) == 0)      /* skip the slash */
-            ptr->name = (char *) notdef;
-        else
-            ptr->name = xstrdup(t1_buf_array + 1);
-    }
-    /*tex Copy |" RD " + cs data| to |t1_buf_array|. */
-    memcpy(t1_buf_array, t1_line_array + cs_start - 4, (unsigned) (t1_cslen + 4));
-    /*tex Copy the end of cs data to |t1_buf_array|. */
-    for (p = t1_line_array + cs_start + t1_cslen, t1_buf_ptr =
-         t1_buf_array + t1_cslen + 4; *p != 10; *t1_buf_ptr++ = *p++);
-    *t1_buf_ptr++ = 10;
-    if (is_subr && cs_token_pair == NULL)
-        cs_token_pair = check_cs_token_pair();
-    ptr->len = (unsigned short) (t1_buf_ptr - t1_buf_array);
-    ptr->cslen = t1_cslen;
-    xfree(ptr->data);
-    ptr->data = xtalloc(ptr->len, byte);
-    memcpy(ptr->data, t1_buf_array, ptr->len);
-    ptr->valid = true;
-}
-
-#define store_subr() cs_store(true)
-#define store_cs()   cs_store(false)
-
-#define CC_STACK_SIZE 24
-
-static int cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
-static cc_entry cc_tab[CS_MAX];
-static boolean is_cc_init = false;
-
-#define cc_pop(N) \
-    if (stack_ptr - cc_stack < (N)) \
-        stack_error(N); \
-    stack_ptr -= N
-
-#define stack_error(N) { \
-    formatted_error("type 1","CharString: invalid access '%i' to stack, '%i' entries", (int) N, (int)(stack_ptr - cc_stack)); \
-    goto cs_error; \
-}
-
-#define cc_get(N)  ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
-#define cc_push(V) *stack_ptr++ = V
-#define cc_clear() stack_ptr = cc_stack
-
-#define set_cc(N, B, A, C) \
-    cc_tab[N].nargs = A;   \
-    cc_tab[N].bottom = B;  \
-    cc_tab[N].clear = C;   \
-    cc_tab[N].valid = true
-
-static void cc_init(void)
-{
-    int i;
-    if (is_cc_init)
-        return;
-    for (i = 0; i < CS_MAX; i++)
-        cc_tab[i].valid = false;
-    set_cc(CS_HSTEM, true, 2, true);
-    set_cc(CS_VSTEM, true, 2, true);
-    set_cc(CS_VMOVETO, true, 1, true);
-    set_cc(CS_RLINETO, true, 2, true);
-    set_cc(CS_HLINETO, true, 1, true);
-    set_cc(CS_VLINETO, true, 1, true);
-    set_cc(CS_RRCURVETO, true, 6, true);
-    set_cc(CS_CLOSEPATH, false, 0, true);
-    set_cc(CS_CALLSUBR, false, 1, false);
-    set_cc(CS_RETURN, false, 0, false);
-    set_cc(CS_HSBW, true, 2, true);
-    set_cc(CS_ENDCHAR, false, 0, true);
-    set_cc(CS_RMOVETO, true, 2, true);
-    set_cc(CS_HMOVETO, true, 1, true);
-    set_cc(CS_VHCURVETO, true, 4, true);
-    set_cc(CS_HVCURVETO, true, 4, true);
-    set_cc(CS_DOTSECTION, false, 0, true);
-    set_cc(CS_VSTEM3, true, 6, true);
-    set_cc(CS_HSTEM3, true, 6, true);
-    set_cc(CS_SEAC, true, 5, true);
-    set_cc(CS_SBW, true, 4, true);
-    set_cc(CS_DIV, false, 2, false);
-    set_cc(CS_CALLOTHERSUBR, false, 0, false);
-    set_cc(CS_POP, false, 0, false);
-    set_cc(CS_SETCURRENTPOINT, true, 2, true);
-    is_cc_init = true;
-}
-
-#define cs_getchar()    cdecrypt(*data++, &cr)
-
-#define mark_subr(n)    cs_mark(0, n)
-#define mark_cs(s)      cs_mark(s, 0)
-
-static void cs_fail(const char *cs_name, int subr, const char *fmt, ...)
-{
-    char buf[SMALL_BUF_SIZE];
-    va_list args;
-    va_start(args, fmt);
-    vsprintf(buf, fmt, args);
-    va_end(args);
-    if (cs_name == NULL)
-        formatted_error("type 1","Subr '%i': %s", (int) subr, buf);
-    else
-        formatted_error("type 1","CharString (/%s): %s", cs_name, buf);
-}
-
-/*tex Fix a return-less subr by appending |CS_RETURN|. */
-
-static void append_cs_return(cs_entry * ptr)
-{
-    unsigned short cr;
-    int i;
-    byte *p, *q, *data, *new_data;
-    /*tex Decrypt the cs data to |t1_buf_array|, append |CS_RETURN|. */
-    p = (byte *) t1_buf_array;
-    data = ptr->data + 4;
-    cr = 4330;
-    for (i = 0; i < ptr->cslen; i++)
-        *p++ = cs_getchar();
-    *p = CS_RETURN;
-    /*tex Encrypt the new cs data to |new_data|. */
-    new_data = xtalloc((unsigned) (ptr->len + 1), byte);
-    memcpy(new_data, ptr->data, 4);
-    p = new_data + 4;
-    q = (byte *) t1_buf_array;
-    cr = 4330;
-    for (i = 0; i < ptr->cslen + 1; i++)
-        *p++ = cencrypt(*q++, &cr);
-    memcpy(p, ptr->data + 4 + ptr->cslen, (size_t) (ptr->len - ptr->cslen - 4));
-    /*tex Update |*ptr|. */
-    xfree(ptr->data);
-    ptr->data = new_data;
-    ptr->len++;
-    ptr->cslen++;
-}
-
-static void cs_mark(const char *cs_name, int subr)
-{
-    byte *data;
-    int i, b, cs_len;
-    int last_cmd = 0;
-    int a, a1, a2;
-    unsigned short cr;
-    /*tex The argument of last call to |OtherSubrs[3]|. */
-    static int lastargOtherSubr3 = 3;
-    cs_entry *ptr;
-    cc_entry *cc;
-    if (cs_name == NULL) {
-        check_subr(subr);
-        ptr = subr_tab + subr;
-        if (!ptr->valid)
-            return;
-    } else if (cs_notdef != NULL && (cs_name == notdef || strcmp(cs_name, notdef) == 0)) {
-        ptr = cs_notdef;
-    }else {
-        for (ptr = cs_tab; ptr < cs_ptr; ptr++)
-            if (strcmp(ptr->name, cs_name) == 0)
-                break;
-        if (ptr == cs_ptr) {
-            formatted_warning("type 1","glyph '%s' undefined", cs_name);
-            return;
-        }
-        if (ptr->name == notdef)
-            cs_notdef = ptr;
-    }
-    /*tex
-        Only marked CharString entries and invalid entries can be skipped; valid
-        marked subrs must be parsed to keep the stack in sync.
-    */
-    if (!ptr->valid || (ptr->used && cs_name != NULL))
-        return;
-    ptr->used = true;
-    cr = 4330;
-    cs_len = ptr->cslen;
-    data = ptr->data + 4;
-    for (i = 0; i < t1_lenIV; i++, cs_len--)
-        cs_getchar();
-    while (cs_len > 0) {
-        --cs_len;
-        b = cs_getchar();
-        if (b >= 32) {
-            if (b <= 246)
-                a = b - 139;
-            else if (b <= 250) {
-                --cs_len;
-                a = ((b - 247) << 8) + 108 + cs_getchar();
-            } else if (b <= 254) {
-                --cs_len;
-                a = -((b - 251) << 8) - 108 - cs_getchar();
-            } else {
-                cs_len -= 4;
-                a = (cs_getchar() & 0xff) << 24;
-                a |= (cs_getchar() & 0xff) << 16;
-                a |= (cs_getchar() & 0xff) << 8;
-                a |= (cs_getchar() & 0xff) << 0;
-                if (sizeof(int) > 4 && (a & 0x80000000))
-                    a |= ~0x7FFFFFFF;
-            }
-            cc_push(a);
-        } else {
-            if (b == CS_ESCAPE) {
-                b = cs_getchar() + CS_1BYTE_MAX;
-                cs_len--;
-            }
-            if (b >= CS_MAX) {
-                cs_fail(cs_name, subr, "command value out of range: %i", (int) b);
-                goto cs_error;
-            }
-            cc = cc_tab + b;
-            if (!cc->valid) {
-                cs_fail(cs_name, subr, "command not valid: %i", (int) b);
-                goto cs_error;
-            }
-            if (cc->bottom) {
-                if (stack_ptr - cc_stack < cc->nargs)
-                    cs_fail(cs_name, subr,
-                            "less arguments on stack '%i' than required '%i'",
-                            (int) (stack_ptr - cc_stack), (int) cc->nargs);
-                else if (stack_ptr - cc_stack > cc->nargs)
-                    cs_fail(cs_name, subr,
-                            "more arguments on stack '%i' than required '%i'",
-                            (int) (stack_ptr - cc_stack), (int) cc->nargs);
-            }
-            last_cmd = b;
-            switch (cc - cc_tab) {
-                case CS_CALLSUBR:
-                    a1 = cc_get(-1);
-                    cc_pop(1);
-                    mark_subr(a1);
-                    if (!subr_tab[a1].valid) {
-                        cs_fail(cs_name, subr, "cannot call subr '%i'", (int) a1);
-                        goto cs_error;
-                    }
-                    break;
-                case CS_DIV:
-                    cc_pop(2);
-                    cc_push(0);
-                    break;
-                case CS_CALLOTHERSUBR:
-                    if (cc_get(-1) == 3)
-                        lastargOtherSubr3 = cc_get(-3);
-                    a1 = cc_get(-2) + 2;
-                    cc_pop(a1);
-                    break;
-                case CS_POP:
-                    cc_push(lastargOtherSubr3);
-                    /*tex
-                        The only case when we care about the value being pushed
-                        onto stack is when |POP| follows |CALLOTHERSUBR| changing
-                        hints by |OtherSubrs[3]|.
-                    */
-                    break;
-                case CS_SEAC:
-                    a1 = cc_get(3);
-                    a2 = cc_get(4);
-                    cc_clear();
-                    mark_cs(standard_glyph_names[a1]);
-                    mark_cs(standard_glyph_names[a2]);
-                    break;
-                default:
-                    if (cc->clear)
-                        cc_clear();
-            }
-        }
-    }
-    if (cs_name == NULL && last_cmd != CS_RETURN) {
-        formatted_warning("type 1",
-            "last command in subr '%i' is not a RETURN; I will add it now but please consider fixing the font",
-            (int) subr);
-        append_cs_return(ptr);
-    }
-    return;
-    /*tex An error occured during parsing: */
-  cs_error:
-    cc_clear();
-    ptr->valid = false;
-    ptr->used = false;
-}
-
-/* AVL search tree for glyph code by glyph name. */
-
-static int comp_t1_glyphs(const void *pa, const void *pb, void *p
-    __attribute__ ((unused)))
-{
-    return strcmp(*(const char *const *) pa, *(const char *const *) pb);
-}
-
-static struct avl_table *create_t1_glyph_tree(char **glyph_names)
-{
-    int i;
-    void **aa;
-    static struct avl_table *gl_tree;
-    gl_tree = avl_create(comp_t1_glyphs, NULL, &avl_xallocator);
-    for (i = 0; i < 256; i++) {
-        if (glyph_names[i] != notdef &&
-            (char **) avl_find(gl_tree, &glyph_names[i]) == NULL) {
-            /*tex No |strdup| here, just point to the |glyph_names| array members. */
-            aa = avl_probe(gl_tree, &glyph_names[i]);
-            if (aa == NULL) {
-                /*tex Is this a problem? */
-            }
-        }
-    }
-    return gl_tree;
-}
-
-static void destroy_t1_glyph_tree(struct avl_table *gl_tree)
-{
-    avl_destroy(gl_tree, NULL);
-}
-
-static void t1_subset_ascii_part(PDF pdf)
-{
-    int j, *p;
-    char *glyph, **gg, **glyph_names;
-    struct avl_table *gl_tree;
-    struct avl_traverser t;
-    void **aa;
-    t1_getline();
-    while (!t1_prefix("/Encoding")) {
-        t1_scan_param(pdf);
-        t1_putline(pdf);
-        t1_getline();
-    }
-    glyph_names = t1_builtin_enc();
-    fd_cur->builtin_glyph_names = glyph_names;
-    if (is_subsetted(fd_cur->fm)) {
-        if (fd_cur->tx_tree != NULL) {
-            /*tex Take over collected non-reencoded characters from \TeX. */
-            avl_t_init(&t, fd_cur->tx_tree);
-            for (p = (int *) avl_t_first(&t, fd_cur->tx_tree); p != NULL;
-                 p = (int *) avl_t_next(&t)) {
-                if ((char *) avl_find(fd_cur->gl_tree, glyph_names[*p]) == NULL) {
-                    glyph = xstrdup(glyph_names[*p]);
-                    aa = avl_probe(fd_cur->gl_tree, glyph);
-                    assert(aa != NULL);
-                }
-            }
-        }
-        make_subset_tag(fd_cur);
-        strncpy((char *) pdf->fb->data + t1_fontname_offset, fd_cur->subset_tag,6);
-    }
-    /*tex Now really all glyphs needed from this font are in the |fd_cur->gl_tree|. */
-    if (t1_encoding == ENC_STANDARD)
-        t1_puts(pdf, "/Encoding StandardEncoding def\n");
-    else {
-        t1_puts(pdf,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
-        gl_tree = create_t1_glyph_tree(glyph_names);
-        avl_t_init(&t, fd_cur->gl_tree);
-        j = 0;
-        for (glyph = (char *) avl_t_first(&t, fd_cur->gl_tree); glyph != NULL;
-             glyph = (char *) avl_t_next(&t)) {
-            if ((gg = (char **) avl_find(gl_tree, &glyph)) != NULL) {
-                t1_printf(pdf, "dup %i /%s put\n", (int) (gg - glyph_names),*gg);
-                j++;
-            }
-        }
-        destroy_t1_glyph_tree(gl_tree);
-        if (j == 0) {
-            /*tex
-                We didn't mark anything for the Encoding array. We add |{dup 0
-                /.notdef put}| for compatibility with Acrobat 5.0.
-            */
-            t1_puts(pdf, "dup 0 /.notdef put\n");
-        }
-        t1_puts(pdf, "readonly def\n");
-    }
-    do {
-        t1_getline();
-        t1_scan_param(pdf);
-        if (!t1_prefix("/UniqueID")) {
-            /*tex Ignore |/UniqueID| for subsetted fonts. */
-            t1_putline(pdf);
-        }
-    }
-    while (t1_in_eexec == 0);
-}
-
-static void cs_init(void)
-{
-    cs_ptr = cs_tab = NULL;
-    cs_dict_start = cs_dict_end = NULL;
-    cs_counter = cs_size = cs_size_pos = 0;
-    cs_token_pair = NULL;
-    subr_tab = NULL;
-    subr_array_start = subr_array_end = NULL;
-    subr_max = subr_size = subr_size_pos = 0;
-}
-
-static void init_cs_entry(cs_entry * cs)
-{
-    cs->data = NULL;
-    cs->name = NULL;
-    cs->len = 0;
-    cs->cslen = 0;
-    cs->used = false;
-    cs->valid = false;
-}
-
-static void t1_read_subrs(PDF pdf)
-{
-    int i, s;
-    cs_entry *ptr;
-    t1_getline();
-    while (!(t1_charstrings() || t1_subrs())) {
-        t1_scan_param(pdf);
-        if (!t1_prefix("/UniqueID")) {
-            /*tex Ignore |/UniqueID| for subsetted fonts. */
-            t1_putline(pdf);
-        }
-        t1_getline();
-    }
-  found:
-    t1_cs = true;
-    t1_scan = false;
-    if (!t1_subrs())
-        return;
-    subr_size_pos = strlen("/Subrs") + 1;
-    /*tex |subr_size_pos| points to the number indicating dict size after |Subrs|. */
-    subr_size = (int) t1_scan_num(t1_line_array + subr_size_pos, 0);
-    if (subr_size == 0) {
-        while (!t1_charstrings())
-            t1_getline();
-        return;
-    }
-    subr_tab = xtalloc((unsigned) subr_size, cs_entry);
-    for (ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
-        init_cs_entry(ptr);
-    subr_array_start = xstrdup(t1_line_array);
-    t1_getline();
-    while (t1_cslen) {
-        store_subr();
-        t1_getline();
-    }
-    /*tex Mark the first four entries without parsing. */
-    for (i = 0; i < subr_size && i < 4; i++)
-        subr_tab[i].used = true;
-    /*tex
-
-        The end of the |Subrs| array might have more than one line so we need to
-        concatenate them to |subr_array_end|. Unfortunately some fonts don't have
-        the |Subrs| array followed by the |CharStrings| dict immediately (synthetic
-        fonts). If we cannot find |CharStrings| in next |POST_SUBRS_SCAN| lines
-        then we will treat the font as synthetic and ignore everything until next
-        |Subrs| is found.
-
-     */
-#define POST_SUBRS_SCAN 5
-    s = 0;
-    *t1_buf_array = 0;
-    for (i = 0; i < POST_SUBRS_SCAN; i++) {
-        if (t1_charstrings())
-            break;
-        s = (int) (s + t1_line_ptr - t1_line_array);
-        alloc_array(t1_buf, s, T1_BUF_SIZE);
-        strcat(t1_buf_array, t1_line_array);
-        t1_getline();
-    }
-    subr_array_end = xstrdup(t1_buf_array);
-    if (i == POST_SUBRS_SCAN) {
-        /*tex |CharStrings| not found: assume a synthetic font. */
-        for (ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
-            if (ptr->valid)
-                xfree(ptr->data);
-        xfree(subr_tab);
-        xfree(subr_array_start);
-        xfree(subr_array_end);
-        cs_init();
-        t1_cs = false;
-        t1_synthetic = true;
-        while (!(t1_charstrings() || t1_subrs()))
-            t1_getline();
-        goto found;
-    }
-}
-
-#define t1_subr_flush()  t1_flush_cs(pdf, true)
-#define t1_cs_flush()    t1_flush_cs(pdf, false)
-
-static void t1_flush_cs(PDF pdf, boolean is_subr)
-{
-    char *p;
-    byte *r, *return_cs = NULL;
-    cs_entry *tab, *end_tab, *ptr;
-    char *start_line, *line_end;
-    int count, size_pos;
-    unsigned short cr, cs_len;
-    if (is_subr) {
-        start_line = subr_array_start;
-        line_end = subr_array_end;
-        size_pos = subr_size_pos;
-        tab = subr_tab;
-        count = subr_max + 1;
-        end_tab = subr_tab + count;
-    } else {
-        start_line = cs_dict_start;
-        line_end = cs_dict_end;
-        size_pos = cs_size_pos;
-        tab = cs_tab;
-        end_tab = cs_ptr;
-        count = cs_counter;
-    }
-    t1_line_ptr = t1_line_array;
-    for (p = start_line; p - start_line < size_pos;)
-        *t1_line_ptr++ = *p++;
-    while (isdigit((unsigned char)*p))
-        p++;
-    sprintf(t1_line_ptr, "%u", count);
-    strcat(t1_line_ptr, p);
-    t1_line_ptr = eol(t1_line_array);
-    t1_putline(pdf);
-    /*tex For |-Wall|. */
-    cs_len = 0;
-    /*tex Create |return_cs| to replace unsused |subr|s. */
-    if (is_subr) {
-        cr = 4330;
-        cs_len = 0;
-        /*tex
-            At this point we have |t1_lenIV >= 0;| a negative value would be
-            caught in |t1_scan_param|.
-        */
-        return_cs = xtalloc((unsigned) (t1_lenIV + 1), byte);
-        for (cs_len = 0, r = return_cs; cs_len < t1_lenIV; cs_len++, r++)
-            *r = cencrypt(0x00, &cr);
-        *r = cencrypt(CS_RETURN, &cr);
-        cs_len++;
-    }
-    for (ptr = tab; ptr < end_tab; ptr++) {
-        if (ptr->used) {
-            if (is_subr)
-                sprintf(t1_line_array, "dup %li %u", (long int) (ptr - tab),
-                        ptr->cslen);
-            else
-                sprintf(t1_line_array, "/%s %u", ptr->name, ptr->cslen);
-            p = strend(t1_line_array);
-            memcpy(p, ptr->data, ptr->len);
-            t1_line_ptr = p + ptr->len;
-            t1_putline(pdf);
-        } else {
-            /*tex Replace unsused subr's by |return_cs|. */
-            if (is_subr) {
-                sprintf(t1_line_array, "dup %li %u%s ", (long int) (ptr - tab),
-                        cs_len, cs_token_pair[0]);
-                p = strend(t1_line_array);
-                memcpy(p, return_cs, cs_len);
-                t1_line_ptr = p + cs_len;
-                t1_putline(pdf);
-                sprintf(t1_line_array, " %s", cs_token_pair[1]);
-                t1_line_ptr = eol(t1_line_array);
-                t1_putline(pdf);
-            }
-        }
-        xfree(ptr->data);
-        if (is_subr)
-            ptr->valid = false;
-        if (ptr->name != notdef)
-            xfree(ptr->name);
-    }
-    sprintf(t1_line_array, "%s", line_end);
-    t1_line_ptr = eol(t1_line_array);
-    t1_putline(pdf);
-    if (is_subr) {
-        end_tab = subr_tab + subr_size;
-        for (ptr = tab; ptr < end_tab; ptr++) {
-            if (ptr->valid) {
-                xfree(ptr->data);
-                if (ptr->name != notdef)
-                    xfree(ptr->name);
-            }
-        }
-        xfree(return_cs);
-    }
-    xfree(tab);
-    xfree(start_line);
-    xfree(line_end);
-}
-
-static void t1_mark_glyphs(void)
-{
-    char *glyph;
-    struct avl_traverser t;
-    cs_entry *ptr;
-    if (t1_synthetic || fd_cur->all_glyphs) {
-        /*tex Mark everything. */
-        if (cs_tab != NULL)
-            for (ptr = cs_tab; ptr < cs_ptr; ptr++)
-                if (ptr->valid)
-                    ptr->used = true;
-        if (subr_tab != NULL) {
-            for (ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
-                if (ptr->valid)
-                    ptr->used = true;
-            subr_max = subr_size - 1;
-        }
-        return;
-    }
-    mark_cs(notdef);
-    avl_t_init(&t, fd_cur->gl_tree);
-    for (glyph = (char *) avl_t_first(&t, fd_cur->gl_tree); glyph != NULL;
-         glyph = (char *) avl_t_next(&t)) {
-        mark_cs(glyph);
-    }
-    if (subr_tab != NULL)
-        for (subr_max = -1, ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
-            if (ptr->used && ptr - subr_tab > subr_max)
-                subr_max = (int) (ptr - subr_tab);
-}
-
-
-/*tex
-
-    When |t1_subset_charstrings| is called, the |t1_line_array| contains
-    |/CharStrings|. When we hit a case like this:
-
-    \starttyping
-     dup/CharStrings
-     229 dict dup begin
-    \stoptyping
-
-    we read the next line and concatenate to |t1_line_array| before moving on.
-    That is what |t1_check_unusual_charstring| is for.
-
-*/
-
-static void t1_check_unusual_charstring(void)
-{
-    char *p = strstr(t1_line_array, charstringname) + strlen(charstringname);
-    int i;
-    /*tex If no number follows |/CharStrings|, let's read the next line. */
-    if (sscanf(p, "%i", &i) != 1) {
-        strcpy(t1_buf_array, t1_line_array);
-        t1_getline();
-        alloc_array(t1_buf, strlen(t1_line_array) + strlen(t1_buf_array) + 1, T1_BUF_SIZE);
-        strcat(t1_buf_array, t1_line_array);
-        alloc_array(t1_line, strlen(t1_buf_array) + 1, T1_BUF_SIZE);
-        strcpy(t1_line_array, t1_buf_array);
-        t1_line_ptr = eol(t1_line_array);
-    }
-}
-
-static void t1_subset_charstrings(PDF pdf)
-{
-    cs_entry *ptr;
-    t1_check_unusual_charstring();
-    cs_size_pos = (int) (strstr(t1_line_array, charstringname) + strlen(charstringname) - t1_line_array + 1);
-    /*tex |cs_size_pos| points to the number indicating dict size after |/CharStrings|. */
-    cs_size = (int) t1_scan_num(t1_line_array + cs_size_pos, 0);
-    cs_ptr = cs_tab = xtalloc((unsigned) cs_size, cs_entry);
-    for (ptr = cs_tab; ptr - cs_tab < cs_size; ptr++)
-        init_cs_entry(ptr);
-    cs_notdef = NULL;
-    cs_dict_start = xstrdup(t1_line_array);
-    t1_getline();
-    while (t1_cslen) {
-        store_cs();
-        t1_getline();
-    }
-    cs_dict_end = xstrdup(t1_line_array);
-    t1_mark_glyphs();
-    if (subr_tab != NULL) {
-        if (cs_token_pair == NULL)
-            formatted_error("type 1","mismatched subroutine begin/end token pairs");
-        t1_subr_flush();
-    }
-    for (cs_counter = 0, ptr = cs_tab; ptr < cs_ptr; ptr++)
-        if (ptr->used)
-            cs_counter++;
-    t1_cs_flush();
-}
-
-static void t1_subset_end(PDF pdf)
-{
-    if (t1_synthetic) {
-        /*tex Copy to |dup /FontName get exch definefont pop|. */
-        while (!strstr(t1_line_array, "definefont")) {
-            t1_getline();
-            t1_putline(pdf);
-        }
-        while (!t1_end_eexec()) {
-            /*tex Ignore the rest. */
-            t1_getline();
-        }
-        /*tex Write \.{mark currentfile closefile}. */
-        t1_putline(pdf);
-    } else {
-        while (!t1_end_eexec()) {
-            /*tex Copy to \.{mark currentfile closefile}. */
-            t1_getline();
-            t1_putline(pdf);
-        }
-    }
-    t1_stop_eexec(pdf);
-    if (fixedcontent) {
-        /*tex Copy 512 zeros (not needed for PDF). */
-        while (!t1_cleartomark()) {
-            t1_getline();
-            t1_putline(pdf);
-        }
-        /*tex Don't check \.{{restore}if} for synthetic fonts. */
-        if (!t1_synthetic) {
-            /*tex Write \.{{restore}if} if found. */
-            t1_check_end(pdf);
-        }
-    }
-    get_length3();
-}
-
-void writet1(PDF pdf, fd_entry * fd)
-{
-    /*tex |fd_cur| is global inside |writet1.c|. */
-    fd_cur = fd;
-    assert(fd_cur->fm != NULL);
-    assert(is_type1(fd->fm));
-    assert(is_included(fd->fm));
-
-    t1_save_offset = 0;
-    if (!is_subsetted(fd_cur->fm)) {
-        /*tex Include entire font. */
-        if (!(fd->ff_found = t1_open_fontfile(filetype_font)))
-            return;
-        t1_include(pdf);
-        t1_close_font_file(filetype_font);
-        xfree(t1_buffer);
-        return;
-    }
-    /*tex Partial downloading. */
-    if (!(fd->ff_found = t1_open_fontfile(filetype_subset)))
-        return;
-    t1_subset_ascii_part(pdf);
-    t1_start_eexec(pdf);
-    cc_init();
-    cs_init();
-    t1_read_subrs(pdf);
-    t1_subset_charstrings(pdf);
-    t1_subset_end(pdf);
-    t1_close_font_file(filetype_subset);
-    xfree(t1_buffer);
-}
-
-void t1_free(void)
-{
-    xfree(t1_line_array);
-    xfree(t1_buf_array);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/writet3.w
@@ -0,0 +1,371 @@
+% writet3.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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 <kpathsea/tex-glyph.h>
+#include <kpathsea/magstep.h>
+#include <string.h>
+
+#define T3_BUF_SIZE   1024
+
+typedef char t3_line_entry;
+define_array(t3_line);
+
+FILE *t3_file;
+static boolean t3_image_used;
+
+static int t3_char_procs[256];
+static float t3_char_widths[256];
+static int t3_glyph_num;
+static float t3_font_scale;
+static int t3_b0, t3_b1, t3_b2, t3_b3;
+static boolean is_pk_font;
+
+/* not static because used by pkin.c  */
+unsigned char *t3_buffer = NULL;
+int t3_size = 0;
+int t3_curbyte = 0;
+
+#define t3_check_eof() \
+    if (t3_eof()) \
+        normal_error("type 3","unexpected end of file");
+
+@
+@c
+static void update_bbox(int llx, int lly, int urx, int ury, boolean is_first_glyph)
+{
+    if (is_first_glyph) {
+        t3_b0 = llx;
+        t3_b1 = lly;
+        t3_b2 = urx;
+        t3_b3 = ury;
+    } else {
+        if (llx < t3_b0)
+            t3_b0 = llx;
+        if (lly < t3_b1)
+            t3_b1 = lly;
+        if (urx > t3_b2)
+            t3_b2 = urx;
+        if (ury > t3_b3)
+            t3_b3 = ury;
+    }
+}
+
+/* fixed precision 3 (+5 in pdfgen.w)*/
+
+#define get_pk_font_scale(pdf,f,scale_factor) \
+    divide_scaled(scale_factor, divide_scaled(font_size(f),one_hundred_bp,pk_decimal_digits(pdf,2)), 0)
+
+#define pk_char_width(pdf,f,w,scale_factor) \
+    divide_scaled(divide_scaled(w,font_size(f),pk_decimal_digits(pdf,4)), get_pk_font_scale(pdf,f,scale_factor), 0)
+
+@
+@c
+static boolean writepk(PDF pdf, internal_font_number f)
+{
+    kpse_glyph_file_type font_ret;
+    int llx, lly, urx, ury;
+    int cw, rw, i, j;
+    pdffloat pf;
+    halfword *row;
+    char *name;
+    chardesc cd;
+    boolean is_null_glyph, check_preamble;
+    int dpi = 0;
+    int newdpi = 0;
+    int callback_id = 0;
+    int file_opened = 0;
+    xfree(t3_buffer);
+    t3_curbyte = 0;
+    t3_size = 0;
+
+    callback_id = callback_defined(find_pk_file_callback);
+
+    if (pdf->pk_fixed_dpi) {
+        newdpi = pdf->pk_resolution;
+    } else {
+        newdpi = dpi;
+    }
+
+    if (callback_id > 0) {
+        /* <base>.dpi/<fontname>.<tdpi>pk */
+        dpi = round((float) pdf->pk_resolution * (((float) font_size(f)) / (float) font_dsize(f)));
+        cur_file_name = font_name(f);
+        run_callback(callback_id, "Sd->S", cur_file_name, (int) newdpi, &name);
+        if (name == NULL || strlen(name) == 0) {
+            formatted_warning("type 3","font %s at %i not found", cur_file_name, (int) dpi);
+            return false;
+        }
+    } else {
+        dpi = (int) kpse_magstep_fix(
+            (unsigned) round ((float)pdf->pk_resolution * ((float)font_size(f)/(float)font_dsize(f))),
+            (unsigned) pdf->pk_resolution, NULL
+        );
+        cur_file_name = font_name(f);
+        name = kpse_find_pk(cur_file_name, (unsigned) dpi, &font_ret);
+        if (name == NULL || !FILESTRCASEEQ(cur_file_name, font_ret.name)
+            || !kpse_bitmap_tolerance((float) font_ret.dpi, (float) dpi)) {
+                formatted_error("type 3","font %s at %i not found", cur_file_name, (int) dpi);
+                return false;
+        }
+    }
+    callback_id = callback_defined(read_pk_file_callback);
+    if (callback_id > 0) {
+        if (!(run_callback(callback_id, "S->bSd", name, &file_opened, &t3_buffer, &t3_size) && file_opened && t3_size > 0)) {
+            formatted_warning("type 3","font %s at %i not found", cur_file_name, (int) dpi);
+            cur_file_name = NULL;
+            return false;
+        }
+    } else {
+        t3_file = xfopen(name, FOPEN_RBIN_MODE);
+        recorder_record_input(name);
+        t3_read_file();
+        t3_close();
+    }
+    t3_image_used = true;
+    is_pk_font = true;
+    report_start_file(filetype_font,(char *) name);
+    cd.rastersize = 256;
+    cd.raster = xtalloc((unsigned long) cd.rastersize, halfword);
+    check_preamble = true;
+    if (dpi==0) {
+        normal_error("type 3","invalid dpi value 0");
+    }  else if (newdpi==0) {
+        newdpi = dpi;
+    }
+    while (readchar(check_preamble, &cd) != 0) {
+        check_preamble = false;
+        if (!pdf_char_marked(f, cd.charcode))
+            continue;
+        t3_char_widths[cd.charcode] = (float) pk_char_width(pdf, f, get_charwidth(f, cd.charcode), pdf->pk_scale_factor);
+        if (cd.cwidth < 1 || cd.cheight < 1) {
+            cd.xescape = cd.cwidth = round(t3_char_widths[cd.charcode] / 100.0);
+            cd.cheight = 1;
+            cd.xoff = 0;
+            cd.yoff = 0;
+            is_null_glyph = true;
+        } else
+            is_null_glyph = false;
+        llx = -cd.xoff;
+        lly = cd.yoff - cd.cheight + 1;
+        urx = cd.cwidth + llx + 1;
+        ury = cd.cheight + lly;
+        update_bbox(llx, lly, urx, ury, t3_glyph_num == 0);
+        t3_glyph_num++;
+        t3_char_procs[cd.charcode] = pdf_create_obj(pdf, obj_type_others, 0);
+        pdf_begin_obj(pdf, t3_char_procs[cd.charcode], OBJSTM_NEVER);
+        pdf_begin_dict(pdf);
+        pdf_dict_add_streaminfo(pdf);
+        pdf_end_dict(pdf);
+        pdf_begin_stream(pdf);
+        setpdffloat(pf, (int64_t) t3_char_widths[cd.charcode], 2);
+        print_pdffloat(pdf, pf);
+        pdf_printf(pdf, " 0 %i %i %i %i d1\n", (int) llx, (int) lly, (int) urx, (int) ury);
+        if (is_null_glyph)
+            goto end_stream;
+        pdf_printf(pdf, "q\n%i 0 0 %i %i %i cm\nBI\n",
+            (int) (dpi * cd.cwidth  / newdpi),
+            (int) (dpi * cd.cheight / newdpi),
+            (int) llx, (int) lly);
+        pdf_printf(pdf, "/W %i\n/H %i\n", (int) cd.cwidth, (int) cd.cheight);
+        pdf_puts(pdf, "/IM true\n/BPC 1\n/D [1 0]\nID ");
+        cw = (cd.cwidth + 7) / 8;
+        rw = (cd.cwidth + 15) / 16;
+        row = cd.raster;
+        for (i = 0; i < cd.cheight; i++) {
+            for (j = 0; j < rw - 1; j++) {
+                pdf_out(pdf, (unsigned char) (*row / 256));
+                pdf_out(pdf, (unsigned char) (*row % 256));
+                row++;
+            }
+            pdf_out(pdf, (unsigned char) (*row / 256));
+            if (2 * rw == cw)
+                pdf_out(pdf, (unsigned char) (*row % 256));
+            row++;
+        }
+        pdf_puts(pdf, "\nEI\nQ\n");
+      end_stream:
+        pdf_end_stream(pdf);
+        pdf_end_obj(pdf);
+    }
+    xfree(cd.raster);
+    cur_file_name = NULL;
+    return true;
+}
+
+@
+@c
+void writet3(PDF pdf, internal_font_number f)
+{
+    int i;
+    char s[32];
+    int wptr, eptr, cptr;
+    int first_char, last_char;
+    int pk_font_scale;
+    pdffloat pf;
+    boolean is_notdef;
+
+    t3_glyph_num = 0;
+    t3_image_used = false;
+    for (i = 0; i < 256; i++) {
+        t3_char_procs[i] = 0;
+        t3_char_widths[i] = 0;
+    }
+    is_pk_font = false;
+
+    xfree(t3_buffer);
+    t3_curbyte = 0;
+    t3_size = 0;
+    if (!writepk(pdf, f))
+        return;
+    for (i = font_bc(f); i <= font_ec(f); i++)
+        if (pdf_char_marked(f, i))
+            break;
+    first_char = i;
+    for (i = font_ec(f); i > first_char; i--)
+        if (pdf_char_marked(f, i))
+            break;
+    last_char = i;
+
+    /* Type 3 font dictionary */
+    pdf_begin_obj(pdf, pdf_font_num(f), OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "Font");
+    pdf_dict_add_name(pdf, "Subtype", "Type3");
+    snprintf(s, 31, "F%i", (int) f);
+    pdf_dict_add_name(pdf, "Name", s);
+    if (pdf_font_attr(f) != get_nullstr() && pdf_font_attr(f) != 0) {
+        pdf_out(pdf, '\n');
+        pdf_print(pdf, pdf_font_attr(f));
+        pdf_out(pdf, '\n');
+    }
+    if (is_pk_font) {
+        pk_font_scale = get_pk_font_scale(pdf,f,pdf->pk_scale_factor);
+        pdf_add_name(pdf, "FontMatrix");
+        pdf_begin_array(pdf);
+        setpdffloat(pf, (int64_t) pk_font_scale, 5);
+        print_pdffloat(pdf, pf);
+        pdf_puts(pdf, " 0 0 ");
+        print_pdffloat(pdf, pf);
+        pdf_puts(pdf, " 0 0");
+        pdf_end_array(pdf);
+    } else {
+        pdf_add_name(pdf, "FontMatrix");
+        pdf_begin_array(pdf);
+        pdf_printf(pdf, "%g 0 0 %g 0 0", (double) t3_font_scale, (double) t3_font_scale);
+        pdf_end_array(pdf);
+    }
+    pdf_add_name(pdf, font_key[FONTBBOX1_CODE].pdfname);
+    pdf_begin_array(pdf);
+    pdf_add_int(pdf, (int) t3_b0);
+    pdf_add_int(pdf, (int) t3_b1);
+    pdf_add_int(pdf, (int) t3_b2);
+    pdf_add_int(pdf, (int) t3_b3);
+    pdf_end_array(pdf);
+    pdf_add_name(pdf, "Resources");
+    pdf_begin_dict(pdf);
+    pdf_add_name(pdf, "ProcSet");
+    pdf_begin_array(pdf);
+    pdf_add_name(pdf, "PDF");
+    if (t3_image_used) {
+        pdf_add_name(pdf, "ImageB");
+    }
+    pdf_end_array(pdf);
+    pdf_end_dict(pdf);
+    pdf_dict_add_int(pdf, "FirstChar", first_char);
+    pdf_dict_add_int(pdf, "LastChar", last_char);
+    wptr = pdf_create_obj(pdf, obj_type_others, 0);
+    eptr = pdf_create_obj(pdf, obj_type_others, 0);
+    cptr = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_dict_add_ref(pdf, "Widths", (int) wptr);
+    pdf_dict_add_ref(pdf, "Encoding", (int) eptr);
+    pdf_dict_add_ref(pdf, "CharProcs", (int) cptr);
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+
+    /* chars width array */
+    pdf_begin_obj(pdf, wptr, OBJSTM_ALWAYS);
+    pdf_begin_array(pdf);
+    if (is_pk_font) {
+        for (i = first_char; i <= last_char; i++) {
+            setpdffloat(pf, (int64_t) t3_char_widths[i], 2);
+            print_pdffloat(pdf, pf);
+            pdf_out(pdf, ' ');
+        }
+    } else {
+        for (i = first_char; i <= last_char; i++) {
+            pdf_add_int(pdf, (int) t3_char_widths[i]);
+        }
+    }
+    pdf_end_array(pdf);
+    pdf_end_obj(pdf);
+
+    /* encoding dictionary */
+    pdf_begin_obj(pdf, eptr, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "Encoding");
+    pdf_add_name(pdf, "Differences");
+    pdf_begin_array(pdf);
+    pdf_add_int(pdf, first_char);
+    if (t3_char_procs[first_char] == 0) {
+        pdf_add_name(pdf, notdef);
+        is_notdef = true;
+    } else {
+        snprintf(s, 31, "a%i", first_char);
+        pdf_add_name(pdf, s);
+        is_notdef = false;
+    }
+    for (i = first_char + 1; i <= last_char; i++) {
+        if (t3_char_procs[i] == 0) {
+            if (!is_notdef) {
+                pdf_add_int(pdf, i);
+                pdf_add_name(pdf, notdef);
+                is_notdef = true;
+            }
+        } else {
+            if (is_notdef) {
+                pdf_add_int(pdf, i);
+                is_notdef = false;
+            }
+            snprintf(s, 31, "a%i", i);
+            pdf_add_name(pdf, s);
+        }
+    }
+    pdf_end_array(pdf);
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+
+    /* CharProcs dictionary */
+    pdf_begin_obj(pdf, cptr, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    for (i = first_char; i <= last_char; i++) {
+        if (t3_char_procs[i] != 0) {
+            snprintf(s, 31, "a%i", (int) i);
+            pdf_dict_add_ref(pdf, s, (int) t3_char_procs[i]);
+        }
+    }
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+    report_stop_file(filetype_font);
+    cur_file_name = NULL;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/writet3.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <kpathsea/tex-glyph.h>
-#include <kpathsea/magstep.h>
-#include <string.h>
-
-#define T3_BUF_SIZE   1024
-
-typedef char t3_line_entry;
-define_array(t3_line);
-
-FILE *t3_file;
-static boolean t3_image_used;
-
-static int t3_char_procs[256];
-static float t3_char_widths[256];
-static int t3_glyph_num;
-static float t3_font_scale;
-static int t3_b0, t3_b1, t3_b2, t3_b3;
-static boolean is_pk_font;
-
-/*tex Not static because used elsewhere. */
-
-unsigned char *t3_buffer = NULL;
-int t3_size = 0;
-int t3_curbyte = 0;
-
-#define t3_check_eof() \
-    if (t3_eof()) \
-        normal_error("type 3","unexpected end of file");
-
-static void update_bbox(int llx, int lly, int urx, int ury, boolean is_first_glyph)
-{
-    if (is_first_glyph) {
-        t3_b0 = llx;
-        t3_b1 = lly;
-        t3_b2 = urx;
-        t3_b3 = ury;
-    } else {
-        if (llx < t3_b0)
-            t3_b0 = llx;
-        if (lly < t3_b1)
-            t3_b1 = lly;
-        if (urx > t3_b2)
-            t3_b2 = urx;
-        if (ury > t3_b3)
-            t3_b3 = ury;
-    }
-}
-
-/*tex Fixed precision 3 (+5 in |pdfgen.c|)*/
-
-#define get_pk_font_scale(pdf,f,scale_factor) \
-    divide_scaled(scale_factor, divide_scaled(font_size(f),one_hundred_bp,pk_decimal_digits(pdf,2)), 0)
-
-#define pk_char_width(pdf,f,w,scale_factor) \
-    divide_scaled(divide_scaled(w,font_size(f),pk_decimal_digits(pdf,4)), get_pk_font_scale(pdf,f,scale_factor), 0)
-
-static boolean writepk(PDF pdf, internal_font_number f)
-{
-    kpse_glyph_file_type font_ret;
-    int llx, lly, urx, ury;
-    int cw, rw, i, j;
-    pdffloat pf;
-    halfword *row;
-    char *name;
-    chardesc cd;
-    boolean is_null_glyph, check_preamble;
-    int dpi = 0;
-    int newdpi = 0;
-    int callback_id = 0;
-    int file_opened = 0;
-    xfree(t3_buffer);
-    t3_curbyte = 0;
-    t3_size = 0;
-    callback_id = callback_defined(find_pk_file_callback);
-    if (pdf->pk_fixed_dpi) {
-        newdpi = pdf->pk_resolution;
-    } else {
-        newdpi = dpi;
-    }
-    if (callback_id > 0) {
-        /*tex |<base>.dpi/<fontname>.<tdpi>pk| */
-        dpi = round((float) pdf->pk_resolution * (((float) font_size(f)) / (float) font_dsize(f)));
-        cur_file_name = font_name(f);
-        run_callback(callback_id, "Sd->S", cur_file_name, (int) newdpi, &name);
-        if (name == NULL || strlen(name) == 0) {
-            formatted_warning("type 3","font %s at %i not found", cur_file_name, (int) dpi);
-            return false;
-        }
-    } else {
-        dpi = (int) kpse_magstep_fix(
-            (unsigned) round ((float)pdf->pk_resolution * ((float)font_size(f)/(float)font_dsize(f))),
-            (unsigned) pdf->pk_resolution, NULL
-        );
-        cur_file_name = font_name(f);
-        name = kpse_find_pk(cur_file_name, (unsigned) dpi, &font_ret);
-        if (name == NULL || !FILESTRCASEEQ(cur_file_name, font_ret.name)
-            || !kpse_bitmap_tolerance((float) font_ret.dpi, (float) dpi)) {
-                formatted_error("type 3","font %s at %i not found", cur_file_name, (int) dpi);
-                return false;
-        }
-    }
-    callback_id = callback_defined(read_pk_file_callback);
-    if (callback_id > 0) {
-        if (!(run_callback(callback_id, "S->bSd", name, &file_opened, &t3_buffer, &t3_size) && file_opened && t3_size > 0)) {
-            formatted_warning("type 3","font %s at %i not found", cur_file_name, (int) dpi);
-            cur_file_name = NULL;
-            return false;
-        }
-    } else {
-        t3_file = xfopen(name, FOPEN_RBIN_MODE);
-        recorder_record_input(name);
-        t3_read_file();
-        t3_close();
-    }
-    t3_image_used = true;
-    is_pk_font = true;
-    report_start_file(filetype_font,(char *) name);
-    cd.rastersize = 256;
-    cd.raster = xtalloc((unsigned long) cd.rastersize, halfword);
-    check_preamble = true;
-    if (dpi==0) {
-        normal_error("type 3","invalid dpi value 0");
-    }  else if (newdpi==0) {
-        newdpi = dpi;
-    }
-    while (readchar(check_preamble, &cd) != 0) {
-        check_preamble = false;
-        if (!pdf_char_marked(f, cd.charcode))
-            continue;
-        t3_char_widths[cd.charcode] = (float) pk_char_width(pdf, f, get_charwidth(f, cd.charcode), pdf->pk_scale_factor);
-        if (cd.cwidth < 1 || cd.cheight < 1) {
-            cd.xescape = cd.cwidth = round(t3_char_widths[cd.charcode] / 100.0);
-            cd.cheight = 1;
-            cd.xoff = 0;
-            cd.yoff = 0;
-            is_null_glyph = true;
-        } else
-            is_null_glyph = false;
-        llx = -cd.xoff;
-        lly = cd.yoff - cd.cheight + 1;
-        urx = cd.cwidth + llx + 1;
-        ury = cd.cheight + lly;
-        update_bbox(llx, lly, urx, ury, t3_glyph_num == 0);
-        t3_glyph_num++;
-        t3_char_procs[cd.charcode] = pdf_create_obj(pdf, obj_type_others, 0);
-        pdf_begin_obj(pdf, t3_char_procs[cd.charcode], OBJSTM_NEVER);
-        pdf_begin_dict(pdf);
-        pdf_dict_add_streaminfo(pdf);
-        pdf_end_dict(pdf);
-        pdf_begin_stream(pdf);
-        setpdffloat(pf, (int64_t) t3_char_widths[cd.charcode], 2);
-        print_pdffloat(pdf, pf);
-        pdf_printf(pdf, " 0 %i %i %i %i d1\n", (int) llx, (int) lly, (int) urx, (int) ury);
-        if (is_null_glyph)
-            goto end_stream;
-        pdf_printf(pdf, "q\n%i 0 0 %i %i %i cm\nBI\n",
-            (int) (dpi * cd.cwidth  / newdpi),
-            (int) (dpi * cd.cheight / newdpi),
-            (int) llx, (int) lly);
-        pdf_printf(pdf, "/W %i\n/H %i\n", (int) cd.cwidth, (int) cd.cheight);
-        pdf_puts(pdf, "/IM true\n/BPC 1\n/D [1 0]\nID ");
-        cw = (cd.cwidth + 7) / 8;
-        rw = (cd.cwidth + 15) / 16;
-        row = cd.raster;
-        for (i = 0; i < cd.cheight; i++) {
-            for (j = 0; j < rw - 1; j++) {
-                pdf_out(pdf, (unsigned char) (*row / 256));
-                pdf_out(pdf, (unsigned char) (*row % 256));
-                row++;
-            }
-            pdf_out(pdf, (unsigned char) (*row / 256));
-            if (2 * rw == cw)
-                pdf_out(pdf, (unsigned char) (*row % 256));
-            row++;
-        }
-        pdf_puts(pdf, "\nEI\nQ\n");
-      end_stream:
-        pdf_end_stream(pdf);
-        pdf_end_obj(pdf);
-    }
-    xfree(cd.raster);
-    cur_file_name = NULL;
-    return true;
-}
-
-void writet3(PDF pdf, internal_font_number f)
-{
-    int i;
-    char s[32];
-    int wptr, eptr, cptr;
-    int first_char, last_char;
-    int pk_font_scale;
-    pdffloat pf;
-    boolean is_notdef;
-    t3_glyph_num = 0;
-    t3_image_used = false;
-    for (i = 0; i < 256; i++) {
-        t3_char_procs[i] = 0;
-        t3_char_widths[i] = 0;
-    }
-    is_pk_font = false;
-    xfree(t3_buffer);
-    t3_curbyte = 0;
-    t3_size = 0;
-    if (!writepk(pdf, f))
-        return;
-    for (i = font_bc(f); i <= font_ec(f); i++)
-        if (pdf_char_marked(f, i))
-            break;
-    first_char = i;
-    for (i = font_ec(f); i > first_char; i--)
-        if (pdf_char_marked(f, i))
-            break;
-    last_char = i;
-    /*tex We create a |Type3| font dictionary: */
-    pdf_begin_obj(pdf, pdf_font_num(f), OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "Font");
-    pdf_dict_add_name(pdf, "Subtype", "Type3");
-    snprintf(s, 31, "F%i", (int) f);
-    pdf_dict_add_name(pdf, "Name", s);
-    if (pdf_font_attr(f) != get_nullstr() && pdf_font_attr(f) != 0) {
-        pdf_out(pdf, '\n');
-        pdf_print(pdf, pdf_font_attr(f));
-        pdf_out(pdf, '\n');
-    }
-    if (is_pk_font) {
-        pk_font_scale = get_pk_font_scale(pdf,f,pdf->pk_scale_factor);
-        pdf_add_name(pdf, "FontMatrix");
-        pdf_begin_array(pdf);
-        setpdffloat(pf, (int64_t) pk_font_scale, 5);
-        print_pdffloat(pdf, pf);
-        pdf_puts(pdf, " 0 0 ");
-        print_pdffloat(pdf, pf);
-        pdf_puts(pdf, " 0 0");
-        pdf_end_array(pdf);
-    } else {
-        pdf_add_name(pdf, "FontMatrix");
-        pdf_begin_array(pdf);
-        pdf_printf(pdf, "%g 0 0 %g 0 0", (double) t3_font_scale, (double) t3_font_scale);
-        pdf_end_array(pdf);
-    }
-    pdf_add_name(pdf, font_key[FONTBBOX1_CODE].pdfname);
-    pdf_begin_array(pdf);
-    pdf_add_int(pdf, (int) t3_b0);
-    pdf_add_int(pdf, (int) t3_b1);
-    pdf_add_int(pdf, (int) t3_b2);
-    pdf_add_int(pdf, (int) t3_b3);
-    pdf_end_array(pdf);
-    pdf_add_name(pdf, "Resources");
-    pdf_begin_dict(pdf);
-    pdf_add_name(pdf, "ProcSet");
-    pdf_begin_array(pdf);
-    pdf_add_name(pdf, "PDF");
-    if (t3_image_used) {
-        pdf_add_name(pdf, "ImageB");
-    }
-    pdf_end_array(pdf);
-    pdf_end_dict(pdf);
-    pdf_dict_add_int(pdf, "FirstChar", first_char);
-    pdf_dict_add_int(pdf, "LastChar", last_char);
-    wptr = pdf_create_obj(pdf, obj_type_others, 0);
-    eptr = pdf_create_obj(pdf, obj_type_others, 0);
-    cptr = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_dict_add_ref(pdf, "Widths", (int) wptr);
-    pdf_dict_add_ref(pdf, "Encoding", (int) eptr);
-    pdf_dict_add_ref(pdf, "CharProcs", (int) cptr);
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-    /*tex The |Widths| array: */
-    pdf_begin_obj(pdf, wptr, OBJSTM_ALWAYS);
-    pdf_begin_array(pdf);
-    if (is_pk_font) {
-        for (i = first_char; i <= last_char; i++) {
-            setpdffloat(pf, (int64_t) t3_char_widths[i], 2);
-            print_pdffloat(pdf, pf);
-            pdf_out(pdf, ' ');
-        }
-    } else {
-        for (i = first_char; i <= last_char; i++) {
-            pdf_add_int(pdf, (int) t3_char_widths[i]);
-        }
-    }
-    pdf_end_array(pdf);
-    pdf_end_obj(pdf);
-    /*tex The |Encoding| dictionary: */
-    pdf_begin_obj(pdf, eptr, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "Encoding");
-    pdf_add_name(pdf, "Differences");
-    pdf_begin_array(pdf);
-    pdf_add_int(pdf, first_char);
-    if (t3_char_procs[first_char] == 0) {
-        pdf_add_name(pdf, notdef);
-        is_notdef = true;
-    } else {
-        snprintf(s, 31, "a%i", first_char);
-        pdf_add_name(pdf, s);
-        is_notdef = false;
-    }
-    for (i = first_char + 1; i <= last_char; i++) {
-        if (t3_char_procs[i] == 0) {
-            if (!is_notdef) {
-                pdf_add_int(pdf, i);
-                pdf_add_name(pdf, notdef);
-                is_notdef = true;
-            }
-        } else {
-            if (is_notdef) {
-                pdf_add_int(pdf, i);
-                is_notdef = false;
-            }
-            snprintf(s, 31, "a%i", i);
-            pdf_add_name(pdf, s);
-        }
-    }
-    pdf_end_array(pdf);
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-    /*tex The |CharProcs| dictionary: */
-    pdf_begin_obj(pdf, cptr, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    for (i = first_char; i <= last_char; i++) {
-        if (t3_char_procs[i] != 0) {
-            snprintf(s, 31, "a%i", (int) i);
-            pdf_dict_add_ref(pdf, s, (int) t3_char_procs[i]);
-        }
-    }
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-    report_stop_file(filetype_font);
-    cur_file_name = NULL;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/writettf.w
@@ -0,0 +1,1797 @@
+% writettf.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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 "font/writettf.h"
+#include <string.h>
+
+#define DEFAULT_NTABS       14
+#define NEW_CMAP_SIZE       2
+
+#define ttf_putchar(A)     strbuf_putchar(pdf->fb, (A))
+#define ttf_offset()       strbuf_offset(pdf->fb)
+#define ttf_seek_outbuf(A) strbuf_seek(pdf->fb, (A))
+
+unsigned char *ttf_buffer = NULL;
+int ttf_size = 0;
+int ttf_curbyte = 0;
+
+typedef struct {
+    char *name;                 /* name of glyph */
+    long newindex;              /* new index of glyph in output file */
+
+} ttfenc_entry;
+
+typedef struct {
+    TTF_USHORT platform_id;
+    TTF_USHORT encoding_id;
+    TTF_USHORT language_id;
+    TTF_USHORT name_id;
+    TTF_USHORT length;
+    TTF_USHORT offset;
+    TTF_USHORT new_offset;
+    TTF_USHORT new_length;
+} name_record;
+
+typedef struct {
+    char *ttf_name;
+    TTF_USHORT pid;
+    TTF_USHORT eid;
+    long *table;
+} ttf_cmap_entry;
+
+static TTF_USHORT ntabs;
+static TTF_USHORT upem;
+static TTF_FIXED post_format;
+static TTF_SHORT loca_format;
+static TTF_ULONG last_glyf_offset;
+static TTF_USHORT glyphs_count;
+static TTF_USHORT new_glyphs_count;
+static TTF_USHORT nhmtxs;
+static TTF_USHORT new_ntabs;
+
+glyph_entry *glyph_tab;
+dirtab_entry *dir_tab;
+
+static long *glyph_index;
+static cmap_entry *cmap_tab, new_cmap_tab[NEW_CMAP_SIZE];
+static name_record *name_tab;
+static int name_record_num;
+static char *name_buf;
+static int name_buf_size;
+static char *glyph_name_buf;
+static TTF_ULONG checksum;
+static TTF_ULONG tab_length;
+static TTF_ULONG tmp_ulong;
+static TTF_ULONG checkSumAdjustment_offset;
+FILE *ttf_file;
+static ttfenc_entry ttfenc_tab[256];
+
+fd_entry *fd_cur;               /* pointer to the current font descriptor */
+
+static struct avl_table *ttf_cmap_tree = NULL;
+
+int ttf_length;
+
+@ This used to be macnames.c
+
+@c
+char notdef[] = ".notdef";
+
+const char *mac_glyph_names[] = {
+/* 0x00 */
+    notdef,
+    ".null",
+    "CR",
+    "space",
+    "exclam",
+    "quotedbl",
+    "numbersign",
+    "dollar",
+    "percent",
+    "ampersand",
+    "quotesingle",
+    "parenleft",
+    "parenright",
+    "asterisk",
+    "plus",
+    "comma",
+/* 0x10 */
+    "hyphen",
+    "period",
+    "slash",
+    "zero",
+    "one",
+    "two",
+    "three",
+    "four",
+    "five",
+    "six",
+    "seven",
+    "eight",
+    "nine",
+    "colon",
+    "semicolon",
+    "less",
+/* 0x20 */
+    "equal",
+    "greater",
+    "question",
+    "at",
+    "A",
+    "B",
+    "C",
+    "D",
+    "E",
+    "F",
+    "G",
+    "H",
+    "I",
+    "J",
+    "K",
+    "L",
+/* 0x30 */
+    "M",
+    "N",
+    "O",
+    "P",
+    "Q",
+    "R",
+    "S",
+    "T",
+    "U",
+    "V",
+    "W",
+    "X",
+    "Y",
+    "Z",
+    "bracketleft",
+    "backslash",
+/* 0x40 */
+    "bracketright",
+    "asciicircum",
+    "underscore",
+    "grave",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+/* 0x50 */
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "braceleft",
+    "bar",
+/* 0x60 */
+    "braceright",
+    "asciitilde",
+    "Adieresis",
+    "Aring",
+    "Ccedilla",
+    "Eacute",
+    "Ntilde",
+    "Odieresis",
+    "Udieresis",
+    "aacute",
+    "agrave",
+    "acircumflex",
+    "adieresis",
+    "atilde",
+    "aring",
+    "ccedilla",
+/* 0x70 */
+    "eacute",
+    "egrave",
+    "ecircumflex",
+    "edieresis",
+    "iacute",
+    "igrave",
+    "icircumflex",
+    "idieresis",
+    "ntilde",
+    "oacute",
+    "ograve",
+    "ocircumflex",
+    "odieresis",
+    "otilde",
+    "uacute",
+    "ugrave",
+/* 0x80 */
+    "ucircumflex",
+    "udieresis",
+    "dagger",
+    "degree",
+    "cent",
+    "sterling",
+    "section",
+    "bullet",
+    "paragraph",
+    "germandbls",
+    "registered",
+    "copyright",
+    "trademark",
+    "acute",
+    "dieresis",
+    "notequal",
+/* 0x90 */
+    "AE",
+    "Oslash",
+    "infinity",
+    "plusminus",
+    "lessequal",
+    "greaterequal",
+    "yen",
+    "mu",
+    "partialdiff",
+    "Sigma",
+    "Pi",
+    "pi",
+    "integral",
+    "ordfeminine",
+    "ordmasculine",
+    "Omega",
+/* 0xa0 */
+    "ae",
+    "oslash",
+    "questiondown",
+    "exclamdown",
+    "logicalnot",
+    "radical",
+    "florin",
+    "approxequal",
+    "Delta",
+    "guillemotleft",
+    "guillemotright",
+    "ellipsis",
+    "nbspace",
+    "Agrave",
+    "Atilde",
+    "Otilde",
+/* 0xb0 */
+    "OE",
+    "oe",
+    "endash",
+    "emdash",
+    "quotedblleft",
+    "quotedblright",
+    "quoteleft",
+    "quoteright",
+    "divide",
+    "lozenge",
+    "ydieresis",
+    "Ydieresis",
+    "fraction",
+    "currency",
+    "guilsinglleft",
+    "guilsinglright",
+/* 0xc0 */
+    "fi",
+    "fl",
+    "daggerdbl",
+    "periodcentered",
+    "quotesinglbase",
+    "quotedblbase",
+    "perthousand",
+    "Acircumflex",
+    "Ecircumflex",
+    "Aacute",
+    "Edieresis",
+    "Egrave",
+    "Iacute",
+    "Icircumflex",
+    "Idieresis",
+    "Igrave",
+/* 0xd0 */
+    "Oacute",
+    "Ocircumflex",
+    "applelogo",
+    "Ograve",
+    "Uacute",
+    "Ucircumflex",
+    "Ugrave",
+    "dotlessi",
+    "circumflex",
+    "tilde",
+    "macron",
+    "breve",
+    "dotaccent",
+    "ring",
+    "cedilla",
+    "hungarumlaut",
+/* 0xe0 */
+    "ogonek",
+    "caron",
+    "Lslash",
+    "lslash",
+    "Scaron",
+    "scaron",
+    "Zcaron",
+    "zcaron",
+    "brokenbar",
+    "Eth",
+    "eth",
+    "Yacute",
+    "yacute",
+    "Thorn",
+    "thorn",
+    "minus",
+/* 0xf0 */
+    "multiply",
+    "onesuperior",
+    "twosuperior",
+    "threesuperior",
+    "onehalf",
+    "onequarter",
+    "threequarters",
+    "franc",
+    "Gbreve",
+    "gbreve",
+    "Idot",
+    "Scedilla",
+    "scedilla",
+    "Cacute",
+    "cacute",
+    "Ccaron",
+/* 0x100 */
+    "ccaron",
+    "dmacron"
+};
+
+const char *ambiguous_names[] = {
+    "Delta",                    /*   increment   */
+    "Omega",                    /*   Ohm         */
+    "Pi",                       /*   product     */
+    "Sigma",                    /*   summation   */
+    "dmacron",                  /*   dslash      */
+    "macron",                   /*   overscore   */
+    "periodcentered",           /*   middot      */
+    NULL
+};
+
+static const char *newtabnames[] = {
+    "OS/2",
+    "PCLT",
+    "cmap",
+    "cvt ",
+    "fpgm",
+    "glyf",
+    "head",
+    "hhea",
+    "hmtx",
+    "loca",
+    "maxp",
+    "name",
+    "post",
+    "prep"
+};
+
+@ Back to code. Low-level helpers first.
+
+@c
+static ttf_cmap_entry *new_ttf_cmap_entry(void)
+{
+    ttf_cmap_entry *e;
+    e = xtalloc(1, ttf_cmap_entry);
+    e->ttf_name = NULL;
+    e->table = NULL;
+    return e;
+}
+
+static void destroy_ttf_cmap_entry(void *pa, void *pb)
+{
+    ttf_cmap_entry *p;
+    (void) pb;
+    p = (ttf_cmap_entry *) pa;
+    xfree(p->ttf_name);
+    xfree(p->table);
+    xfree(p);
+}
+
+void ttf_free(void)
+{
+    if (ttf_cmap_tree != NULL)
+        avl_destroy(ttf_cmap_tree, destroy_ttf_cmap_entry);
+}
+
+static int comp_ttf_cmap_entry(const void *pa, const void *pb, void *p)
+{
+    const ttf_cmap_entry *p1 = (const ttf_cmap_entry *) pa,
+        *p2 = (const ttf_cmap_entry *) pb;
+    int i;
+    (void) p;
+    assert(p1->ttf_name != NULL && p2->ttf_name != NULL);
+    if ((i = strcmp(p1->ttf_name, p2->ttf_name)) != 0)
+        return i;
+    cmp_return(p1->pid, p2->pid);
+    cmp_return(p1->eid, p2->eid);
+    return 0;
+}
+
+static unsigned char ttf_addchksm(unsigned char b)
+{
+    tmp_ulong = (tmp_ulong << 8) + b;
+    tab_length++;
+    if (tab_length % 4 == 0) {
+        checksum += tmp_ulong;
+        tmp_ulong = 0;
+    }
+    return b;
+}
+
+static TTF_ULONG ttf_getchksm(PDF pdf)
+{
+    while (tab_length % 4 != 0)
+        ttf_putchar(ttf_addchksm(0));   /* |ttf_addchksm| updates |tab_length| */
+    return checksum;
+}
+
+long ttf_putnum(PDF pdf, int s, long n)
+{
+    long i = n;
+    char buf[TTF_LONG_SIZE + 1], *p = buf;
+    while (s-- > 0) {
+        *p++ = (char) (i & 0xFF);
+        i >>= 8;
+    }
+    p--;
+    while (p >= buf)
+        ttf_putchar(ttf_addchksm((unsigned char) (*p--)));
+    return n;
+}
+
+long ttf_getnum(int s)
+{
+    long i = 0;
+    int c;
+    while (s > 0) {
+        if (ttf_eof())
+            normal_error("ttf font","unexpected EOF");
+        c = ttf_getchar();
+        i = (i << 8) + c;
+        s--;
+    }
+    return i;
+}
+
+static long ttf_funit(long n)
+{
+    if (n < 0)
+        return -((-n / upem) * 1000 + ((-n % upem) * 1000) / upem);
+    else
+        return (n / upem) * 1000 + ((n % upem) * 1000) / upem;
+}
+
+static void ttf_ncopy(PDF pdf, int n)
+{
+    while (n-- > 0)
+        copy_byte();
+}
+
+dirtab_entry *ttf_name_lookup(const char *s, boolean required)
+{
+    dirtab_entry *tab;
+    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
+        if (strncmp(tab->tag, s, 4) == 0)
+            break;
+    }
+    if (tab - dir_tab == ntabs) {
+        if (required)
+            formatted_error("ttf font","can't find table '%s'", s);
+        else
+            tab = NULL;
+    }
+    return tab;
+}
+
+dirtab_entry *ttf_seek_tab(const char *name, TTF_LONG offset)
+{
+    dirtab_entry *tab = ttf_name_lookup(name, true);
+    ttf_curbyte = (int) (tab->offset + (unsigned long) offset);
+    return tab;
+}
+
+static void ttf_seek_off(TTF_LONG offset)
+{
+    ttf_curbyte = (int) offset;
+}
+
+static void ttf_copy_encoding(void)
+{
+    int i, *q;
+    void **aa;
+    char **glyph_names;
+    /*long *charcodes;*/
+    /*static char buf[SMALL_BUF_SIZE];*/
+    struct avl_traverser t;
+    /*ttfenc_entry *e = ttfenc_tab;*/
+
+    assert(fd_cur->tx_tree != NULL);    /* this must be set in |create_fontdictionary| */
+
+    if (fd_cur->fe != NULL) {
+        glyph_names = fd_cur->fe->glyph_names;
+        assert(glyph_names != NULL);
+
+        for (i = 0; i < 256; i++)
+            ttfenc_tab[i].name = (char *) notdef;
+
+        /* a workaround for a bug of AcroReader 4.0 */
+        if (strcmp(glyph_names[97], "a") == 0) {
+            q = xtalloc(1, int);
+            *q = 'a';
+            aa = avl_probe(fd_cur->tx_tree, q);
+            assert(aa != NULL);
+        }
+        /* end of workaround */
+
+        /* take over collected characters from \TeX, reencode them */
+        avl_t_init(&t, fd_cur->tx_tree);
+        for (q = (int *) avl_t_first(&t, fd_cur->tx_tree); q != NULL;
+             q = (int *) avl_t_next(&t)) {
+            assert(*q >= 0 && *q < 256);
+            ttfenc_tab[*q].name = glyph_names[*q];
+        }
+        make_subset_tag(fd_cur);
+    } else
+        assert(0);
+}
+
+@
+@c
+#define ttf_append_byte(B)\
+do {\
+    if (name_tab[i].platform_id == 3)\
+        *q++ = 0;\
+    *q++ = B;\
+} while (0)
+
+static char *strip_spaces_and_delims(char *s, int l)
+{
+    static char buf[SMALL_BUF_SIZE];
+    char *p = buf;
+    int i;
+
+    assert(l >= 0 && l < (int) sizeof(buf));
+
+    for (i = 0; i < l; s++, i++) {
+        if (*s == '(' || *s == ')' || *s == '<' || *s == '>' ||
+            *s == '[' || *s == ']' || *s == '{' || *s == '}' ||
+            *s == '/' || *s == '%' || isspace((unsigned char)*s))
+            continue;
+        *p++ = *s;
+    }
+    *p = 0;
+    return buf;
+}
+
+static void ttf_read_name(void)
+{
+    int i, j;
+    dirtab_entry *tab = ttf_seek_tab("name", TTF_USHORT_SIZE);
+    char *p, buf[SMALL_BUF_SIZE];
+    name_record_num = get_ushort();
+    name_tab = xtalloc((unsigned) name_record_num, name_record);
+    name_buf_size = (int) ((unsigned) tab->length -
+                           (3 * TTF_USHORT_SIZE +
+                            (TTF_ULONG) name_record_num * 6 * TTF_USHORT_SIZE));
+    name_buf = xtalloc((unsigned) name_buf_size, char);
+    ttf_skip(TTF_USHORT_SIZE);
+    for (i = 0; i < name_record_num; i++) {
+        name_tab[i].platform_id = get_ushort();
+        name_tab[i].encoding_id = get_ushort();
+        name_tab[i].language_id = get_ushort();
+        name_tab[i].name_id = get_ushort();
+        name_tab[i].length = get_ushort();
+        name_tab[i].offset = get_ushort();
+    }
+    for (p = name_buf; p - name_buf < name_buf_size; p++)
+        *p = get_char();
+    /* look for PS font name */
+    for (i = 0; i < name_record_num; i++) {
+        if (name_tab[i].platform_id == 1 &&
+            name_tab[i].encoding_id == 0 && name_tab[i].name_id == 6) {
+            xfree(fd_cur->fontname);
+            fd_cur->fontname =
+                xstrdup(strip_spaces_and_delims(name_buf + name_tab[i].offset,
+                                                name_tab[i].length));
+            fd_cur->font_dim[FONTNAME_CODE].set = true;
+            break;
+        }
+    }
+    if (!fd_cur->font_dim[FONTNAME_CODE].set) {
+        for (i = 0; i < name_record_num; i++) {
+            if (name_tab[i].platform_id == 3 &&
+                (name_tab[i].encoding_id == 0 || name_tab[i].encoding_id == 1)
+                && name_tab[i].name_id == 6) {
+                xfree(fd_cur->fontname);
+                assert(name_tab[i].length < sizeof(buf));
+                for (j = 0, p = buf; j < name_tab[i].length; j += 2)
+                    *p++ = name_buf[name_tab[i].offset + j + 1];
+                *p = 0;
+                fd_cur->fontname =
+                    xstrdup(strip_spaces_and_delims(buf, (int) strlen(buf)));
+                fd_cur->font_dim[FONTNAME_CODE].set = true;
+                break;
+            }
+        }
+    }
+}
+
+static void ttf_read_mapx(void)
+{
+    glyph_entry *glyph;
+    ttf_seek_tab("maxp", TTF_FIXED_SIZE);
+    glyph_tab =
+        xtalloc((unsigned) (1 + (glyphs_count = get_ushort())), glyph_entry);
+    for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++) {
+        glyph->newindex = -1;
+        glyph->newoffset = 0;
+        glyph->name_index = 0;
+        glyph->name = (char *) notdef;
+    }
+    glyph_index = xtalloc((unsigned) (glyphs_count + 1), long);
+    glyph_index[0] = 0;         /* index of ".notdef" glyph */
+    glyph_index[1] = 1;         /* index of ".null" glyph */
+}
+
+void ttf_read_head(void)
+{
+    ttf_seek_tab("head",
+                 2 * TTF_FIXED_SIZE + 2 * TTF_ULONG_SIZE + TTF_USHORT_SIZE);
+    upem = get_ushort();
+    ttf_skip(16);
+    fd_cur->font_dim[FONTBBOX1_CODE].val = (int) ttf_funit(get_fword());
+    fd_cur->font_dim[FONTBBOX2_CODE].val = (int) ttf_funit(get_fword());
+    fd_cur->font_dim[FONTBBOX3_CODE].val = (int) ttf_funit(get_fword());
+    fd_cur->font_dim[FONTBBOX4_CODE].val = (int) ttf_funit(get_fword());
+    fd_cur->font_dim[FONTBBOX1_CODE].set = true;
+    fd_cur->font_dim[FONTBBOX2_CODE].set = true;
+    fd_cur->font_dim[FONTBBOX3_CODE].set = true;
+    fd_cur->font_dim[FONTBBOX4_CODE].set = true;
+    ttf_skip(2 * TTF_USHORT_SIZE + TTF_SHORT_SIZE);
+    loca_format = get_short();
+}
+
+void ttf_read_hhea(void)
+{
+    ttf_seek_tab("hhea", TTF_FIXED_SIZE);
+    fd_cur->font_dim[ASCENT_CODE].val = (int) ttf_funit(get_fword());
+    fd_cur->font_dim[DESCENT_CODE].val = (int) ttf_funit(get_fword());
+    fd_cur->font_dim[ASCENT_CODE].set = true;
+    fd_cur->font_dim[DESCENT_CODE].set = true;
+    ttf_skip(TTF_FWORD_SIZE + TTF_UFWORD_SIZE + 3 * TTF_FWORD_SIZE +
+             8 * TTF_SHORT_SIZE);
+    nhmtxs = get_ushort();
+}
+
+void ttf_read_pclt(void)
+{
+    if (ttf_name_lookup("PCLT", false) == NULL)
+        return;
+    ttf_seek_tab("PCLT", TTF_FIXED_SIZE + TTF_ULONG_SIZE + TTF_USHORT_SIZE);
+    fd_cur->font_dim[XHEIGHT_CODE].val = (int) ttf_funit(get_ushort());
+    ttf_skip(2 * TTF_USHORT_SIZE);
+    fd_cur->font_dim[CAPHEIGHT_CODE].val = (int) ttf_funit(get_ushort());
+    fd_cur->font_dim[XHEIGHT_CODE].set = true;
+    fd_cur->font_dim[CAPHEIGHT_CODE].set = true;
+}
+
+static void ttf_read_hmtx(void)
+{
+    glyph_entry *glyph;
+    TTF_UFWORD last_advWidth;
+    ttf_seek_tab("hmtx", 0);
+    for (glyph = glyph_tab; glyph - glyph_tab < nhmtxs; glyph++) {
+        glyph->advWidth = get_ufword();
+        glyph->lsb = (TTF_FWORD) get_ufword();
+    }
+    if (nhmtxs < glyphs_count) {
+        last_advWidth = glyph[-1].advWidth;
+        for (; glyph - glyph_tab < glyphs_count; glyph++) {
+            glyph->advWidth = last_advWidth;
+            glyph->lsb = (TTF_FWORD) get_ufword();
+        }
+    }
+}
+
+void ttf_read_post(void)
+{
+    int k, nnames;
+    long length;
+    long int_part, frac_part;
+    int sign = 1;
+    TTF_FIXED italic_angle;
+    char *p;
+    glyph_entry *glyph;
+    const dirtab_entry *tab = ttf_seek_tab("post", 0);
+    post_format = get_fixed();
+    italic_angle = get_fixed();
+    int_part = (long) (italic_angle >> 16);
+    if (int_part > 0x7FFF) {    /* a negative number */
+        int_part = 0x10000 - int_part;
+        sign = -1;
+    }
+    frac_part = (long) (italic_angle % 0x10000);
+    fd_cur->font_dim[ITALIC_ANGLE_CODE].val =
+        (int) (sign * ((double) int_part + (double) frac_part * 1.0 / 0x10000));
+    fd_cur->font_dim[ITALIC_ANGLE_CODE].set = true;
+    if (glyph_tab == NULL)
+        return;                 /* being called from writeotf() */
+    ttf_skip(2 * TTF_FWORD_SIZE + 5 * TTF_ULONG_SIZE);
+    switch (post_format) {
+    case 0x10000:
+        for (glyph = glyph_tab; glyph - glyph_tab < NMACGLYPHS; glyph++) {
+            glyph->name = (const char *) mac_glyph_names[glyph - glyph_tab];
+            glyph->name_index = (TTF_USHORT) (glyph - glyph_tab);
+        }
+        break;
+    case 0x20000:
+        nnames = get_ushort();  /* some fonts have this value different from nglyphs */
+        for (glyph = glyph_tab; glyph - glyph_tab < nnames; glyph++)
+            glyph->name_index = get_ushort();
+        length =
+            (long) ((long) tab->length -
+                    (long) ((long) ttf_curbyte - (long) tab->offset));
+        glyph_name_buf = xtalloc((unsigned) length, char);
+        for (p = glyph_name_buf; p - glyph_name_buf < length;) {
+            for (k = get_byte(); k > 0; k--)
+                *p++ = get_char();
+            *p++ = 0;
+        }
+        for (glyph = glyph_tab; glyph - glyph_tab < nnames; glyph++) {
+            if (glyph->name_index < NMACGLYPHS)
+                glyph->name = mac_glyph_names[glyph->name_index];
+            else {
+                p = glyph_name_buf;
+                k = glyph->name_index - NMACGLYPHS;
+                for (; k > 0; k--)
+                    p = strend(p) + 1;
+                glyph->name = p;
+            }
+        }
+        break;
+    default:
+        formatted_warning("ttf font", "unsupported format '%.8X' of 'post' table, assuming 3.0", (unsigned int) post_format);
+    case 0x00030000:
+        for (glyph = glyph_tab; glyph - glyph_tab < NMACGLYPHS; glyph++) {
+            glyph->name_index = (TTF_USHORT) (glyph - glyph_tab);
+        }
+    }
+}
+
+static void ttf_read_loca(void)
+{
+    glyph_entry *glyph;
+    ttf_seek_tab("loca", 0);
+    if (loca_format != 0)
+        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count + 1; glyph++)
+            glyph->offset = (TTF_LONG) get_ulong();
+    else
+        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count + 1; glyph++)
+            glyph->offset = get_ushort() << 1;
+}
+
+void otc_read_tabdir(int index)
+{
+    unsigned long i, num, rem=0;
+    dirtab_entry *tab;
+    ttf_skip(TTF_FIXED_SIZE);   /* ignore TTCTag 'ttcf' */
+    ttf_skip(TTF_ULONG_SIZE);   /* ignorethe version number */
+    num = get_ulong();
+    for (i = 0; i < num; i++)  {
+        if (i==index) rem = get_ulong(); else ttf_skip(TTF_ULONG_SIZE);
+    }
+    ttf_skip(rem - TTF_FIXED_SIZE - (num+2)*TTF_ULONG_SIZE);
+    ttf_skip(TTF_FIXED_SIZE);   /* ignore the sfnt number */
+    dir_tab = xtalloc(ntabs = get_ushort(), dirtab_entry);
+    ttf_skip(3 * TTF_USHORT_SIZE);
+    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
+        for (i = 0; i < 4; i++)
+            tab->tag[i] = get_char();
+        tab->checksum = get_ulong();
+        tab->offset = get_ulong();
+        tab->length = get_ulong();
+    }
+}
+
+void ttf_read_tabdir(void)
+{
+    int i;
+    dirtab_entry *tab;
+    ttf_skip(TTF_FIXED_SIZE);   /* ignore the sfnt number */
+    dir_tab = xtalloc(ntabs = get_ushort(), dirtab_entry);
+    ttf_skip(3 * TTF_USHORT_SIZE);
+    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
+        for (i = 0; i < 4; i++)
+            tab->tag[i] = get_char();
+        tab->checksum = get_ulong();
+        tab->offset = get_ulong();
+        tab->length = get_ulong();
+    }
+}
+
+static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid,
+                                     boolean warn)
+{
+    seg_entry *seg_tab, *s;
+    TTF_USHORT *glyphId, format, segCount;
+    TTF_USHORT ncmapsubtabs, tmp_pid, tmp_eid;
+    TTF_ULONG cmap_offset, tmp_offset;
+    long n, i, k, length, index;
+    ttf_cmap_entry tmp_e, *p;
+    void **aa;
+
+    /* look up in |ttf_cmap_tree| first, return if found */
+    tmp_e.ttf_name = ttf_name;
+    tmp_e.pid = (TTF_USHORT) pid;
+    tmp_e.eid = (TTF_USHORT) eid;
+    if (ttf_cmap_tree == NULL) {
+        ttf_cmap_tree = avl_create(comp_ttf_cmap_entry, NULL, &avl_xallocator);
+        assert(ttf_cmap_tree != NULL);
+    }
+    p = (ttf_cmap_entry *) avl_find(ttf_cmap_tree, &tmp_e);
+    if (p != NULL)
+        return p;
+
+    /* not found, have to read it */
+    ttf_seek_tab("cmap", TTF_USHORT_SIZE);      /* skip the table version number (=0) */
+    ncmapsubtabs = get_ushort();
+    cmap_offset = (TTF_ULONG) (ttf_curbyte - 2 * TTF_USHORT_SIZE);
+    cmap_tab = xtalloc(ncmapsubtabs, cmap_entry);
+    for (i = 0; i < ncmapsubtabs; ++i) {
+        tmp_pid = get_ushort();
+        tmp_eid = get_ushort();
+        tmp_offset = get_ulong();
+        if (tmp_pid == pid && tmp_eid == eid) {
+            ttf_seek_off((TTF_LONG) (cmap_offset + tmp_offset));
+            format = get_ushort();
+            if (format == 4)
+                goto read_cmap_format_4;
+            else {
+                if (warn)
+                    formatted_warning("ttf font", "cmap format %i unsupported", format);
+                return NULL;
+            }
+        }
+    }
+    if (warn)
+        formatted_warning("ttf font", "cannot find cmap subtable for (pid,eid) = (%i,%i)", pid, eid);
+    return NULL;
+  read_cmap_format_4:
+    /* initialize the new entry */
+    p = new_ttf_cmap_entry();
+    p->ttf_name = xstrdup(ttf_name);
+    p->pid = (TTF_USHORT) pid;
+    p->eid = (TTF_USHORT) eid;
+    p->table = xtalloc(0x10000, long);
+    for (i = 0; i < 0x10000; ++i)
+        p->table[i] = -1;       /* unassigned yet */
+
+    /* read the subtable */
+    length = get_ushort();      /* length of subtable */
+    (void) get_ushort();        /* skip the version number */
+    segCount = get_ushort() / 2;
+    (void) get_ushort();        /* skip searchRange */
+    (void) get_ushort();        /* skip entrySelector */
+    (void) get_ushort();        /* skip rangeShift */
+    seg_tab = xtalloc(segCount, seg_entry);
+    for (s = seg_tab; s - seg_tab < segCount; s++)
+        s->endCode = get_ushort();
+    (void) get_ushort();        /* skip reversedPad */
+    for (s = seg_tab; s - seg_tab < segCount; s++)
+        s->startCode = get_ushort();
+    for (s = seg_tab; s - seg_tab < segCount; s++)
+        s->idDelta = get_ushort();
+    for (s = seg_tab; s - seg_tab < segCount; s++)
+        s->idRangeOffset = get_ushort();
+    length -= 8 * TTF_USHORT_SIZE + 4 * segCount * TTF_USHORT_SIZE;
+    n = length / TTF_USHORT_SIZE;       /* number of glyphID's */
+    glyphId = xtalloc((unsigned) n, TTF_USHORT);
+    for (i = 0; i < n; i++)
+        glyphId[i] = get_ushort();
+    for (s = seg_tab; s - seg_tab < segCount; s++) {
+        for (i = s->startCode; i <= s->endCode; i++) {
+            if (i == 0xFFFF)
+                break;
+            if (s->idRangeOffset != 0xFFFF) {
+                if (s->idRangeOffset == 0)
+                    index = (s->idDelta + i) & 0xFFFF;
+                else {
+                    k = (i - s->startCode) + s->idRangeOffset / 2 +
+                        (s - seg_tab) - segCount;
+                    assert(k >= 0 && k < n);
+                    index = glyphId[k];
+                    if (index != 0)
+                        index = (index + s->idDelta) & 0xFFFF;
+                }
+                if (index >= glyphs_count)
+                    formatted_error("ttf font",
+                        "cmap issue, glyph index %li out of range [0..%i)",
+                        index, glyphs_count);
+                if (p->table[i] != -1)
+                    formatted_warning("ttf font",
+                        "cmap issue, multiple glyphs are mapped to unicode %.4lX, %li will be used, %li is ignored)",
+                        i, p->table[i], index);
+                else
+                    p->table[i] = index;
+            }
+        }
+    }
+    xfree(seg_tab);
+    xfree(glyphId);
+    aa = avl_probe(ttf_cmap_tree, p);
+    assert(aa != NULL);
+    return p;
+}
+
+@
+@c
+static void ttf_read_font(void)
+{
+    ttf_read_tabdir();
+    if (ttf_name_lookup("PCLT", false) == NULL)
+        new_ntabs--;
+    if (ttf_name_lookup("fpgm", false) == NULL)
+        new_ntabs--;
+    if (ttf_name_lookup("cvt ", false) == NULL)
+        new_ntabs--;
+    if (ttf_name_lookup("prep", false) == NULL)
+        new_ntabs--;
+    ttf_read_mapx();
+    ttf_read_head();
+    ttf_read_hhea();
+    ttf_read_pclt();
+    ttf_read_hmtx();
+    ttf_read_post();
+    ttf_read_loca();
+    ttf_read_name();
+}
+
+static void ttf_reset_chksm(PDF pdf, dirtab_entry * tab)
+{
+    checksum = 0;
+    tab_length = 0;
+    tmp_ulong = 0;
+    tab->offset = (TTF_ULONG) ttf_offset();
+    if (tab->offset % 4 != 0)
+        formatted_warning("ttf font","offset of `%4.4s' is not a multiple of 4", tab->tag);
+}
+
+
+static void ttf_set_chksm(PDF pdf, dirtab_entry * tab)
+{
+    tab->length = (TTF_ULONG) ttf_offset() - tab->offset;
+    tab->checksum = ttf_getchksm(pdf);
+}
+
+static void ttf_copytab(PDF pdf, const char *name)
+{
+    long i;
+    dirtab_entry *tab = ttf_seek_tab(name, 0);
+    ttf_reset_chksm(pdf, tab);
+    for (i = (long) tab->length; i > 0; i--)
+        copy_char();
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+#define BYTE_ENCODING_LENGTH  \
+    ((256)*TTF_BYTE_SIZE + 3*TTF_USHORT_SIZE)
+
+static void ttf_byte_encoding(PDF pdf)
+{
+    ttfenc_entry *e;
+    (void) put_ushort(0);       /* format number (0: byte encoding table) */
+    (void) put_ushort(BYTE_ENCODING_LENGTH);    /* length of table */
+    (void) put_ushort(0);       /* version number */
+    for (e = ttfenc_tab; e - ttfenc_tab < 256; e++)
+        if (e->newindex < 256) {
+            put_byte(e->newindex);
+        } else {
+            if (e->name != notdef)
+                formatted_warning("ttf font",
+                    "glyph '%s' has been mapped to '%s' in 'ttf_byte_encoding' cmap table",
+                    e->name, notdef);
+            put_byte(0);        /* notdef */
+        }
+}
+
+@
+@c
+#define TRIMMED_TABLE_MAP_LENGTH (TTF_USHORT_SIZE*(5 + (256)))
+
+static void ttf_trimmed_table_map(PDF pdf)
+{
+    ttfenc_entry *e;
+    (void) put_ushort(6);       /* format number (6): trimmed table mapping */
+    (void) put_ushort(TRIMMED_TABLE_MAP_LENGTH);
+    (void) put_ushort(0);       /* version number (0) */
+    (void) put_ushort(0);       /* first character code */
+    (void) put_ushort(256);     /* number of character code in table */
+    for (e = ttfenc_tab; e - ttfenc_tab < 256; e++)
+        (void) put_ushort(e->newindex);
+}
+
+@
+@c
+#define SEG_MAP_DELTA_LENGTH ((16 + (256))*TTF_USHORT_SIZE)
+
+static void ttf_seg_map_delta(PDF pdf)
+{
+    ttfenc_entry *e;
+    (void) put_ushort(4);       /* format number (4: segment mapping to delta values) */
+    (void) put_ushort(SEG_MAP_DELTA_LENGTH);
+    (void) put_ushort(0);       /* version number */
+    (void) put_ushort(4);       /* 2*segCount */
+    (void) put_ushort(4);       /* searchRange */
+    (void) put_ushort(1);       /* entrySelector */
+    (void) put_ushort(0);       /* rangeShift */
+    (void) put_ushort(0xF0FF);  /* endCount[0] */
+    (void) put_ushort(0xFFFF);  /* endCount[1] */
+    (void) put_ushort(0);       /* reversedPad */
+    (void) put_ushort(0xF000);  /* startCount[0] */
+    (void) put_ushort(0xFFFF);  /* startCount[1] */
+    (void) put_ushort(0);       /* idDelta[0] */
+    (void) put_ushort(1);       /* idDelta[1] */
+    (void) put_ushort(2 * TTF_USHORT_SIZE);     /* idRangeOffset[0] */
+    (void) put_ushort(0);       /* idRangeOffset[1] */
+    for (e = ttfenc_tab; e - ttfenc_tab < 256; e++)
+        (void) put_ushort(e->newindex);
+}
+
+@
+@c
+#define CMAP_ENTRY_LENGTH (2*TTF_USHORT_SIZE + TTF_ULONG_SIZE)
+
+static void ttf_select_cmap(void)
+{
+    assert(sizeof(new_cmap_tab) <= NEW_CMAP_SIZE * sizeof(cmap_entry));
+    new_cmap_tab[0].platform_id = 1;    /* Macintosh */
+    new_cmap_tab[0].encoding_id = 0;    /* Symbol; ignore code page */
+    new_cmap_tab[0].format = (TTF_USHORT) (new_glyphs_count < 256 ? 0   /* byte encoding */
+                                           : 6);        /* trimmed table mapping */
+    new_cmap_tab[1].platform_id = 3;    /* Microsoft */
+    new_cmap_tab[1].encoding_id = 0;    /* Symbol; ignore code page */
+    new_cmap_tab[1].format = 4; /* segment mapping to delta */
+}
+
+static void ttf_write_cmap(PDF pdf)
+{
+    cmap_entry *ce;
+    long offset;
+    dirtab_entry *tab = ttf_name_lookup("cmap", true);
+    ttf_select_cmap();
+    ttf_reset_chksm(pdf, tab);
+    (void) put_ushort(0);       /* table version number (0) */
+    (void) put_ushort(NEW_CMAP_SIZE);   /* number of encoding tables */
+    offset = 2 * TTF_USHORT_SIZE + NEW_CMAP_SIZE * CMAP_ENTRY_LENGTH;
+    for (ce = new_cmap_tab; ce - new_cmap_tab < NEW_CMAP_SIZE; ce++) {
+        ce->offset = (TTF_ULONG) offset;
+        switch (ce->format) {
+        case 0:
+            offset += BYTE_ENCODING_LENGTH;
+            break;
+        case 4:
+            offset += SEG_MAP_DELTA_LENGTH;
+            break;
+        case 6:
+            offset += TRIMMED_TABLE_MAP_LENGTH;
+            break;
+        default:
+            normal_error("ttf font","invalid format (it should not have happened)");
+        }
+        (void) put_ushort(ce->platform_id);
+        (void) put_ushort(ce->encoding_id);
+        put_ulong((long) ce->offset);
+    }
+    for (ce = new_cmap_tab; ce - new_cmap_tab < NEW_CMAP_SIZE; ce++) {
+        switch (ce->format) {
+        case 0:
+            ttf_byte_encoding(pdf);
+            break;
+        case 4:
+            ttf_seg_map_delta(pdf);
+            break;
+        case 6:
+            ttf_trimmed_table_map(pdf);
+            break;
+        }
+    }
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+static int prepend_subset_tags(int index, char *p)
+{
+    boolean is_unicode;
+    int i;
+    assert(index >= 0 && index < name_record_num && fd_cur->subset_tag != NULL);
+    is_unicode = (name_tab[index].platform_id == 3);
+    if (is_unicode) {
+        for (i = 0; i < 6; ++i) {
+            *p++ = 0;
+            *p++ = fd_cur->subset_tag[i];
+        }
+        *p++ = 0;
+        *p++ = '+';
+        return 14;
+    } else {
+        strncpy(p, fd_cur->subset_tag, 6);
+        p += 6;
+        *p++ = '+';
+        return 7;
+    }
+}
+
+
+static void ttf_write_name(PDF pdf)
+{
+    int i, l;
+    char *p;
+    int new_name_buf_size;
+    char *new_name_buf;
+    name_record *n;
+    dirtab_entry *tab = ttf_name_lookup("name", true);
+    if (is_subsetted(fd_cur->fm)) {
+        l = 0;
+        for (i = 0; i < name_record_num; i++)
+            l += name_tab[i].length + 14;       /* maximum lengh of new stogare area */
+        new_name_buf = xtalloc((unsigned) l, char);
+        /* additional space for subset tags */
+        p = new_name_buf;
+        for (i = 0; i < name_record_num; i++) {
+            n = name_tab + i;
+            n->new_offset = (TTF_USHORT) (p - new_name_buf);
+            if ((n->name_id == 1 || n->name_id == 3 ||
+                 n->name_id == 4 || n->name_id == 6) &&
+                ((n->platform_id == 1 && n->encoding_id == 0) ||
+                 (n->platform_id == 3 && n->encoding_id == 0) ||
+                 (n->platform_id == 3 && n->encoding_id == 1))) {
+                l = prepend_subset_tags(i, p);
+                p += l;
+            } else
+                l = 0;
+            memcpy(p, name_buf + n->offset, n->length);
+            p += n->length;
+            n->new_length = (TTF_USHORT) (n->length + l);
+        }
+        new_name_buf_size = (int) (p - new_name_buf);
+    } else {
+        new_name_buf = name_buf;
+        new_name_buf_size = name_buf_size;
+    }
+    ttf_reset_chksm(pdf, tab);
+    (void) put_ushort(0);       /* Format selector */
+    (void) put_ushort(name_record_num);
+    (void) put_ushort(3 * TTF_USHORT_SIZE +
+                      name_record_num * 6 * TTF_USHORT_SIZE);
+    for (i = 0; i < name_record_num; i++) {
+        (void) put_ushort(name_tab[i].platform_id);
+        (void) put_ushort(name_tab[i].encoding_id);
+        (void) put_ushort(name_tab[i].language_id);
+        (void) put_ushort(name_tab[i].name_id);
+        (void) put_ushort(name_tab[i].new_length);
+        (void) put_ushort(name_tab[i].new_offset);
+    }
+    for (p = new_name_buf; p - new_name_buf < new_name_buf_size; p++)
+        put_char(*p);
+    ttf_set_chksm(pdf, tab);
+    if (new_name_buf != name_buf)
+        xfree(new_name_buf);
+}
+
+@
+@c
+static void ttf_write_dirtab(PDF pdf)
+{
+    dirtab_entry *tab;
+    TTF_ULONG i, k;
+    char *p;
+    const int save_offset = ttf_offset();
+    ttf_seek_outbuf(TABDIR_OFF);
+    if (is_subsetted(fd_cur->fm)) {
+        for (i = 0; i < DEFAULT_NTABS; i++) {
+            tab = ttf_name_lookup(newtabnames[i], false);
+            if (tab == NULL)
+                continue;
+            for (k = 0; k < 4; k++)
+                put_char(tab->tag[k]);
+            put_ulong((long) tab->checksum);
+            put_ulong((long) tab->offset);
+            put_ulong((long) tab->length);
+        }
+    } else {
+        for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
+            for (k = 0; k < 4; k++)
+                put_char(tab->tag[k]);
+            put_ulong((long) tab->checksum);
+            put_ulong((long) tab->offset);
+            put_ulong((long) tab->length);
+        }
+    }
+    /* adjust checkSumAdjustment */
+    tmp_ulong = 0;
+    checksum = 0;
+    for (p = (char *) pdf->fb->data, i = 0; i < (unsigned) save_offset;) {
+        tmp_ulong = (tmp_ulong << 8) + (TTF_ULONG) * p++;
+        i++;
+        if (i % 4 == 0) {
+            checksum += tmp_ulong;
+            tmp_ulong = 0;
+        }
+    }
+    if (i % 4 != 0) {
+        formatted_warning("ttf font","font length '%li' is not a multiple of 4", i);
+        checksum <<= 8 * (4 - i % 4);
+    }
+    k = 0xB1B0AFBA - checksum;
+    ttf_seek_outbuf((int) checkSumAdjustment_offset);
+    put_ulong((long) k);
+    ttf_seek_outbuf(save_offset);
+}
+
+@
+@c
+static void ttf_write_glyf(PDF pdf)
+{
+    long *id, k;
+    TTF_USHORT idx;
+    TTF_USHORT flags;
+    dirtab_entry *tab = ttf_name_lookup("glyf", true);
+    const long glyf_offset = (long) tab->offset;
+    const long new_glyf_offset = ttf_offset();
+    ttf_reset_chksm(pdf, tab);
+    for (id = glyph_index; id - glyph_index < new_glyphs_count; id++) {
+        glyph_tab[*id].newoffset = ttf_offset() - new_glyf_offset;
+        if (glyph_tab[*id].offset != glyph_tab[*id + 1].offset) {
+            ttf_seek_off(glyf_offset + glyph_tab[*id].offset);
+            k = copy_short();
+            ttf_ncopy(pdf, 4 * TTF_FWORD_SIZE);
+            if (k < 0) {
+                do {
+                    flags = copy_ushort();
+                    idx = get_ushort();
+                    if (glyph_tab[idx].newindex < 0) {
+                        glyph_tab[idx].newindex = (TTF_SHORT) new_glyphs_count;
+                        glyph_index[new_glyphs_count++] = idx;
+                        /*
+                           N.B.: Here we change |new_glyphs_count|,
+                           which appears in the condition of the |for| loop
+                         */
+                    }
+                    (void) put_ushort(glyph_tab[idx].newindex);
+                    if (flags & ARG_1_AND_2_ARE_WORDS)
+                        ttf_ncopy(pdf, 2 * TTF_SHORT_SIZE);
+                    else
+                        ttf_ncopy(pdf, TTF_USHORT_SIZE);
+                    if (flags & WE_HAVE_A_SCALE)
+                        ttf_ncopy(pdf, TTF_F2DOT14_SIZE);
+                    else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
+                        ttf_ncopy(pdf, 2 * TTF_F2DOT14_SIZE);
+                    else if (flags & WE_HAVE_A_TWO_BY_TWO)
+                        ttf_ncopy(pdf, 4 * TTF_F2DOT14_SIZE);
+                } while (flags & MORE_COMPONENTS);
+                if (flags & WE_HAVE_INSTRUCTIONS)
+                    ttf_ncopy(pdf, copy_ushort());
+            } else
+                ttf_ncopy(pdf, (int)
+                          (glyph_tab[*id + 1].offset - glyph_tab[*id].offset -
+                           TTF_USHORT_SIZE - 4 * TTF_FWORD_SIZE));
+        }
+    }
+    last_glyf_offset = (TTF_ULONG) ttf_offset() - (TTF_ULONG) new_glyf_offset;
+    ttf_set_chksm(pdf, tab);
+}
+
+@ Reindexing glyphs: we append index of used glyphs to |glyph_index|
+ while going through |ttfenc_tab|. After appending a new entry to
+ |glyph_index| we set field |newindex| of corresponding entries in both
+ |glyph_tab| and |ttfenc_tab| to the newly created index.
+
+@c
+static void ttf_reindex_glyphs(void)
+{
+    ttfenc_entry *e;
+    glyph_entry *glyph;
+    int index;
+    long *t;
+    ttf_cmap_entry *cmap = NULL;
+    boolean cmap_not_found = false;
+
+    for (e = ttfenc_tab; e - ttfenc_tab < 256; e++) {
+        e->newindex = 0;        /* index of ".notdef" glyph */
+
+        /* handle case of reencoded fonts */
+        if (e->name == notdef)
+            continue;
+        /* scan form `index123' */
+        if (sscanf(e->name, GLYPH_PREFIX_INDEX "%i", &index) == 1) {
+            if (index >= glyphs_count) {
+                formatted_warning("ttf font","'%s' out of valid range [0..%i)", e->name, glyphs_count);
+                continue;
+            }
+            glyph = glyph_tab + index;
+            goto append_new_glyph;
+        }
+        /* scan form `uniABCD' */
+        if (sscanf(e->name, GLYPH_PREFIX_UNICODE "%X", &index) == 1) {
+            if (cmap == NULL && !cmap_not_found) {
+                /* need to read the unicode mapping, ie (pid,eid) = (3,1) or (0,3) */
+                cmap = ttf_read_cmap(fd_cur->fm->ff_name, 3, 1, false);
+                if (cmap == NULL)
+                    cmap = ttf_read_cmap(fd_cur->fm->ff_name, 0, 3, false);
+                if (cmap == NULL) {
+                    normal_warning("ttf font", "no unicode mapping found, all 'uniXXXX' names will be ignored");
+                    cmap_not_found = true;      /* once only */
+                }
+            }
+            if (cmap == NULL)
+                continue;
+            t = cmap->table;
+            assert(t != NULL);
+            if (t[index] != -1) {
+                if (t[index] >= glyphs_count) {
+                    formatted_warning("ttf font", "'%s' is mapped to index %li which is out of valid range [0..%i)",
+                         e->name, t[index], glyphs_count);
+                    continue;
+                }
+                glyph = glyph_tab + t[index];
+                goto append_new_glyph;
+            } else {
+                formatted_warning("ttf font","unicode %s%.4X is not mapped to any glyph", GLYPH_PREFIX_UNICODE, index);
+                continue;
+            }
+        }
+        /* look up by name */
+        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++)
+            if (glyph->name != notdef && strcmp(glyph->name, e->name) == 0)
+                break;
+        if (!(glyph - glyph_tab < glyphs_count)) {
+            formatted_warning("ttf font","glyph '%s' not found", e->name);
+            continue;
+        }
+      append_new_glyph:
+        assert(glyph > glyph_tab && glyph - glyph_tab < glyphs_count);
+        if (glyph->newindex < 0) {
+            glyph_index[new_glyphs_count] = (short) (glyph - glyph_tab);
+            glyph->newindex = (TTF_SHORT) new_glyphs_count;
+            new_glyphs_count++;
+        }
+        e->newindex = glyph->newindex;
+    }
+}
+
+@  To calculate the checkSum for the 'head' table which itself includes the
+   checkSumAdjustment entry for the entire font, do the following:
+
+     \item Set the checkSumAdjustment to 0.
+     \item  Calculate the checksum for all the tables including the 'head' table
+       and enter that value into the table directory.
+     \item  Calculate the checksum for the entire font.
+     \item  Subtract that value from the hex value B1B0AFBA.
+     \item  Store the result in checkSumAdjustment.
+
+   The checkSum for the 'head table which includes the checkSumAdjustment
+   entry for the entire font is now incorrect. That is not a problem. Do not
+   change it. An application attempting to verify that the 'head' table has
+   not changed should calculate the checkSum for that table by not including
+   the checkSumAdjustment value, and compare the result with the entry in the
+   table directory.
+
+   The table directory also includes the offset of the associated tagged
+   table from the beginning of the font file and the length of that table.
+
+
+@c
+static void ttf_write_head(PDF pdf)
+{
+    dirtab_entry *tab;
+    tab = ttf_seek_tab("head", 0);
+    ttf_reset_chksm(pdf, tab);
+    ttf_ncopy(pdf, 2 * TTF_FIXED_SIZE);
+    checkSumAdjustment_offset = (TTF_ULONG) ttf_offset();
+    put_ulong(0);
+    ttf_skip(TTF_ULONG_SIZE);   /* skip checkSumAdjustment */
+    ttf_ncopy(pdf, TTF_ULONG_SIZE + 2 * TTF_USHORT_SIZE + 16 +
+              4 * TTF_FWORD_SIZE + 2 * TTF_USHORT_SIZE + TTF_SHORT_SIZE);
+    if (is_subsetted(fd_cur->fm)) {
+        (void) put_short(loca_format);
+        (void) put_short(0);
+    } else
+        ttf_ncopy(pdf, 2 * TTF_SHORT_SIZE);
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+static void ttf_write_hhea(PDF pdf)
+{
+    dirtab_entry *tab;
+    tab = ttf_seek_tab("hhea", 0);
+    ttf_reset_chksm(pdf, tab);
+    ttf_ncopy(pdf, TTF_FIXED_SIZE + 3 * TTF_FWORD_SIZE + TTF_UFWORD_SIZE +
+              3 * TTF_FWORD_SIZE + 8 * TTF_SHORT_SIZE);
+    (void) put_ushort(new_glyphs_count);
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+static void ttf_write_htmx(PDF pdf)
+{
+    long *id;
+    dirtab_entry *tab = ttf_seek_tab("hmtx", 0);
+    ttf_reset_chksm(pdf, tab);
+    for (id = glyph_index; id - glyph_index < new_glyphs_count; id++) {
+        put_ufword(glyph_tab[*id].advWidth);
+        put_ufword(glyph_tab[*id].lsb);
+    }
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+static void ttf_write_loca(PDF pdf)
+{
+    long *id;
+    dirtab_entry *tab = ttf_seek_tab("loca", 0);
+    ttf_reset_chksm(pdf, tab);
+    loca_format = 0;
+    if (last_glyf_offset >= 0x00020000 || (last_glyf_offset & 1))
+        loca_format = 1;
+    else
+        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++)
+            if (glyph_tab[*id].newoffset & 1) {
+                loca_format = 1;
+                break;
+            }
+    if (loca_format != 0) {
+        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++)
+            put_ulong(glyph_tab[*id].newoffset);
+        put_ulong((long) last_glyf_offset);
+    } else {
+        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++)
+            (void) put_ushort(glyph_tab[*id].newoffset / 2);
+        (void) put_ushort((long) (last_glyf_offset / 2));
+    }
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+static void ttf_write_mapx(PDF pdf)
+{
+    dirtab_entry *tab = ttf_seek_tab("maxp", TTF_FIXED_SIZE + TTF_USHORT_SIZE);
+    ttf_reset_chksm(pdf, tab);
+    put_fixed(0x00010000);
+    (void) put_ushort(new_glyphs_count);
+    ttf_ncopy(pdf, 13 * TTF_USHORT_SIZE);
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+static void ttf_write_OS2(PDF pdf)
+{
+    dirtab_entry *tab = ttf_seek_tab("OS/2", 0);
+    TTF_USHORT version;
+    ttf_reset_chksm(pdf, tab);
+    version = get_ushort();
+    if (version > 3)
+        formatted_error("ttf font","unknown version '%.4X' of OS/2 table", version);
+    (void) put_ushort(0x0001);  /* fix version to 1 */
+    ttf_ncopy(pdf,
+              2 * TTF_USHORT_SIZE + 13 * TTF_SHORT_SIZE + 10 * TTF_BYTE_SIZE);
+    ttf_skip(4 * TTF_ULONG_SIZE);       /* ulUnicodeRange 1--4 */
+    put_ulong(0x00000003);      /* Basic Latin + Latin-1 Supplement (0x0000--0x00FF) */
+    put_ulong(0x10000000);      /* Private Use (0xE000--0xF8FF) */
+    put_ulong(0x00000000);
+    put_ulong(0x00000000);
+    ttf_ncopy(pdf, 4 * TTF_CHAR_SIZE + TTF_USHORT_SIZE);        /* achVendID + fsSelection */
+    ttf_skip(2 * TTF_USHORT_SIZE);
+    (void) put_ushort(0x0000);  /* usFirstCharIndex */
+    (void) put_ushort(0xF0FF);  /* usLastCharIndex */
+    ttf_ncopy(pdf, 5 * TTF_USHORT_SIZE);
+    /* for version 0 the OS/2 table ends here, the rest is for version 1 */
+    put_ulong(0x80000000);      /* Symbol Character Set---don't use any code page */
+    put_ulong(0x00000000);
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+static boolean unsafe_name(const char *s)
+{
+    const char **p;
+    for (p = ambiguous_names; *p != NULL; p++)
+        if (strcmp(s, *p) == 0)
+            return true;
+    return false;
+}
+
+static void ttf_write_post(PDF pdf)
+{
+    dirtab_entry *tab = ttf_seek_tab("post", TTF_FIXED_SIZE);
+    glyph_entry *glyph;
+    const char *s;
+    long *id;
+    int k, l;
+    ttf_reset_chksm(pdf, tab);
+    if (!fd_cur->write_ttf_glyph_names || post_format == 0x00030000) {
+        put_fixed(0x00030000);
+        ttf_ncopy(pdf,
+                  TTF_FIXED_SIZE + 2 * TTF_FWORD_SIZE + 5 * TTF_ULONG_SIZE);
+    } else {
+        put_fixed(0x00020000);
+        ttf_ncopy(pdf,
+                  TTF_FIXED_SIZE + 2 * TTF_FWORD_SIZE + 5 * TTF_ULONG_SIZE);
+        (void) put_ushort(new_glyphs_count);
+        k = 0;
+        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++) {
+            glyph = glyph_tab + *id;
+            if (glyph->name_index >= NMACGLYPHS || unsafe_name(glyph->name))
+                glyph->name_index = (TTF_USHORT) (NMACGLYPHS + k++);
+            (void) put_ushort(glyph->name_index);
+        }
+        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++) {
+            glyph = glyph_tab + *id;
+            if (glyph->name_index >= NMACGLYPHS) {
+                s = glyph->name;
+                l = (int) strlen(s);
+                put_byte(l);
+                while (l-- > 0)
+                    put_char(*s++);
+            }
+        }
+    }
+    ttf_set_chksm(pdf, tab);
+}
+
+@
+@c
+static void ttf_init_font(PDF pdf, int n)
+{
+    int i, k;
+    for (i = 1, k = 0; i <= n; i <<= 1, k++);
+    put_fixed(0x00010000);      /* font version */
+    (void) put_ushort(n);       /* number of tables */
+    (void) put_ushort(i << 3);  /* search range */
+    (void) put_ushort(k - 1);   /* entry selector */
+    (void) put_ushort((n << 4) - (i << 3));     /* range shift */
+    ttf_seek_outbuf(TABDIR_OFF + n * 4 * TTF_ULONG_SIZE);
+}
+
+@
+@c
+static void ttf_subset_font(PDF pdf)
+{
+    ttf_init_font(pdf, new_ntabs);
+    if (ttf_name_lookup("PCLT", false) != NULL)
+        ttf_copytab(pdf, "PCLT");
+    if (ttf_name_lookup("fpgm", false) != NULL)
+        ttf_copytab(pdf, "fpgm");
+    if (ttf_name_lookup("cvt ", false) != NULL)
+        ttf_copytab(pdf, "cvt ");
+    if (ttf_name_lookup("prep", false) != NULL)
+        ttf_copytab(pdf, "prep");
+    ttf_reindex_glyphs();
+    ttf_write_glyf(pdf);
+    ttf_write_loca(pdf);
+    ttf_write_OS2(pdf);
+    ttf_write_head(pdf);
+    ttf_write_hhea(pdf);
+    ttf_write_htmx(pdf);
+    ttf_write_mapx(pdf);
+    ttf_write_name(pdf);
+    ttf_write_post(pdf);
+    ttf_write_cmap(pdf);
+    ttf_write_dirtab(pdf);
+}
+
+@
+@c
+static void ttf_copy_font(PDF pdf)
+{
+    dirtab_entry *tab;
+    ttf_init_font(pdf, ntabs);
+    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
+        if (strncmp(tab->tag, "head", 4) == 0)
+            ttf_write_head(pdf);
+        else
+            ttf_copytab(pdf, tab->tag);
+    }
+    ttf_write_dirtab(pdf);
+}
+
+@
+@c
+void writettf(PDF pdf, fd_entry * fd)
+{
+    int callback_id;
+    int file_opened = 0;
+    fd_cur = fd;                /* |fd_cur| is global inside \.{writettf.w} */
+    assert(fd_cur->fm != NULL);
+    assert(is_truetype(fd_cur->fm));
+    assert(is_included(fd_cur->fm));
+
+    if (is_subsetted(fd_cur->fm) && (fd_cur->fe == NULL)) {
+        normal_error("ttf font","subset must be a reencoded font");
+    }
+    ttf_curbyte = 0;
+    ttf_size = 0;
+
+    cur_file_name =
+        luatex_find_file(fd_cur->fm->ff_name, find_truetype_file_callback);
+    if (cur_file_name == NULL) {
+        formatted_error("ttf font","cannot find font file for reading '%s'", fd_cur->fm->ff_name);
+    }
+    callback_id = callback_defined(read_truetype_file_callback);
+    if (callback_id > 0) {
+        if (run_callback(callback_id, "S->bSd", cur_file_name,
+                         &file_opened, &ttf_buffer, &ttf_size) &&
+            file_opened && ttf_size > 0) {
+        } else {
+            formatted_error("ttf font","cannot open font file for reading '%s'", cur_file_name);
+        }
+    } else {
+        if (!ttf_open(cur_file_name)) {
+            formatted_error("ttf font","cannot open font file for reading '%s'", cur_file_name);
+        }
+        ttf_read_file();
+        ttf_close();
+    }
+    if (tracefilenames) {
+        if (is_subsetted(fd_cur->fm))
+            tex_printf("<%s", cur_file_name);
+        else
+            tex_printf("<<%s", cur_file_name);
+    }
+    fd_cur->ff_found = true;
+    new_glyphs_count = 2;
+    new_ntabs = DEFAULT_NTABS;
+    dir_tab = NULL;
+    glyph_tab = NULL;
+    glyph_index = NULL;
+    glyph_name_buf = NULL;
+    name_tab = NULL;
+    name_buf = NULL;
+    ttf_read_font();
+
+    pdf_save_offset(pdf);
+    pdf_flush(pdf);
+    if (is_subsetted(fd_cur->fm)) {
+        ttf_copy_encoding();
+        ttf_subset_font(pdf);
+    } else
+        ttf_copy_font(pdf);
+    ttf_length = ttf_offset();
+
+    xfree(dir_tab);
+    xfree(glyph_tab);
+    xfree(glyph_index);
+    xfree(glyph_name_buf);
+    xfree(name_tab);
+    xfree(name_buf);
+    if (tracefilenames) {
+        if (is_subsetted(fd_cur->fm))
+            tex_printf(">");
+        else
+            tex_printf(">>");
+    }
+    xfree(ttf_buffer);
+    cur_file_name = NULL;
+}
+
+static void do_writeotf(PDF pdf, fd_entry * fd)
+{
+    long i;
+    dirtab_entry *tab;
+    (void) fd;
+    dir_tab = NULL;
+    glyph_tab = NULL;
+    if (tracefilenames)
+        tex_printf("<<%s", cur_file_name);
+    ttf_read_tabdir();
+    /* read font parameters */
+    if (ttf_name_lookup("head", false) != NULL)
+        ttf_read_head();
+    if (ttf_name_lookup("hhea", false) != NULL)
+        ttf_read_hhea();
+    if (ttf_name_lookup("PCLT", false) != NULL)
+        ttf_read_pclt();
+    if (ttf_name_lookup("post", false) != NULL)
+        ttf_read_post();
+    /* copy font file */
+    if (ttf_name_lookup("CFF2", false) != NULL)      /* HH */
+        tab = ttf_seek_tab("CFF2", 0);               /* HH */
+    else                                             /* HH */
+        tab = ttf_seek_tab("CFF ", 0);
+    for (i = (long) tab->length; i > 0; i--) {
+        copy_char();
+    }
+    xfree(dir_tab);
+    if (tracefilenames)
+        tex_printf(">>");
+}
+
+@
+@c
+void writeotf(PDF pdf, fd_entry * fd)
+{
+    int callback_id;
+    int file_opened = 0;
+
+    fd_cur = fd;
+    assert(fd_cur->fm != NULL);
+    assert(is_opentype(fd_cur->fm));
+    assert(is_included(fd_cur->fm));
+
+    ttf_curbyte = 0;
+    ttf_size = 0;
+    cur_file_name =
+        luatex_find_file(fd_cur->fm->ff_name, find_opentype_file_callback);
+    if (cur_file_name == NULL) {
+        formatted_error("otf font","cannot find font file for reading '%s'", fd_cur->fm->ff_name);
+    }
+    callback_id = callback_defined(read_opentype_file_callback);
+    if (callback_id > 0) {
+        if (run_callback(callback_id, "S->bSd", cur_file_name,
+                         &file_opened, &ttf_buffer, &ttf_size) &&
+            file_opened && ttf_size > 0) {
+        } else {
+            formatted_error("otf font","cannot open font file for reading '%s'", cur_file_name);
+        }
+    } else {
+        if (!otf_open(cur_file_name)) {
+            formatted_error("otf font","cannot open font file for reading '%s'", cur_file_name);
+        }
+        ttf_read_file();
+        ttf_close();
+    }
+
+    fd_cur->ff_found = true;
+    do_writeotf(pdf, fd);
+    xfree(ttf_buffer);
+    cur_file_name = NULL;
+}
+
--- texlive-bin.orig/texk/web2c/luatexdir/font/writettf.c
+++ /dev/null
@@ -1,1573 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "font/writettf.h"
-#include <string.h>
-
-#define DEFAULT_NTABS       14
-#define NEW_CMAP_SIZE        2
-
-#define ttf_putchar(A)     strbuf_putchar(pdf->fb, (A))
-#define ttf_offset()       strbuf_offset(pdf->fb)
-#define ttf_seek_outbuf(A) strbuf_seek(pdf->fb, (A))
-
-unsigned char *ttf_buffer = NULL;
-int ttf_size = 0;
-int ttf_curbyte = 0;
-
-typedef struct {
-    /*tex the name of glyph */
-    char *name;
-    /*tex the new index of glyph in output file */
-    long newindex;
-
-} ttfenc_entry;
-
-typedef struct {
-    TTF_USHORT platform_id;
-    TTF_USHORT encoding_id;
-    TTF_USHORT language_id;
-    TTF_USHORT name_id;
-    TTF_USHORT length;
-    TTF_USHORT offset;
-    TTF_USHORT new_offset;
-    TTF_USHORT new_length;
-} name_record;
-
-typedef struct {
-    char *ttf_name;
-    TTF_USHORT pid;
-    TTF_USHORT eid;
-    long *table;
-} ttf_cmap_entry;
-
-static TTF_USHORT ntabs;
-static TTF_USHORT upem;
-static TTF_FIXED post_format;
-static TTF_SHORT loca_format;
-static TTF_ULONG last_glyf_offset;
-static TTF_USHORT glyphs_count;
-static TTF_USHORT new_glyphs_count;
-static TTF_USHORT nhmtxs;
-static TTF_USHORT new_ntabs;
-
-glyph_entry *glyph_tab;
-dirtab_entry *dir_tab;
-
-static long *glyph_index;
-static cmap_entry *cmap_tab, new_cmap_tab[NEW_CMAP_SIZE];
-static name_record *name_tab;
-static int name_record_num;
-static char *name_buf;
-static int name_buf_size;
-static char *glyph_name_buf;
-static TTF_ULONG checksum;
-static TTF_ULONG tab_length;
-static TTF_ULONG tmp_ulong;
-static TTF_ULONG checkSumAdjustment_offset;
-FILE *ttf_file;
-static ttfenc_entry ttfenc_tab[256];
-
-/*tex A pointer to the current font descriptor: */
-
-fd_entry *fd_cur;
-
-static struct avl_table *ttf_cmap_tree = NULL;
-
-int ttf_length;
-
-char notdef[] = ".notdef";
-
-const char *mac_glyph_names[] = {
-    /* 0x00 */
-    notdef, ".null", "CR", "space", "exclam", "quotedbl", "numbersign", "dollar",
-    "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk",
-    "plus", "comma",
-    /* 0x10 */
-    "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five",
-    "six", "seven", "eight", "nine", "colon", "semicolon", "less",
-    /* 0x20 */
-    "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H",
-    "I", "J", "K", "L",
-    /* 0x30 */
-    "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
-    "bracketleft", "backslash",
-    /* 0x40 */
-    "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d",
-    "e", "f", "g", "h", "i", "j", "k", "l",
-    /* 0x50 */
-    "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
-    "braceleft", "bar",
-    /* 0x60 */
-    "braceright", "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute",
-    "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex",
-    "adieresis", "atilde", "aring", "ccedilla",
-    /* 0x70 */
-    "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave",
-    "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex",
-    "odieresis", "otilde", "uacute", "ugrave",
-    /* 0x80 */
-    "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling",
-    "section", "bullet", "paragraph", "germandbls", "registered", "copyright",
-    "trademark", "acute", "dieresis", "notequal",
-    /* 0x90 */
-    "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen",
-    "mu", "partialdiff", "Sigma", "Pi", "pi", "integral", "ordfeminine",
-    "ordmasculine", "Omega",
-    /* 0xa0 */
-    "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical",
-    "florin", "approxequal", "Delta", "guillemotleft", "guillemotright",
-    "ellipsis", "nbspace", "Agrave", "Atilde", "Otilde",
-    /* 0xb0 */
-    "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft",
-    "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction",
-    "currency", "guilsinglleft", "guilsinglright",
-    /* 0xc0 */
-    "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase",
-    "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave",
-    "Iacute", "Icircumflex", "Idieresis", "Igrave",
-    /* 0xd0 */
-    "Oacute", "Ocircumflex", "applelogo", "Ograve", "Uacute", "Ucircumflex",
-    "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent",
-    "ring", "cedilla", "hungarumlaut",
-    /* 0xe0 */
-    "ogonek", "caron", "Lslash", "lslash", "Scaron", "scaron", "Zcaron",
-    "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn", "thorn",
-    "minus",
-    /* 0xf0 */
-    "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf",
-    "onequarter", "threequarters", "franc", "Gbreve", "gbreve", "Idot",
-    "Scedilla", "scedilla", "Cacute", "cacute", "Ccaron",
-    /* 0x100 */
-    "ccaron", "dmacron"
-};
-
-const char *ambiguous_names[] = {
-    "Delta",          /* increment */
-    "Omega",          /* Ohm       */
-    "Pi",             /* product   */
-    "Sigma",          /* summation */
-    "dmacron",        /* dslash    */
-    "macron",         /* overscore */
-    "periodcentered", /* middot    */
-    NULL
-};
-
-static const char *newtabnames[] = {
-    "OS/2",
-    "PCLT",
-    "cmap",
-    "cvt ",
-    "fpgm",
-    "glyf",
-    "head",
-    "hhea",
-    "hmtx",
-    "loca",
-    "maxp",
-    "name",
-    "post",
-    "prep"
-};
-
-/* Back to code. Low-level helpers first. */
-
-static ttf_cmap_entry *new_ttf_cmap_entry(void)
-{
-    ttf_cmap_entry *e;
-    e = xtalloc(1, ttf_cmap_entry);
-    e->ttf_name = NULL;
-    e->table = NULL;
-    return e;
-}
-
-static void destroy_ttf_cmap_entry(void *pa, void *pb)
-{
-    ttf_cmap_entry *p;
-    (void) pb;
-    p = (ttf_cmap_entry *) pa;
-    xfree(p->ttf_name);
-    xfree(p->table);
-    xfree(p);
-}
-
-void ttf_free(void)
-{
-    if (ttf_cmap_tree != NULL)
-        avl_destroy(ttf_cmap_tree, destroy_ttf_cmap_entry);
-}
-
-static int comp_ttf_cmap_entry(const void *pa, const void *pb, void *p)
-{
-    const ttf_cmap_entry *p1 = (const ttf_cmap_entry *) pa,
-        *p2 = (const ttf_cmap_entry *) pb;
-    int i;
-    (void) p;
-    if ((i = strcmp(p1->ttf_name, p2->ttf_name)) != 0)
-        return i;
-    cmp_return(p1->pid, p2->pid);
-    cmp_return(p1->eid, p2->eid);
-    return 0;
-}
-
-static unsigned char ttf_addchksm(unsigned char b)
-{
-    tmp_ulong = (tmp_ulong << 8) + b;
-    tab_length++;
-    if (tab_length % 4 == 0) {
-        checksum += tmp_ulong;
-        tmp_ulong = 0;
-    }
-    return b;
-}
-
-static TTF_ULONG ttf_getchksm(PDF pdf)
-{
-    while (tab_length % 4 != 0) {
-        /*tex |ttf_addchksm| updates |tab_length| */
-        ttf_putchar(ttf_addchksm(0));
-    }
-    return checksum;
-}
-
-long ttf_putnum(PDF pdf, int s, long n)
-{
-    long i = n;
-    char buf[TTF_LONG_SIZE + 1], *p = buf;
-    while (s-- > 0) {
-        *p++ = (char) (i & 0xFF);
-        i >>= 8;
-    }
-    p--;
-    while (p >= buf)
-        ttf_putchar(ttf_addchksm((unsigned char) (*p--)));
-    return n;
-}
-
-long ttf_getnum(int s)
-{
-    long i = 0;
-    int c;
-    while (s > 0) {
-        if (ttf_eof())
-            normal_error("ttf font","unexpected EOF");
-        c = ttf_getchar();
-        i = (i << 8) + c;
-        s--;
-    }
-    return i;
-}
-
-static long ttf_funit(long n)
-{
-    if (n < 0)
-        return -((-n / upem) * 1000 + ((-n % upem) * 1000) / upem);
-    else
-        return (n / upem) * 1000 + ((n % upem) * 1000) / upem;
-}
-
-static void ttf_ncopy(PDF pdf, int n)
-{
-    while (n-- > 0)
-        copy_byte();
-}
-
-dirtab_entry *ttf_name_lookup(const char *s, boolean required)
-{
-    dirtab_entry *tab;
-    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
-        if (strncmp(tab->tag, s, 4) == 0)
-            break;
-    }
-    if (tab - dir_tab == ntabs) {
-        if (required)
-            formatted_error("ttf font","can't find table '%s'", s);
-        else
-            tab = NULL;
-    }
-    return tab;
-}
-
-dirtab_entry *ttf_seek_tab(const char *name, TTF_LONG offset)
-{
-    dirtab_entry *tab = ttf_name_lookup(name, true);
-    ttf_curbyte = (int) (tab->offset + (unsigned long) offset);
-    return tab;
-}
-
-static void ttf_seek_off(TTF_LONG offset)
-{
-    ttf_curbyte = (int) offset;
-}
-
-static void ttf_copy_encoding(void)
-{
-    int i, *q;
-    void **aa;
-    char **glyph_names;
-    struct avl_traverser t;
-    if (fd_cur->fe != NULL) {
-        glyph_names = fd_cur->fe->glyph_names;
-        for (i = 0; i < 256; i++)
-            ttfenc_tab[i].name = (char *) notdef;
-        /*tex This is a workaround for a bug of AcroReader 4.0: */
-        if (strcmp(glyph_names[97], "a") == 0) {
-            q = xtalloc(1, int);
-            *q = 'a';
-            aa = avl_probe(fd_cur->tx_tree, q);
-            if (aa == NULL) {
-                /*tex Is this a problem? */
-            }
-        }
-        /*tex Take over collected characters from \TeX, reencode them. */
-        avl_t_init(&t, fd_cur->tx_tree);
-        for (q = (int *) avl_t_first(&t, fd_cur->tx_tree); q != NULL; q = (int *) avl_t_next(&t)) {
-            ttfenc_tab[*q].name = glyph_names[*q];
-        }
-        make_subset_tag(fd_cur);
-    }
-}
-
-#define ttf_append_byte(B) do {         \
-    if (name_tab[i].platform_id == 3) { \
-        *q++ = 0;                       \
-    }                                   \
-    *q++ = B;                           \
-} while (0)
-
-static char *strip_spaces_and_delims(char *s, int l)
-{
-    static char buf[SMALL_BUF_SIZE];
-    char *p = buf;
-    int i;
-    for (i = 0; i < l; s++, i++) {
-        if (*s == '(' || *s == ')' || *s == '<' || *s == '>' ||
-            *s == '[' || *s == ']' || *s == '{' || *s == '}' ||
-            *s == '/' || *s == '%' || isspace((unsigned char)*s))
-            continue;
-        *p++ = *s;
-    }
-    *p = 0;
-    return buf;
-}
-
-static void ttf_read_name(void)
-{
-    int i, j;
-    dirtab_entry *tab = ttf_seek_tab("name", TTF_USHORT_SIZE);
-    char *p, buf[SMALL_BUF_SIZE];
-    name_record_num = get_ushort();
-    name_tab = xtalloc((unsigned) name_record_num, name_record);
-    name_buf_size = (int) ((unsigned) tab->length - (3 * TTF_USHORT_SIZE + (TTF_ULONG) name_record_num * 6 * TTF_USHORT_SIZE));
-    name_buf = xtalloc((unsigned) name_buf_size, char);
-    ttf_skip(TTF_USHORT_SIZE);
-    for (i = 0; i < name_record_num; i++) {
-        name_tab[i].platform_id = get_ushort();
-        name_tab[i].encoding_id = get_ushort();
-        name_tab[i].language_id = get_ushort();
-        name_tab[i].name_id = get_ushort();
-        name_tab[i].length = get_ushort();
-        name_tab[i].offset = get_ushort();
-    }
-    for (p = name_buf; p - name_buf < name_buf_size; p++)
-        *p = get_char();
-    /*tex Look for the \POSTSCRIPT\ font name. */
-    for (i = 0; i < name_record_num; i++) {
-        if (name_tab[i].platform_id == 1 &&
-            name_tab[i].encoding_id == 0 && name_tab[i].name_id == 6) {
-            xfree(fd_cur->fontname);
-            fd_cur->fontname = xstrdup(strip_spaces_and_delims(name_buf + name_tab[i].offset, name_tab[i].length));
-            fd_cur->font_dim[FONTNAME_CODE].set = true;
-            break;
-        }
-    }
-    if (!fd_cur->font_dim[FONTNAME_CODE].set) {
-        for (i = 0; i < name_record_num; i++) {
-            if (name_tab[i].platform_id == 3 &&
-                    (name_tab[i].encoding_id == 0 || name_tab[i].encoding_id == 1)
-                    && name_tab[i].name_id == 6) {
-                xfree(fd_cur->fontname);
-                assert(name_tab[i].length < sizeof(buf));
-                for (j = 0, p = buf; j < name_tab[i].length; j += 2)
-                    *p++ = name_buf[name_tab[i].offset + j + 1];
-                *p = 0;
-                fd_cur->fontname = xstrdup(strip_spaces_and_delims(buf, (int) strlen(buf)));
-                fd_cur->font_dim[FONTNAME_CODE].set = true;
-                break;
-            }
-        }
-    }
-}
-
-static void ttf_read_mapx(void)
-{
-    glyph_entry *glyph;
-    ttf_seek_tab("maxp", TTF_FIXED_SIZE);
-    glyph_tab = xtalloc((unsigned) (1 + (glyphs_count = get_ushort())), glyph_entry);
-    for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++) {
-        glyph->newindex = -1;
-        glyph->newoffset = 0;
-        glyph->name_index = 0;
-        glyph->name = (char *) notdef;
-    }
-    glyph_index = xtalloc((unsigned) (glyphs_count + 1), long);
-    /*tex index of the |.notdef| glyph */
-    glyph_index[0] = 0;
-    /*tex index of the |.null| glyph */
-    glyph_index[1] = 1;
-}
-
-void ttf_read_head(void)
-{
-    ttf_seek_tab("head", 2 * TTF_FIXED_SIZE + 2 * TTF_ULONG_SIZE + TTF_USHORT_SIZE);
-    upem = get_ushort();
-    ttf_skip(16);
-    fd_cur->font_dim[FONTBBOX1_CODE].val = (int) ttf_funit(get_fword());
-    fd_cur->font_dim[FONTBBOX2_CODE].val = (int) ttf_funit(get_fword());
-    fd_cur->font_dim[FONTBBOX3_CODE].val = (int) ttf_funit(get_fword());
-    fd_cur->font_dim[FONTBBOX4_CODE].val = (int) ttf_funit(get_fword());
-    fd_cur->font_dim[FONTBBOX1_CODE].set = true;
-    fd_cur->font_dim[FONTBBOX2_CODE].set = true;
-    fd_cur->font_dim[FONTBBOX3_CODE].set = true;
-    fd_cur->font_dim[FONTBBOX4_CODE].set = true;
-    ttf_skip(2 * TTF_USHORT_SIZE + TTF_SHORT_SIZE);
-    loca_format = get_short();
-}
-
-void ttf_read_hhea(void)
-{
-    ttf_seek_tab("hhea", TTF_FIXED_SIZE);
-    fd_cur->font_dim[ASCENT_CODE].val = (int) ttf_funit(get_fword());
-    fd_cur->font_dim[DESCENT_CODE].val = (int) ttf_funit(get_fword());
-    fd_cur->font_dim[ASCENT_CODE].set = true;
-    fd_cur->font_dim[DESCENT_CODE].set = true;
-    ttf_skip(TTF_FWORD_SIZE + TTF_UFWORD_SIZE + 3 * TTF_FWORD_SIZE + 8 * TTF_SHORT_SIZE);
-    nhmtxs = get_ushort();
-}
-
-void ttf_read_pclt(void)
-{
-    if (ttf_name_lookup("PCLT", false) == NULL)
-        return;
-    ttf_seek_tab("PCLT", TTF_FIXED_SIZE + TTF_ULONG_SIZE + TTF_USHORT_SIZE);
-    fd_cur->font_dim[XHEIGHT_CODE].val = (int) ttf_funit(get_ushort());
-    ttf_skip(2 * TTF_USHORT_SIZE);
-    fd_cur->font_dim[CAPHEIGHT_CODE].val = (int) ttf_funit(get_ushort());
-    fd_cur->font_dim[XHEIGHT_CODE].set = true;
-    fd_cur->font_dim[CAPHEIGHT_CODE].set = true;
-}
-
-static void ttf_read_hmtx(void)
-{
-    glyph_entry *glyph;
-    TTF_UFWORD last_advWidth;
-    ttf_seek_tab("hmtx", 0);
-    for (glyph = glyph_tab; glyph - glyph_tab < nhmtxs; glyph++) {
-        glyph->advWidth = get_ufword();
-        glyph->lsb = (TTF_FWORD) get_ufword();
-    }
-    if (nhmtxs < glyphs_count) {
-        last_advWidth = glyph[-1].advWidth;
-        for (; glyph - glyph_tab < glyphs_count; glyph++) {
-            glyph->advWidth = last_advWidth;
-            glyph->lsb = (TTF_FWORD) get_ufword();
-        }
-    }
-}
-
-void ttf_read_post(void)
-{
-    int k, nnames;
-    long length;
-    long int_part, frac_part;
-    int sign = 1;
-    TTF_FIXED italic_angle;
-    char *p;
-    glyph_entry *glyph;
-    const dirtab_entry *tab = ttf_seek_tab("post", 0);
-    post_format = get_fixed();
-    italic_angle = get_fixed();
-    int_part = (long) (italic_angle >> 16);
-    if (int_part > 0x7FFF) {
-        /*tex a negative number */
-        int_part = 0x10000 - int_part;
-        sign = -1;
-    }
-    frac_part = (long) (italic_angle % 0x10000);
-    fd_cur->font_dim[ITALIC_ANGLE_CODE].val = (int) (sign * ((double) int_part + (double) frac_part * 1.0 / 0x10000));
-    fd_cur->font_dim[ITALIC_ANGLE_CODE].set = true;
-    if (glyph_tab == NULL) {
-        /*tex We were being called from |writeotf|. */
-        return;
-    }
-    ttf_skip(2 * TTF_FWORD_SIZE + 5 * TTF_ULONG_SIZE);
-    switch (post_format) {
-        case 0x10000:
-            for (glyph = glyph_tab; glyph - glyph_tab < NMACGLYPHS; glyph++) {
-                glyph->name = (const char *) mac_glyph_names[glyph - glyph_tab];
-                glyph->name_index = (TTF_USHORT) (glyph - glyph_tab);
-            }
-            break;
-        case 0x20000:
-            /*tex Some fonts have this value different from |nglyphs|: */
-            nnames = get_ushort();
-            for (glyph = glyph_tab; glyph - glyph_tab < nnames; glyph++) {
-                glyph->name_index = get_ushort();
-            }
-            length = (long) ((long) tab->length - (long) ((long) ttf_curbyte - (long) tab->offset));
-            glyph_name_buf = xtalloc((unsigned) length, char);
-            for (p = glyph_name_buf; p - glyph_name_buf < length;) {
-                for (k = get_byte(); k > 0; k--)
-                    *p++ = get_char();
-                *p++ = 0;
-            }
-            for (glyph = glyph_tab; glyph - glyph_tab < nnames; glyph++) {
-                if (glyph->name_index < NMACGLYPHS)
-                    glyph->name = mac_glyph_names[glyph->name_index];
-                else {
-                    p = glyph_name_buf;
-                    k = glyph->name_index - NMACGLYPHS;
-                    for (; k > 0; k--)
-                        p = strend(p) + 1;
-                    glyph->name = p;
-                }
-            }
-            break;
-        default:
-            formatted_warning("ttf font", "unsupported format '%.8X' of 'post' table, assuming 3.0", (unsigned int) post_format);
-        case 0x00030000:
-            for (glyph = glyph_tab; glyph - glyph_tab < NMACGLYPHS; glyph++) {
-                glyph->name_index = (TTF_USHORT) (glyph - glyph_tab);
-            }
-    }
-}
-
-static void ttf_read_loca(void)
-{
-    glyph_entry *glyph;
-    ttf_seek_tab("loca", 0);
-    if (loca_format != 0)
-        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count + 1; glyph++)
-            glyph->offset = (TTF_LONG) get_ulong();
-    else
-        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count + 1; glyph++)
-            glyph->offset = get_ushort() << 1;
-}
-
-void otc_read_tabdir(int index)
-{
-    unsigned long i, num, rem=0;
-    dirtab_entry *tab;
-    /*tex Ignore the tag |ttcf|. */
-    ttf_skip(TTF_FIXED_SIZE);
-    /*tex Ignore the version number. */
-    ttf_skip(TTF_ULONG_SIZE);
-    num = get_ulong();
-    for (i = 0; i < num; i++)  {
-        if (i==index) rem = get_ulong(); else ttf_skip(TTF_ULONG_SIZE);
-    }
-    ttf_skip(rem - TTF_FIXED_SIZE - (num+2)*TTF_ULONG_SIZE);
-    /*tex Ignore the |sfnt| number. */
-    ttf_skip(TTF_FIXED_SIZE);
-    dir_tab = xtalloc(ntabs = get_ushort(), dirtab_entry);
-    ttf_skip(3 * TTF_USHORT_SIZE);
-    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
-        for (i = 0; i < 4; i++)
-            tab->tag[i] = get_char();
-        tab->checksum = get_ulong();
-        tab->offset = get_ulong();
-        tab->length = get_ulong();
-    }
-}
-
-void ttf_read_tabdir(void)
-{
-    int i;
-    dirtab_entry *tab;
-    /*tex Ignore the |sfnt| number. */
-    ttf_skip(TTF_FIXED_SIZE);
-    dir_tab = xtalloc(ntabs = get_ushort(), dirtab_entry);
-    ttf_skip(3 * TTF_USHORT_SIZE);
-    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
-        for (i = 0; i < 4; i++)
-            tab->tag[i] = get_char();
-        tab->checksum = get_ulong();
-        tab->offset = get_ulong();
-        tab->length = get_ulong();
-    }
-}
-
-static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid, boolean warn)
-{
-    seg_entry *seg_tab, *s;
-    TTF_USHORT *glyphId, format, segCount;
-    TTF_USHORT ncmapsubtabs, tmp_pid, tmp_eid;
-    TTF_ULONG cmap_offset, tmp_offset;
-    long n, i, k, length, index;
-    ttf_cmap_entry tmp_e, *p;
-    void **aa;
-    /*tex Look up in |ttf_cmap_tree| first, return if found. */
-    tmp_e.ttf_name = ttf_name;
-    tmp_e.pid = (TTF_USHORT) pid;
-    tmp_e.eid = (TTF_USHORT) eid;
-    if (ttf_cmap_tree == NULL) {
-        ttf_cmap_tree = avl_create(comp_ttf_cmap_entry, NULL, &avl_xallocator);
-        assert(ttf_cmap_tree != NULL);
-    }
-    p = (ttf_cmap_entry *) avl_find(ttf_cmap_tree, &tmp_e);
-    if (p != NULL)
-        return p;
-    /*tex It's not found so we have to read it. We skip the table version number (0). */
-    ttf_seek_tab("cmap", TTF_USHORT_SIZE);
-    ncmapsubtabs = get_ushort();
-    cmap_offset = (TTF_ULONG) (ttf_curbyte - 2 * TTF_USHORT_SIZE);
-    cmap_tab = xtalloc(ncmapsubtabs, cmap_entry);
-    for (i = 0; i < ncmapsubtabs; ++i) {
-        tmp_pid = get_ushort();
-        tmp_eid = get_ushort();
-        tmp_offset = get_ulong();
-        if (tmp_pid == pid && tmp_eid == eid) {
-            ttf_seek_off((TTF_LONG) (cmap_offset + tmp_offset));
-            format = get_ushort();
-            if (format == 4)
-                goto read_cmap_format_4;
-            else {
-                if (warn) {
-                    formatted_warning("ttf font", "cmap format %i unsupported", format);
-                }
-                return NULL;
-            }
-        }
-    }
-    if (warn) {
-        formatted_warning("ttf font", "cannot find cmap subtable for (pid,eid) = (%i,%i)", pid, eid);
-    }
-    return NULL;
-    /*tex We jump here: */
-  read_cmap_format_4:
-    /*tex Initialize the new entry. */
-    p = new_ttf_cmap_entry();
-    p->ttf_name = xstrdup(ttf_name);
-    p->pid = (TTF_USHORT) pid;
-    p->eid = (TTF_USHORT) eid;
-    p->table = xtalloc(0x10000, long);
-    for (i = 0; i < 0x10000; ++i) {
-        /*tex Yet unassigned: */
-        p->table[i] = -1;
-    }
-    /*tex Read the subtable. */
-    /*tex length of subtable */
-    length = get_ushort();
-    /*tex skip the version number */
-    (void) get_ushort();
-    segCount = get_ushort() / 2;
-    /*tex skip searchRange */
-    (void) get_ushort();
-    /*tex skip entrySelector */
-    (void) get_ushort();
-    /*tex skip rangeShift */
-    (void) get_ushort();
-    seg_tab = xtalloc(segCount, seg_entry);
-    for (s = seg_tab; s - seg_tab < segCount; s++)
-        s->endCode = get_ushort();
-    /*tex skip reversedPad */
-    (void) get_ushort();
-    for (s = seg_tab; s - seg_tab < segCount; s++)
-        s->startCode = get_ushort();
-    for (s = seg_tab; s - seg_tab < segCount; s++)
-        s->idDelta = get_ushort();
-    for (s = seg_tab; s - seg_tab < segCount; s++)
-        s->idRangeOffset = get_ushort();
-    length -= 8 * TTF_USHORT_SIZE + 4 * segCount * TTF_USHORT_SIZE;
-    /*tex number of glyphID's */
-    n = length / TTF_USHORT_SIZE;
-    glyphId = xtalloc((unsigned) n, TTF_USHORT);
-    for (i = 0; i < n; i++)
-        glyphId[i] = get_ushort();
-    for (s = seg_tab; s - seg_tab < segCount; s++) {
-        for (i = s->startCode; i <= s->endCode; i++) {
-            if (i == 0xFFFF)
-                break;
-            if (s->idRangeOffset != 0xFFFF) {
-                if (s->idRangeOffset == 0)
-                    index = (s->idDelta + i) & 0xFFFF;
-                else {
-                    k = (i - s->startCode) + s->idRangeOffset / 2 + (s - seg_tab) - segCount;
-                    index = glyphId[k];
-                    if (index != 0)
-                        index = (index + s->idDelta) & 0xFFFF;
-                }
-                if (index >= glyphs_count)
-                    formatted_error("ttf font",
-                        "cmap issue, glyph index %li out of range [0..%i)",
-                        index, glyphs_count);
-                if (p->table[i] != -1)
-                    formatted_warning("ttf font",
-                        "cmap issue, multiple glyphs are mapped to unicode %.4lX, %li will be used, %li is ignored)",
-                        i, p->table[i], index);
-                else
-                    p->table[i] = index;
-            }
-        }
-    }
-    xfree(seg_tab);
-    xfree(glyphId);
-    aa = avl_probe(ttf_cmap_tree, p);
-    if (aa == NULL) {
-        /*tex Is this a problem? */
-    }
-    return p;
-}
-
-static void ttf_read_font(void)
-{
-    ttf_read_tabdir();
-    if (ttf_name_lookup("PCLT", false) == NULL)
-        new_ntabs--;
-    if (ttf_name_lookup("fpgm", false) == NULL)
-        new_ntabs--;
-    if (ttf_name_lookup("cvt ", false) == NULL)
-        new_ntabs--;
-    if (ttf_name_lookup("prep", false) == NULL)
-        new_ntabs--;
-    ttf_read_mapx();
-    ttf_read_head();
-    ttf_read_hhea();
-    ttf_read_pclt();
-    ttf_read_hmtx();
-    ttf_read_post();
-    ttf_read_loca();
-    ttf_read_name();
-}
-
-static void ttf_reset_chksm(PDF pdf, dirtab_entry * tab)
-{
-    checksum = 0;
-    tab_length = 0;
-    tmp_ulong = 0;
-    tab->offset = (TTF_ULONG) ttf_offset();
-    if (tab->offset % 4 != 0)
-        formatted_warning("ttf font","offset of `%4.4s' is not a multiple of 4", tab->tag);
-}
-
-static void ttf_set_chksm(PDF pdf, dirtab_entry * tab)
-{
-    tab->length = (TTF_ULONG) ttf_offset() - tab->offset;
-    tab->checksum = ttf_getchksm(pdf);
-}
-
-static void ttf_copytab(PDF pdf, const char *name)
-{
-    long i;
-    dirtab_entry *tab = ttf_seek_tab(name, 0);
-    ttf_reset_chksm(pdf, tab);
-    for (i = (long) tab->length; i > 0; i--)
-        copy_char();
-    ttf_set_chksm(pdf, tab);
-}
-
-#define BYTE_ENCODING_LENGTH ((256)*TTF_BYTE_SIZE + 3*TTF_USHORT_SIZE)
-
-static void ttf_byte_encoding(PDF pdf)
-{
-    ttfenc_entry *e;
-    /*tex Format number (0: byte encoding table) */
-    (void) put_ushort(0);
-    /*tex The length of the table */
-    (void) put_ushort(BYTE_ENCODING_LENGTH);
-    /*tex The version number */
-    (void) put_ushort(0);
-    for (e = ttfenc_tab; e - ttfenc_tab < 256; e++)
-        if (e->newindex < 256) {
-            put_byte(e->newindex);
-        } else {
-            if (e->name != notdef)
-                formatted_warning("ttf font",
-                    "glyph '%s' has been mapped to '%s' in 'ttf_byte_encoding' cmap table",
-                    e->name, notdef);
-            /*tex |.notdef|: */
-            put_byte(0);
-        }
-}
-
-#define TRIMMED_TABLE_MAP_LENGTH (TTF_USHORT_SIZE*(5 + (256)))
-
-static void ttf_trimmed_table_map(PDF pdf)
-{
-    ttfenc_entry *e;
-    /*tex format number 6: trimmed table mapping */
-    (void) put_ushort(6);
-    (void) put_ushort(TRIMMED_TABLE_MAP_LENGTH);
-    /*tex version number 0 */
-    (void) put_ushort(0);
-    /*tex first character code */
-    (void) put_ushort(0);
-    /*tex number of character code in table: */
-    (void) put_ushort(256);
-    for (e = ttfenc_tab; e - ttfenc_tab < 256; e++) {
-        (void) put_ushort(e->newindex);
-    }
-}
-
-#define SEG_MAP_DELTA_LENGTH ((16 + (256))*TTF_USHORT_SIZE)
-
-static void ttf_seg_map_delta(PDF pdf)
-{
-    ttfenc_entry *e;
-    /*tex format number (4: segment mapping to delta values) */
-    (void) put_ushort(4);
-    (void) put_ushort(SEG_MAP_DELTA_LENGTH);
-    /*tex version number */
-    (void) put_ushort(0);
-    /*tex 2*segCount */
-    (void) put_ushort(4);
-    /*tex searchRange */
-    (void) put_ushort(4);
-    /*tex entrySelector */
-    (void) put_ushort(1);
-    /*tex rangeShift */
-    (void) put_ushort(0);
-    /*tex endCount[0] */
-    (void) put_ushort(0xF0FF);
-    /*tex endCount[1] */
-    (void) put_ushort(0xFFFF);
-    /*tex reversedPad */
-    (void) put_ushort(0);
-    /*tex startCount[0] */
-    (void) put_ushort(0xF000);
-    /*tex startCount[1] */
-    (void) put_ushort(0xFFFF);
-    /*tex idDelta[0] */
-    (void) put_ushort(0);
-    /*tex idDelta[1] */
-    (void) put_ushort(1);
-    /*tex idRangeOffset[0] */
-    (void) put_ushort(2 * TTF_USHORT_SIZE);
-    /*tex idRangeOffset[1] */
-    (void) put_ushort(0);
-    for (e = ttfenc_tab; e - ttfenc_tab < 256; e++) {
-        (void) put_ushort(e->newindex);
-    }
-}
-
-#define CMAP_ENTRY_LENGTH (2*TTF_USHORT_SIZE + TTF_ULONG_SIZE)
-
-static void ttf_select_cmap(void)
-{
-    /*tex Macintosh */
-    new_cmap_tab[0].platform_id = 1;
-    /*tex Symbol; ignore code page */
-    new_cmap_tab[0].encoding_id = 0;
-    /*tex byte encoding (0) or trimmed table mapping (6) */
-    new_cmap_tab[0].format = (TTF_USHORT) (new_glyphs_count < 256 ? 0 : 6);
-    /*tex Microsoft */
-    new_cmap_tab[1].platform_id = 3;
-    /*tex Symbol; ignore code page */
-    new_cmap_tab[1].encoding_id = 0;
-    /*tex segment mapping to delta */
-    new_cmap_tab[1].format = 4;
-}
-
-static void ttf_write_cmap(PDF pdf)
-{
-    cmap_entry *ce;
-    long offset;
-    dirtab_entry *tab = ttf_name_lookup("cmap", true);
-    ttf_select_cmap();
-    ttf_reset_chksm(pdf, tab);
-    /*tex Table version number 0. */
-    (void) put_ushort(0);
-    /*tex Number of encoding tables. */
-    (void) put_ushort(NEW_CMAP_SIZE);
-    offset = 2 * TTF_USHORT_SIZE + NEW_CMAP_SIZE * CMAP_ENTRY_LENGTH;
-    for (ce = new_cmap_tab; ce - new_cmap_tab < NEW_CMAP_SIZE; ce++) {
-        ce->offset = (TTF_ULONG) offset;
-        switch (ce->format) {
-            case 0:
-                offset += BYTE_ENCODING_LENGTH;
-                break;
-            case 4:
-                offset += SEG_MAP_DELTA_LENGTH;
-                break;
-            case 6:
-                offset += TRIMMED_TABLE_MAP_LENGTH;
-                break;
-            default:
-                normal_error("ttf font","invalid format (it should not have happened)");
-        }
-        (void) put_ushort(ce->platform_id);
-        (void) put_ushort(ce->encoding_id);
-        put_ulong((long) ce->offset);
-    }
-    for (ce = new_cmap_tab; ce - new_cmap_tab < NEW_CMAP_SIZE; ce++) {
-        switch (ce->format) {
-            case 0:
-                ttf_byte_encoding(pdf);
-                break;
-            case 4:
-                ttf_seg_map_delta(pdf);
-                break;
-            case 6:
-                ttf_trimmed_table_map(pdf);
-                break;
-            }
-    }
-    ttf_set_chksm(pdf, tab);
-}
-
-static int prepend_subset_tags(int index, char *p)
-{
-    boolean is_unicode;
-    int i;
-    is_unicode = (name_tab[index].platform_id == 3);
-    if (is_unicode) {
-        for (i = 0; i < 6; ++i) {
-            *p++ = 0;
-            *p++ = fd_cur->subset_tag[i];
-        }
-        *p++ = 0;
-        *p++ = '+';
-        return 14;
-    } else {
-        strncpy(p, fd_cur->subset_tag, 6);
-        p += 6;
-        *p++ = '+';
-        return 7;
-    }
-}
-
-
-static void ttf_write_name(PDF pdf)
-{
-    int i, l;
-    char *p;
-    int new_name_buf_size;
-    char *new_name_buf;
-    name_record *n;
-    dirtab_entry *tab = ttf_name_lookup("name", true);
-    if (is_subsetted(fd_cur->fm)) {
-        l = 0;
-        for (i = 0; i < name_record_num; i++) {
-            /*tex Maximum lengh of new stogare area. */
-            l += name_tab[i].length + 14;
-        }
-        new_name_buf = xtalloc((unsigned) l, char);
-        /*tex Additional space for subset tags. */
-        p = new_name_buf;
-        for (i = 0; i < name_record_num; i++) {
-            n = name_tab + i;
-            n->new_offset = (TTF_USHORT) (p - new_name_buf);
-            if ((n->name_id == 1 || n->name_id == 3 ||
-                 n->name_id == 4 || n->name_id == 6) &&
-                ((n->platform_id == 1 && n->encoding_id == 0) ||
-                 (n->platform_id == 3 && n->encoding_id == 0) ||
-                 (n->platform_id == 3 && n->encoding_id == 1))) {
-                l = prepend_subset_tags(i, p);
-                p += l;
-            } else
-                l = 0;
-            memcpy(p, name_buf + n->offset, n->length);
-            p += n->length;
-            n->new_length = (TTF_USHORT) (n->length + l);
-        }
-        new_name_buf_size = (int) (p - new_name_buf);
-    } else {
-        new_name_buf = name_buf;
-        new_name_buf_size = name_buf_size;
-    }
-    ttf_reset_chksm(pdf, tab);
-    (void) put_ushort(0);
-    /*tex Format selector. */
-    (void) put_ushort(name_record_num);
-    (void) put_ushort(3 * TTF_USHORT_SIZE + name_record_num * 6 * TTF_USHORT_SIZE);
-    for (i = 0; i < name_record_num; i++) {
-        (void) put_ushort(name_tab[i].platform_id);
-        (void) put_ushort(name_tab[i].encoding_id);
-        (void) put_ushort(name_tab[i].language_id);
-        (void) put_ushort(name_tab[i].name_id);
-        (void) put_ushort(name_tab[i].new_length);
-        (void) put_ushort(name_tab[i].new_offset);
-    }
-    for (p = new_name_buf; p - new_name_buf < new_name_buf_size; p++)
-        put_char(*p);
-    ttf_set_chksm(pdf, tab);
-    if (new_name_buf != name_buf)
-        xfree(new_name_buf);
-}
-
-static void ttf_write_dirtab(PDF pdf)
-{
-    dirtab_entry *tab;
-    TTF_ULONG i, k;
-    char *p;
-    const int save_offset = ttf_offset();
-    ttf_seek_outbuf(TABDIR_OFF);
-    if (is_subsetted(fd_cur->fm)) {
-        for (i = 0; i < DEFAULT_NTABS; i++) {
-            tab = ttf_name_lookup(newtabnames[i], false);
-            if (tab == NULL)
-                continue;
-            for (k = 0; k < 4; k++)
-                put_char(tab->tag[k]);
-            put_ulong((long) tab->checksum);
-            put_ulong((long) tab->offset);
-            put_ulong((long) tab->length);
-        }
-    } else {
-        for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
-            for (k = 0; k < 4; k++)
-                put_char(tab->tag[k]);
-            put_ulong((long) tab->checksum);
-            put_ulong((long) tab->offset);
-            put_ulong((long) tab->length);
-        }
-    }
-    /*tex adjust |checkSumAdjustment| */
-    tmp_ulong = 0;
-    checksum = 0;
-    for (p = (char *) pdf->fb->data, i = 0; i < (unsigned) save_offset;) {
-        tmp_ulong = (tmp_ulong << 8) + (TTF_ULONG) * p++;
-        i++;
-        if (i % 4 == 0) {
-            checksum += tmp_ulong;
-            tmp_ulong = 0;
-        }
-    }
-    if (i % 4 != 0) {
-        formatted_warning("ttf font","font length '%li' is not a multiple of 4", i);
-        checksum <<= 8 * (4 - i % 4);
-    }
-    k = 0xB1B0AFBA - checksum;
-    ttf_seek_outbuf((int) checkSumAdjustment_offset);
-    put_ulong((long) k);
-    ttf_seek_outbuf(save_offset);
-}
-
-static void ttf_write_glyf(PDF pdf)
-{
-    long *id, k;
-    TTF_USHORT idx;
-    TTF_USHORT flags;
-    dirtab_entry *tab = ttf_name_lookup("glyf", true);
-    const long glyf_offset = (long) tab->offset;
-    const long new_glyf_offset = ttf_offset();
-    ttf_reset_chksm(pdf, tab);
-    for (id = glyph_index; id - glyph_index < new_glyphs_count; id++) {
-        glyph_tab[*id].newoffset = ttf_offset() - new_glyf_offset;
-        if (glyph_tab[*id].offset != glyph_tab[*id + 1].offset) {
-            ttf_seek_off(glyf_offset + glyph_tab[*id].offset);
-            k = copy_short();
-            ttf_ncopy(pdf, 4 * TTF_FWORD_SIZE);
-            if (k < 0) {
-                do {
-                    flags = copy_ushort();
-                    idx = get_ushort();
-                    if (glyph_tab[idx].newindex < 0) {
-                        glyph_tab[idx].newindex = (TTF_SHORT) new_glyphs_count;
-                        glyph_index[new_glyphs_count++] = idx;
-                        /*tex
-                           Here we change |new_glyphs_count|, which appears in
-                           the condition of the |for| loop.
-                         */
-                    }
-                    (void) put_ushort(glyph_tab[idx].newindex);
-                    if (flags & ARG_1_AND_2_ARE_WORDS)
-                        ttf_ncopy(pdf, 2 * TTF_SHORT_SIZE);
-                    else
-                        ttf_ncopy(pdf, TTF_USHORT_SIZE);
-                    if (flags & WE_HAVE_A_SCALE)
-                        ttf_ncopy(pdf, TTF_F2DOT14_SIZE);
-                    else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
-                        ttf_ncopy(pdf, 2 * TTF_F2DOT14_SIZE);
-                    else if (flags & WE_HAVE_A_TWO_BY_TWO)
-                        ttf_ncopy(pdf, 4 * TTF_F2DOT14_SIZE);
-                } while (flags & MORE_COMPONENTS);
-                if (flags & WE_HAVE_INSTRUCTIONS)
-                    ttf_ncopy(pdf, copy_ushort());
-            } else
-                ttf_ncopy(pdf, (int) (glyph_tab[*id + 1].offset - glyph_tab[*id].offset - TTF_USHORT_SIZE - 4 * TTF_FWORD_SIZE));
-        }
-    }
-    last_glyf_offset = (TTF_ULONG) ttf_offset() - (TTF_ULONG) new_glyf_offset;
-    ttf_set_chksm(pdf, tab);
-}
-
-/*tex
-
-    Reindexing glyphs: we append index of used glyphs to |glyph_index| while
-    going through |ttfenc_tab|. After appending a new entry to |glyph_index| we
-    set field |newindex| of corresponding entries in both |glyph_tab| and
-    |ttfenc_tab| to the newly created index.
-
-*/
-
-static void ttf_reindex_glyphs(void)
-{
-    ttfenc_entry *e;
-    glyph_entry *glyph;
-    int index;
-    long *t;
-    ttf_cmap_entry *cmap = NULL;
-    boolean cmap_not_found = false;
-    for (e = ttfenc_tab; e - ttfenc_tab < 256; e++) {
-        /*tex The index of the |.notdef| glyph */
-        e->newindex = 0;
-        /*tex Handle the case of reencoded fonts. */
-        if (e->name == notdef)
-            continue;
-        /*tex Scan form |index123|. */
-        if (sscanf(e->name, GLYPH_PREFIX_INDEX "%i", &index) == 1) {
-            if (index >= glyphs_count) {
-                formatted_warning("ttf font","'%s' out of valid range [0..%i)", e->name, glyphs_count);
-                continue;
-            }
-            glyph = glyph_tab + index;
-            goto append_new_glyph;
-        }
-        /*tex Scan form |uniABCD|. */
-        if (sscanf(e->name, GLYPH_PREFIX_UNICODE "%X", &index) == 1) {
-            if (cmap == NULL && !cmap_not_found) {
-                /*tex Need to read the \UNICODE\ mapping, i.e. |(pid,eid) = (3,1) or (0,3)|. */
-                cmap = ttf_read_cmap(fd_cur->fm->ff_name, 3, 1, false);
-                if (cmap == NULL)
-                    cmap = ttf_read_cmap(fd_cur->fm->ff_name, 0, 3, false);
-                if (cmap == NULL) {
-                    /*tex Once only. */
-                    normal_warning("ttf font", "no unicode mapping found, all 'uniXXXX' names will be ignored");
-                    cmap_not_found = true;
-                }
-            }
-            if (cmap == NULL)
-                continue;
-            t = cmap->table;
-            if (t[index] != -1) {
-                if (t[index] >= glyphs_count) {
-                    formatted_warning("ttf font", "'%s' is mapped to index %li which is out of valid range [0..%i)",
-                         e->name, t[index], glyphs_count);
-                    continue;
-                }
-                glyph = glyph_tab + t[index];
-                goto append_new_glyph;
-            } else {
-                formatted_warning("ttf font","unicode %s%.4X is not mapped to any glyph", GLYPH_PREFIX_UNICODE, index);
-                continue;
-            }
-        }
-        /*tex Look up by name: */
-        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++)
-            if (glyph->name != notdef && strcmp(glyph->name, e->name) == 0)
-                break;
-        if (!(glyph - glyph_tab < glyphs_count)) {
-            formatted_warning("ttf font","glyph '%s' not found", e->name);
-            continue;
-        }
-      append_new_glyph:
-        if (glyph->newindex < 0) {
-            glyph_index[new_glyphs_count] = (short) (glyph - glyph_tab);
-            glyph->newindex = (TTF_SHORT) new_glyphs_count;
-            new_glyphs_count++;
-        }
-        e->newindex = glyph->newindex;
-    }
-}
-
-/*tex
-
-    To calculate the checkSum for the 'head' table which itself includes the
-    checkSumAdjustment entry for the entire font, do the following:
-
-    \startitemize
-        \startitem
-            Set the checkSumAdjustment to 0.
-        \stopitem
-        \startitem
-            Calculate the checksum for all the tables including the |head| table
-            and enter that value into the table directory.
-        \stopitem
-        \startitem
-            Calculate the checksum for the entire font.
-        \stopitem
-        \startitem
-            Subtract that value from the hex value B1B0AFBA.
-        \stopitem
-        \startitem
-            Store the result in checkSumAdjustment.
-        \stopitem
-    \startitemize
-
-    The checkSum for the 'head table which includes the checkSumAdjustment entry
-    for the entire font is now incorrect. That is not a problem. Do not change
-    it. An application attempting to verify that the 'head' table has not changed
-    should calculate the checkSum for that table by not including the
-    checkSumAdjustment value, and compare the result with the entry in the table
-    directory.
-
-    The table directory also includes the offset of the associated tagged table
-    from the beginning of the font file and the length of that table.
-
-*/
-
-static void ttf_write_head(PDF pdf)
-{
-    dirtab_entry *tab;
-    tab = ttf_seek_tab("head", 0);
-    ttf_reset_chksm(pdf, tab);
-    ttf_ncopy(pdf, 2 * TTF_FIXED_SIZE);
-    checkSumAdjustment_offset = (TTF_ULONG) ttf_offset();
-    put_ulong(0);
-    /*tex skip |checkSumAdjustment| */
-    ttf_skip(TTF_ULONG_SIZE);
-    ttf_ncopy(pdf, TTF_ULONG_SIZE + 2 * TTF_USHORT_SIZE + 16 + 4 * TTF_FWORD_SIZE + 2 * TTF_USHORT_SIZE + TTF_SHORT_SIZE);
-    if (is_subsetted(fd_cur->fm)) {
-        (void) put_short(loca_format);
-        (void) put_short(0);
-    } else
-        ttf_ncopy(pdf, 2 * TTF_SHORT_SIZE);
-    ttf_set_chksm(pdf, tab);
-}
-
-static void ttf_write_hhea(PDF pdf)
-{
-    dirtab_entry *tab;
-    tab = ttf_seek_tab("hhea", 0);
-    ttf_reset_chksm(pdf, tab);
-    ttf_ncopy(pdf, TTF_FIXED_SIZE + 3 * TTF_FWORD_SIZE + TTF_UFWORD_SIZE + 3 * TTF_FWORD_SIZE + 8 * TTF_SHORT_SIZE);
-    (void) put_ushort(new_glyphs_count);
-    ttf_set_chksm(pdf, tab);
-}
-
-static void ttf_write_htmx(PDF pdf)
-{
-    long *id;
-    dirtab_entry *tab = ttf_seek_tab("hmtx", 0);
-    ttf_reset_chksm(pdf, tab);
-    for (id = glyph_index; id - glyph_index < new_glyphs_count; id++) {
-        put_ufword(glyph_tab[*id].advWidth);
-        put_ufword(glyph_tab[*id].lsb);
-    }
-    ttf_set_chksm(pdf, tab);
-}
-
-static void ttf_write_loca(PDF pdf)
-{
-    long *id;
-    dirtab_entry *tab = ttf_seek_tab("loca", 0);
-    ttf_reset_chksm(pdf, tab);
-    loca_format = 0;
-    if (last_glyf_offset >= 0x00020000 || (last_glyf_offset & 1))
-        loca_format = 1;
-    else
-        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++)
-            if (glyph_tab[*id].newoffset & 1) {
-                loca_format = 1;
-                break;
-            }
-    if (loca_format != 0) {
-        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++)
-            put_ulong(glyph_tab[*id].newoffset);
-        put_ulong((long) last_glyf_offset);
-    } else {
-        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++)
-            (void) put_ushort(glyph_tab[*id].newoffset / 2);
-        (void) put_ushort((long) (last_glyf_offset / 2));
-    }
-    ttf_set_chksm(pdf, tab);
-}
-
-static void ttf_write_mapx(PDF pdf)
-{
-    dirtab_entry *tab = ttf_seek_tab("maxp", TTF_FIXED_SIZE + TTF_USHORT_SIZE);
-    ttf_reset_chksm(pdf, tab);
-    put_fixed(0x00010000);
-    (void) put_ushort(new_glyphs_count);
-    ttf_ncopy(pdf, 13 * TTF_USHORT_SIZE);
-    ttf_set_chksm(pdf, tab);
-}
-
-static void ttf_write_OS2(PDF pdf)
-{
-    dirtab_entry *tab = ttf_seek_tab("OS/2", 0);
-    TTF_USHORT version;
-    ttf_reset_chksm(pdf, tab);
-    version = get_ushort();
-    if (version > 3) {
-        formatted_error("ttf font","unknown version '%.4X' of OS/2 table", version);
-    }
-    /*tex fix version to 1 */
-    (void) put_ushort(0x0001);
-    ttf_ncopy(pdf,2 * TTF_USHORT_SIZE + 13 * TTF_SHORT_SIZE + 10 * TTF_BYTE_SIZE);
-    /*tex |ulUnicodeRange| 1--4 */
-    ttf_skip(4 * TTF_ULONG_SIZE);
-    /*tex Basic Latin + Latin-1 Supplement (0x0000--0x00FF) */
-    put_ulong(0x00000003);
-    /*tex Private Use (0xE000--0xF8FF) */
-    put_ulong(0x10000000);
-    put_ulong(0x00000000);
-    put_ulong(0x00000000);
-    /*tex |achVendID| + |fsSelection| */
-    ttf_ncopy(pdf, 4 * TTF_CHAR_SIZE + TTF_USHORT_SIZE);
-    ttf_skip(2 * TTF_USHORT_SIZE);
-    /*tex |usFirstCharIndex| */
-    (void) put_ushort(0x0000);
-    /*tex |usLastCharIndex| */
-    (void) put_ushort(0xF0FF);
-    ttf_ncopy(pdf, 5 * TTF_USHORT_SIZE);
-    /*tex For version 0 the OS/2 table ends here, the rest is for version 1. */
-    /*tex Symbol Character Set: don't use any code page */
-    put_ulong(0x80000000);
-    put_ulong(0x00000000);
-    ttf_set_chksm(pdf, tab);
-}
-
-static boolean unsafe_name(const char *s)
-{
-    const char **p;
-    for (p = ambiguous_names; *p != NULL; p++)
-        if (strcmp(s, *p) == 0)
-            return true;
-    return false;
-}
-
-static void ttf_write_post(PDF pdf)
-{
-    dirtab_entry *tab = ttf_seek_tab("post", TTF_FIXED_SIZE);
-    glyph_entry *glyph;
-    const char *s;
-    long *id;
-    int k, l;
-    ttf_reset_chksm(pdf, tab);
-    if (!fd_cur->write_ttf_glyph_names || post_format == 0x00030000) {
-        put_fixed(0x00030000);
-        ttf_ncopy(pdf, TTF_FIXED_SIZE + 2 * TTF_FWORD_SIZE + 5 * TTF_ULONG_SIZE);
-    } else {
-        put_fixed(0x00020000);
-        ttf_ncopy(pdf, TTF_FIXED_SIZE + 2 * TTF_FWORD_SIZE + 5 * TTF_ULONG_SIZE);
-        (void) put_ushort(new_glyphs_count);
-        k = 0;
-        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++) {
-            glyph = glyph_tab + *id;
-            if (glyph->name_index >= NMACGLYPHS || unsafe_name(glyph->name))
-                glyph->name_index = (TTF_USHORT) (NMACGLYPHS + k++);
-            (void) put_ushort(glyph->name_index);
-        }
-        for (id = glyph_index; id - glyph_index < new_glyphs_count; id++) {
-            glyph = glyph_tab + *id;
-            if (glyph->name_index >= NMACGLYPHS) {
-                s = glyph->name;
-                l = (int) strlen(s);
-                put_byte(l);
-                while (l-- > 0)
-                    put_char(*s++);
-            }
-        }
-    }
-    ttf_set_chksm(pdf, tab);
-}
-
-static void ttf_init_font(PDF pdf, int n)
-{
-    int i, k;
-    for (i = 1, k = 0; i <= n; i <<= 1, k++);
-    /*tex font version */
-    put_fixed(0x00010000);
-    /*tex number of tables */
-    (void) put_ushort(n);
-    /*tex search range */
-    (void) put_ushort(i << 3);
-    /*tex entry selector */
-    (void) put_ushort(k - 1);
-    /*tex range shift */
-    (void) put_ushort((n << 4) - (i << 3));
-    ttf_seek_outbuf(TABDIR_OFF + n * 4 * TTF_ULONG_SIZE);
-}
-
-static void ttf_subset_font(PDF pdf)
-{
-    ttf_init_font(pdf, new_ntabs);
-    if (ttf_name_lookup("PCLT", false) != NULL)
-        ttf_copytab(pdf, "PCLT");
-    if (ttf_name_lookup("fpgm", false) != NULL)
-        ttf_copytab(pdf, "fpgm");
-    if (ttf_name_lookup("cvt ", false) != NULL)
-        ttf_copytab(pdf, "cvt ");
-    if (ttf_name_lookup("prep", false) != NULL)
-        ttf_copytab(pdf, "prep");
-    ttf_reindex_glyphs();
-    ttf_write_glyf(pdf);
-    ttf_write_loca(pdf);
-    ttf_write_OS2(pdf);
-    ttf_write_head(pdf);
-    ttf_write_hhea(pdf);
-    ttf_write_htmx(pdf);
-    ttf_write_mapx(pdf);
-    ttf_write_name(pdf);
-    ttf_write_post(pdf);
-    ttf_write_cmap(pdf);
-    ttf_write_dirtab(pdf);
-}
-
-static void ttf_copy_font(PDF pdf)
-{
-    dirtab_entry *tab;
-    ttf_init_font(pdf, ntabs);
-    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
-        if (strncmp(tab->tag, "head", 4) == 0)
-            ttf_write_head(pdf);
-        else
-            ttf_copytab(pdf, tab->tag);
-    }
-    ttf_write_dirtab(pdf);
-}
-
-void writettf(PDF pdf, fd_entry * fd)
-{
-    int callback_id;
-    int file_opened = 0;
-    /* The next one is global inside |writettf.c| */
-    fd_cur = fd;
-    if (is_subsetted(fd_cur->fm) && (fd_cur->fe == NULL)) {
-        normal_error("ttf font","subset must be a reencoded font");
-    }
-    ttf_curbyte = 0;
-    ttf_size = 0;
-    cur_file_name = luatex_find_file(fd_cur->fm->ff_name, find_truetype_file_callback);
-    if (cur_file_name == NULL) {
-        formatted_error("ttf font","cannot find font file for reading '%s'", fd_cur->fm->ff_name);
-    }
-    callback_id = callback_defined(read_truetype_file_callback);
-    if (callback_id > 0) {
-        if (run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &ttf_buffer, &ttf_size) && file_opened && ttf_size > 0) {
-            /* We're okay. */
-        } else {
-            formatted_error("ttf font","cannot open font file for reading '%s'", cur_file_name);
-        }
-    } else {
-        if (!ttf_open(cur_file_name)) {
-            formatted_error("ttf font","cannot open font file for reading '%s'", cur_file_name);
-        }
-        ttf_read_file();
-        ttf_close();
-    }
-    if (tracefilenames) {
-        if (is_subsetted(fd_cur->fm))
-            tex_printf("<%s", cur_file_name);
-        else
-            tex_printf("<<%s", cur_file_name);
-    }
-    fd_cur->ff_found = true;
-    new_glyphs_count = 2;
-    new_ntabs = DEFAULT_NTABS;
-    dir_tab = NULL;
-    glyph_tab = NULL;
-    glyph_index = NULL;
-    glyph_name_buf = NULL;
-    name_tab = NULL;
-    name_buf = NULL;
-    ttf_read_font();
-    pdf_save_offset(pdf);
-    pdf_flush(pdf);
-    if (is_subsetted(fd_cur->fm)) {
-        ttf_copy_encoding();
-        ttf_subset_font(pdf);
-    } else
-        ttf_copy_font(pdf);
-    ttf_length = ttf_offset();
-    xfree(dir_tab);
-    xfree(glyph_tab);
-    xfree(glyph_index);
-    xfree(glyph_name_buf);
-    xfree(name_tab);
-    xfree(name_buf);
-    if (tracefilenames) {
-        if (is_subsetted(fd_cur->fm))
-            tex_printf(">");
-        else
-            tex_printf(">>");
-    }
-    xfree(ttf_buffer);
-    cur_file_name = NULL;
-}
-
-static void do_writeotf(PDF pdf, fd_entry * fd)
-{
-    long i;
-    dirtab_entry *tab;
-    (void) fd;
-    dir_tab = NULL;
-    glyph_tab = NULL;
-    if (tracefilenames)
-        tex_printf("<<%s", cur_file_name);
-    ttf_read_tabdir();
-    /*tex Read teh font parameters. */
-    if (ttf_name_lookup("head", false) != NULL)
-        ttf_read_head();
-    if (ttf_name_lookup("hhea", false) != NULL)
-        ttf_read_hhea();
-    if (ttf_name_lookup("PCLT", false) != NULL)
-        ttf_read_pclt();
-    if (ttf_name_lookup("post", false) != NULL)
-        ttf_read_post();
-    /*tex Copy the font file: */
-    if (ttf_name_lookup("CFF2", false) != NULL)
-        tab = ttf_seek_tab("CFF2", 0);
-    else
-        tab = ttf_seek_tab("CFF ", 0);
-    for (i = (long) tab->length; i > 0; i--) {
-        copy_char();
-    }
-    xfree(dir_tab);
-    if (tracefilenames)
-        tex_printf(">>");
-}
-
-void writeotf(PDF pdf, fd_entry * fd)
-{
-    int callback_id;
-    int file_opened = 0;
-    fd_cur = fd;
-    ttf_curbyte = 0;
-    ttf_size = 0;
-    cur_file_name = luatex_find_file(fd_cur->fm->ff_name, find_opentype_file_callback);
-    if (cur_file_name == NULL) {
-        formatted_error("otf font","cannot find font file for reading '%s'", fd_cur->fm->ff_name);
-    }
-    callback_id = callback_defined(read_opentype_file_callback);
-    if (callback_id > 0) {
-        if (run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &ttf_buffer, &ttf_size) && file_opened && ttf_size > 0) {
-            /*tex We're okay. */
-        } else {
-            formatted_error("otf font","cannot open font file for reading '%s'", cur_file_name);
-        }
-    } else {
-        if (!otf_open(cur_file_name)) {
-            formatted_error("otf font","cannot open font file for reading '%s'", cur_file_name);
-        }
-        ttf_read_file();
-        ttf_close();
-    }
-    fd_cur->ff_found = true;
-    do_writeotf(pdf, fd);
-    xfree(ttf_buffer);
-    cur_file_name = NULL;
-}
-
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/writetype0.w
@@ -0,0 +1,136 @@
+% writetype0.w
+%
+% Copyright 2006-2008 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 "font/writettf.h"
+#include "font/writecff.h"
+
+@ @c
+extern unsigned char *ttf_buffer;
+
+void writetype0(PDF pdf, fd_entry * fd)
+{
+    int callback_id;
+    int file_opened = 0;
+    long i = 0;
+    dirtab_entry *tab;
+    cff_font *cff;
+    sfnt *sfont;
+
+    dir_tab = NULL;
+    glyph_tab = NULL;
+
+    fd_cur = fd;                /* |fd_cur| is global inside \.{writettf.w} */
+    assert(fd_cur->fm != NULL);
+    assert(is_opentype(fd_cur->fm) || is_truetype(fd_cur->fm));
+    assert(is_included(fd_cur->fm));
+
+    ttf_curbyte = 0;
+    ttf_size = 0;
+    cur_file_name =
+        luatex_find_file(fd_cur->fm->ff_name, find_opentype_file_callback);
+    if (cur_file_name == NULL) {
+        cur_file_name =
+            luatex_find_file(fd_cur->fm->ff_name, find_truetype_file_callback);
+        if (cur_file_name == NULL) {
+            formatted_error("type 0","cannot find file '%s'", fd_cur->fm->ff_name);
+        }
+    }
+    callback_id = callback_defined(read_opentype_file_callback);
+    if (callback_id > 0) {
+        if (run_callback(callback_id, "S->bSd", cur_file_name,
+                         &file_opened, &ttf_buffer, &ttf_size) &&
+            file_opened && ttf_size > 0) {
+        } else {
+            formatted_error("type 0","cannot find file '%s'", cur_file_name);
+        }
+    } else {
+        if (!otf_open(cur_file_name)) {
+            formatted_error("type 0","cannot find file '%s'", cur_file_name);
+        }
+        ttf_read_file();
+        ttf_close();
+    }
+
+    fd_cur->ff_found = true;
+
+    sfont = sfnt_open(ttf_buffer, ttf_size);
+    if (sfont->type == SFNT_TYPE_TTC)
+        i = ff_get_ttc_index(fd->fm->ff_name, fd->fm->ps_name);
+
+    if (is_subsetted(fd_cur->fm)) {
+        report_start_file(filetype_subset, cur_file_name);
+    } else {
+        report_start_file(filetype_font, cur_file_name);
+    }
+
+    if (sfont->type == SFNT_TYPE_TTC) otc_read_tabdir(i);
+    else ttf_read_tabdir();
+    sfnt_close(sfont);
+
+    /* read font parameters */
+    if (ttf_name_lookup("head", false) != NULL)
+        ttf_read_head();
+    if (ttf_name_lookup("hhea", false) != NULL)
+        ttf_read_hhea();
+    if (ttf_name_lookup("PCLT", false) != NULL)
+        ttf_read_pclt();
+    if (ttf_name_lookup("post", false) != NULL)
+        ttf_read_post();
+
+    /* copy font file */
+    if (ttf_name_lookup("CFF2", false) != NULL)      /* HH */
+        tab = ttf_seek_tab("CFF2", 0);               /* HH */
+    else                                             /* HH */
+        tab = ttf_seek_tab("CFF ", 0);
+
+    /* TODO the next 0 is a subfont index */
+    cff = read_cff(ttf_buffer + ttf_curbyte, (long) tab->length, 0);
+    if (!is_subsetted(fd_cur->fm)) {
+        /* not subsetted, just do a copy */
+        for (i = (long) tab->length; i > 0; i--)
+            strbuf_putchar(pdf->fb, (unsigned char) ttf_getnum(1));
+    } else {
+        if (cff != NULL) {
+            if (cff_is_cidfont(cff)) {
+                write_cid_cff(pdf, cff, fd_cur);
+#if 0
+                for (i = tab->length; i > 0; i--)
+                    strbuf_putchar(pdf->fb, (unsigned char) ttf_getnum(1));
+#endif
+            } else {
+                write_cff(pdf, cff, fd_cur);
+            }
+        } else {
+            /* not understood, just do a copy */
+            for (i = (long) tab->length; i > 0; i--)
+                strbuf_putchar(pdf->fb, (unsigned char) ttf_getnum(1));
+        }
+    }
+    xfree(dir_tab);
+    xfree(ttf_buffer);
+    if (is_subsetted(fd_cur->fm)) {
+        report_stop_file(filetype_subset);
+    } else {
+        report_stop_file(filetype_font);
+    }
+    cur_file_name = NULL;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/writetype0.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
-
-Copyright 2006-2008 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "font/writettf.h"
-#include "font/writecff.h"
-
-/*tex
-
-    Here we also support the newer CFF2 specification,
-
-*/
-
-extern unsigned char *ttf_buffer;
-
-void writetype0(PDF pdf, fd_entry * fd)
-{
-    int callback_id;
-    int file_opened = 0;
-    long i = 0;
-    dirtab_entry *tab;
-    cff_font *cff;
-    sfnt *sfont;
-    dir_tab = NULL;
-    glyph_tab = NULL;
-    /*tex |fd_cur| is global inside |writettf.c| */
-    fd_cur = fd;
-    assert(fd_cur->fm != NULL);
-    assert(is_opentype(fd_cur->fm) || is_truetype(fd_cur->fm));
-    assert(is_included(fd_cur->fm));
-    ttf_curbyte = 0;
-    ttf_size = 0;
-    cur_file_name =
-        luatex_find_file(fd_cur->fm->ff_name, find_opentype_file_callback);
-    if (cur_file_name == NULL) {
-        cur_file_name =
-            luatex_find_file(fd_cur->fm->ff_name, find_truetype_file_callback);
-        if (cur_file_name == NULL) {
-            formatted_error("type 0","cannot find file '%s'", fd_cur->fm->ff_name);
-        }
-    }
-    callback_id = callback_defined(read_opentype_file_callback);
-    if (callback_id > 0) {
-        if (run_callback(callback_id, "S->bSd", cur_file_name,
-                         &file_opened, &ttf_buffer, &ttf_size) &&
-            file_opened && ttf_size > 0) {
-        } else {
-            formatted_error("type 0","cannot find file '%s'", cur_file_name);
-        }
-    } else {
-        if (!otf_open(cur_file_name)) {
-            formatted_error("type 0","cannot find file '%s'", cur_file_name);
-        }
-        ttf_read_file();
-        ttf_close();
-    }
-    fd_cur->ff_found = true;
-    sfont = sfnt_open(ttf_buffer, ttf_size);
-    if (sfont->type == SFNT_TYPE_TTC)
-        i = ff_get_ttc_index(fd->fm->ff_name, fd->fm->ps_name);
-
-    if (is_subsetted(fd_cur->fm)) {
-        report_start_file(filetype_subset, cur_file_name);
-    } else {
-        report_start_file(filetype_font, cur_file_name);
-    }
-
-    if (sfont->type == SFNT_TYPE_TTC) otc_read_tabdir(i);
-    else ttf_read_tabdir();
-    sfnt_close(sfont);
-
-    /*tex Read font parameters: */
-    if (ttf_name_lookup("head", false) != NULL)
-        ttf_read_head();
-    if (ttf_name_lookup("hhea", false) != NULL)
-        ttf_read_hhea();
-    if (ttf_name_lookup("PCLT", false) != NULL)
-        ttf_read_pclt();
-    if (ttf_name_lookup("post", false) != NULL)
-        ttf_read_post();
-    /*tex Copy font file, including the newer variant: */
-    if (ttf_name_lookup("CFF2", false) != NULL)
-        tab = ttf_seek_tab("CFF2", 0);
-    else
-        tab = ttf_seek_tab("CFF ", 0);
-    cff = read_cff(ttf_buffer + ttf_curbyte, (long) tab->length, 0);
-    if (!is_subsetted(fd_cur->fm)) {
-        /*tex not subsetted, copy: */
-        for (i = (long) tab->length; i > 0; i--)
-            strbuf_putchar(pdf->fb, (unsigned char) ttf_getnum(1));
-    } else {
-        if (cff != NULL) {
-            if (cff_is_cidfont(cff)) {
-                write_cid_cff(pdf, cff, fd_cur);
-            } else {
-                write_cff(pdf, cff, fd_cur);
-            }
-        } else {
-            /*tex Just copy: */
-            for (i = (long) tab->length; i > 0; i--)
-                strbuf_putchar(pdf->fb, (unsigned char) ttf_getnum(1));
-        }
-    }
-    xfree(dir_tab);
-    xfree(ttf_buffer);
-    if (is_subsetted(fd_cur->fm)) {
-        report_stop_file(filetype_subset);
-    } else {
-        report_stop_file(filetype_font);
-    }
-    cur_file_name = NULL;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/font/writetype2.w
@@ -0,0 +1,432 @@
+% writetype2.w
+%
+% Copyright 2006-2012 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 "font/writettf.h"
+#include "font/writecff.h"
+#include "lua/luatex-api.h"
+
+#include "font/sfnt.h"
+#include "font/tt_glyf.h"
+
+@ forward declaration
+@c
+boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen);
+
+@ @c
+unsigned long cidtogid_obj = 0;
+
+@ low-level helpers
+@c
+#define test_loc(l) \
+    if ((f->loc + l) > f->buflen) { \
+        normal_error("type 2","the file ended prematurely"); \
+    }
+
+BYTE get_unsigned_byte(sfnt * f)
+{
+    test_loc(1);
+    return (BYTE) f->buffer[(f->loc++)];
+}
+
+ICHAR get_signed_byte(sfnt * f)
+{
+    test_loc(1);
+    return (ICHAR) f->buffer[(f->loc++)];
+}
+
+USHORT get_unsigned_pair(sfnt * f)
+{
+    USHORT l;
+    test_loc(2);
+    l = f->buffer[(f->loc++)];
+    l = (USHORT) (l * 0x100 + f->buffer[(f->loc++)]);
+    return l;
+}
+
+SHORT get_signed_pair(sfnt * f)
+{
+    long l;
+    test_loc(2);
+    l = f->buffer[(f->loc++)];
+    if (l > 0x80)
+        l -= 0x100;
+    l = l * 0x100 + f->buffer[(f->loc++)];
+    return (SHORT) l;
+}
+
+ULONG get_unsigned_quad(sfnt * f)
+{
+    ULONG l;
+    test_loc(4);
+    l = f->buffer[(f->loc++)];
+    l = l * 0x100 + f->buffer[(f->loc++)];
+    l = l * 0x100 + f->buffer[(f->loc++)];
+    l = l * 0x100 + f->buffer[(f->loc++)];
+    return l;
+}
+
+int do_sfnt_read(unsigned char *dest, int len, sfnt * f)
+{
+    int i;
+    test_loc(len);
+    for (i = 0; i < len; i++) {
+        *(dest + i) = f->buffer[f->loc + i];
+    }
+    f->loc += len;
+    return len;
+}
+
+pdf_obj *pdf_new_stream(void)
+{
+    pdf_obj *stream = xmalloc(sizeof(pdf_obj));
+    stream->length = 0;
+    stream->data = NULL;
+    return stream;
+}
+
+void pdf_add_stream(pdf_obj * stream, unsigned char *buf, long len)
+{
+    int i;
+    assert(stream != NULL);
+    if (stream->data == NULL) {
+        stream->data = xmalloc((unsigned) len);
+    } else {
+        stream->data =
+            xrealloc(stream->data, (unsigned) len + (unsigned) stream->length);
+    }
+    for (i = 0; i < len; i++) {
+        *(stream->data + stream->length + i) = *(buf + i);
+    }
+    stream->length += (unsigned) len;
+}
+
+void pdf_release_obj(pdf_obj * stream)
+{
+    if (stream != NULL) {
+        if (stream->data != NULL) {
+            xfree(stream->data);
+        }
+        xfree(stream);
+    }
+}
+
+@ The main function.
+@c
+boolean writetype2(PDF pdf, fd_entry * fd)
+{
+    int callback_id;
+    int file_opened = 0;
+    boolean ret;
+
+    glyph_tab = NULL;
+
+    fd_cur = fd;                /* |fd_cur| is global inside \.{writettf.w} */
+    assert(fd_cur->fm != NULL);
+    assert(is_truetype(fd_cur->fm));
+    assert(is_included(fd_cur->fm));
+
+    ttf_curbyte = 0;
+    ttf_size = 0;
+    cur_file_name =
+        luatex_find_file(fd_cur->fm->ff_name, find_opentype_file_callback);
+    if (cur_file_name == NULL) {
+        formatted_error("type 2","cannot find file '%s'", fd_cur->fm->ff_name);
+    }
+    callback_id = callback_defined(read_opentype_file_callback);
+    if (callback_id > 0) {
+        if (run_callback(callback_id, "S->bSd", cur_file_name,
+                         &file_opened, &ttf_buffer, &ttf_size) &&
+            file_opened && ttf_size > 0) {
+        } else {
+            formatted_error("type 2","cannot find file '%s'", cur_file_name);
+        }
+    } else {
+        if (!otf_open(cur_file_name)) {
+            formatted_error("type 2","cannot find file '%s'", cur_file_name);
+        }
+        ttf_read_file();
+        ttf_close();
+    }
+
+    fd_cur->ff_found = true;
+
+    if (is_subsetted(fd_cur->fm))
+        report_start_file(filetype_subset,cur_file_name);
+     else
+        report_start_file(filetype_font,cur_file_name);
+
+    /* here is the real work */
+
+    ret = make_tt_subset(pdf, fd, ttf_buffer, ttf_size);
+#if 0
+    xfree (dir_tab);
+#endif
+    xfree(ttf_buffer);
+    if (is_subsetted(fd_cur->fm))
+        report_stop_file(filetype_subset);
+     else
+        report_stop_file(filetype_font);
+    cur_file_name = NULL;
+    return ret;
+}
+
+@ PDF viewer applications use following tables (CIDFontType 2)
+
+\.{head, hhea, loca, maxp, glyf, hmtx, fpgm, cvt\_, prep}
+
+\rightline{from PDF Ref. v.1.3, 2nd ed.}
+
+ The \.{fpgm}, \.{cvt\_} and \.{prep} tables appears only when TrueType instructions
+ requires them. Those tables must be preserved if they exist.
+ We use |must_exist| flag to indicate `preserve it if present'
+ and to make sure not to cause an error when it does not exist.
+
+ \.{post} and \.{name} table must exist in ordinary TrueType font file,
+ but when a TrueType font is converted to CIDFontType 2 font, those tables
+ are no longer required.
+
+ The OS/2 table (required for TrueType font for Windows and OS/2) contains
+ liscencing information, but PDF viewers seems not using them.
+
+ The \.{name} table added. See comments in \.{writettf.w}.
+
+@c
+static struct {
+    const char *name;
+    int must_exist;
+} required_table[] = {
+    {
+    "OS/2", 0}, {
+    "cmap", 0}, {
+    "head", 1}, {
+    "hhea", 1}, {
+    "loca", 1}, {
+    "maxp", 0}, {
+    "name", 1}, {
+    "glyf", 1}, {
+    "hmtx", 1}, {
+    "fpgm", 0}, {
+    "cvt ", 0}, {
+    "prep", 0}, {
+    NULL, 0}
+};
+
+
+unsigned long ttc_read_offset(sfnt * sfont, int ttc_idx, fd_entry * fd)
+{
+    /*ULONG version;*/
+    unsigned long offset = 0;
+    unsigned long num_dirs = 0;
+
+    sfnt_seek_set(sfont, 4);    /* skip version tag */
+
+    /*version = */(void)sfnt_get_ulong(sfont);
+    num_dirs = sfnt_get_ulong(sfont);
+    if (ttc_idx < 0 || ttc_idx > (int) (num_dirs - 1)) {
+        formatted_error("type 2","invalid TTC index number %i (0..%i), using index 0 for font %s",
+            ttc_idx,(int) (num_dirs - 1),(fd->fm->ps_name ? fd->fm->ps_name : ""));
+        return 0 ;
+    }
+    sfnt_seek_set(sfont, 12 + ttc_idx * 4);
+    offset = sfnt_get_ulong(sfont);
+
+    return offset;
+}
+
+@ Creating the subset.
+@c
+extern int cidset;
+boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen)
+{
+
+    long i, cid;
+    unsigned int last_cid = 0;
+    glw_entry *found;
+    struct avl_traverser t;
+    unsigned char *cidtogidmap;
+    unsigned short num_glyphs, gid;
+    struct tt_glyphs *glyphs;
+    char *used_chars = NULL;
+    sfnt *sfont;
+    pdf_obj *fontfile;
+    int error = 0;
+
+    cidtogidmap = NULL;
+
+    sfont = sfnt_open(buff, buflen);
+
+    if (sfont->type == SFNT_TYPE_TTC) {
+        i = ff_get_ttc_index(fd->fm->ff_name, fd->fm->ps_name);
+        error = sfnt_read_table_directory(sfont, ttc_read_offset(sfont, (int) i, fd));
+    } else {
+        error = sfnt_read_table_directory(sfont, 0);
+    }
+
+    if (error < 0) {
+        normal_error("type 2","parsing the TTF directory fails");
+    }
+
+    if (sfont->type == SFNT_TYPE_TTC && sfnt_find_table_pos(sfont, "CFF ")) {
+        sfnt_close(sfont);
+	return false;
+    }
+
+    if (is_subsetted(fd->fm)) {
+        /* rebuild the glyph tables and create a fresh cidmap */
+        glyphs = tt_build_init();
+
+        last_cid = 0;
+
+        avl_t_init(&t, fd->gl_tree);
+        for (found = (glw_entry *) avl_t_first(&t, fd->gl_tree);
+             found != NULL; found = (glw_entry *) avl_t_next(&t)) {
+            if (found->id > last_cid)
+                last_cid = found->id;
+        }
+
+#ifndef NO_GHOSTSCRIPT_BUG
+        cidtogidmap = NULL;
+#else
+        cidtogidmap = xmalloc(((last_cid + 1) * 2) * sizeof(unsigned char));
+        memset(cidtogidmap, 0, (last_cid + 1) * 2);
+#endif
+
+        /* fill |used_chars| */
+        used_chars = xmalloc((last_cid + 1) * sizeof(char));
+        memset(used_chars, 0, (last_cid + 1));
+        avl_t_init(&t, fd->gl_tree);
+        for (found = (glw_entry *) avl_t_first(&t, fd->gl_tree);
+             found != NULL; found = (glw_entry *) avl_t_next(&t)) {
+            used_chars[found->id] = 1;
+        }
+
+        /* Map CIDs to GIDs. */
+
+        num_glyphs = 1;         /* \.{.notdef} */
+        for (cid = 1; cid <= (long) last_cid; cid++) {
+            if (used_chars[cid] == 0)
+                continue;
+            gid = (short unsigned) cid;
+
+
+#ifndef NO_GHOSTSCRIPT_BUG
+            gid = tt_add_glyph(glyphs, (USHORT) gid, (USHORT) cid);
+#else
+            gid = tt_add_glyph(glyphs, (USHORT) gid, (USHORT) num_glyphs);
+            cidtogidmap[2 * cid] = gid >> 8;
+            cidtogidmap[2 * cid + 1] = gid & 0xff;
+#endif                          /* |!NO_GHOSTSCRIPT_BUG| */
+
+            num_glyphs++;
+        }
+
+        if (num_glyphs == 1) {
+            normal_error("type 2","there are no glyphs in the subset");
+        }
+
+        if (tt_build_tables(sfont, glyphs, fd) < 0) {
+            normal_error("type 2","the TTF buffer can't be parsed");
+        }
+
+        tt_build_finish(glyphs);
+    }
+
+    /* Create font file */
+
+    for (i = 0; required_table[i].name; i++) {
+        if (sfnt_require_table(sfont,required_table[i].name, required_table[i].must_exist) < 0) {
+            normal_error("type 2","some required TrueType table does not exist");
+        }
+    }
+
+    fontfile = sfnt_create_FontFile_stream(sfont);
+
+    /* squeeze in the cidgidmap */
+    if (cidtogidmap != NULL) {
+        cidtogid_obj = (unsigned long) pdf_create_obj(pdf, obj_type_others, 0);
+        pdf_begin_obj(pdf, (int) cidtogid_obj, OBJSTM_NEVER);
+        pdf_begin_dict(pdf);
+        pdf_dict_add_int(pdf, "Length", ((last_cid + 1) * 2));
+        pdf_end_dict(pdf);
+        assert(0);              /* code unused */
+        pdf_begin_stream(pdf);
+        pdf_room(pdf, (int) ((last_cid + 1) * 2));
+        for (i = 0; i < ((int) (last_cid + 1) * 2); i++) {
+            pdf_quick_out(pdf, cidtogidmap[i]);
+        }
+        pdf_end_stream(pdf);
+        pdf_end_obj(pdf);
+    }
+
+    /* the tff subset */
+    for (i = 0; i < (int) (fontfile->length); i++)
+        strbuf_putchar(pdf->fb, fontfile->data[i]);
+
+    pdf_release_obj(fontfile);
+
+    /* CIDSet: a table of bits indexed by cid, bytes with high order bit first,
+       each (set) bit is a (present) CID. */
+    if (is_subsetted(fd->fm)) {
+        if ((! pdf->omit_cidset) && (pdf->major_version == 1)) {
+            cidset = pdf_create_obj(pdf, obj_type_others, 0);
+            if (cidset != 0) {
+                size_t l = (last_cid / 8) + 1;
+                char *stream = xmalloc(l);
+                memset(stream, 0, l);
+                for (cid = 1; cid <= (long) last_cid; cid++) {
+                    if (used_chars[cid]) {
+                        stream[(cid / 8)] |= (1 << (7 - (cid % 8)));
+                    }
+                }
+                pdf_begin_obj(pdf, cidset, OBJSTM_NEVER);
+                pdf_begin_dict(pdf);
+                pdf_dict_add_streaminfo(pdf);
+                pdf_end_dict(pdf);
+                pdf_begin_stream(pdf);
+                pdf_out_block(pdf, stream, l);
+                pdf_end_stream(pdf);
+                pdf_end_obj(pdf);
+            }
+        }
+    }
+
+    /* TODO other stuff that needs fixing: */
+
+    /* DW, W, DW2, and W2 */
+#if 0
+    if (opt_flags & CIDFONT_FORCE_FIXEDPITCH) {
+        pdf_add_dict(font->fontdict,
+                     pdf_new_name("DW"), pdf_new_number(1000.0));
+    } else {
+        add_TTCIDHMetrics(font->fontdict, glyphs, used_chars, cidtogidmap,
+                          last_cid);
+        if (v_used_chars)
+            add_TTCIDVMetrics(font->fontdict, glyphs, used_chars, cidtogidmap,
+                              last_cid);
+    }
+#endif
+
+    xfree(used_chars);
+    sfnt_close(sfont);
+    return true;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/font/writetype2.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
-
-Copyright 2006-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "font/writettf.h"
-#include "font/writecff.h"
-#include "lua/luatex-api.h"
-
-#include "font/sfnt.h"
-#include "font/tt_glyf.h"
-
-/*tex
-
-    Forward declarations
-
-*/
-
-boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen);
-
-unsigned long cidtogid_obj = 0;
-
-/*tex
-
-    Low-level helpers (a weird place):
-
-*/
-
-#define test_loc(l) \
-    if ((f->loc + l) > f->buflen) { \
-        normal_error("type 2","the file ended prematurely"); \
-    }
-
-BYTE get_unsigned_byte(sfnt * f)
-{
-    test_loc(1);
-    return (BYTE) f->buffer[(f->loc++)];
-}
-
-ICHAR get_signed_byte(sfnt * f)
-{
-    test_loc(1);
-    return (ICHAR) f->buffer[(f->loc++)];
-}
-
-USHORT get_unsigned_pair(sfnt * f)
-{
-    USHORT l;
-    test_loc(2);
-    l = f->buffer[(f->loc++)];
-    l = (USHORT) (l * 0x100 + f->buffer[(f->loc++)]);
-    return l;
-}
-
-SHORT get_signed_pair(sfnt * f)
-{
-    long l;
-    test_loc(2);
-    l = f->buffer[(f->loc++)];
-    if (l > 0x80)
-        l -= 0x100;
-    l = l * 0x100 + f->buffer[(f->loc++)];
-    return (SHORT) l;
-}
-
-ULONG get_unsigned_quad(sfnt * f)
-{
-    ULONG l;
-    test_loc(4);
-    l = f->buffer[(f->loc++)];
-    l = l * 0x100 + f->buffer[(f->loc++)];
-    l = l * 0x100 + f->buffer[(f->loc++)];
-    l = l * 0x100 + f->buffer[(f->loc++)];
-    return l;
-}
-
-int do_sfnt_read(unsigned char *dest, int len, sfnt * f)
-{
-    int i;
-    test_loc(len);
-    for (i = 0; i < len; i++) {
-        *(dest + i) = f->buffer[f->loc + i];
-    }
-    f->loc += len;
-    return len;
-}
-
-/*tex
-
-    The main function:
-
-*/
-
-boolean writetype2(PDF pdf, fd_entry * fd)
-{
-    int callback_id;
-    int file_opened = 0;
-    boolean ret;
-    glyph_tab = NULL;
-    /*tex |fd_cur| is global inside |writettf.c| */
-    fd_cur = fd;
-    assert(fd_cur->fm != NULL);
-    assert(is_truetype(fd_cur->fm));
-    assert(is_included(fd_cur->fm));
-    ttf_curbyte = 0;
-    ttf_size = 0;
-    cur_file_name =
-        luatex_find_file(fd_cur->fm->ff_name, find_opentype_file_callback);
-    if (cur_file_name == NULL) {
-        formatted_error("type 2","cannot find file '%s'", fd_cur->fm->ff_name);
-    }
-    callback_id = callback_defined(read_opentype_file_callback);
-    if (callback_id > 0) {
-        if (run_callback(callback_id, "S->bSd", cur_file_name,
-                         &file_opened, &ttf_buffer, &ttf_size) &&
-            file_opened && ttf_size > 0) {
-        } else {
-            formatted_error("type 2","cannot find file '%s'", cur_file_name);
-        }
-    } else {
-        if (!otf_open(cur_file_name)) {
-            formatted_error("type 2","cannot find file '%s'", cur_file_name);
-        }
-        ttf_read_file();
-        ttf_close();
-    }
-    fd_cur->ff_found = true;
-    if (is_subsetted(fd_cur->fm))
-        report_start_file(filetype_subset,cur_file_name);
-     else
-        report_start_file(filetype_font,cur_file_name);
-    /*tex Here is the real work done: */
-    ret = make_tt_subset(pdf, fd, ttf_buffer, ttf_size);
-    xfree(ttf_buffer);
-    if (is_subsetted(fd_cur->fm))
-        report_stop_file(filetype_subset);
-     else
-        report_stop_file(filetype_font);
-    cur_file_name = NULL;
-    return ret;
-}
-
-/*tex
-
-    The \PDF\ viewer applications use following tables for CIDFontType 2: |head|,
-    |hhea|, |loca|, |maxp|, |glyf|, |hmtx|, |fpgm|, |cvt| and |prep|. According
-    to PDF Ref. v.1.3, 2nd ed. The |fpgm\, |cvt| and |prep| tables appears only
-    when TrueType instructions requires them. Those tables must be preserved if
-    they exist. We use |must_exist| flag to indicate `preserve it if present' and
-    to make sure not to cause an error when it does not exist. The |post\ and
-    |name| tables must exist in ordinary TrueType font file, but when a TrueType
-    font is converted to CIDFontType 2 font, those tables are no longer required.
-    The |OS/2| table (required for TrueType font for Windows and OS/2) contains
-    liscencing information, but PDF viewers seems not using them. The |name|
-    table as been added added too (see comments in |writettf.c|.
-
-*/
-
-static struct {
-    const char *name;
-    int must_exist;
-}
-
-required_table[] = {
-    { "OS/2", 0 },
-    { "cmap", 0 },
-    { "head", 1 },
-    { "hhea", 1 },
-    { "loca", 1 },
-    { "maxp", 0 },
-    { "name", 1 },
-    { "glyf", 1 },
-    { "hmtx", 1 },
-    { "fpgm", 0 },
-    { "cvt ", 0 },
-    { "prep", 0 },
-    { NULL,   0 }
-};
-
-unsigned long ttc_read_offset(sfnt * sfont, int ttc_idx, fd_entry * fd)
-{
-    unsigned long offset = 0;
-    unsigned long num_dirs = 0;
-    /*tex Skip the |ULONG| version tag: */
-    sfnt_seek_set(sfont, 4);
-    /* version = */ (void)sfnt_get_ulong(sfont);
-    num_dirs = sfnt_get_ulong(sfont);
-    if (ttc_idx < 0 || ttc_idx > (int) (num_dirs - 1)) {
-        formatted_error("type 2",
-            "invalid TTC index number %i (0..%i), using index 0 for font %s",
-            ttc_idx,(int) (num_dirs - 1),(fd->fm->ps_name ? fd->fm->ps_name : ""));
-        return 0 ;
-    }
-    sfnt_seek_set(sfont, 12 + ttc_idx * 4);
-    offset = sfnt_get_ulong(sfont);
-    return offset;
-}
-
-/*tex
-
-    Creating the subset.
-*/
-
-extern int cidset;
-
-boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen)
-{
-
-    long i, cid;
-    unsigned int last_cid = 0;
-    glw_entry *found;
-    struct avl_traverser t;
-    unsigned char *cidtogidmap;
-    unsigned short num_glyphs, gid;
-    struct tt_glyphs *glyphs;
-    char *used_chars = NULL;
-    sfnt *sfont;
-    pdf_obj *fontfile;
-    int error = 0;
-    cidtogidmap = NULL;
-    sfont = sfnt_open(buff, buflen);
-    if (sfont->type == SFNT_TYPE_TTC) {
-        i = ff_get_ttc_index(fd->fm->ff_name, fd->fm->ps_name);
-        error = sfnt_read_table_directory(sfont, ttc_read_offset(sfont, (int) i, fd));
-    } else {
-        error = sfnt_read_table_directory(sfont, 0);
-    }
-    if (error < 0) {
-        normal_error("type 2","parsing the TTF directory fails");
-    }
-    if (sfont->type == SFNT_TYPE_TTC && sfnt_find_table_pos(sfont, "CFF ")) {
-        sfnt_close(sfont);
-        return false;
-    }
-    if (is_subsetted(fd->fm)) {
-        /*tex Rebuild the glyph tables and create a fresh cidmap :*/
-        glyphs = tt_build_init();
-        last_cid = 0;
-        avl_t_init(&t, fd->gl_tree);
-        for (found = (glw_entry *) avl_t_first(&t, fd->gl_tree);
-             found != NULL; found = (glw_entry *) avl_t_next(&t)) {
-            if (found->id > last_cid)
-                last_cid = found->id;
-        }
-#ifndef NO_GHOSTSCRIPT_BUG
-        cidtogidmap = NULL;
-#else
-        cidtogidmap = xmalloc(((last_cid + 1) * 2) * sizeof(unsigned char));
-        memset(cidtogidmap, 0, (last_cid + 1) * 2);
-#endif
-        /*tex Fill |used_chars|: */
-        used_chars = xmalloc((last_cid + 1) * sizeof(char));
-        memset(used_chars, 0, (last_cid + 1));
-        avl_t_init(&t, fd->gl_tree);
-        for (found = (glw_entry *) avl_t_first(&t, fd->gl_tree);
-             found != NULL; found = (glw_entry *) avl_t_next(&t)) {
-            used_chars[found->id] = 1;
-        }
-        /*tex Map CIDs to GIDs, with |.notdef| in slot zero: */
-        num_glyphs = 1;
-        for (cid = 1; cid <= (long) last_cid; cid++) {
-            if (used_chars[cid] == 0)
-                continue;
-            gid = (short unsigned) cid;
-#ifndef NO_GHOSTSCRIPT_BUG
-            gid = tt_add_glyph(glyphs, (USHORT) gid, (USHORT) cid);
-#else
-            gid = tt_add_glyph(glyphs, (USHORT) gid, (USHORT) num_glyphs);
-            cidtogidmap[2 * cid] = gid >> 8;
-            cidtogidmap[2 * cid + 1] = gid & 0xff;
-#endif
-            num_glyphs++;
-        }
-
-        if (num_glyphs == 1) {
-            normal_error("type 2","there are no glyphs in the subset");
-        }
-        if (tt_build_tables(sfont, glyphs, fd) < 0) {
-            normal_error("type 2","the TTF buffer can't be parsed");
-        }
-        tt_build_finish(glyphs);
-    }
-    /*tex Create the font file: */
-    for (i = 0; required_table[i].name; i++) {
-        if (sfnt_require_table(sfont,required_table[i].name, required_table[i].must_exist) < 0) {
-            normal_error("type 2","some required TrueType table does not exist");
-        }
-    }
-    fontfile = sfnt_create_FontFile_stream(sfont);
-    /*tex The |cidgidmap|: */
-    if (cidtogidmap != NULL) {
-        cidtogid_obj = (unsigned long) pdf_create_obj(pdf, obj_type_others, 0);
-        pdf_begin_obj(pdf, (int) cidtogid_obj, OBJSTM_NEVER);
-        pdf_begin_dict(pdf);
-        pdf_dict_add_int(pdf, "Length", ((last_cid + 1) * 2));
-        pdf_end_dict(pdf);
-        pdf_begin_stream(pdf);
-        pdf_room(pdf, (int) ((last_cid + 1) * 2));
-        for (i = 0; i < ((int) (last_cid + 1) * 2); i++) {
-            pdf_quick_out(pdf, cidtogidmap[i]);
-        }
-        pdf_end_stream(pdf);
-        pdf_end_obj(pdf);
-    }
-    /*tex The tff subset: */
-    for (i = 0; i < (int) (fontfile->length); i++)
-        strbuf_putchar(pdf->fb, fontfile->data[i]);
-    pdf_release_obj(fontfile);
-    /*tex
-        |CIDSet| is a table of bits indexed by cid, bytes with high order bit
-        first, each (set) bit is a (present) CID.
-    */
-    if (is_subsetted(fd->fm)) {
-        if ((! pdf->omit_cidset) && (pdf->major_version == 1)) {
-            cidset = pdf_create_obj(pdf, obj_type_others, 0);
-            if (cidset != 0) {
-                size_t l = (last_cid / 8) + 1;
-                char *stream = xmalloc(l);
-                memset(stream, 0, l);
-                for (cid = 1; cid <= (long) last_cid; cid++) {
-                    if (used_chars[cid]) {
-                        stream[(cid / 8)] |= (1 << (7 - (cid % 8)));
-                    }
-                }
-                pdf_begin_obj(pdf, cidset, OBJSTM_NEVER);
-                pdf_begin_dict(pdf);
-                pdf_dict_add_streaminfo(pdf);
-                pdf_end_dict(pdf);
-                pdf_begin_stream(pdf);
-                pdf_out_block(pdf, stream, l);
-                pdf_end_stream(pdf);
-                pdf_end_obj(pdf);
-            }
-        }
-    }
-    xfree(used_chars);
-    sfnt_close(sfont);
-    return true;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/image/pdftoepdf.c
+++ /dev/null
@@ -1,1049 +0,0 @@
-/*
-pdftoepdf.w
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-Copyright 2006-2015 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/>.
-*/
-
-#define __STDC_FORMAT_MACROS /* for PRId64 etc.  */
-
-#include "image/epdf.h"
-
-/* Conflict with pdfgen.h */
-
-#ifndef pdf_out
-
-#define pdf_out(pdf, A) do { pdf_room(pdf, 1); *(pdf->buf->p++) = A; } while (0)
-
-#define pdf_check_space(pdf) do { \
-    if (pdf->cave > 0) { \
-        pdf_out(pdf, ' '); \
-        pdf->cave = 0; \
-    } \
-} while (0)
-
-#define pdf_set_space(pdf) \
-    pdf->cave = 1;
-
-#define pdf_reset_space(pdf) \
-    pdf->cave = 0;
-
-#endif
-
-/* Maintain AVL tree of all PDF files for embedding */
-
-static avl_table *PdfDocumentTree = NULL;
-
-/* AVL sort PdfDocument into PdfDocumentTree by file_path */
-
-static int CompPdfDocument(const void *pa, const void *pb, void *p )
-{
-    return strcmp(((const PdfDocument *) pa)->file_path, ((const PdfDocument *) pb)->file_path);
-}
-
-/* Returns pointer to PdfDocument structure for PDF file. */
-
-static PdfDocument *findPdfDocument(char *file_path)
-{
-    PdfDocument *pdf_doc, tmp;
-    if (file_path == NULL) {
-        normal_error("pdf backend","empty filename when loading pdf file");
-    } else if (PdfDocumentTree == NULL) {
-        return NULL;
-    }
-    tmp.file_path = file_path;
-    pdf_doc = (PdfDocument *) avl_find(PdfDocumentTree, &tmp);
-    return pdf_doc;
-}
-
-#define PDF_CHECKSUM_SIZE 32
-
-static char *get_file_checksum(const char *a, file_error_mode fe)
-{
-    struct stat finfo;
-    char *ck = NULL;
-    if (stat(a, &finfo) == 0) {
-        off_t size = finfo.st_size;
-        time_t mtime = finfo.st_mtime;
-        ck = (char *) malloc(PDF_CHECKSUM_SIZE);
-        if (ck == NULL)
-            formatted_error("pdf inclusion","out of memory while processing '%s'", a);
-        snprintf(ck, PDF_CHECKSUM_SIZE, "%" PRIu64 "_%" PRIu64, (uint64_t) size,(uint64_t) mtime);
-    } else {
-        switch (fe) {
-            case FE_FAIL:
-                formatted_error("pdf inclusion","could not stat() file '%s'", a);
-                break;
-            case FE_RETURN_NULL:
-                if (ck != NULL)
-                    free(ck);
-                ck = NULL;
-                break;
-            default:
-                assert(0);
-        }
-    }
-    return ck;
-}
-
-static char *get_stream_checksum (const char *str, unsigned long long str_size){
-    /* http://www.cse.yorku.ca/~oz/hash.html */
-    /* djb2                                  */
-    unsigned long hash ;
-    char *ck = NULL;
-    unsigned int i;
-    hash = 5381;
-    ck = (char *) malloc(STRSTREAM_CHECKSUM_SIZE+1);
-    if (ck == NULL)
-        normal_error("pdf inclusion","out of memory while processing a memstream");
-    for(i=0; i<(unsigned int)(str_size); i++) {
-        hash = ((hash << 5) + hash) + str[i]; /* hash * 33 + str[i] */
-    }
-    snprintf(ck,STRSTREAM_CHECKSUM_SIZE+1,"%lx",hash);
-    ck[STRSTREAM_CHECKSUM_SIZE]='\0';
-    return ck;
-}
-
-/*
-    Returns pointer to PdfDocument structure for PDF file.
-    Creates a new PdfDocument structure if it doesn't exist yet.
-    When fe = FE_RETURN_NULL, the function returns NULL in error case.
-*/
-
-PdfDocument *refPdfDocument(const char *file_path, file_error_mode fe, const char *userpassword, const char *ownerpassword)
-{
-    char *checksum, *path_copy;
-    PdfDocument *pdf_doc;
-    ppdoc *pdfe = NULL;
-    int new_flag = 0;
-    if ((checksum = get_file_checksum(file_path, fe)) == NULL) {
-        return (PdfDocument *) NULL;
-    }
-    path_copy = xstrdup(file_path);
-    if ((pdf_doc = findPdfDocument(path_copy)) == NULL) {
-        new_flag = 1;
-        pdf_doc = (PdfDocument*) xmalloc(sizeof( PdfDocument));
-        pdf_doc->file_path = path_copy;
-        pdf_doc->checksum = checksum;
-        pdf_doc->pdfe = NULL;
-        pdf_doc->inObjList = NULL;
-        pdf_doc->ObjMapTree = NULL;
-        pdf_doc->occurences = 0; /* 0 = unreferenced */
-        pdf_doc->pc = 0;
-        pdf_doc->is_mem = 0;
-    } else {
-        if (strncmp(pdf_doc->checksum, checksum, PDF_CHECKSUM_SIZE) != 0) {
-            formatted_error("pdf inclusion","file has changed '%s'", file_path);
-        }
-        free(checksum);
-        free(path_copy);
-    }
-    if (pdf_doc->pdfe == NULL) {
-        pdfe = ppdoc_load(file_path);
-        pdf_doc->pc++;
-        /* todo: check if we might print the document */
-        if (pdfe == NULL) {
-            switch (fe) {
-                case FE_FAIL:
-                    normal_error("pdf inclusion","reading image failed");
-                    break;
-                case FE_RETURN_NULL:
-                    if (pdf_doc->pdfe != NULL) {
-                        ppdoc_free(pdfe);
-                        pdf_doc->pdfe = NULL;
-                    }
-                    /* delete docName */
-                    if (new_flag == 1) {
-                        if (pdf_doc->file_path != NULL)
-                            free(pdf_doc->file_path);
-                        if (pdf_doc->checksum != NULL)
-                            free(pdf_doc->checksum);
-                        free(pdf_doc);
-                    }
-                    return (PdfDocument *) NULL;
-                    break;
-                default:
-                    assert(0);
-            }
-        }
-        if (pdfe != NULL) {
-            if (ppdoc_crypt_status(pdfe) < 0) {
-                ppdoc_crypt_pass(pdfe,userpassword,strlen(userpassword),NULL,0);
-            }
-            if (ppdoc_crypt_status(pdfe) < 0) {
-                ppdoc_crypt_pass(pdfe,NULL,0,ownerpassword,strlen(ownerpassword));
-            }
-            if (ppdoc_crypt_status(pdfe) < 0) {
-                formatted_error("pdf inclusion","the pdf file '%s' is encrypted, provide proper passwords",file_path);
-            }
-        }
-        pdf_doc->pdfe = pdfe;
-    }
-    /* PDF file could be opened without problems, checksum ok. */
-    if (PdfDocumentTree == NULL)
-        PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator);
-    if ((PdfDocument *) avl_find(PdfDocumentTree, pdf_doc) == NULL) {
-        avl_probe(PdfDocumentTree, pdf_doc);
-    }
-    pdf_doc->occurences++;
-    return pdf_doc;
-}
-
-/*
-    Returns pointer to PdfDocument structure for a PDF stream in memory of streamsize
-    dimension. As before, creates a new PdfDocument structure if it doesn't exist yet
-    with file_path = file_id
-*/
-
-PdfDocument *refMemStreamPdfDocument(char *docstream, unsigned long long streamsize,const char *file_id)
-{
-    char *checksum;
-    char *file_path;
-    PdfDocument *pdf_doc;
-    ppdoc *pdfe = NULL;
-    size_t  cnt = 0;
-    checksum = get_stream_checksum(docstream, streamsize);
-    cnt = strlen(file_id);
-    file_path = (char *) malloc(cnt+STREAM_URI_LEN+STRSTREAM_CHECKSUM_SIZE+1); /* 1 for \0 */
-    strcpy(file_path,STREAM_URI);
-    strcat(file_path,file_id);
-    strcat(file_path,checksum);
-    file_path[cnt+STREAM_URI_LEN+STRSTREAM_CHECKSUM_SIZE]='\0';
-    if ((pdf_doc = findPdfDocument(file_path)) == NULL) {
-        /*new_flag = 1;*/
-        pdf_doc = (PdfDocument*) xmalloc(sizeof( PdfDocument));
-        pdf_doc->file_path = file_path;
-        pdf_doc->checksum = checksum;
-        pdf_doc->pdfe = NULL;
-        pdf_doc->inObjList = NULL;
-        pdf_doc->ObjMapTree = NULL;
-        pdf_doc->occurences = 0; /* 0 = unreferenced */
-        pdf_doc->pc = 0;
-        pdf_doc->is_mem = 1;
-        pdf_doc->memstream = docstream;
-    } else {
-        /* As is now, checksum is in file_path, so this check should be useless. */
-        if (strncmp(pdf_doc->checksum, checksum, STRSTREAM_CHECKSUM_SIZE) != 0) {
-            formatted_error("pdf inclusion","stream has changed '%s'", file_path);
-        }
-        free(file_path);
-        free(checksum);
-    }
-    if (pdf_doc->pdfe == NULL) {
-        pdfe = ppdoc_mem(docstream, streamsize);
-        pdf_doc->pc++;
-        if (pdfe == NULL) {
-            normal_error("pdf inclusion","reading pdf Stream failed");
-        }
-        pdf_doc->pdfe = pdfe;
-    }
-    /* PDF file could be opened without problems, checksum ok. */
-    if (PdfDocumentTree == NULL)
-        PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator);
-    if ((PdfDocument *) avl_find(PdfDocumentTree, pdf_doc) == NULL) {
-        avl_probe(PdfDocumentTree, pdf_doc);
-    }
-    pdf_doc->occurences++;
-    return pdf_doc;
-}
-
-/*
-    AVL sort ObjMap into ObjMapTree by object number and generation keep the ObjMap
-    struct small, as these are accumulated until the end
-*/
-
-typedef struct ObjMap ObjMap ;
-
-struct ObjMap {
-    ppref * in;
-    int out_num;
-};
-
-static int CompObjMap(const void *pa, const void *pb, void *p)
-{
-    const ppref *a = (((const ObjMap *) pa)->in);
-    const ppref *b = (((const ObjMap *) pb)->in);
-    if (a->number > b->number)
-        return 1;
-    else if (a->number < b->number)
-        return -1;
-    else if (a->version == b->version)
-        return 0;
-    else if (a->version < b->version)
-        return -1;
-    return 1;
-}
-
-static ObjMap *findObjMap(PdfDocument * pdf_doc, ppref * in)
-{
-    ObjMap *obj_map, tmp;
-    if (pdf_doc->ObjMapTree == NULL)
-        return NULL;
-    tmp.in = in;
-    obj_map = (ObjMap *) avl_find(pdf_doc->ObjMapTree, &tmp);
-    return obj_map;
-}
-
-static void addObjMap(PdfDocument * pdf_doc, ppref * in, int out_num)
-{
-    ObjMap *obj_map = NULL;
-    if (pdf_doc->ObjMapTree == NULL)
-        pdf_doc->ObjMapTree = avl_create(CompObjMap, NULL, &avl_xallocator);
-    obj_map = (ObjMap*)xmalloc(sizeof(ObjMap));
-    obj_map->in = in;
-    obj_map->out_num = out_num;
-    avl_probe(pdf_doc->ObjMapTree, obj_map);
-}
-
-/*
-    When copying the Resources of the selected page, all objects are
-    copied recursively top-down.  The findObjMap() function checks if an
-    object has already been copied; if so, instead of copying just the
-    new object number will be referenced.  The ObjMapTree guarantees,
-    that during the entire LuaTeX run any object from any embedded PDF
-    file will end up max. once in the output PDF file.  Indirect objects
-    are not fetched during copying, but get a new object number from
-    LuaTeX and then will be appended into a linked list.
-*/
-
-static int addInObj(PDF pdf, PdfDocument * pdf_doc, ppref * ref)
-{
-    ObjMap *obj_map;
-    InObj *p, *q, *n;
-    if (ref->number == 0) {
-        normal_error("pdf inclusion","reference to invalid object (broken pdf)");
-    }
-    if ((obj_map = findObjMap(pdf_doc, ref)) != NULL) {
-        return obj_map->out_num;
-    }
-    n = (InObj*)xmalloc(sizeof(InObj));
-    n->ref = ref;
-    n->next = NULL;
-    n->num = pdf_create_obj(pdf, obj_type_others, 0);
-    addObjMap(pdf_doc, ref, n->num);
-    if (pdf_doc->inObjList == NULL) {
-        pdf_doc->inObjList = n;
-    } else {
-        /*
-            It is important to add new objects at the end of the list,
-            because new objects are being added while the list is being
-            written out by writeRefs().
-        */
-        for (p = pdf_doc->inObjList; p != NULL; p = p->next)
-            q = p;
-        q->next = n;
-    }
-    return n->num;
-}
-
-static void copyObject(PDF, PdfDocument *, ppobj *);
-
-static void copyString(PDF pdf, ppstring str)
-{
-    pdf_check_space(pdf);
-    switch (ppstring_type((void *)(str))) {
-        case PPSTRING_PLAIN:
-            pdf_out(pdf, '(');
-            pdf_out_block(pdf, (const char *) str, ppstring_size((void *)(str)));
-            pdf_out(pdf, ')');
-            break;
-        case PPSTRING_BASE16:
-            pdf_out(pdf, '<');
-            pdf_out_block(pdf, (const char *) str, ppstring_size((void *)(str)));
-            pdf_out(pdf, '>');
-            break;
-        case PPSTRING_BASE85:
-            pdf_out(pdf, '<');
-            pdf_out(pdf, '~');
-            pdf_out_block(pdf, (const char *) str, ppstring_size((void *)(str)));
-            pdf_out(pdf, '~');
-            pdf_out(pdf, '>');
-            break;
-    }
-    pdf_set_space(pdf);
-}
-
-/*
-static void copyName(PDF pdf, ppname *name)
-{
-    pdf_add_name(pdf, (const char *) name);
-}
-*/
-
-static void copyArray(PDF pdf, PdfDocument * pdf_doc, pparray * array)
-{
-    int i;
-    int n = array->size;
-    pdf_begin_array(pdf);
-    for (i=0; i<n; ++i) {
-        copyObject(pdf, pdf_doc, pparray_at(array,i));
-    }
-    pdf_end_array(pdf);
-}
-
-static void copyDict(PDF pdf, PdfDocument * pdf_doc, ppdict *dict)
-{
-    int i;
-    int n = dict->size;
-    pdf_begin_dict(pdf);
-    for (i=0; i<n; ++i) {
-        pdf_add_name(pdf, (const char *) ppdict_key(dict,i));
-        copyObject(pdf, pdf_doc, ppdict_at(dict,i));
-    }
-    pdf_end_dict(pdf);
-}
-
-static void copyStreamStream(PDF pdf, ppstream * stream, int decode)
-{
-    uint8_t *data = NULL;
-    size_t size = 0;
-    if (0) {
-        for (data = ppstream_first(stream, &size, decode); data != NULL; data = ppstream_next(stream, &size)) {
-            pdf_out_block(pdf, (const char *) data, size);
-        }
-    } else {
-        data = ppstream_all(stream,&size,decode);
-        if (data != NULL) {
-            pdf_out_block(pdf, (const char *) data, size);
-        }
-    }
-    ppstream_done(stream);
-}
-
-static void copyStream(PDF pdf, PdfDocument * pdf_doc, ppstream * stream)
-{
-    ppdict *dict = stream->dict; /* bug in: stream_dict(stream) */
-    if (pdf->compress_level == 0 || pdf->recompress) {
-        const char *ignoredkeys[] = {
-            "Filter", "Decode", "Length", "DL", NULL
-        };
-        int i;
-        int n = dict->size;
-        pdf_begin_dict(pdf);
-        for (i=0; i<n; ++i) {
-            const char *key = ppdict_key(dict,i);
-            int okay = 1;
-            int k;
-            for (k = 0; ignoredkeys[k] != NULL; k++) {
-                if (strcmp(key,ignoredkeys[k]) == 0) {
-                    okay = 0;
-                    break;
-                }
-            }
-            if (okay) {
-                pdf_add_name(pdf, key);
-                copyObject(pdf, pdf_doc, ppdict_at(dict,i));
-            }
-        }
-        pdf_dict_add_streaminfo(pdf);
-        pdf_end_dict(pdf);
-        pdf_begin_stream(pdf);
-        copyStreamStream(pdf, stream, 1);
-        pdf_end_stream(pdf);
-    } else {
-        copyDict(pdf, pdf_doc, dict);
-        pdf_begin_stream(pdf);
-        copyStreamStream(pdf, stream, 0);
-        pdf_end_stream(pdf);
-    }
-}
-
-static void copyObject(PDF pdf, PdfDocument * pdf_doc, ppobj * obj)
-{
-    switch (obj->type) {
-        case PPNULL:
-            pdf_add_null(pdf);
-            break;
-        case PPBOOL:
-            pdf_add_bool(pdf,obj->integer);                     /* ppobj_get_bool_value(obj) */
-            break;
-        case PPINT:
-            pdf_add_int(pdf,obj->integer);                      /* ppobj_get_int_value(obj) */
-            break;
-        case PPNUM:
-            pdf_add_real(pdf,obj->number);                      /* ppobj_get_num_value(obj) */
-            break;
-        case PPNAME:
-            pdf_add_name(pdf, (const char *) obj->name);        /* ppobj_get_name(obj) */
-            break;
-        case PPSTRING:
-            copyString(pdf, obj->string);                       /* ppobj_get_string(obj) */
-            break;
-        case PPARRAY:
-            copyArray(pdf, pdf_doc, obj->array);                /* ppobj_get_array(obj) */
-            break;
-        case PPDICT:
-            copyDict(pdf, pdf_doc, obj->dict);                  /* ppobj_get_dict(obj) */
-            break;
-        case PPSTREAM:
-            copyStream(pdf, pdf_doc, obj->stream);              /* ppobj_get_stream(obj) */
-            break;
-        case PPREF:
-            pdf_add_ref(pdf, addInObj(pdf, pdf_doc, obj->ref)); /* ppobj_get_ref(obj) */
-            break;
-        default:
-            break;
-    }
-}
-
-static void writeRefs(PDF pdf, PdfDocument * pdf_doc)
-{
-    InObj *r, *n;
-    ppobj * obj;
-    for (r = pdf_doc->inObjList; r != NULL;) {
-        obj = ppref_obj(r->ref);
-        if (obj->type == PPSTREAM)
-            pdf_begin_obj(pdf, r->num, OBJSTM_NEVER);
-        else
-            pdf_begin_obj(pdf, r->num, 2);
-        copyObject(pdf, pdf_doc, obj);
-        pdf_end_obj(pdf);
-        n = r->next;
-        free(r);
-        r = n;
-        pdf_doc->inObjList = n;
-    }
-}
-
-/* get the pagebox coordinates according to the pagebox_spec */
-
-static void somebox(ppdict *page, const char * key, pprect * box)
-{
-    pprect * r = ppdict_get_box(page, key, box);
-    if (r != NULL) {
-        box->lx = r->lx;
-        box->ly = r->ly;
-        box->rx = r->rx;
-        box->ry = r->ry;
-    }
-}
-
-static void get_pagebox(ppdict * page, int pagebox_spec, pprect * box)
-{
-    box->lx = box->rx = box->ly = box->ry = 0;
-    somebox(page,"MediaBox",box);
-    if (pagebox_spec == PDF_BOX_SPEC_MEDIA) {
-        return;
-    }
-    somebox(page,"CropBox",box);
-    if (pagebox_spec == PDF_BOX_SPEC_CROP) {
-        return;
-    }
-    switch (pagebox_spec) {
-        case PDF_BOX_SPEC_BLEED:
-            somebox(page,"BleedBox",box);
-            break;
-        case PDF_BOX_SPEC_TRIM:
-            somebox(page,"TrimBox",box);
-            break;
-        case PDF_BOX_SPEC_ART:
-            somebox(page,"ArtBox",box);
-            break;
-        default:
-            break;
-    }
-}
-
-/*
-    Reads various information about the PDF and sets it up for later inclusion.
-    This will fail if the PDF version of the PDF is higher than minor_pdf_version_wanted
-    or page_name is given and can not be found. It makes no sense to give page_name and
-    page_num. Returns the page number.
-*/
-
-static ppdict * get_pdf_page_dict(ppdoc *pdfe, int n)
-{
-    ppref *r;
-    int i;
-    for (r=ppdoc_first_page(pdfe), i=1; r != NULL; r = ppdoc_next_page(pdfe), ++i) {
-        if (i == n) {
-            return ppref_obj(r)->dict;
-        }
-    }
-    return NULL;
-}
-
-// static ppdict * get_pdf_page_dict(ppdoc *pdfe, int n)
-// {
-//     return ppref_obj(ppdoc_page(pdfe,n))->dict;
-// }
-
-void read_pdf_info(image_dict * idict)
-{
-    PdfDocument *pdf_doc = NULL;
-    ppdoc * pdfe = NULL;
-    ppdict *pageDict, *groupDict;
-    pprect pagebox;
-    ppint rotate = 0;
-    int pdf_major_version_found = 1;
-    int pdf_minor_version_found = 3;
-    float xsize, ysize, xorig, yorig;
-    if (img_type(idict) == IMG_TYPE_PDF) {
-        pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict));
-    } else if (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) {
-        pdf_doc = findPdfDocument(img_filepath(idict)) ;
-        if (pdf_doc == NULL )
-           normal_error("pdf inclusion", "memstream not initialized");
-        if (pdf_doc->pdfe == NULL)
-           normal_error("pdf inclusion", "memstream document is empty");
-        pdf_doc->occurences++;
-    } else {
-        normal_error("pdf inclusion","unknown document");
-    }
-    pdfe = pdf_doc->pdfe;
-    /*
-        Check PDF version. This works only for PDF 1.x but since any versions of
-        PDF newer than 1.x will not be backwards compatible to PDF 1.x, we will
-        then have to changed drastically anyway.
-    */
-    pdf_major_version_found = ppdoc_version_number(pdfe,&pdf_minor_version_found);
-    if ((100 * pdf_major_version_found + pdf_major_version_found) > (100 * img_pdfmajorversion(idict) + img_pdfminorversion(idict))) {
-        const char *msg = "PDF inclusion: found PDF version '%d.%d', but at most version '%d.%d' allowed";
-        if (img_errorlevel(idict) > 0) {
-            formatted_error("pdf inclusion",msg, pdf_major_version_found, pdf_minor_version_found, img_pdfmajorversion(idict), img_pdfminorversion(idict));
-        } else {
-            formatted_warning("pdf inclusion",msg, pdf_major_version_found, pdf_minor_version_found, img_pdfmajorversion(idict), img_pdfminorversion(idict));
-        }
-    }
-    img_totalpages(idict) = ppdoc_page_count(pdfe);
-    if (img_pagename(idict)) {
-        /*
-            get page by name is obsolete
-        */
-        normal_error("pdf inclusion","named pages are not supported");
-    } else {
-        /*
-            get page by number
-        */
-        if (img_pagenum(idict) <= 0
-            || img_pagenum(idict) > img_totalpages(idict))
-            formatted_error("pdf inclusion","required page '%i' does not exist",(int) img_pagenum(idict));
-    }
-    /*
-        get the required page
-    */
-    pageDict = get_pdf_page_dict(pdfe,img_pagenum(idict));
-    /*
-        get the pagebox coordinates (media, crop,...) to use
-    */
-    get_pagebox(pageDict, img_pagebox(idict), &pagebox);
-    if (pagebox.rx > pagebox.lx) {
-        xorig = pagebox.lx;
-        xsize = pagebox.rx - pagebox.lx;
-    } else {
-        xorig = pagebox.rx;
-        xsize = pagebox.lx - pagebox.rx;
-    }
-    if (pagebox.ry > pagebox.ly) {
-        yorig = pagebox.ly;
-        ysize = pagebox.ry - pagebox.ly;
-    } else {
-        yorig = pagebox.ry;
-        ysize = pagebox.ly - pagebox.ry;
-    }
-    /*
-        The following 4 parameters are raw. Do _not_ modify by /Rotate!
-    */
-    img_xsize(idict) = bp2sp(xsize);
-    img_ysize(idict) = bp2sp(ysize);
-    img_xorig(idict) = bp2sp(xorig);
-    img_yorig(idict) = bp2sp(yorig);
-    /*
-        Handle /Rotate parameter. Only multiples of 90 deg. are allowed (PDF Ref. v1.3,
-        p. 78). We also accept negative angles. Beware: PDF counts clockwise!
-    */
-    if (ppdict_get_int(pageDict, "Rotate", &rotate)) {
-        switch ((((int)rotate % 360) + 360) % 360) {
-            case 0:
-                img_rotation(idict) = 0;
-                break;
-            case 90:
-                img_rotation(idict) = 3;
-                break;
-            case 180:
-                img_rotation(idict) = 2;
-                break;
-            case 270:
-                img_rotation(idict) = 1;
-                break;
-            default:
-                formatted_warning("pdf inclusion","/Rotate parameter in PDF file not multiple of 90 degrees");
-        }
-    }
-    /*
-        currently unused info whether PDF contains a /Group
-    */
-    groupDict = ppdict_get_dict(pageDict, "Group");
-    if (groupDict != NULL) {
-         img_set_group(idict);
-    }
-    /*
-        LuaTeX pre 0.85 versions did this:
-
-        if (readtype == IMG_CLOSEINBETWEEN) {
-            unrefPdfDocument(img_filepath(idict));
-        }
-
-        and also unref'd in the finalizer so we got an extra unrefs when garbage was
-        collected. However it is more efficient to keep the file open so we do that
-        now. The (slower) alternative is to unref here (which in most cases forcing a
-        close of the file) but then we must not call flush_pdf_info.
-
-        A close (unref) can be forced by nilling the dict object at the lua end and
-        forcing a collectgarbage("collect") after that.
-
-    */
-    if (! img_keepopen(idict)) {
-        unrefPdfDocument(img_filepath(idict));
-    }
-}
-
-void flush_pdf_info(image_dict * idict)
-{
-    if (img_keepopen(idict)) {
-        unrefPdfDocument(img_filepath(idict));
-    }
-}
-
-/*
-    Write the current epf_doc. Here the included PDF is copied, so most errors
-    that can happen during PDF inclusion will arise here.
-*/
-
-void write_epdf(PDF pdf, image_dict * idict, int suppress_optional_info)
-{
-    PdfDocument *pdf_doc = NULL;
-    ppdoc *pdfe = NULL;
-    ppdict *pageDict, *infoDict;
-    ppobj *obj, *content, *resources;
-    pprect pagebox;
-    int i;
-    double bbox[4];
-    const char *pagedictkeys[] = {
-        "Group", "LastModified", "Metadata", "PieceInfo", "SeparationInfo", NULL
-    };
-    /*
-        open PDF file
-    */
-    if (img_type(idict) == IMG_TYPE_PDF) {
-        pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict));
-    } else if (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) {
-        pdf_doc = findPdfDocument(img_filepath(idict)) ;
-        pdf_doc->occurences++;
-    } else {
-        normal_error("pdf inclusion","unknown document");
-    }
-    pdfe = pdf_doc->pdfe;
-    pageDict = get_pdf_page_dict(pdfe,img_pagenum(idict));
-    /*
-        write the Page header
-    */
-    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "XObject");
-    pdf_dict_add_name(pdf, "Subtype", "Form");
-    pdf_dict_add_int(pdf, "FormType", 1);
-    /*
-        write additional information
-    */
-    pdf_dict_add_img_filename(pdf, idict);
-    if ((suppress_optional_info & 4) == 0) {
-        pdf_dict_add_int(pdf, "PTEX.PageNumber", (int) img_pagenum(idict));
-    }
-    if ((suppress_optional_info & 8) == 0) {
-        infoDict = ppdoc_info(pdfe);
-        if (infoDict != NULL) {
-            /* todo : check this
-                pdf_dict_add_ref(pdf, "PTEX.InfoDict", addInObj(pdf, pdf_doc, infoDict));
-            */
-            pdf_add_name(pdf, "PTEX.InfoDict");
-            copyDict(pdf, pdf_doc, infoDict);
-        }
-    }
-    if (img_is_bbox(idict)) {
-        bbox[0] = sp2bp(img_bbox(idict)[0]);
-        bbox[1] = sp2bp(img_bbox(idict)[1]);
-        bbox[2] = sp2bp(img_bbox(idict)[2]);
-        bbox[3] = sp2bp(img_bbox(idict)[3]);
-    } else {
-        /*
-            get the pagebox coordinates (media, crop,...) to use.
-        */
-        get_pagebox(pageDict, img_pagebox(idict), &pagebox);
-        bbox[0] = pagebox.lx;
-        bbox[1] = pagebox.ly;
-        bbox[2] = pagebox.rx;
-        bbox[3] = pagebox.ry;
-    }
-    pdf_add_name(pdf, "BBox");
-    pdf_begin_array(pdf);
-    pdf_add_real(pdf, bbox[0]);
-    pdf_add_real(pdf, bbox[1]);
-    pdf_add_real(pdf, bbox[2]);
-    pdf_add_real(pdf, bbox[3]);
-    pdf_end_array(pdf);
-    /*
-        Now all relevant parts of the Page dictionary are copied. Metadata validity
-        check is needed(as a stream it must be indirect).
-    */
-    obj = ppdict_get_obj(pageDict, "Metadata");
-    if (obj != NULL && obj->type != PPREF) {
-        formatted_warning("pdf inclusion","/Metadata must be indirect object");
-    }
-    /*
-        copy selected items in Page dictionary
-    */
-    for (i = 0; pagedictkeys[i] != NULL; i++) {
-        obj = ppdict_rget_obj(pageDict, pagedictkeys[i]);
-        if (obj != NULL) {
-            pdf_add_name(pdf, pagedictkeys[i]);
-            /*
-                preserves indirection
-            */
-            copyObject(pdf, pdf_doc, obj);
-        }
-    }
-    resources = ppdict_rget_obj(pageDict, "Resources");
-    if (resources == NULL) {
-        /*
-            If there are no Resources in the Page dict of the embedded page,
-            try to inherit the Resources from the Pages tree of the embedded
-            PDF file, climbing up the tree until the Resources are found.
-            (This fixes a problem with Scribus 1.3.3.14.)
-        */
-            obj = ppdict_rget_obj(pageDict, "Parent");
-            while (obj != NULL && obj->type == PPDICT) {
-                resources = ppdict_rget_obj(obj->dict, "Resources");
-                if (resources != NULL) {
-                    break;
-                }
-                obj = ppdict_get_obj(obj->dict, "Parent");
-            }
-    }
-    if (resources != NULL) {
-        pdf_add_name(pdf, "Resources");
-        copyObject(pdf, pdf_doc, resources);
-    } else {
-        formatted_warning("pdf inclusion","Page /Resources missing");
-    }
-    /*
-        User supplied entries.
-    */
-    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) {
-        pdf_printf(pdf, "\n%s\n", img_attr(idict));
-    }
-    /*
-        Write the Page contents.
-    */
-    content = ppdict_rget_obj(pageDict, "Contents");
-    if (content->type == PPSTREAM) {
-        if (pdf->compress_level == 0 || pdf->recompress) {
-            pdf_dict_add_streaminfo(pdf);
-            pdf_end_dict(pdf);
-            pdf_begin_stream(pdf);
-            copyStreamStream(pdf, content->stream,1); /* decompress */
-        } else {
-            /* copies compressed stream */
-            ppstream * stream = content->stream;
-            ppdict *streamDict = stream->dict; /* */
-            obj = ppdict_rget_obj(streamDict, "Length");
-            if (obj != NULL) {
-                pdf_add_name(pdf, "Length");
-                copyObject(pdf, pdf_doc, obj);
-                obj = ppdict_rget_obj(streamDict, "Filter");
-                if (obj != NULL) {
-                    pdf_add_name(pdf, "Filter");
-                    copyObject(pdf, pdf_doc, obj);
-                    /* the next one is irrelevant, only for inline images: */
-                    /*
-                    obj = ppdict_rget_obj(streamDict, "DecodeParms");
-                    if (obj != NULL) {
-                        pdf_add_name(pdf, "DecodeParms");
-                        copyObject(pdf, pdf_doc, obj);
-                    }
-                    */
-                }
-               pdf_end_dict(pdf);
-                pdf_begin_stream(pdf);
-                copyStreamStream(pdf, stream,0);
-            } else {
-                pdf_dict_add_streaminfo(pdf);
-                pdf_end_dict(pdf);
-                pdf_begin_stream(pdf);
-                copyStreamStream(pdf, stream,1);
-            }
-        }
-        pdf_end_stream(pdf);
-    } else if (content->type == PPARRAY) {
-        /* listens to compresslevel */
-        pdf_dict_add_streaminfo(pdf);
-        pdf_end_dict(pdf);
-        pdf_begin_stream(pdf);
-        {
-            int i;
-            int b = 0;
-            int n = content->array->size;
-            for (i=0; i<n; ++i) {
-                ppobj *o = pparray_at(content->array,i);
-                while (o != NULL && o->type == PPREF) {
-                    o = ppref_obj((ppref *) o->ref);
-                }
-                if (o != NULL && o->type == PPSTREAM) {
-                    if (b) {
-                        /*
-                            Put a space between streams to be on the safe side (streams
-                            should have a trailing space here, but one never knows)
-                        */
-                        pdf_out(pdf, ' ');
-                    } else {
-                        b = 1;
-                    }
-                    copyStreamStream(pdf, (ppstream *) o->stream,1);
-                }
-            }
-        }
-        pdf_end_stream(pdf);
-    } else {
-        /*
-            the contents are optional, but we need to include an empty stream
-        */
-        pdf_dict_add_streaminfo(pdf);
-        pdf_end_dict(pdf);
-        pdf_begin_stream(pdf);
-        pdf_end_stream(pdf);
-    }
-    pdf_end_obj(pdf);
-    /*
-        write out all indirect objects
-    */
-    writeRefs(pdf, pdf_doc);
-    /*
-        unrefPdfDocument() must come after freeing whatever is used
-
-    */
-    if (! img_keepopen(idict)) {
-        unrefPdfDocument(img_filepath(idict));
-    }
-}
-
-/* a special simple case of inclusion, e.g. an appearance stream */
-
-int write_epdf_object(PDF pdf, image_dict * idict, int n)
-{
-    int num = 0 ;
-    if (img_type(idict) != IMG_TYPE_PDF) {
-        normal_error("pdf inclusion","unknown document");
-    } else {
-        PdfDocument * pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict));
-        ppdoc * pdfe = pdf_doc->pdfe;
-        ppref * ref = ppxref_find(ppdoc_xref(pdfe), (ppuint) n);
-        if (ref != NULL) {
-            ppobj *obj;
-            num = pdf->obj_count++;
-            obj = ppref_obj(ref);
-            if (obj->type == PPSTREAM) {
-                pdf_begin_obj(pdf, num, OBJSTM_NEVER);
-            } else {
-                pdf_begin_obj(pdf, num, 2);
-            }
-            copyObject(pdf, pdf_doc, obj);
-            pdf_end_obj(pdf);
-            writeRefs(pdf, pdf_doc);
-        }
-        if (! img_keepopen(idict)) {
-            unrefPdfDocument(img_filepath(idict));
-        }
-    }
-    return num;
-}
-
-/* Deallocate a PdfDocument with all its resources. */
-
-static void deletePdfDocumentPdfDoc(PdfDocument * pdf_doc)
-{
-    InObj *r, *n;
-    /* this may be probably needed for an emergency destroyPdfDocument() */
-    for (r = pdf_doc->inObjList; r != NULL; r = n) {
-        n = r->next;
-        free(r);
-    }
-    if (pdf_doc->pdfe != NULL) {
-        ppdoc_free(pdf_doc->pdfe);
-        pdf_doc->pdfe = NULL;
-    }
-    if (pdf_doc->memstream != NULL) {
-     /* pplib does this: free(pdf_doc->memstream); */
-        pdf_doc->memstream = NULL;
-    }
- /* pdf_doc->pc++; */
-    pdf_doc->pc = 0;
-}
-
-static void destroyPdfDocument(void *pa, void * p)
-{
-    PdfDocument *pdf_doc = (PdfDocument *) pa;
-    deletePdfDocumentPdfDoc(pdf_doc);
-    /* TODO: delete rest of pdf_doc */
-}
-
-/*
-    Called when an image has been written and its resources in image_tab are
-    freed and it's not referenced anymore.
-*/
-
-void unrefPdfDocument(char *file_path)
-{
-    PdfDocument *pdf_doc = findPdfDocument(file_path);
-    if (pdf_doc == NULL) {
-        /* we're ok */
-    } else if (pdf_doc->occurences > 0) {
-        pdf_doc->occurences--;
-        if (pdf_doc->occurences == 0) {
-            deletePdfDocumentPdfDoc(pdf_doc);
-        }
-    } else {
-        /*
-            We either have a mismatch in ref and unref or we're somehow out of sync
-            which can happen when we mess with the same file in lua and tex.
-        */
-        formatted_warning("pdf inclusion","there can be a mismatch in opening and closing file '%s'",file_path);
-    }
-}
-
-/*
-    For completeness, but it isn't currently used (unreferencing is done by mean
-    of file_path.
-*/
-
-void unrefMemStreamPdfDocument(char *file_id)
-{
-  (void) unrefPdfDocument(file_id);
-
-}
-
-/*
-    Called when PDF embedding system is finalized.  We now deallocate all remaining
-    PdfDocuments.
-*/
-
-void epdf_free(void)
-{
-    if (PdfDocumentTree != NULL)
-        avl_destroy(PdfDocumentTree, destroyPdfDocument);
-    PdfDocumentTree = NULL;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/image/pdftoepdf.w
@@ -0,0 +1,972 @@
+% pdftoepdf.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% Copyright 2006-2015 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
+
+#define __STDC_FORMAT_MACROS /* for PRId64 etc.  */
+
+#include "image/epdf.h"
+
+/*
+    This file is mostly C and not very much C++; it's just used to interface
+    the functions of poppler, which happens to be written in C++.
+    Patches for the new poppler 0.59 from
+    https://www.mail-archive.com/arch-commits@archlinux.org/msg357548.html
+    with some modifications to comply the poppler API.
+
+*/
+
+extern void md5(Guchar *msg, int msgLen, Guchar *digest);
+
+static GBool isInit = gFalse;
+
+/* Maintain AVL tree of all PDF files for embedding */
+
+static avl_table *PdfDocumentTree = NULL;
+
+/* AVL sort PdfDocument into PdfDocumentTree by file_path */
+
+static int CompPdfDocument(const void *pa, const void *pb, void * /*p */ )
+{
+    return strcmp(((const PdfDocument *) pa)->file_path, ((const PdfDocument *) pb)->file_path);
+}
+
+/* Returns pointer to PdfDocument structure for PDF file. */
+
+static PdfDocument *findPdfDocument(char *file_path)
+{
+    PdfDocument *pdf_doc, tmp;
+    if (file_path == NULL) {
+        normal_error("pdf backend","empty filename when loading pdf file");
+    } else if (PdfDocumentTree == NULL) {
+        return NULL;
+    }
+    tmp.file_path = file_path;
+    pdf_doc = (PdfDocument *) avl_find(PdfDocumentTree, &tmp);
+    return pdf_doc;
+}
+
+#define PDF_CHECKSUM_SIZE 32
+
+static char *get_file_checksum(const char *a, file_error_mode fe)
+{
+    struct stat finfo;
+    char *ck = NULL;
+    if (stat(a, &finfo) == 0) {
+        off_t size = finfo.st_size;
+        time_t mtime = finfo.st_mtime;
+        ck = (char *) malloc(PDF_CHECKSUM_SIZE);
+        if (ck == NULL)
+            formatted_error("pdf inclusion","out of memory while processing '%s'", a);
+        snprintf(ck, PDF_CHECKSUM_SIZE, "%"@= @>PRIu64@= @>"_%"@=  @>PRIu64, (uint64_t) size,(uint64_t) mtime);
+   } else {
+        switch (fe) {
+            case FE_FAIL:
+                formatted_error("pdf inclusion","could not stat() file '%s'", a);
+                break;
+            case FE_RETURN_NULL:
+                if (ck != NULL)
+                    free(ck);
+                ck = NULL;
+                break;
+            default:
+                assert(0);
+        }
+    }
+    return ck;
+}
+
+
+static char *get_stream_checksum (const char *str, unsigned long long str_size){
+    /* http://www.cse.yorku.ca/~oz/hash.html */
+    /* djb2                                  */
+    unsigned long hash ;
+    char *ck = NULL;
+    unsigned int i;
+    hash = 5381;
+    ck = (char *) malloc(STRSTREAM_CHECKSUM_SIZE+1);
+    if (ck == NULL)
+        normal_error("pdf inclusion","out of memory while processing a memstream");
+    for(i=0; i<(unsigned int)(str_size); i++) {
+        hash = ((hash << 5) + hash) + str[i]; /* hash * 33 + str[i] */
+    }
+    snprintf(ck,STRSTREAM_CHECKSUM_SIZE+1,"%lx",hash);
+    ck[STRSTREAM_CHECKSUM_SIZE]='\0';
+    return ck;
+}
+
+/*
+    Returns pointer to PdfDocument structure for PDF file.
+    Creates a new PdfDocument structure if it doesn't exist yet.
+    When fe = FE_RETURN_NULL, the function returns NULL in error case.
+*/
+
+PdfDocument *refPdfDocument(const char *file_path, file_error_mode fe)
+{
+    char *checksum, *path_copy;
+    PdfDocument *pdf_doc;
+    PDFDoc *doc = NULL;
+    GooString *docName = NULL;
+    int new_flag = 0;
+    if ((checksum = get_file_checksum(file_path, fe)) == NULL) {
+        return (PdfDocument *) NULL;
+    }
+    path_copy = xstrdup(file_path);
+    if ((pdf_doc = findPdfDocument(path_copy)) == NULL) {
+        new_flag = 1;
+        pdf_doc = new PdfDocument;
+        pdf_doc->file_path = path_copy;
+        pdf_doc->checksum = checksum;
+        pdf_doc->doc = NULL;
+        pdf_doc->inObjList = NULL;
+        pdf_doc->ObjMapTree = NULL;
+        pdf_doc->occurences = 0; /* 0 = unreferenced */
+        pdf_doc->pc = 0;
+    } else {
+        if (strncmp(pdf_doc->checksum, checksum, PDF_CHECKSUM_SIZE) != 0) {
+            formatted_error("pdf inclusion","file has changed '%s'", file_path);
+        }
+        free(checksum);
+        free(path_copy);
+    }
+    if (pdf_doc->doc == NULL) {
+        docName = new GooString(file_path);
+        doc = new PDFDoc(docName); /* takes ownership of docName */
+        pdf_doc->pc++;
+
+        if (!doc->isOk() || !doc->okToPrint()) {
+            switch (fe) {
+            case FE_FAIL:
+                normal_error("pdf inclusion","reading image failed");
+                break;
+            case FE_RETURN_NULL:
+                delete doc;
+                /* delete docName */
+                if (new_flag == 1) {
+                    if (pdf_doc->file_path != NULL)
+                        free(pdf_doc->file_path);
+                    if (pdf_doc->checksum != NULL)
+                        free(pdf_doc->checksum);
+                    delete pdf_doc;
+                }
+                return (PdfDocument *) NULL;
+                break;
+            default:
+                assert(0);
+            }
+        }
+        pdf_doc->doc = doc;
+    }
+    /* PDF file could be opened without problems, checksum ok. */
+    if (PdfDocumentTree == NULL)
+        PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator);
+    if ((PdfDocument *) avl_find(PdfDocumentTree, pdf_doc) == NULL) {
+        avl_probe(PdfDocumentTree, pdf_doc);
+    }
+    pdf_doc->occurences++;
+    return pdf_doc;
+}
+
+/*
+    Returns pointer to PdfDocument structure for a PDF stream in memory of streamsize
+    dimension. As before, creates a new PdfDocument structure if it doesn't exist yet
+    with file_path = file_id
+*/
+
+PdfDocument *refMemStreamPdfDocument(char *docstream, unsigned long long streamsize,const char *file_id)
+{
+    char *checksum;
+    char *file_path;
+    PdfDocument *pdf_doc;
+    PDFDoc *doc = NULL;
+    Object obj;
+    MemStream *docmemstream = NULL;
+    /*int new_flag = 0;*/
+    size_t  cnt = 0;
+    checksum = get_stream_checksum(docstream, streamsize);
+    cnt = strlen(file_id);
+    assert(cnt>0 && cnt <STREAM_FILE_ID_LEN);
+    file_path = (char *) malloc(cnt+STREAM_URI_LEN+STRSTREAM_CHECKSUM_SIZE+1); /* 1 for \0 */
+    assert(file_path != NULL);
+    strcpy(file_path,STREAM_URI);
+    strcat(file_path,file_id);
+    strcat(file_path,checksum);
+    file_path[cnt+STREAM_URI_LEN+STRSTREAM_CHECKSUM_SIZE]='\0';
+    if ((pdf_doc = findPdfDocument(file_path)) == NULL) {
+        /*new_flag = 1;*/
+        pdf_doc = new PdfDocument;
+        pdf_doc->file_path = file_path;
+        pdf_doc->checksum = checksum;
+        pdf_doc->doc = NULL;
+        pdf_doc->inObjList = NULL;
+        pdf_doc->ObjMapTree = NULL;
+        pdf_doc->occurences = 0; /* 0 = unreferenced */
+        pdf_doc->pc = 0;
+    } else {
+        /* As is now, checksum is in file_path, so this check should be useless. */
+        if (strncmp(pdf_doc->checksum, checksum, STRSTREAM_CHECKSUM_SIZE) != 0) {
+            formatted_error("pdf inclusion","stream has changed '%s'", file_path);
+        }
+        free(file_path);
+        free(checksum);
+    }
+    if (pdf_doc->doc == NULL) {
+        docmemstream = new MemStream( docstream,0,streamsize, Object(objNull) );
+        doc = new PDFDoc(docmemstream); /* takes ownership of docmemstream */
+        pdf_doc->pc++;
+        if (!doc->isOk() || !doc->okToPrint()) {
+            normal_error("pdf inclusion","reading pdf Stream failed");
+    }
+        pdf_doc->doc = doc;
+    }
+    /* PDF file could be opened without problems, checksum ok. */
+    if (PdfDocumentTree == NULL)
+        PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator);
+    if ((PdfDocument *) avl_find(PdfDocumentTree, pdf_doc) == NULL) {
+        avl_probe(PdfDocumentTree, pdf_doc);
+    }
+    pdf_doc->occurences++;
+    return pdf_doc;
+}
+
+/*
+    AVL sort ObjMap into ObjMapTree by object number and generation keep the ObjMap
+    struct small, as these are accumulated until the end
+*/
+
+struct ObjMap {
+    Ref in;
+    int out_num;
+};
+
+static int CompObjMap(const void *pa, const void *pb, void * /*p */ )
+{
+    const Ref *a = &(((const ObjMap *) pa)->in);
+    const Ref *b = &(((const ObjMap *) pb)->in);
+    if (a->num > b->num)
+        return 1;
+    else if (a->num < b->num)
+        return -1;
+    else if (a->gen == b->gen)
+        return 0;
+    else if (a->gen < b->gen)
+        return -1;
+    return 1;
+}
+
+static ObjMap *findObjMap(PdfDocument * pdf_doc, Ref in)
+{
+    ObjMap *obj_map, tmp;
+    if (pdf_doc->ObjMapTree == NULL)
+        return NULL;
+    tmp.in = in;
+    obj_map = (ObjMap *) avl_find(pdf_doc->ObjMapTree, &tmp);
+    return obj_map;
+}
+
+static void addObjMap(PdfDocument * pdf_doc, Ref in, int out_num)
+{
+    ObjMap *obj_map = NULL;
+    if (pdf_doc->ObjMapTree == NULL)
+        pdf_doc->ObjMapTree = avl_create(CompObjMap, NULL, &avl_xallocator);
+    obj_map = new ObjMap;
+    obj_map->in = in;
+    obj_map->out_num = out_num;
+    avl_probe(pdf_doc->ObjMapTree, obj_map);
+}
+
+/*
+    When copying the Resources of the selected page, all objects are
+    copied recursively top-down.  The findObjMap() function checks if an
+    object has already been copied; if so, instead of copying just the
+    new object number will be referenced.  The ObjMapTree guarantees,
+    that during the entire LuaTeX run any object from any embedded PDF
+    file will end up max. once in the output PDF file.  Indirect objects
+    are not fetched during copying, but get a new object number from
+    LuaTeX and then will be appended into a linked list.
+*/
+
+static int addInObj(PDF pdf, PdfDocument * pdf_doc, Ref ref)
+{
+    ObjMap *obj_map;
+    InObj *p, *q, *n;
+    if (ref.num == 0) {
+        normal_error("pdf inclusion","reference to invalid object (broken pdf)");
+    }
+    if ((obj_map = findObjMap(pdf_doc, ref)) != NULL)
+        return obj_map->out_num;
+    n = new InObj;
+    n->ref = ref;
+    n->next = NULL;
+    n->num = pdf_create_obj(pdf, obj_type_others, 0);
+    addObjMap(pdf_doc, ref, n->num);
+    if (pdf_doc->inObjList == NULL) {
+        pdf_doc->inObjList = n;
+    } else {
+        /*
+            It is important to add new objects at the end of the list,
+            because new objects are being added while the list is being
+            written out by writeRefs().
+        */
+        for (p = pdf_doc->inObjList; p != NULL; p = p->next)
+            q = p;
+        q->next = n;
+    }
+    return n->num;
+}
+
+/*
+    Function converts double to pdffloat; very small and very large numbers
+    are NOT converted to scientific notation. Here n must be a number or real
+    conforming to the implementation limits of PDF as specified in appendix C.1
+    of the PDF Ref. These are:
+
+    maximum value of ints is +2^32
+    maximum value of reals is +2^15
+    smalles values of reals is 1/(2^16)
+*/
+
+static pdffloat conv_double_to_pdffloat(double n)
+{
+    pdffloat a;
+    a.e = 6;
+    a.m = i64round(n * ten_pow[a.e]);
+    return a;
+}
+
+static void copyObject(PDF, PdfDocument *, Object *);
+
+void copyReal(PDF pdf, double d)
+{
+    if (pdf->cave)
+        pdf_out(pdf, ' ');
+    print_pdffloat(pdf, conv_double_to_pdffloat(d));
+    pdf->cave = true;
+}
+
+static void copyString(PDF pdf, GooString * string)
+{
+    char *p;
+    unsigned char c;
+    size_t i, l;
+    p = string->getCString();
+    l = (size_t) string->getLength();
+    if (pdf->cave)
+        pdf_out(pdf, ' ');
+    if (strlen(p) == l) {
+        pdf_out(pdf, '(');
+        for (; *p != 0; p++) {
+            c = (unsigned char) *p;
+            if (c == '(' || c == ')' || c == '\\')
+                pdf_printf(pdf, "\\%c", c);
+            else if (c < 0x20 || c > 0x7F)
+                pdf_printf(pdf, "\\%03o", (int) c);
+            else
+                pdf_out(pdf, c);
+        }
+        pdf_out(pdf, ')');
+    } else {
+        pdf_out(pdf, '<');
+        for (i = 0; i < l; i++) {
+            c = (unsigned char) string->getChar(i);
+            pdf_printf(pdf, "%.2x", (int) c);
+        }
+        pdf_out(pdf, '>');
+    }
+    pdf->cave = true;
+}
+
+static void copyName(PDF pdf, char *s)
+{
+    pdf_out(pdf, '/');
+    for (; *s != 0; s++) {
+        if (isdigit(*s) || isupper(*s) || islower(*s) || *s == '_' ||
+            *s == '.' || *s == '-' || *s == '+')
+            pdf_out(pdf, *s);
+        else
+            pdf_printf(pdf, "#%.2X", *s & 0xFF);
+    }
+    pdf->cave = true;
+}
+
+static void copyArray(PDF pdf, PdfDocument * pdf_doc, Array * array)
+{
+    int i, l;
+    Object obj1;
+    pdf_begin_array(pdf);
+    for (i = 0, l = array->getLength(); i < l; ++i) {
+        obj1 = array->getNF(i);
+        copyObject(pdf, pdf_doc, &obj1);
+    }
+    pdf_end_array(pdf);
+}
+
+static void copyDict(PDF pdf, PdfDocument * pdf_doc, Dict * dict)
+{
+    int i, l;
+    Object obj1;
+    pdf_begin_dict(pdf);
+    for (i = 0, l = dict->getLength(); i < l; ++i) {
+        copyName(pdf, dict->getKey(i));
+        obj1 = dict->getValNF(i);
+        copyObject(pdf, pdf_doc, &obj1);
+    }
+    pdf_end_dict(pdf);
+}
+
+static void copyStreamStream(PDF pdf, Stream * str)
+{
+    int c, i, len = 1024;
+    str->reset();
+    i = len;
+    while ((c = str->getChar()) != EOF) {
+        if (i == len) {
+            pdf_room(pdf, len);
+            i = 0;
+        }
+        pdf_quick_out(pdf, c);
+        i++;
+    }
+}
+
+static void copyStream(PDF pdf, PdfDocument * pdf_doc, Stream * stream)
+{
+    copyDict(pdf, pdf_doc, stream->getDict());
+    pdf_begin_stream(pdf);
+    copyStreamStream(pdf, stream->getUndecodedStream());
+    pdf_end_stream(pdf);
+}
+
+static void copyObject(PDF pdf, PdfDocument * pdf_doc, Object * obj)
+{
+    switch (obj->getType()) {
+    case objBool:
+        pdf_add_bool(pdf, (int) obj->getBool());
+        break;
+    case objInt:
+        pdf_add_int(pdf, obj->getInt());
+        break;
+    case objReal:
+        copyReal(pdf, obj->getReal());
+        break;
+    /*
+    case objNum:
+        GBool isNum() { return type == objInt || type == objReal; }
+        break;
+    */
+    case objString:
+        copyString(pdf, (GooString *)obj->getString());
+        break;
+    case objName:
+        copyName(pdf, (char *)obj->getName());
+        break;
+    case objNull:
+        pdf_add_null(pdf);
+        break;
+    case objArray:
+        copyArray(pdf, pdf_doc, obj->getArray());
+        break;
+    case objDict:
+        copyDict(pdf, pdf_doc, obj->getDict());
+        break;
+    case objStream:
+        copyStream(pdf, pdf_doc, obj->getStream());
+        break;
+    case objRef:
+        pdf_add_ref(pdf, addInObj(pdf, pdf_doc, obj->getRef()));
+        break;
+    case objCmd:
+    case objError:
+    case objEOF:
+    case objNone:
+        formatted_error("pdf inclusion","type '%s' cannot be copied", obj->getTypeName());
+        break;
+    default:
+        /* poppler doesn't have any other types */
+        assert(0);
+    }
+}
+
+static void writeRefs(PDF pdf, PdfDocument * pdf_doc)
+{
+    InObj *r, *n;
+    Object obj1;
+    XRef *xref;
+    PDFDoc *doc = pdf_doc->doc;
+    xref = doc->getXRef();
+    for (r = pdf_doc->inObjList; r != NULL;) {
+        obj1 = xref->fetch(r->ref.num, r->ref.gen);
+        if (obj1.isStream())
+            pdf_begin_obj(pdf, r->num, OBJSTM_NEVER);
+        else
+            pdf_begin_obj(pdf, r->num, 2);
+        copyObject(pdf, pdf_doc, &obj1);
+        pdf_end_obj(pdf);
+        n = r->next;
+        delete r;
+        pdf_doc->inObjList = r = n;
+    }
+}
+
+/* get the pagebox coordinates according to the pagebox_spec */
+
+static PDFRectangle *get_pagebox(Page * page, int pagebox_spec)
+{
+    switch (pagebox_spec) {
+        case PDF_BOX_SPEC_MEDIA:
+            return page->getMediaBox();
+            break;
+        case PDF_BOX_SPEC_CROP:
+            return page->getCropBox();
+            break;
+        case PDF_BOX_SPEC_BLEED:
+            return page->getBleedBox();
+            break;
+        case PDF_BOX_SPEC_TRIM:
+            return page->getTrimBox();
+            break;
+        case PDF_BOX_SPEC_ART:
+            return page->getArtBox();
+            break;
+        default:
+            return page->getMediaBox();
+            break;
+    }
+}
+
+/*
+    Reads various information about the PDF and sets it up for later inclusion.
+    This will fail if the PDF version of the PDF is higher than minor_pdf_version_wanted
+    or page_name is given and can not be found. It makes no sense to give page_name and
+    page_num. Returns the page number.
+*/
+
+void flush_pdf_info(image_dict * idict)
+{
+    if (img_keepopen(idict)) {
+        unrefPdfDocument(img_filepath(idict));
+    }
+}
+
+/*
+    void flush_pdfstream_info(image_dict * idict)
+    {
+        if (img_pdfstream_ptr(idict) != NULL) {
+            xfree(img_pdfstream_stream(idict));
+            xfree(img_pdfstream_ptr(idict));
+            img_pdfstream_stream(idict) = NULL;
+            img_pdfstream_ptr(idict) = NULL;
+        }
+    }
+*/
+
+void read_pdf_info(image_dict * idict)
+{
+    PdfDocument *pdf_doc = NULL;
+    PDFDoc *doc = NULL;
+    Catalog *catalog;
+    Page *page;
+    int rotate;
+    PDFRectangle *pagebox;
+    int pdf_major_version_found, pdf_minor_version_found;
+    float xsize, ysize, xorig, yorig;
+    if (isInit == gFalse) {
+        if (!(globalParams))
+            globalParams = new GlobalParams();
+        globalParams->setErrQuiet(gFalse);
+        isInit = gTrue;
+    }
+    if (img_type(idict) == IMG_TYPE_PDF)
+        pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL);
+    else if (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) {
+        pdf_doc = findPdfDocument(img_filepath(idict)) ;
+        if (pdf_doc == NULL )
+           normal_error("pdf inclusion", "memstream not initialized");
+        if (pdf_doc->doc == NULL)
+           normal_error("pdf inclusion", "memstream document is empty");
+        pdf_doc->occurences++;
+    } else {
+        normal_error("pdf inclusion","unknown document");
+    }
+    doc = pdf_doc->doc;
+    catalog = doc->getCatalog();
+    /*
+        Check PDF version. This works only for PDF 1.x but since any versions of
+        PDF newer than 1.x will not be backwards compatible to PDF 1.x, we will
+        then have to changed drastically anyway.
+    */
+    pdf_major_version_found = doc->getPDFMajorVersion();
+    pdf_minor_version_found = doc->getPDFMinorVersion();
+    if ((100 * pdf_major_version_found + pdf_major_version_found) > (100 * img_pdfmajorversion(idict) + img_pdfminorversion(idict))) {
+        const char *msg = "PDF inclusion: found PDF version '%d.%d', but at most version '%d.%d' allowed";
+        if (img_errorlevel(idict) > 0) {
+            formatted_error("pdf inclusion",msg, pdf_major_version_found, pdf_minor_version_found, img_pdfmajorversion(idict), img_pdfminorversion(idict));
+        } else {
+            formatted_warning("pdf inclusion",msg, pdf_major_version_found, pdf_minor_version_found, img_pdfmajorversion(idict), img_pdfminorversion(idict));
+        }
+    }
+    img_totalpages(idict) = catalog->getNumPages();
+    if (img_pagename(idict)) {
+        /* get page by name */
+        GooString name(img_pagename(idict));
+        LinkDest *link = doc->findDest(&name);
+        if (link == NULL || !link->isOk())
+            formatted_error("pdf inclusion","invalid destination '%s'",img_pagename(idict));
+        Ref ref = link->getPageRef();
+        img_pagenum(idict) = catalog->findPage(ref.num, ref.gen);
+        if (img_pagenum(idict) == 0)
+            formatted_error("pdf inclusion","destination is not a page '%s'",img_pagename(idict));
+        delete link;
+    } else {
+        /* get page by number */
+        if (img_pagenum(idict) <= 0
+            || img_pagenum(idict) > img_totalpages(idict))
+            formatted_error("pdf inclusion","required page '%i' does not exist",(int) img_pagenum(idict));
+    }
+    /* get the required page */
+    page = catalog->getPage(img_pagenum(idict));
+    /* get the pagebox coordinates (media, crop,...) to use. */
+    pagebox = get_pagebox(page, img_pagebox(idict));
+    if (pagebox->x2 > pagebox->x1) {
+        xorig = pagebox->x1;
+        xsize = pagebox->x2 - pagebox->x1;
+    } else {
+        xorig = pagebox->x2;
+        xsize = pagebox->x1 - pagebox->x2;
+    }
+    if (pagebox->y2 > pagebox->y1) {
+        yorig = pagebox->y1;
+        ysize = pagebox->y2 - pagebox->y1;
+    } else {
+        yorig = pagebox->y2;
+        ysize = pagebox->y1 - pagebox->y2;
+    }
+    /* The following 4 parameters are raw. Do _not_ modify by /Rotate! */
+    img_xsize(idict) = bp2sp(xsize);
+    img_ysize(idict) = bp2sp(ysize);
+    img_xorig(idict) = bp2sp(xorig);
+    img_yorig(idict) = bp2sp(yorig);
+    /*
+        Handle /Rotate parameter. Only multiples of 90 deg. are allowed (PDF Ref. v1.3,
+        p. 78). We also accept negative angles. Beware: PDF counts clockwise! */
+    rotate = page->getRotate();
+    switch (((rotate % 360) + 360) % 360) {
+        case 0:
+            img_rotation(idict) = 0;
+            break;
+        case 90:
+            img_rotation(idict) = 3;
+            break;
+        case 180:
+            img_rotation(idict) = 2;
+            break;
+        case 270:
+            img_rotation(idict) = 1;
+            break;
+        default:
+            formatted_warning("pdf inclusion","/Rotate parameter in PDF file not multiple of 90 degrees");
+    }
+    /* currently unused info whether PDF contains a /Group */
+    if (page->getGroup() != NULL)
+        img_set_group(idict);
+    /*
+        LuaTeX pre 0.85 versions did this:
+
+        if (readtype == IMG_CLOSEINBETWEEN) {
+            unrefPdfDocument(img_filepath(idict));
+        }
+
+        and also unref'd in the finalizer so we got an extra unrefs when garbage was
+        collected. However it is more efficient to keep the file open so we do that
+        now. The (slower) alternative is to unref here (which in most cases forcing a
+        close of the file) but then we must not call flush_pdf_info.
+
+        A close (unref) can be forced by nilling the dict object at the lua end and
+        forcing a collectgarbage("collect") after that.
+
+    */
+    if (! img_keepopen(idict)) {
+        unrefPdfDocument(img_filepath(idict));
+    }
+}
+
+/*
+    Write the current epf_doc. Here the included PDF is copied, so most errors
+    that can happen during PDF inclusion will arise here.
+*/
+
+void write_epdf(PDF pdf, image_dict * idict, int suppress_optional_info)
+{
+    PdfDocument *pdf_doc = NULL;
+    PDFDoc *doc = NULL;
+    Catalog *catalog;
+    Page *page;
+    Ref *pageref;
+    Dict *pageDict;
+    Object obj1, contents, pageobj, pagesobj1, pagesobj2, *op1, *op2, *optmp;
+    PDFRectangle *pagebox;
+    int i, l;
+    double bbox[4];
+    /* char s[256]; */
+    const char *pagedictkeys[] = {
+        "Group", "LastModified", "Metadata", "PieceInfo", "Resources", "SeparationInfo", NULL
+    };
+    /* open PDF file */
+    if (img_type(idict) == IMG_TYPE_PDF) {
+        pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL);
+    } else if (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) {
+        pdf_doc = findPdfDocument(img_filepath(idict)) ;
+        pdf_doc->occurences++;
+    } else {
+        normal_error("pdf inclusion","unknown document");
+    }
+    doc = pdf_doc->doc;
+    catalog = doc->getCatalog();
+    page = catalog->getPage(img_pagenum(idict));
+    pageref = catalog->getPageRef(img_pagenum(idict));
+    pageobj = doc->getXRef()->fetch(pageref->num, pageref->gen);
+    pageDict = pageobj.getDict();
+    /* write the Page header */
+    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "XObject");
+    pdf_dict_add_name(pdf, "Subtype", "Form");
+    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) {
+        pdf_printf(pdf, "\n%s\n", img_attr(idict));
+    }
+    pdf_dict_add_int(pdf, "FormType", 1);
+    /* write additional information */
+    pdf_dict_add_img_filename(pdf, idict);
+    if ((suppress_optional_info & 4) == 0) {
+        pdf_dict_add_int(pdf, "PTEX.PageNumber", (int) img_pagenum(idict));
+    }
+    if ((suppress_optional_info & 8) == 0) {
+        obj1 = doc->getDocInfoNF();
+        if (obj1.isRef()) {
+            /* the info dict must be indirect (PDF Ref p. 61) */
+            pdf_dict_add_ref(pdf, "PTEX.InfoDict", addInObj(pdf, pdf_doc, obj1.getRef()));
+        }
+    }
+    if (img_is_bbox(idict)) {
+        bbox[0] = sp2bp(img_bbox(idict)[0]);
+        bbox[1] = sp2bp(img_bbox(idict)[1]);
+        bbox[2] = sp2bp(img_bbox(idict)[2]);
+        bbox[3] = sp2bp(img_bbox(idict)[3]);
+    } else {
+        /* get the pagebox coordinates (media, crop,...) to use. */
+        pagebox = get_pagebox(page, img_pagebox(idict));
+        bbox[0] = pagebox->x1;
+        bbox[1] = pagebox->y1;
+        bbox[2] = pagebox->x2;
+        bbox[3] = pagebox->y2;
+    }
+    pdf_add_name(pdf, "BBox");
+    pdf_begin_array(pdf);
+    copyReal(pdf, bbox[0]);
+    copyReal(pdf, bbox[1]);
+    copyReal(pdf, bbox[2]);
+    copyReal(pdf, bbox[3]);
+    pdf_end_array(pdf);
+    /*
+        Now all relevant parts of the Page dictionary are copied. Metadata validity
+        check is needed(as a stream it must be indirect).
+    */
+    obj1 = pageDict->lookupNF("Metadata");
+    if (!obj1.isNull() && !obj1.isRef())
+        formatted_warning("pdf inclusion","/Metadata must be indirect object");
+    /* copy selected items in Page dictionary */
+    for (i = 0; pagedictkeys[i] != NULL; i++) {
+        obj1 = pageDict->lookupNF(pagedictkeys[i]);
+        if (!obj1.isNull()) {
+            pdf_add_name(pdf, pagedictkeys[i]);
+            /* preserves indirection */
+            copyObject(pdf, pdf_doc, &obj1);
+        }
+    }
+    /*
+        If there are no Resources in the Page dict of the embedded page,
+        try to inherit the Resources from the Pages tree of the embedded
+        PDF file, climbing up the tree until the Resources are found.
+        (This fixes a problem with Scribus 1.3.3.14.)
+    */
+    obj1 = pageDict->lookupNF("Resources");
+    if (obj1.isNull()) {
+        op1 = &pagesobj1;
+        op2 = &pagesobj2;
+        *op1 = pageDict->lookup("Parent");
+        while (op1->isDict()) {
+            obj1 = op1->dictLookupNF("Resources");
+            if (!obj1.isNull()) {
+                pdf_add_name(pdf, "Resources");
+                copyObject(pdf, pdf_doc, &obj1);
+                break;
+            }
+            *op2 = op1->dictLookup("Parent");
+            optmp = op1;
+            op1 = op2;
+            op2 = optmp;
+        };
+        if (!op1->isDict())
+            formatted_warning("pdf inclusion","Page /Resources missing");
+    }
+    /* Write the Page contents. */
+    contents = page->getContents();
+    if (contents.isStream()) {
+        /*
+            Variant A: get stream and recompress under control of \pdfcompresslevel
+
+            pdf_begin_stream();
+            copyStreamStream(contents->getStream());
+            pdf_end_stream();
+
+            Variant B: copy stream without recompressing
+        */
+        obj1 = contents.streamGetDict()->lookup("F");
+        if (!obj1.isNull()) {
+            normal_error("pdf inclusion","unsupported external stream");
+        }
+        obj1 = contents.streamGetDict()->lookup("Length");
+        pdf_add_name(pdf, "Length");
+        copyObject(pdf, pdf_doc, &obj1);
+        obj1 = contents.streamGetDict()->lookup("Filter");
+        if (!obj1.isNull()) {
+            pdf_add_name(pdf, "Filter");
+            copyObject(pdf, pdf_doc, &obj1);
+            obj1 = contents.streamGetDict()->lookup("DecodeParms");
+            if (!obj1.isNull()) {
+                pdf_add_name(pdf, "DecodeParms");
+                copyObject(pdf, pdf_doc, &obj1);
+            }
+        }
+        pdf_end_dict(pdf);
+        pdf_begin_stream(pdf);
+        copyStreamStream(pdf, contents.getStream()->getUndecodedStream());
+        pdf_end_stream(pdf);
+        pdf_end_obj(pdf);
+    } else if (contents.isArray()) {
+        pdf_dict_add_streaminfo(pdf);
+        pdf_end_dict(pdf);
+        pdf_begin_stream(pdf);
+        for (i = 0, l = contents.arrayGetLength(); i < l; ++i) {
+            obj1 = contents.arrayGet(i);
+            copyStreamStream(pdf, obj1.getStream());
+            if (i < (l - 1)) {
+                /*
+                    Put a space between streams to be on the safe side (streams
+                    should have a trailing space here, but one never knows)
+                */
+                pdf_out(pdf, ' ');
+            }
+        }
+        pdf_end_stream(pdf);
+        pdf_end_obj(pdf);
+    } else {
+        /* the contents are optional, but we need to include an empty stream */
+        pdf_dict_add_streaminfo(pdf);
+        pdf_end_dict(pdf);
+        pdf_begin_stream(pdf);
+        pdf_end_stream(pdf);
+        pdf_end_obj(pdf);
+    }
+    /* write out all indirect objects */
+    writeRefs(pdf, pdf_doc);
+    /*
+        unrefPdfDocument() must come after contents.free() and pageobj.free()!
+        TH: The next line makes repeated pdf inclusion unacceptably slow
+
+        unrefPdfDocument(img_filepath(idict));
+    */
+
+if (! img_keepopen(idict)) {
+    unrefPdfDocument(img_filepath(idict));
+}
+
+
+}
+
+/* Deallocate a PdfDocument with all its resources. */
+
+static void deletePdfDocumentPdfDoc(PdfDocument * pdf_doc)
+{
+    InObj *r, *n;
+    /* this may be probably needed for an emergency destroyPdfDocument() */
+    for (r = pdf_doc->inObjList; r != NULL; r = n) {
+        n = r->next;
+        delete r;
+    }
+    delete pdf_doc->doc;
+    pdf_doc->doc = NULL;
+    pdf_doc->pc++;
+}
+
+static void destroyPdfDocument(void *pa, void * /*pb */ )
+{
+    PdfDocument *pdf_doc = (PdfDocument *) pa;
+    deletePdfDocumentPdfDoc(pdf_doc);
+    /* TODO: delete rest of pdf_doc */
+}
+
+/*
+    Called when an image has been written and its resources in image_tab are
+    freed and it's not referenced anymore.
+*/
+
+void unrefPdfDocument(char *file_path)
+{
+    PdfDocument *pdf_doc = findPdfDocument(file_path);
+    if (pdf_doc->occurences > 0) {
+        pdf_doc->occurences--;
+        if (pdf_doc->occurences == 0) {
+            deletePdfDocumentPdfDoc(pdf_doc);
+        }
+    } else {
+        /*
+            We either have a mismatch in ref and unref or we're somehow out of sync
+            which can happen when we mess with the same file in lua and tex.
+        */
+        formatted_warning("pdf inclusion","there can be a mismatch in opening and closing file '%s'",file_path);
+    }
+}
+
+/*
+    For completeness, but it isn't currently used (unreferencing is done by mean
+    of file_path.
+*/
+
+void unrefMemStreamPdfDocument(char *file_id)
+{
+  (void) unrefPdfDocument(file_id);
+
+}
+
+/*
+    Called when PDF embedding system is finalized.  We now deallocate all remaining
+    PdfDocuments.
+*/
+
+void epdf_free()
+{
+    if (PdfDocumentTree != NULL)
+        avl_destroy(PdfDocumentTree, destroyPdfDocument);
+    PdfDocumentTree = NULL;
+    if (isInit == gTrue)
+        delete globalParams;
+    isInit = gFalse;
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/image/writeimg.w
@@ -0,0 +1,782 @@
+% writeimg.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% Copyright 2006-2012 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/>.
+
+@* Image inclusion.
+
+@ @c
+#include "ptexlib.h"
+#include <kpathsea/c-auto.h>
+#include <kpathsea/c-memstr.h>
+
+@ @c
+#include "image/image.h"
+#include "image/writejpg.h"
+#include "image/writejp2.h"
+#include "image/writepng.h"
+#include "image/writejbig2.h"
+
+#include "lua.h"          /* for |LUA_NOREF| */
+#include "lauxlib.h"
+
+@* Patch ImageTypeDetection 2003/02/08 by Heiko Oberdiek.
+
+Function |readimage| performs some basic initializations. Then it looks at the
+file extension to determine the image type and calls specific code/functions. The
+main disadvantage is that standard file extensions have to be used, otherwise
+pdfTeX is not able to detect the correct image type. The patch now looks at the
+file header first regardless of the file extension. This is implemented in
+function |check_type_by_header|. If this check fails, the traditional test of
+standard file extension is tried, done in function |check_type_by_extension|.
+
+Magic headers:
+
+* "PNG (Portable Network Graphics) Specification", Version 1.2
+  (http://www.libpng.org/pub/png):
+
+   3.1. PNG file signature
+
+      The first eight bytes of a PNG file always contain the following
+      (decimal) values: 137 80 78 71 13 10 26 10
+
+Translation to C: |"\x89PNG\r\n\x1A\n"|
+
+* "JPEG File Interchange Format", Version 1.02:
+
+  * you can identify a JFIF file by looking for the following sequence:
+    X'FF', SOI X'FF', APP0, <2 bytes to be skipped>, "JFIF", X'00'.
+
+Function |check_type_by_header| only looks at the first two bytes: |"\xFF\xD8"|
+
+* ISO/IEC JTC 1/SC 29/WG 1
+  (ITU-T SG8)
+  Coding of Still Pictures
+  Title: 14492 FCD
+  Source: JBIG Committee
+  Project: JTC 1.29.10
+  Status: Final Committee Draft
+
+ D.4.1, ID string
+
+ This is an 8-byte sequence containing 0x97 0x4A 0x42 0x32 0x0D 0x0A 0x1A 0x0A.
+
+* "PDF Reference", third edition:
+
+  * The first line should contain \%PDF-1.0 -- \%PDF-1.4 (section 3.4.1 "File Header").
+  * The "implementation notes" say:
+
+   3.4.1,  File Header
+     12. Acrobat viewers require only that the header appear somewhere within the
+         first 1024 bytes of the file.
+     13. Acrobat viewers will also accept a header of the form \%!PS-Adobe-N.n PDF-M.m
+
+The check in function |check_type_by_header| only implements the first issue. The
+implementation notes are not considered. Therefore files with garbage at start of
+file must have the standard extension.
+
+Functions |check_type_by_header| and |check_type_by_extension|: |img_type(img)|
+is set to |IMG_TYPE_NONE| by |new_image_dict()|. Both functions try to detect a
+type and set |img_type(img)|. Thus a value other than |IMG_TYPE_NONE| indicates
+that a type has been found.
+
+@c
+#define HEADER_JPG "\xFF\xD8"
+#define HEADER_PNG "\x89PNG\r\n\x1A\n"
+#define HEADER_JBIG2 "\x97\x4A\x42\x32\x0D\x0A\x1A\x0A"
+#define HEADER_JP2 "\x6A\x50\x20\x20"
+#define HEADER_PDF "%PDF-"
+#define MAX_HEADER (sizeof(HEADER_PNG)-1)
+#define HEADER_PDF_MEMSTREAM "data:application/pdf," /* see epdf.h */
+#define LEN_PDF_MEMSTREAM 21 /* see epdf.h */
+
+static void check_type_by_header(image_dict * idict)
+{
+    int i;
+    FILE *file = NULL;
+    char header[MAX_HEADER];
+    char prefix[LEN_PDF_MEMSTREAM+1];
+    if (idict == NULL)
+        return;
+    if (img_type(idict) != IMG_TYPE_NONE)
+        return;
+    /* here we read the and also check for a memstream object */
+    if (!img_filepath(idict) || !FOPEN_RBIN_MODE) {
+        normal_error("pdf backend","reading image file failed");
+    }
+    file = fopen(img_filepath(idict), FOPEN_RBIN_MODE);
+    if (file == NULL) {
+        /* check the prefix of img_filepath(idict) */
+        for (i = 0; (unsigned) i < LEN_PDF_MEMSTREAM; i++) {
+            prefix[i] = (char) (img_filepath(idict)[i]);
+        }
+        prefix[LEN_PDF_MEMSTREAM]='\0';
+        if (strncmp(prefix, HEADER_PDF_MEMSTREAM, LEN_PDF_MEMSTREAM) == 0) {
+            img_type(idict) = IMG_TYPE_PDFMEMSTREAM;
+            return;
+        } else {
+            formatted_error("pdf backend","reading image file '%s' failed",img_filepath(idict));
+        }
+    }
+    /* a valid file, but perhaps unsupported */
+    for (i = 0; (unsigned) i < MAX_HEADER; i++) {
+        header[i] = (char) xgetc(file);
+        if (feof(file)) {
+            normal_error("pdf backend","reading image file failed");
+        }
+    }
+    xfclose(file, img_filepath(idict));
+    /* tests */
+    if (strncmp(header, HEADER_JPG, sizeof(HEADER_JPG) - 1) == 0)
+        img_type(idict) = IMG_TYPE_JPG;
+    else if (strncmp(header + 4, HEADER_JP2, sizeof(HEADER_JP2) - 1) == 0)
+        img_type(idict) = IMG_TYPE_JP2;
+    else if (strncmp(header, HEADER_PNG, sizeof(HEADER_PNG) - 1) == 0)
+        img_type(idict) = IMG_TYPE_PNG;
+    else if (strncmp(header, HEADER_JBIG2, sizeof(HEADER_JBIG2) - 1) == 0)
+        img_type(idict) = IMG_TYPE_JBIG2;
+    else if (strncmp(header, HEADER_PDF, sizeof(HEADER_PDF) - 1) == 0)
+        img_type(idict) = IMG_TYPE_PDF;
+}
+
+@ @c
+static void check_type_by_extension(image_dict * idict)
+{
+    char *image_suffix;
+    if (idict != NULL)
+        return;
+    if (img_type(idict) != IMG_TYPE_NONE)       /* nothing to do */
+        return;
+    /* tests */
+    if ((image_suffix = strrchr(img_filename(idict), '.')) == 0)
+        img_type(idict) = IMG_TYPE_NONE;
+    else if (strcasecmp(image_suffix, ".png") == 0)
+        img_type(idict) = IMG_TYPE_PNG;
+    else if (strcasecmp(image_suffix, ".jpg") == 0 ||
+             strcasecmp(image_suffix, ".jpeg") == 0)
+        img_type(idict) = IMG_TYPE_JPG;
+    else if (strcasecmp(image_suffix, ".jp2") == 0)
+        img_type(idict) = IMG_TYPE_JP2;
+    else if (strcasecmp(image_suffix, ".jbig2") == 0 ||
+             strcasecmp(image_suffix, ".jb2") == 0)
+        img_type(idict) = IMG_TYPE_JBIG2;
+    else if (strcasecmp(image_suffix, ".pdf") == 0)
+        img_type(idict) = IMG_TYPE_PDF;
+}
+
+@ @c
+void new_img_pdfstream_struct(image_dict * p)
+{
+    img_pdfstream_ptr(p) = xtalloc(1, pdf_stream_struct);
+    img_pdfstream_stream(p) = NULL;
+}
+
+@ @c
+image *new_image(void)
+{
+    image *p = xtalloc(1, image);
+    set_wd_running(p);
+    set_ht_running(p);
+    set_dp_running(p);
+    img_transform(p) = 0;
+    img_dict(p) = NULL;
+    img_dictref(p) = LUA_NOREF;
+    return p;
+}
+
+@ @c
+image_dict *new_image_dict(void)
+{
+    image_dict *p = xtalloc(1, image_dict);
+    memset(p, 0, sizeof(image_dict));
+    set_wd_running(p);
+    set_ht_running(p);
+    set_dp_running(p);
+    img_transform(p) = 0;
+    img_pagenum(p) = 1;
+    img_type(p) = IMG_TYPE_NONE;
+    img_pagebox(p) = PDF_BOX_SPEC_MEDIA;
+    img_unset_bbox(p);
+    img_unset_group(p);
+    img_state(p) = DICT_NEW;
+    img_index(p) = -1; /* -1 = unused, used count from 0 */
+    img_luaref(p) = 0;
+    img_errorlevel(p) = pdf_inclusion_errorlevel;
+    fix_pdf_version(static_pdf);
+    img_pdfmajorversion(p) = pdf_major_version;
+    img_pdfminorversion(p) = pdf_minor_version;
+    return p;
+}
+
+@ @c
+static void free_dict_strings(image_dict * p)
+{
+    xfree(img_filename(p));
+    xfree(img_filepath(p));
+    xfree(img_attr(p));
+    xfree(img_pagename(p));
+}
+
+@ @c
+void free_image_dict(image_dict * p)
+{
+    if (ini_version)
+        return;                 /* The image may be \.{\\dump}ed to a format */
+    /* called from limglib.c */
+    switch (img_type(p)) {
+        case IMG_TYPE_PDFMEMSTREAM:
+        case IMG_TYPE_PDF:
+            flush_pdf_info(p);
+            break;
+        case IMG_TYPE_PNG:
+            flush_png_info(p);
+            break;
+        case IMG_TYPE_JPG:
+            flush_jpg_info(p);
+            break;
+        case IMG_TYPE_JP2:
+            flush_jp2_info(p);
+            break;
+        case IMG_TYPE_JBIG2:
+            flush_jbig2_info(p);
+            break;
+        case IMG_TYPE_PDFSTREAM:
+            /* flush_pdfstream_info(p); */
+            if (img_pdfstream_ptr(p) != NULL) {
+                xfree(img_pdfstream_stream(p));
+                xfree(img_pdfstream_ptr(p));
+            }
+            break;
+        case IMG_TYPE_NONE:
+            break;
+        default:
+            normal_error("pdf backend","unknown image type");
+    }
+    free_dict_strings(p);
+    xfree(p);
+}
+
+@ @c
+void read_img(image_dict * idict)
+{
+    char *filepath = NULL;
+    int callback_id;
+    if (img_filename(idict) == NULL) {
+        normal_error("pdf backend","image file name missing");
+    }
+    callback_id = callback_defined(find_image_file_callback);
+    if (img_filepath(idict) == NULL) {
+        if (callback_id > 0) {
+            /* we always callback, also for a mem stream */
+            if (run_callback(callback_id, "S->S", img_filename(idict),&filepath)) {
+                if (filepath && (strlen(filepath) > 0)) {
+                    img_filepath(idict) = strdup(filepath);
+                }
+            }
+        }
+        if (img_filepath(idict) == NULL && (strstr(img_filename(idict),"data:application/pdf,") != NULL)) {
+            /* we need to check here for a pdf memstream */
+            img_filepath(idict) = strdup(img_filename(idict));
+        } else if (callback_id == 0) {
+            /* otherwise we use kpse but only when we don't callback  */
+            img_filepath(idict) = kpse_find_file(img_filename(idict), kpse_tex_format, true);
+        }
+        if (img_filepath(idict) == NULL) {
+            /* in any case we need a name */
+            formatted_error("pdf backend","cannot find image file '%s'", img_filename(idict));
+        }
+    }
+    recorder_record_input(img_filepath(idict));
+    /* type checks */
+    check_type_by_header(idict);
+    check_type_by_extension(idict);
+    /* read image */
+    switch (img_type(idict)) {
+        case IMG_TYPE_PDFMEMSTREAM:
+        case IMG_TYPE_PDF:
+            read_pdf_info(idict);
+            break;
+        case IMG_TYPE_PNG:
+            read_png_info(idict);
+            break;
+        case IMG_TYPE_JPG:
+            read_jpg_info(idict);
+            break;
+        case IMG_TYPE_JP2:
+            read_jp2_info(idict);
+            break;
+        case IMG_TYPE_JBIG2:
+            read_jbig2_info(idict);
+            break;
+        default:
+            img_type(idict) = IMG_TYPE_NONE;
+            if (pdf_ignore_unknown_images) {
+                normal_warning("pdf backend","internal error: ignoring unknown image type");
+            } else {
+                normal_error("pdf backend","internal error: unknown image type");
+            }
+            break;
+    }
+    cur_file_name = NULL;
+    if (img_type(idict) == IMG_TYPE_NONE) {
+        img_state(idict) = DICT_NEW;
+    } else if (img_state(idict) < DICT_FILESCANNED) {
+        img_state(idict) = DICT_FILESCANNED;
+    }
+}
+
+@ @c
+static image_dict *read_image(char *file_name, int page_num, char *page_name, int colorspace, int page_box)
+{
+    image *a = new_image();
+    image_dict *idict = img_dict(a) = new_image_dict();
+    static_pdf->ximage_count++;
+    img_objnum(idict) = pdf_create_obj(static_pdf, obj_type_ximage, static_pdf->ximage_count);
+    img_index(idict) = static_pdf->ximage_count;
+    set_obj_data_ptr(static_pdf, img_objnum(idict), img_index(idict));
+    idict_to_array(idict);
+    img_colorspace(idict) = colorspace;
+    img_pagenum(idict) = page_num;
+    img_pagename(idict) = page_name;
+    if (file_name == NULL) {
+        normal_error("pdf backend","no image filename given");
+    }
+    cur_file_name = file_name;
+    img_filename(idict) = file_name;
+    img_pagebox(idict) = page_box;
+    read_img(idict);
+    return idict;
+}
+
+@ scans PDF pagebox specification
+@c
+static pdfboxspec_e scan_pdf_box_spec(void)
+{
+    if (scan_keyword("mediabox"))
+        return PDF_BOX_SPEC_MEDIA;
+    else if (scan_keyword("cropbox"))
+        return PDF_BOX_SPEC_CROP;
+    else if (scan_keyword("bleedbox"))
+        return PDF_BOX_SPEC_BLEED;
+    else if (scan_keyword("trimbox"))
+        return PDF_BOX_SPEC_TRIM;
+    else if (scan_keyword("artbox"))
+        return PDF_BOX_SPEC_ART;
+    else
+        return PDF_BOX_SPEC_NONE;
+}
+
+@ @c
+void scan_pdfximage(PDF pdf) /* static_pdf */
+{
+    scaled_whd alt_rule;
+    image_dict *idict;
+    int transform = 0, page = 1, pagebox, colorspace = 0;
+    char *named = NULL, *attr = NULL, *file_name = NULL;
+    alt_rule = scan_alt_rule(); /* scans |<rule spec>| to |alt_rule| */
+    if (scan_keyword("attr")) {
+        scan_toks(false, true);
+        attr = tokenlist_to_cstring(def_ref, true, NULL);
+        delete_token_ref(def_ref);
+    }
+    if (scan_keyword("named")) {
+        scan_toks(false, true);
+        named = tokenlist_to_cstring(def_ref, true, NULL);
+        delete_token_ref(def_ref);
+        page = 0;
+    } else if (scan_keyword("page")) {
+        scan_int();
+        page = cur_val;
+    }
+    if (scan_keyword("colorspace")) {
+        scan_int();
+        colorspace = cur_val;
+    }
+    pagebox = scan_pdf_box_spec();
+    if (pagebox == PDF_BOX_SPEC_NONE) {
+        pagebox = pdf_pagebox;
+        if (pagebox == PDF_BOX_SPEC_NONE)
+            pagebox = PDF_BOX_SPEC_CROP;
+    }
+    scan_toks(false, true);
+    file_name = tokenlist_to_cstring(def_ref, true, NULL);
+    if (file_name == NULL) {
+        normal_error("pdf backend","no image filename given");
+    }
+    delete_token_ref(def_ref);
+    idict = read_image(file_name, page, named, colorspace, pagebox);
+    img_attr(idict) = attr;
+    img_dimen(idict) = alt_rule;
+    img_transform(idict) = transform;
+    last_saved_image_index = img_objnum(idict);
+    last_saved_image_pages = img_totalpages(idict);
+}
+
+@ @c
+void scan_pdfrefximage(PDF pdf)
+{
+    /* one could scan transform as well */
+    int transform = 0;
+    /* begin of experiment */
+    int open = 0;
+    /* end of experiment */
+    image_dict *idict;
+    /* scans |<rule spec>| to |alt_rule| */
+    scaled_whd alt_rule, dim;
+    alt_rule = scan_alt_rule();
+    /* begin of experiment */
+    if (scan_keyword("keepopen")) {
+        open = 1;
+    }
+    /* end of experiment */
+    scan_int();
+    check_obj_type(pdf, obj_type_ximage, cur_val);
+    tail_append(new_rule(image_rule));
+    idict = idict_array[obj_data_ptr(pdf, cur_val)];
+    /* begin of experiment */
+    if (open) {
+        /* so we keep the original value when no close is given */
+        idict->keepopen = 1;
+    }
+    /* end of experiment */
+    if (img_state(idict) == DICT_NEW) {
+        normal_warning("image","don't rely on the image data to be okay");
+        width(tail_par) = 0;
+        height(tail_par) = 0;
+        depth(tail_par) = 0;
+    } else {
+        if (alt_rule.wd != null_flag || alt_rule.ht != null_flag || alt_rule.dp != null_flag) {
+            dim = scale_img(idict, alt_rule, transform);
+        } else {
+            dim = scale_img(idict, img_dimen(idict), img_transform(idict));
+        }
+        width(tail_par) = dim.wd;
+        height(tail_par) = dim.ht;
+        depth(tail_par) = dim.dp;
+        rule_transform(tail_par) = transform;
+        rule_index(tail_par) = img_index(idict);
+    }
+}
+
+@ |tex_scale()| sequence of decisions:
+
+{\obeylines\obeyspaces\tt
+wd ht dp : res = tex;
+wd ht --
+wd -- dp
+wd -- --
+-- ht dp
+-- ht --
+-- -- dp
+-- -- -- : res = nat;
+}
+
+@c
+scaled_whd tex_scale(scaled_whd nat, scaled_whd tex)
+{
+    scaled_whd res;
+    if (!is_running(tex.wd) && !is_running(tex.ht) && !is_running(tex.dp)) {
+        /* width, height, and depth specified */
+        res = tex;
+    } else /* max. 2 dimensions are specified */ if (!is_running(tex.wd)) {
+        res.wd = tex.wd;
+        if (!is_running(tex.ht)) {
+            res.ht = tex.ht;
+            /* width and height specified */
+            res.dp = ext_xn_over_d(tex.ht, nat.dp, nat.ht);
+        } else if (!is_running(tex.dp)) {
+            res.dp = tex.dp;
+            /* width and depth specified */
+            res.ht = ext_xn_over_d(tex.wd, nat.ht + nat.dp, nat.wd) - tex.dp;
+        } else {
+            /* only width specified */
+            res.ht = ext_xn_over_d(tex.wd, nat.ht, nat.wd);
+            res.dp = ext_xn_over_d(tex.wd, nat.dp, nat.wd);
+        }
+    } else if (!is_running(tex.ht)) {
+        res.ht = tex.ht;
+        if (!is_running(tex.dp)) {
+            res.dp = tex.dp;
+            /* height and depth specified */
+            res.wd = ext_xn_over_d(tex.ht + tex.dp, nat.wd, nat.ht + nat.dp);
+        } else {
+            /* only height specified */
+            res.wd = ext_xn_over_d(tex.ht, nat.wd, nat.ht);
+            res.dp = ext_xn_over_d(tex.ht, nat.dp, nat.ht);
+        }
+    } else if (!is_running(tex.dp)) {
+        res.dp = tex.dp;
+        /* only depth specified */
+        res.ht = nat.ht - (tex.dp - nat.dp);
+        res.wd = nat.wd;
+    } else {
+        /* nothing specified */
+        res = nat;
+    }
+    return res;
+}
+
+@ Within |scale_img()| only image width and height matter;
+the offsets and positioning are not interesting here.
+But one needs rotation info to swap width and height.
+|img_rotation()| comes from the optional /Rotate key in the PDF file.
+
+@c
+scaled_whd scale_img(image_dict * idict, scaled_whd alt_rule, int transform)
+{
+    int x, y, xr, yr, tmp;      /* size and resolution of image */
+    scaled_whd nat;             /* natural size corresponding to image resolution */
+    int default_res;
+    if ((img_type(idict) == IMG_TYPE_PDF || img_type(idict) == IMG_TYPE_PDFMEMSTREAM
+         || img_type(idict) == IMG_TYPE_PDFSTREAM) && img_is_bbox(idict)) {
+        x = img_xsize(idict) = img_bbox(idict)[2] - img_bbox(idict)[0]; /* dimensions from image.bbox */
+        y = img_ysize(idict) = img_bbox(idict)[3] - img_bbox(idict)[1];
+        img_xorig(idict) = img_bbox(idict)[0];
+        img_yorig(idict) = img_bbox(idict)[1];
+    } else {
+        x = img_xsize(idict);   /* dimensions, resolutions from image file */
+        y = img_ysize(idict);
+    }
+    xr = img_xres(idict);
+    yr = img_yres(idict);
+    if (x <= 0 || y <= 0 || xr < 0 || yr < 0)
+        normal_error("pdf backend","invalid image dimensions");
+    if (xr > 65535 || yr > 65535) {
+        xr = 0;
+        yr = 0;
+        normal_warning("pdf backend","too large image resolution ignored");
+    }
+    if (((transform - img_rotation(idict)) & 1) == 1) {
+        tmp = x;
+        x = y;
+        y = tmp;
+        tmp = xr;
+        xr = yr;
+        yr = tmp;
+    }
+    nat.dp = 0;                 /* always for images */
+    if (img_type(idict) == IMG_TYPE_PDF || img_type(idict) == IMG_TYPE_PDFMEMSTREAM
+        || img_type(idict) == IMG_TYPE_PDFSTREAM) {
+        nat.wd = x;
+        nat.ht = y;
+    } else {
+        default_res = fix_int(pdf_image_resolution, 0, 65535);
+        if (default_res > 0 && (xr == 0 || yr == 0)) {
+            xr = default_res;
+            yr = default_res;
+        }
+        if (xr > 0 && yr > 0) {
+            nat.wd = ext_xn_over_d(one_hundred_inch, x, 100 * xr);
+            nat.ht = ext_xn_over_d(one_hundred_inch, y, 100 * yr);
+        } else {
+            nat.wd = ext_xn_over_d(one_hundred_inch, x, 7200);
+            nat.ht = ext_xn_over_d(one_hundred_inch, y, 7200);
+        }
+    }
+    return tex_scale(nat, alt_rule);
+}
+
+@ @c
+void write_img(PDF pdf, image_dict * idict)
+{
+    if (img_state(idict) < DICT_WRITTEN) {
+        report_start_file(filetype_image, img_filepath(idict));
+        switch (img_type(idict)) {
+        case IMG_TYPE_PNG:
+            write_png(pdf, idict);
+            break;
+        case IMG_TYPE_JPG:
+            write_jpg(pdf, idict);
+            break;
+        case IMG_TYPE_JP2:
+            write_jp2(pdf, idict);
+            break;
+        case IMG_TYPE_JBIG2:
+            write_jbig2(pdf, idict);
+            break;
+        case IMG_TYPE_PDFMEMSTREAM:
+        case IMG_TYPE_PDF:
+            write_epdf(pdf, idict,(int) pdf_suppress_optional_info);
+            break;
+        case IMG_TYPE_PDFSTREAM:
+            write_pdfstream(pdf, idict);
+            break;
+        default:
+            normal_error("pdf backend","internal error: writing unknown image type");
+        }
+        report_stop_file(filetype_image);
+        if (img_type(idict) == IMG_TYPE_PNG) {
+            write_additional_png_objects(pdf);
+        }
+    }
+    if (img_state(idict) < DICT_WRITTEN)
+        img_state(idict) = DICT_WRITTEN;
+}
+
+@ write an image
+@c
+void pdf_write_image(PDF pdf, int n)
+{
+    if (pdf->draftmode == 0)
+        write_img(pdf, idict_array[obj_data_ptr(pdf, n)]);
+}
+
+@ @c
+void check_pdfstream_dict(image_dict * idict)
+{
+    if (!img_is_bbox(idict))
+        normal_error("pdf backend","image.stream: no bbox given");
+    if (img_state(idict) < DICT_FILESCANNED)
+        img_state(idict) = DICT_FILESCANNED;
+}
+
+@ @c
+void write_pdfstream(PDF pdf, image_dict * idict)
+{
+    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "XObject");
+    pdf_dict_add_name(pdf, "Subtype", "Form");
+    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0)
+        pdf_printf(pdf, "\n%s\n", img_attr(idict));
+    pdf_dict_add_int(pdf, "FormType", 1);
+    pdf_add_name(pdf, "BBox");
+    pdf_begin_array(pdf);
+    copyReal(pdf, sp2bp(img_bbox(idict)[0]));
+    copyReal(pdf, sp2bp(img_bbox(idict)[1]));
+    copyReal(pdf, sp2bp(img_bbox(idict)[2]));
+    copyReal(pdf, sp2bp(img_bbox(idict)[3]));
+    pdf_end_array(pdf);
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    if (img_pdfstream_stream(idict) != NULL)
+        pdf_puts(pdf, img_pdfstream_stream(idict));
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+}
+
+@ @c
+idict_entry *idict_ptr, *idict_array = NULL;
+size_t idict_limit;
+
+void idict_to_array(image_dict * idict)
+{
+    if (idict_ptr - idict_array == 0) { /* align to count from 1 */
+        alloc_array(idict, 1, SMALL_BUF_SIZE);  /* /Im0 unused */
+        idict_ptr++;
+    }
+    alloc_array(idict, 1, SMALL_BUF_SIZE);
+    *idict_ptr = idict;
+    idict_ptr++;
+}
+
+void pdf_dict_add_img_filename(PDF pdf, image_dict * idict)
+{
+    char *p;
+    if ((pdf_image_addfilename > 0) && ((pdf_suppress_optional_info & 2) == 0)) {
+        /* for now PTEX.FileName only for PDF, but prepared for JPG, PNG, ... */
+        if (! ( (img_type(idict) == IMG_TYPE_PDF) || (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) ))
+            return;
+        if (img_visiblefilename(idict) != NULL) {
+            if (strlen(img_visiblefilename(idict)) == 0) {
+                return; /* empty string blocks PTEX.FileName output */
+            } else {
+                p = img_visiblefilename(idict);
+            }
+        } else {
+            /* unset so let's use the default */
+            p = img_filepath(idict);
+        }
+        // write additional information
+        pdf_add_name(pdf, "PTEX.FileName");
+        pdf_printf(pdf, " (%s)", convertStringToPDFString(p, strlen(p)));
+    }
+}
+
+/* hh: why store images in the format ... let's get rid of this */
+
+@ To allow the use of box resources inside saved boxes in -ini mode,
+the information in the array has to be (un)dumped with the format.
+The next two routines take care of that.
+
+Most of the work involved in setting up the images is simply
+executed again. This solves the many possible errors resulting from
+the split in two separate runs.
+
+There was only one problem remaining: The pdfversion and
+pdfinclusionerrorlevel can have changed inbetween the call to
+|readimage()| and dump time.
+
+some of the dumped values are really type int, not integer,
+but since the macro falls back to |generic_dump| anyway, that
+does not matter.
+
+@c
+#define dumpinteger generic_dump
+#define undumpinteger generic_undump
+
+@ (un)dumping a string means dumping the allocation size, followed
+ by the bytes. The trailing \.{\\0} is dumped as well, because that
+ makes the code simpler.
+
+@ scan rule spec to |alt_rule|
+@c
+scaled_whd scan_alt_rule(void)
+{
+    boolean loop;
+    scaled_whd alt_rule;
+    alt_rule.wd = null_flag;
+    alt_rule.ht = null_flag;
+    alt_rule.dp = null_flag;
+    do {
+        loop = false;
+        if (scan_keyword("width")) {
+            scan_normal_dimen();
+            alt_rule.wd = cur_val;
+            loop = true;
+        } else if (scan_keyword("height")) {
+            scan_normal_dimen();
+            alt_rule.ht = cur_val;
+            loop = true;
+        } else if (scan_keyword("depth")) {
+            scan_normal_dimen();
+            alt_rule.dp = cur_val;
+            loop = true;
+        }
+    } while (loop);
+    return alt_rule;
+}
+
+@ copy file of arbitrary size to PDF buffer and flush as needed
+@c
+size_t read_file_to_buf(PDF pdf, FILE * f, size_t len)
+{
+    size_t i, j, k = 0;
+    while (len > 0) {
+        i = (size_t) (len > pdf->buf->size) ? (size_t) pdf->buf->size : len;
+        pdf_room(pdf, (int) i);
+        j = fread(pdf->buf->p, 1, i, f);
+        pdf->buf->p += j;
+        k += j;
+        len -= j;
+        if (i != j)
+            break;
+    }
+    return k;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/image/writeimg.c
+++ /dev/null
@@ -1,799 +0,0 @@
-/*
-
-writeimg.c
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-Copyright 2006-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <kpathsea/c-auto.h>
-#include <kpathsea/c-memstr.h>
-
-#include "image/image.h"
-#include "image/writejpg.h"
-#include "image/writejp2.h"
-#include "image/writepng.h"
-#include "image/writejbig2.h"
-
-#include "lua.h"
-#include "lauxlib.h"
-
-/*tex
-
-The function |readimage| performs some basic initializations. Then it looks at
-the file extension to determine the image type and calls specific code/functions.
-The main disadvantage is that standard file extensions have to be used, otherwise
-pdfTeX is not able to detect the correct image type. The patch now looks at the
-file header first regardless of the file extension. This is implemented in
-function |check_type_by_header|. If this check fails, the traditional test of
-standard file extension is tried, done in function |check_type_by_extension|.
-
-The magic headers are as follows:
-
-\startitemize
-    \startitem
-        \type {png}: 89 50 4E 47 0D 0A 1A 0A or |"\137PNG\013\010\026\010"|
-    \stopitem
-    \startitem
-        \type {jpg}: FF D8 FF or |"\255\216\255"|.
-    \stopitem
-    \startitem
-        \type {jp2}: 00 00 00 0C 6A 50 20 20 0D 0A or |"\000\000\000\012\106\080\032\032\013\010"|
-    \stopitem
-    \startitem
-        \type {pdf}: |"%PDF"| somewhere in the beginning
-    \stopitem
-\stopitemize
-
-Functions |check_type_by_header| and |check_type_by_extension|: |img_type(img)|
-is set to |IMG_TYPE_NONE| by |new_image_dict()|. Both functions try to detect a
-type and set |img_type(img)|. Thus a value other than |IMG_TYPE_NONE| indicates
-that a type has been found.
-
-*/
-
-#define HEADER_JPG   "\xFF\xD8"
-#define HEADER_PNG   "\x89PNG\r\n\x1A\n"
-#define HEADER_JBIG2 "\x97\x4A\x42\x32\x0D\x0A\x1A\x0A"
-#define HEADER_JP2   "\x6A\x50\x20\x20"
-#define HEADER_PDF   "%PDF-"
-
-#define MAX_HEADER (sizeof(HEADER_PNG)-1)
-
-#define HEADER_PDF_MEMSTREAM "data:application/pdf,"
-#define LEN_PDF_MEMSTREAM 21
-
-static void check_type_by_header(image_dict * idict)
-{
-    int i;
-    FILE *file = NULL;
-    char header[MAX_HEADER];
-    char prefix[LEN_PDF_MEMSTREAM+1];
-    if (idict == NULL)
-        return;
-    if (img_type(idict) != IMG_TYPE_NONE)
-        return;
-    /*tex Here we read the and also check for a memstream object. */
-    if (!img_filepath(idict) || !FOPEN_RBIN_MODE) {
-        normal_error("pdf backend","reading image file failed");
-    }
-    file = fopen(img_filepath(idict), FOPEN_RBIN_MODE);
-    if (file == NULL) {
-        /*tex We check the prefix of img_filepath(idict). */
-        for (i = 0; (unsigned) i < LEN_PDF_MEMSTREAM; i++) {
-            prefix[i] = (char) (img_filepath(idict)[i]);
-        }
-        prefix[LEN_PDF_MEMSTREAM]='\0';
-        if (strncmp(prefix, HEADER_PDF_MEMSTREAM, LEN_PDF_MEMSTREAM) == 0) {
-            img_type(idict) = IMG_TYPE_PDFMEMSTREAM;
-            return;
-        } else {
-            formatted_error("pdf backend","reading image file '%s' failed",img_filepath(idict));
-        }
-    }
-    /*tex Do we have a valid file but perhaps unsupported? */
-    for (i = 0; (unsigned) i < MAX_HEADER; i++) {
-        header[i] = (char) xgetc(file);
-        if (feof(file)) {
-            normal_error("pdf backend","reading image file failed");
-        }
-    }
-    xfclose(file, img_filepath(idict));
-    /*tex Further tests: */
-    if (strncmp(header, HEADER_JPG, sizeof(HEADER_JPG) - 1) == 0)
-        img_type(idict) = IMG_TYPE_JPG;
-    else if (strncmp(header + 4, HEADER_JP2, sizeof(HEADER_JP2) - 1) == 0)
-        img_type(idict) = IMG_TYPE_JP2;
-    else if (strncmp(header, HEADER_PNG, sizeof(HEADER_PNG) - 1) == 0)
-        img_type(idict) = IMG_TYPE_PNG;
-    else if (strncmp(header, HEADER_JBIG2, sizeof(HEADER_JBIG2) - 1) == 0)
-        img_type(idict) = IMG_TYPE_JBIG2;
-    else if (strncmp(header, HEADER_PDF, sizeof(HEADER_PDF) - 1) == 0)
-        img_type(idict) = IMG_TYPE_PDF;
-}
-
-static void check_type_by_extension(image_dict * idict)
-{
-    char *image_suffix;
-    if (idict != NULL)
-        return;
-    if (img_type(idict) != IMG_TYPE_NONE)
-        return;
-    if ((image_suffix = strrchr(img_filename(idict), '.')) == 0)
-        img_type(idict) = IMG_TYPE_NONE;
-    else if (strcasecmp(image_suffix, ".png") == 0)
-        img_type(idict) = IMG_TYPE_PNG;
-    else if (strcasecmp(image_suffix, ".jpg") == 0 ||
-             strcasecmp(image_suffix, ".jpeg") == 0)
-        img_type(idict) = IMG_TYPE_JPG;
-    else if (strcasecmp(image_suffix, ".jp2") == 0)
-        img_type(idict) = IMG_TYPE_JP2;
-    else if (strcasecmp(image_suffix, ".jbig2") == 0 ||
-             strcasecmp(image_suffix, ".jb2") == 0)
-        img_type(idict) = IMG_TYPE_JBIG2;
-    else if (strcasecmp(image_suffix, ".pdf") == 0)
-        img_type(idict) = IMG_TYPE_PDF;
-}
-
-void new_img_pdfstream_struct(image_dict * p)
-{
-    img_pdfstream_ptr(p) = xtalloc(1, pdf_stream_struct);
-    img_pdfstream_stream(p) = NULL;
-    img_pdfstream_size(p) = 0;
-}
-
-image *new_image(void)
-{
-    image *p = xtalloc(1, image);
-    set_wd_running(p);
-    set_ht_running(p);
-    set_dp_running(p);
-    img_transform(p) = 0;
-    img_dict(p) = NULL;
-    img_dictref(p) = LUA_NOREF;
-    return p;
-}
-
-image_dict *new_image_dict(void)
-{
-    image_dict *p = xtalloc(1, image_dict);
-    memset(p, 0, sizeof(image_dict));
-    set_wd_running(p);
-    set_ht_running(p);
-    set_dp_running(p);
-    img_transform(p) = 0;
-    img_pagenum(p) = 1;
-    img_type(p) = IMG_TYPE_NONE;
-    img_pagebox(p) = PDF_BOX_SPEC_MEDIA;
-    img_unset_bbox(p);
-    img_unset_group(p);
-    img_state(p) = DICT_NEW;
-    /*tex A value of -1 means unused while the used counts from 0 */
-    img_index(p) = -1;
-    img_luaref(p) = 0;
-    img_errorlevel(p) = pdf_inclusion_errorlevel;
-    fix_pdf_version(static_pdf);
-    img_pdfmajorversion(p) = pdf_major_version;
-    img_pdfminorversion(p) = pdf_minor_version;
-    return p;
-}
-
-static void free_dict_strings(image_dict * p)
-{
-    xfree(img_filename(p));
-    xfree(img_filepath(p));
-    xfree(img_attr(p));
-    xfree(img_pagename(p));
-}
-
-void free_image_dict(image_dict * p)
-{
-    if (ini_version) {
-         /*tex The image may be \.{\\dump}ed to a format. */
-        return;
-    }
-    /*tex Called from limglib.c. */
-    switch (img_type(p)) {
-        case IMG_TYPE_PDFMEMSTREAM:
-        case IMG_TYPE_PDF:
-            flush_pdf_info(p);
-            break;
-        case IMG_TYPE_PNG:
-            flush_png_info(p);
-            break;
-        case IMG_TYPE_JPG:
-            flush_jpg_info(p);
-            break;
-        case IMG_TYPE_JP2:
-            flush_jp2_info(p);
-            break;
-        case IMG_TYPE_JBIG2:
-            flush_jbig2_info(p);
-            break;
-        case IMG_TYPE_PDFSTREAM:
-            if (img_pdfstream_ptr(p) != NULL) {
-                xfree(img_pdfstream_stream(p));
-                xfree(img_pdfstream_ptr(p));
-            }
-            break;
-        case IMG_TYPE_NONE:
-            break;
-        default:
-            normal_error("pdf backend","unknown image type");
-    }
-    free_dict_strings(p);
-    xfree(p);
-}
-
-void read_img(image_dict * idict)
-{
-    char *filepath = NULL;
-    int callback_id;
-    if (img_filename(idict) == NULL) {
-        normal_error("pdf backend","image file name missing");
-    }
-    callback_id = callback_defined(find_image_file_callback);
-    if (img_filepath(idict) == NULL) {
-        if (callback_id > 0) {
-            /*tex We always callback, also for a mem stream. */
-            if (run_callback(callback_id, "S->S", img_filename(idict),&filepath)) {
-                if (filepath && (strlen(filepath) > 0)) {
-                    img_filepath(idict) = strdup(filepath);
-                }
-            }
-        }
-        if (img_filepath(idict) == NULL && (strstr(img_filename(idict),"data:application/pdf,") != NULL)) {
-            /*tex We need to check here for a pdf memstream. */
-            img_filepath(idict) = strdup(img_filename(idict));
-        } else if (callback_id == 0) {
-            /*tex Otherwise we use kpse but only when we don't callback. */
-            img_filepath(idict) = kpse_find_file(img_filename(idict), kpse_tex_format, true);
-        }
-        if (img_filepath(idict) == NULL) {
-            /*tex In any case we need a name. */
-            formatted_error("pdf backend","cannot find image file '%s'", img_filename(idict));
-        }
-    }
-    recorder_record_input(img_filepath(idict));
-    /*tex A few type checks. */
-    check_type_by_header(idict);
-    check_type_by_extension(idict);
-    /*tex Now we're ready to read the image. */
-    switch (img_type(idict)) {
-        case IMG_TYPE_PDFMEMSTREAM:
-        case IMG_TYPE_PDF:
-            read_pdf_info(idict);
-            break;
-        case IMG_TYPE_PNG:
-            read_png_info(idict);
-            break;
-        case IMG_TYPE_JPG:
-            read_jpg_info(idict);
-            break;
-        case IMG_TYPE_JP2:
-            read_jp2_info(idict);
-            break;
-        case IMG_TYPE_JBIG2:
-            read_jbig2_info(idict);
-            break;
-        default:
-            img_type(idict) = IMG_TYPE_NONE;
-            if (pdf_ignore_unknown_images) {
-                normal_warning("pdf backend","internal error: ignoring unknown image type");
-            } else {
-                normal_error("pdf backend","internal error: unknown image type");
-            }
-            break;
-    }
-    cur_file_name = NULL;
-    if (img_type(idict) == IMG_TYPE_NONE) {
-        img_state(idict) = DICT_NEW;
-    } else if (img_state(idict) < DICT_FILESCANNED) {
-        img_state(idict) = DICT_FILESCANNED;
-    }
-}
-
-static image_dict *read_image(char *file_name, int page_num, char *page_name, int colorspace, int page_box, char *user_password, char *owner_password, char *visible_filename)
-{
-    image *a = new_image();
-    image_dict *idict = img_dict(a) = new_image_dict();
-    static_pdf->ximage_count++;
-    img_objnum(idict) = pdf_create_obj(static_pdf, obj_type_ximage, static_pdf->ximage_count);
-    img_index(idict) = static_pdf->ximage_count;
-    set_obj_data_ptr(static_pdf, img_objnum(idict), img_index(idict));
-    idict_to_array(idict);
-    img_colorspace(idict) = colorspace;
-    img_pagenum(idict) = page_num;
-    img_pagename(idict) = page_name;
-    img_userpassword(idict) = user_password;
-    img_ownerpassword(idict) = owner_password;
-    img_visiblefilename(idict) = visible_filename;
-    if (file_name == NULL) {
-        normal_error("pdf backend","no image filename given");
-    }
-    cur_file_name = file_name;
-    img_filename(idict) = file_name;
-    img_pagebox(idict) = page_box;
-    read_img(idict);
-    return idict;
-}
-
-/*tex
-
-    There can be several page boxes. Normally the cropbox is used.
-
-*/
-
-static pdfboxspec_e scan_pdf_box_spec(void)
-{
-    if (scan_keyword("mediabox"))
-        return PDF_BOX_SPEC_MEDIA;
-    else if (scan_keyword("cropbox"))
-        return PDF_BOX_SPEC_CROP;
-    else if (scan_keyword("bleedbox"))
-        return PDF_BOX_SPEC_BLEED;
-    else if (scan_keyword("trimbox"))
-        return PDF_BOX_SPEC_TRIM;
-    else if (scan_keyword("artbox"))
-        return PDF_BOX_SPEC_ART;
-    else
-        return PDF_BOX_SPEC_NONE;
-}
-
-void scan_pdfximage(PDF pdf)
-{
-    scaled_whd alt_rule;
-    image_dict *idict;
-    int transform = 0, page = 1, pagebox, colorspace = 0;
-    char *named = NULL, *attr = NULL, *file_name = NULL, *user = NULL, *owner = NULL, *visible = NULL;
-    alt_rule = scan_alt_rule();
-    if (scan_keyword("attr")) {
-        scan_toks(false, true);
-        attr = tokenlist_to_cstring(def_ref, true, NULL);
-        delete_token_ref(def_ref);
-    }
-    if (scan_keyword("named")) {
-        scan_toks(false, true);
-        if (0) {
-            named = tokenlist_to_cstring(def_ref, true, NULL);
-            page = 0;
-        } else {
-            normal_warning("pdf backend","named pages are not supported, using page 1");
-            page = 1;
-        }
-        delete_token_ref(def_ref);
-    } else if (scan_keyword("page")) {
-        scan_int();
-        page = cur_val;
-    }
-    if (scan_keyword("userpassword")) {
-        scan_toks(false, true);
-        user = tokenlist_to_cstring(def_ref, true, NULL);
-        delete_token_ref(def_ref);
-    }
-    if (scan_keyword("ownerpassword")) {
-        scan_toks(false, true);
-        owner = tokenlist_to_cstring(def_ref, true, NULL);
-        delete_token_ref(def_ref);
-    }
-    if (scan_keyword("visiblefilename")) {
-        scan_toks(false, true);
-        visible = tokenlist_to_cstring(def_ref, true, NULL);
-        delete_token_ref(def_ref);
-    }
-    if (scan_keyword("colorspace")) {
-        scan_int();
-        colorspace = cur_val;
-    }
-    pagebox = scan_pdf_box_spec();
-    if (pagebox == PDF_BOX_SPEC_NONE) {
-        pagebox = pdf_pagebox;
-        if (pagebox == PDF_BOX_SPEC_NONE)
-            pagebox = PDF_BOX_SPEC_CROP;
-    }
-    scan_toks(false, true);
-    file_name = tokenlist_to_cstring(def_ref, true, NULL);
-    if (file_name == NULL) {
-        normal_error("pdf backend","no image filename given");
-    }
-    delete_token_ref(def_ref);
-    idict = read_image(file_name, page, named, colorspace, pagebox, user, owner, visible);
-    img_attr(idict) = attr;
-    img_dimen(idict) = alt_rule;
-    img_transform(idict) = transform;
-    last_saved_image_index = img_objnum(idict);
-    last_saved_image_pages = img_totalpages(idict);
-}
-
-void scan_pdfrefximage(PDF pdf)
-{
-    /*tex One could scan transform as well. */
-    int transform = 0;
-    /*tex Begin of experiment. */
-    int open = 0;
-    /*tex End of experiment. */
-    image_dict *idict;
-    /*tex This scans |<rule spec>| to |alt_rule|. */
-    scaled_whd alt_rule, dim;
-    alt_rule = scan_alt_rule();
-    /*tex Begin of experiment. */
-    if (scan_keyword("keepopen")) {
-        open = 1;
-    }
-    /*tex End of experiment. */
-    scan_int();
-    check_obj_type(pdf, obj_type_ximage, cur_val);
-    tail_append(new_rule(image_rule));
-    idict = idict_array[obj_data_ptr(pdf, cur_val)];
-    /*tex Begin of experiment, */
-    if (open) {
-        /*tex So we keep the original value when no close is given. */
-        idict->keepopen = 1;
-    }
-    /*tex End of experiment. */
-    if (img_state(idict) == DICT_NEW) {
-        normal_warning("image","don't rely on the image data to be okay");
-        width(tail_par) = 0;
-        height(tail_par) = 0;
-        depth(tail_par) = 0;
-    } else {
-        if (alt_rule.wd != null_flag || alt_rule.ht != null_flag || alt_rule.dp != null_flag) {
-            dim = scale_img(idict, alt_rule, transform);
-        } else {
-            dim = scale_img(idict, img_dimen(idict), img_transform(idict));
-        }
-        width(tail_par) = dim.wd;
-        height(tail_par) = dim.ht;
-        depth(tail_par) = dim.dp;
-        rule_transform(tail_par) = transform;
-        rule_index(tail_par) = img_index(idict);
-    }
-}
-
-/*
-    The |tex_scale| function follows a sequence of decisions:
-
-    \starttyping
-    wd ht dp : res = tex;
-    wd ht --
-    wd -- dp
-    wd -- --
-    -- ht dp
-    -- ht --
-    -- -- dp
-    -- -- -- : res = nat;
-    \stoptyping
-
-*/
-
-scaled_whd tex_scale(scaled_whd nat, scaled_whd tex)
-{
-    scaled_whd res;
-    if (!is_running(tex.wd) && !is_running(tex.ht) && !is_running(tex.dp)) {
-        /*tex width, height, and depth specified */
-        res = tex;
-    } else if (!is_running(tex.wd)) {
-        /*tex max. 2 dimensions are specified */
-        res.wd = tex.wd;
-        if (!is_running(tex.ht)) {
-            res.ht = tex.ht;
-            /*tex width and height specified */
-            res.dp = ext_xn_over_d(tex.ht, nat.dp, nat.ht);
-        } else if (!is_running(tex.dp)) {
-            res.dp = tex.dp;
-            /*tex width and depth specified */
-            res.ht = ext_xn_over_d(tex.wd, nat.ht + nat.dp, nat.wd) - tex.dp;
-        } else {
-            /*tex only width specified */
-            res.ht = ext_xn_over_d(tex.wd, nat.ht, nat.wd);
-            res.dp = ext_xn_over_d(tex.wd, nat.dp, nat.wd);
-        }
-    } else if (!is_running(tex.ht)) {
-        res.ht = tex.ht;
-        if (!is_running(tex.dp)) {
-            res.dp = tex.dp;
-            /*tex height and depth specified */
-            res.wd = ext_xn_over_d(tex.ht + tex.dp, nat.wd, nat.ht + nat.dp);
-        } else {
-            /*tex only height specified */
-            res.wd = ext_xn_over_d(tex.ht, nat.wd, nat.ht);
-            res.dp = ext_xn_over_d(tex.ht, nat.dp, nat.ht);
-        }
-    } else if (!is_running(tex.dp)) {
-        res.dp = tex.dp;
-        /*tex only depth specified */
-        res.ht = nat.ht - (tex.dp - nat.dp);
-        res.wd = nat.wd;
-    } else {
-        /*tex nothing specified */
-        res = nat;
-    }
-    return res;
-}
-
-/*tex
-
-Within |scale_img| only image width and height matter; the offsets and
-positioning are not interesting here. But one needs rotation info to swap width
-and height. |img_rotation| comes from the optional |/Rotate| key in the PDF file.
-
-*/
-
-scaled_whd scale_img(image_dict * idict, scaled_whd alt_rule, int transform)
-{
-    /*tex size and resolution of image */
-    int x, y, xr, yr, tmp;
-    /*tex natural size corresponding to image resolution */
-    scaled_whd nat;
-    int default_res;
-    if ((img_type(idict) == IMG_TYPE_PDF || img_type(idict) == IMG_TYPE_PDFMEMSTREAM
-         || img_type(idict) == IMG_TYPE_PDFSTREAM) && img_is_bbox(idict)) {
-        /*tex dimensions from image.bbox */
-        x = img_xsize(idict) = img_bbox(idict)[2] - img_bbox(idict)[0];
-        y = img_ysize(idict) = img_bbox(idict)[3] - img_bbox(idict)[1];
-        img_xorig(idict) = img_bbox(idict)[0];
-        img_yorig(idict) = img_bbox(idict)[1];
-    } else {
-        /*tex dimensions, resolutions from image file */
-        x = img_xsize(idict);
-        y = img_ysize(idict);
-    }
-    xr = img_xres(idict);
-    yr = img_yres(idict);
-    if (x <= 0 || y <= 0 || xr < 0 || yr < 0)
-        normal_error("pdf backend","invalid image dimensions");
-    if (xr > 65535 || yr > 65535) {
-        xr = 0;
-        yr = 0;
-        normal_warning("pdf backend","too large image resolution ignored");
-    }
-    if (((transform - img_rotation(idict)) & 1) == 1) {
-        tmp = x;
-        x = y;
-        y = tmp;
-        tmp = xr;
-        xr = yr;
-        yr = tmp;
-    }
-    /*tex always for images */
-    nat.dp = 0;
-    if (img_type(idict) == IMG_TYPE_PDF || img_type(idict) == IMG_TYPE_PDFMEMSTREAM
-        || img_type(idict) == IMG_TYPE_PDFSTREAM) {
-        nat.wd = x;
-        nat.ht = y;
-    } else {
-        default_res = fix_int(pdf_image_resolution, 0, 65535);
-        if (default_res > 0 && (xr == 0 || yr == 0)) {
-            xr = default_res;
-            yr = default_res;
-        }
-        if (xr > 0 && yr > 0) {
-            nat.wd = ext_xn_over_d(one_hundred_inch, x, 100 * xr);
-            nat.ht = ext_xn_over_d(one_hundred_inch, y, 100 * yr);
-        } else {
-            nat.wd = ext_xn_over_d(one_hundred_inch, x, 7200);
-            nat.ht = ext_xn_over_d(one_hundred_inch, y, 7200);
-        }
-    }
-    return tex_scale(nat, alt_rule);
-}
-
-void write_img(PDF pdf, image_dict * idict)
-{
-    if (img_state(idict) < DICT_WRITTEN) {
-        report_start_file(filetype_image, img_filepath(idict));
-        switch (img_type(idict)) {
-        case IMG_TYPE_PNG:
-            write_png(pdf, idict);
-            break;
-        case IMG_TYPE_JPG:
-            write_jpg(pdf, idict);
-            break;
-        case IMG_TYPE_JP2:
-            write_jp2(pdf, idict);
-            break;
-        case IMG_TYPE_JBIG2:
-            write_jbig2(pdf, idict);
-            break;
-        case IMG_TYPE_PDFMEMSTREAM:
-        case IMG_TYPE_PDF:
-            write_epdf(pdf, idict,(int) pdf_suppress_optional_info);
-            break;
-        case IMG_TYPE_PDFSTREAM:
-            write_pdfstream(pdf, idict);
-            break;
-        default:
-            normal_error("pdf backend","internal error: writing unknown image type");
-        }
-        report_stop_file(filetype_image);
-        if (img_type(idict) == IMG_TYPE_PNG) {
-            write_additional_png_objects(pdf);
-        }
-    }
-    if (img_state(idict) < DICT_WRITTEN)
-        img_state(idict) = DICT_WRITTEN;
-}
-
-int write_img_object(PDF pdf, image_dict * idict, int n)
-{
-    return write_epdf_object(pdf, idict, n);
-}
-
-void pdf_write_image(PDF pdf, int n)
-{
-    if (pdf->draftmode == 0)
-        write_img(pdf, idict_array[obj_data_ptr(pdf, n)]);
-}
-
-void check_pdfstream_dict(image_dict * idict)
-{
-    if (!img_is_bbox(idict))
-        normal_error("pdf backend","image.stream: no bbox given");
-    if (img_state(idict) < DICT_FILESCANNED)
-        img_state(idict) = DICT_FILESCANNED;
-}
-
-void write_pdfstream(PDF pdf, image_dict * idict)
-{
-    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "XObject");
-    pdf_dict_add_name(pdf, "Subtype", "Form");
-    pdf_dict_add_int(pdf, "FormType", 1);
-    pdf_add_name(pdf, "BBox");
-    pdf_begin_array(pdf);
-    pdf_add_real(pdf, sp2bp(img_bbox(idict)[0]));
-    pdf_add_real(pdf, sp2bp(img_bbox(idict)[1]));
-    pdf_add_real(pdf, sp2bp(img_bbox(idict)[2]));
-    pdf_add_real(pdf, sp2bp(img_bbox(idict)[3]));
-    pdf_end_array(pdf);
-    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) {
-        pdf_printf(pdf, "\n%s\n", img_attr(idict));
-    }
-    if (!img_nolength(idict)) {
-        pdf_dict_add_streaminfo(pdf);
-    }
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    if (img_pdfstream_stream(idict) != NULL) {
-        pdf_out_block(pdf, (const char *) img_pdfstream_stream(idict), img_pdfstream_size(idict));
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-}
-
-idict_entry *idict_ptr, *idict_array = NULL;
-size_t idict_limit;
-
-void idict_to_array(image_dict * idict)
-{
-    if (idict_ptr - idict_array == 0) {
-        /*tex align to count from 1 */
-        alloc_array(idict, 1, SMALL_BUF_SIZE);
-        idict_ptr++;
-    }
-    alloc_array(idict, 1, SMALL_BUF_SIZE);
-    *idict_ptr = idict;
-    idict_ptr++;
-}
-
-void pdf_dict_add_img_filename(PDF pdf, image_dict * idict)
-{
-    char *p;
-    if ((pdf_image_addfilename > 0) && ((pdf_suppress_optional_info & 2) == 0)) {
-        /*tex
-            For now |PTEX.FileName| is only used for \PDF, but we're prepared
-            for \JPG, \PNG, ...
-        */
-        if (! ( (img_type(idict) == IMG_TYPE_PDF) || (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) ))
-            return;
-        if (img_visiblefilename(idict) != NULL) {
-            if (strlen(img_visiblefilename(idict)) == 0) {
-                /*tex empty string blocks PTEX.FileName output */
-                return;
-            } else {
-                p = img_visiblefilename(idict);
-            }
-        } else {
-            /*tex unset so let's use the default */
-            p = img_filepath(idict);
-        }
-        /*tex write additional information */
-        pdf_add_name(pdf, "PTEX.FileName");
-        pdf_printf(pdf, " (%s)", convertStringToPDFString(p, strlen(p)));
-    }
-}
-
-/*tex
-
-To allow the use of box resources inside saved boxes in -ini mode, the
-information in the array has to be (un)dumped with the format. The next two
-routines take care of that.
-
-Most of the work involved in setting up the images is simply executed again. This
-solves the many possible errors resulting from the split in two separate runs.
-
-There was only one problem remaining: The |pdfversion| and
-|pdfinclusionerrorlevel| can have changed inbetween the call to |readimage| and
-dump time.
-
-Some of the dumped values are really type int, not integer,but since the macro
-falls back to |generic_dump| anyway, that does not matter.
-
-We might drop this feature as it makes no sense to store images in the format.
-
-*/
-
-#define dumpinteger generic_dump
-#define undumpinteger generic_undump
-
-/*tex
-
-(Un)dumping a string means dumping the allocation size, followed by the bytes.
-The trailing \.{\\0} is dumped as well, because that makes the code simpler. The
-rule specification ends up in |alt_rule|.
-
-*/
-
-scaled_whd scan_alt_rule(void)
-{
-    boolean loop;
-    scaled_whd alt_rule;
-    alt_rule.wd = null_flag;
-    alt_rule.ht = null_flag;
-    alt_rule.dp = null_flag;
-    do {
-        loop = false;
-        if (scan_keyword("width")) {
-            scan_normal_dimen();
-            alt_rule.wd = cur_val;
-            loop = true;
-        } else if (scan_keyword("height")) {
-            scan_normal_dimen();
-            alt_rule.ht = cur_val;
-            loop = true;
-        } else if (scan_keyword("depth")) {
-            scan_normal_dimen();
-            alt_rule.dp = cur_val;
-            loop = true;
-        }
-    } while (loop);
-    return alt_rule;
-}
-
-/*tex
-
-    This copy a file of arbitrary size to the buffer and flushed as needed.
-
-*/
-
-size_t read_file_to_buf(PDF pdf, FILE * f, size_t len)
-{
-    size_t i, j, k = 0;
-    while (len > 0) {
-        i = (size_t) (len > pdf->buf->size) ? (size_t) pdf->buf->size : len;
-        pdf_room(pdf, (int) i);
-        j = fread(pdf->buf->p, 1, i, f);
-        pdf->buf->p += j;
-        k += j;
-        len -= j;
-        if (i != j)
-            break;
-    }
-    return k;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/image/writejbig2.w
@@ -0,0 +1,852 @@
+% writejbig2.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% Copyright 2006-2013 Taco Hoekwater <taco@@luatex.org>
+% Copyright 2003-2013 Hartmut Henkel <hartmut@@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/>.
+
+@
+This is experimental JBIG2 image support to pdfTeX. JBIG2 image decoding
+is part of Adobe PDF-1.4, and requires Acroread 5.0 or later.
+
+References
+==========
+
+* 14492 FCD: Information technology -- coded representation of picture
+and audio information -- lossy/lossless coding of bi-level images /
+JBIG committee, 1999 July 16. This JBIG2 Working Draft is available from
+http://www.jpeg.org/public/fcd14492.pdf. The references in the C-code
+correspond to the sections of this document.
+
+* PDF Reference, 5th edition, version 1.6, 1985--2005 Adobe Systems
+Incorporated. Available online:
+
+http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf
+
+News
+====
+
+31 May 2006: no need to wait for |endoffileflag| in sequential access
+organization.
+
+10 May 2006: |ygetc()| for some catching of broken JBIG2 files; modify to
+accept Example 3.4 from PDFRef 5th ed. with short end-of-file segment.
+
+09 May 2006: |pages_maketree()| and |segments_maketree()| by AVL tree,
+some cleaning up.
+
+06 May 2006: File list replaced by AVL tree; |new_fileinfo()|,
+|new_pageinfo()|.
+
+04 May 2006: Updated for pdftex-1.40-beta-20060213.
+
+08 Jan. 2003: Added |flushjbig2page0objects()| function. Now at the end
+of the pdfTeX run all pending page0 objects are written out.
+
+08 Jan. 2003: Release on private webpage.
+
+04 Jan. 2003: Completely rewritten. Now with some data structures.
+Rudimentary local file and image bookkeeping. Multiple image inclusion
+from one JBIG2 file. Only required page0 segments are marked for
+inclusion.
+
+13 Nov. 2002: pdfcrypting removed.
+
+08 Dec. 2002: bug in page 0 stream writing repaired.
+Strategy for multiple page inclusion from same JBIG2 file: When writing
+1st image, create fresh PDF object for page 0, and include any page
+0 segments from complete file (even if these segments are not needed
+for image). When writing next image, check by filename comparison if
+PDF object for page 0 of this JBIG2 file has already been written. This
+can only remember the file name for the direct predecessor JBIG2 image
+(but images of other types might come inbetween). If such page 0 PDF
+object exists, reference it. Else create fresh one.
+
+09 Dec. 2002: JBIG2 seg. page numbers > 0 are now set to 1, see PDF Ref.
+
+@ @c
+#undef DEBUG
+
+#include "ptexlib.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "image/image.h"
+
+@ @c
+/* 7.3 Segment types */
+#define M_SymbolDictionary 0
+#define M_IntermediateTextRegion 4
+#define M_ImmediateTextRegion 6
+#define M_ImmediateLosslessTextRegion 7
+#define M_PatternDictionary 16
+#define M_IntermediateHalftoneRegion 20
+#define M_ImmediateHalftoneRegion 22
+#define M_ImmediateLosslessHalftoneRegion 23
+#define M_IntermediateGenericRegion 36
+#define M_ImmediateGenericRegion 38
+#define M_ImmediateLosslessGenericRegion 39
+#define M_IntermediateGenericRefinementRegion 40
+#define M_ImmediateGenericRefinementRegion 42
+#define M_ImmediateLosslessGenericRefinementRegion 43
+#define M_PageInformation 48
+#define M_EndOfPage 49
+#define M_EndOfStripe 50
+#define M_EndOfFile 51
+#define M_Profiles 52
+#define M_Tables 53
+#define M_Extension 62
+
+@ @c
+typedef enum { INITIAL, HAVEINFO, WRITEPDF } PHASE;
+
+typedef struct _LITEM {
+    struct _LITEM *prev;
+    struct _LITEM *next;
+    void *d; /* data */
+} LITEM;
+
+typedef struct _LIST {
+    LITEM *first;
+    LITEM *last;
+    struct avl_table *tree;
+} LIST;
+
+typedef struct _SEGINFO {
+    unsigned long segnum;
+    boolean isrefered;
+    boolean refers;
+    unsigned int seghdrflags;    /* set by readseghdr() */
+    boolean pageassocsizeflag;   /* set by readseghdr() */
+    unsigned int reftosegcount;  /* set by readseghdr() */
+    unsigned int countofrefered; /* set by readseghdr() */
+    unsigned int fieldlen;       /* set by readseghdr() */
+    unsigned int segnumwidth;    /* set by readseghdr() */
+    long segpage;                /* set by readseghdr() */
+    unsigned long segdatalen;    /* set by readseghdr() */
+    unsigned long hdrstart;      /* set by readseghdr() */
+    unsigned long hdrend;        /* set by readseghdr() */
+    unsigned long datastart;
+    unsigned long dataend;
+    boolean endofstripeflag;     /* set by checkseghdrflags() */
+    boolean endofpageflag;       /* set by checkseghdrflags() */
+    boolean pageinfoflag;        /* set by checkseghdrflags() */
+    boolean endoffileflag;       /* set by checkseghdrflags() */
+} SEGINFO;
+
+typedef struct _PAGEINFO {
+    LIST segments;               /* segments associated with page */
+    unsigned long pagenum;
+    unsigned int width;
+    unsigned int height;
+    unsigned int xres;
+    unsigned int yres;
+    unsigned int pagesegmentflags;
+    unsigned int stripinginfo;
+    unsigned int stripedheight;
+} PAGEINFO;
+
+typedef struct _FILEINFO {
+    FILE *file;
+    char *filepath;
+    long filesize;
+    LIST pages;                  /* not including page0 */
+    LIST page0;
+    unsigned int filehdrflags;   /* set by readfilehdr() */
+    boolean sequentialaccess;    /* set by readfilehdr() */
+    unsigned long numofpages;    /* set by readfilehdr() */
+    unsigned long streamstart;   /* set by |get_jbig2_info()| */
+    unsigned long pdfpage0objnum;
+    PHASE phase;
+} FILEINFO;
+
+@ @c
+static struct avl_table *file_tree = NULL;
+
+static int comp_file_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return strcmp(((const FILEINFO *) pa)->filepath,((const FILEINFO *) pb)->filepath);
+}
+
+static int comp_page_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return (int) (((const PAGEINFO *) pa)->pagenum - ((const PAGEINFO *) pb)->pagenum);
+}
+
+static int comp_segment_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return (int) (((const SEGINFO *) pa)->segnum - ((const SEGINFO *) pb)->segnum);
+}
+
+@ @c
+static int ygetc(FILE * stream)
+{
+    int c = getc(stream);
+    if (c < 0) {
+        if (c == EOF)
+            normal_error("readjbig2","premature end file");
+        else
+            normal_error("readjbig2","can't happen");
+    }
+    return c;
+}
+
+@ @c
+static void initlinkedlist(LIST * lp)
+{
+    lp->first = NULL;
+    lp->last = NULL;
+    lp->tree = NULL;
+}
+
+static LIST *litem_append(LIST * lp)
+{
+    LITEM *ip;
+    ip = xtalloc(1, LITEM);
+    if (lp->first == NULL) {
+        lp->first = ip;
+        ip->prev = NULL;
+    } else {
+        lp->last->next = ip;
+        ip->prev = lp->last;
+    }
+    lp->last = ip;
+    ip->next = NULL;
+    ip->d = NULL;
+    return lp;
+}
+
+@ @c
+static FILEINFO *new_fileinfo(void)
+{
+    FILEINFO *fip;
+    fip = xtalloc(1, FILEINFO);
+    fip->file = NULL;
+    fip->filepath = NULL;
+    fip->filesize = 0;
+    initlinkedlist(&(fip->pages));
+    initlinkedlist(&(fip->page0));
+    fip->filehdrflags = 0;
+    fip->sequentialaccess = false;
+    fip->numofpages = 0;
+    fip->streamstart = 0;
+    fip->pdfpage0objnum = 0;
+    fip->phase = INITIAL;
+    return fip;
+}
+
+@ @c
+static PAGEINFO *new_pageinfo(void)
+{
+    PAGEINFO *pip;
+    pip = xtalloc(1, PAGEINFO);
+    initlinkedlist(&(pip->segments));
+    pip->pagenum = 0;
+    pip->width = 0;
+    pip->height = 0;
+    pip->xres = 0;
+    pip->yres = 0;
+    pip->pagesegmentflags = 0;
+    pip->stripinginfo = 0;
+    pip->stripedheight = 0;
+    return pip;
+}
+
+@ @c
+static void init_seginfo(SEGINFO * sip)
+{
+    sip->segnum = 0;
+    sip->isrefered = false;
+    sip->refers = false;
+    sip->seghdrflags = 0;
+    sip->pageassocsizeflag = false;
+    sip->reftosegcount = 0;
+    sip->countofrefered = 0;
+    sip->fieldlen = 0;
+    sip->segnumwidth = 0;
+    sip->segpage = 0;
+    sip->segdatalen = 0;
+    sip->hdrstart = 0;
+    sip->hdrend = 0;
+    sip->datastart = 0;
+    sip->dataend = 0;
+    sip->endofstripeflag = false;
+    sip->endofpageflag = false;
+    sip->pageinfoflag = false;
+    sip->endoffileflag = false;
+}
+
+@ @c
+static void pages_maketree(LIST * plp)
+{
+    LITEM *ip;
+    void **aa;
+    assert(plp->tree == NULL);
+    plp->tree = avl_create(comp_page_entry, NULL, &avl_xallocator);
+    assert(plp->tree != NULL);
+    for (ip = plp->first; ip != NULL; ip = ip->next) {
+        aa = avl_probe(plp->tree, (PAGEINFO *) ip->d);
+        assert(aa != NULL);
+    }
+}
+
+@ @c
+static void segments_maketree(LIST * slp)
+{
+    LITEM *ip;
+    void **aa;
+    assert(slp->tree == NULL);
+    slp->tree = avl_create(comp_segment_entry, NULL, &avl_xallocator);
+    assert(slp->tree != NULL);
+    for (ip = slp->first; ip != NULL; ip = ip->next) {
+        aa = avl_probe(slp->tree, (SEGINFO *) ip->d);
+        assert(aa != NULL);
+    }
+}
+
+@ @c
+static PAGEINFO *find_pageinfo(LIST * plp, unsigned long pagenum)
+{
+    PAGEINFO tmp;
+    tmp.pagenum = pagenum;
+    assert(plp->tree != NULL);
+    return (PAGEINFO *) avl_find(plp->tree, &tmp);
+}
+
+@ @c
+static SEGINFO *find_seginfo(LIST * slp, unsigned long segnum)
+{
+    SEGINFO tmp;
+    tmp.segnum = segnum;
+    assert(slp->tree != NULL);
+    return (SEGINFO *) avl_find(slp->tree, &tmp);
+}
+
+@ @c
+unsigned int read2bytes(FILE * f)
+{
+    unsigned int c = (unsigned int) ygetc(f);
+    return (c << 8) + (unsigned int) ygetc(f);
+}
+
+@ @c
+unsigned int read4bytes(FILE * f)
+{
+    unsigned int l = read2bytes(f);
+    return (l << 16) + read2bytes(f);
+}
+
+@ @c
+static unsigned long getstreamlen(LITEM * slip, boolean refer)
+{
+    SEGINFO *sip;
+    unsigned long len = 0;
+    for (; slip != NULL; slip = slip->next) {
+        sip = slip->d;
+        if (refer || sip->isrefered)
+            len += sip->hdrend - sip->hdrstart + sip->dataend - sip->datastart;
+    }
+    return len;
+}
+
+@ @c
+static void readfilehdr(FILEINFO * fip)
+{
+    unsigned int i;
+    /* Annex D.4 File header syntax */
+    /* Annex D.4.1 ID string */
+    unsigned char jbig2_id[] = { 0x97, 'J', 'B', '2', 0x0d, 0x0a, 0x1a, 0x0a };
+    xfseek(fip->file, 0, SEEK_SET, fip->filepath);
+    for (i = 0; i < 8; i++)
+        if (ygetc(fip->file) != jbig2_id[i])
+            normal_error("readjbig2","ID string missing");
+    /* Annex D.4.2 File header flags */
+    fip->filehdrflags = (unsigned int) ygetc(fip->file);
+    fip->sequentialaccess = (fip->filehdrflags & 0x01) ? true : false;
+    if (fip->sequentialaccess) {        /* Annex D.1 vs. Annex D.2 */
+        xfseek(fip->file, 0, SEEK_END, fip->filepath);
+        fip->filesize = (long) xftello(fip->file, fip->filepath);
+        xfseek(fip->file, 9, SEEK_SET, fip->filepath);
+    }
+    /* Annex D.4.3 Number of pages */
+    if (!(fip->filehdrflags >> 1) & 0x01)       /* known number of pages */
+        fip->numofpages = read4bytes(fip->file);
+    /* --- at end of file header --- */
+}
+
+@ @c
+static void checkseghdrflags(SEGINFO * sip)
+{
+    sip->endofstripeflag = false;
+    sip->endofpageflag = false;
+    sip->pageinfoflag = false;
+    sip->endoffileflag = false;
+    /* 7.3 Segment types */
+    switch (sip->seghdrflags & 0x3f) {
+        case M_SymbolDictionary:
+        case M_IntermediateTextRegion:
+        case M_ImmediateTextRegion:
+        case M_ImmediateLosslessTextRegion:
+        case M_PatternDictionary:
+        case M_IntermediateHalftoneRegion:
+        case M_ImmediateHalftoneRegion:
+        case M_ImmediateLosslessHalftoneRegion:
+        case M_IntermediateGenericRegion:
+        case M_ImmediateGenericRegion:
+        case M_ImmediateLosslessGenericRegion:
+        case M_IntermediateGenericRefinementRegion:
+        case M_ImmediateGenericRefinementRegion:
+        case M_ImmediateLosslessGenericRefinementRegion:
+            break;
+        case M_PageInformation:
+            sip->pageinfoflag = true;
+            break;
+        case M_EndOfPage:
+            sip->endofpageflag = true;
+            break;
+        case M_EndOfStripe:
+            sip->endofstripeflag = true;
+            break;
+        case M_EndOfFile:
+            sip->endoffileflag = true;
+            break;
+        case M_Profiles:
+        case M_Tables:
+        case M_Extension:
+            break;
+        default:
+            normal_error("readjbig2","unknown segment type file");
+            break;
+    }
+}
+
+@ for first reading of file; return value tells if header been read
+
+@c
+static boolean readseghdr(FILEINFO * fip, SEGINFO * sip)
+{
+    unsigned int i;
+    sip->hdrstart = xftell(fip->file, fip->filepath);
+    if (fip->sequentialaccess && sip->hdrstart == (unsigned) fip->filesize)
+        return false;           /* no endoffileflag is ok for sequentialaccess */
+    /* 7.2.2 Segment number */
+    sip->segnum = read4bytes(fip->file);
+    /* 7.2.3 Segment header flags */
+    sip->seghdrflags = (unsigned int) ygetc(fip->file);
+    checkseghdrflags(sip);
+    if (fip->sequentialaccess && sip->endoffileflag)    /* accept shorter segment, */
+        return true;            /* makes it compliant with Example 3.4 of PDFRef. 5th ed. */
+    sip->pageassocsizeflag = ((sip->seghdrflags >> 6) & 0x01) ? true : false;
+    /* 7.2.4 Referred-to segment count and retention flags */
+    sip->reftosegcount = (unsigned int) ygetc(fip->file);
+    sip->countofrefered = sip->reftosegcount >> 5;
+    if (sip->countofrefered < 5)
+        sip->fieldlen = 1;
+    else {
+        sip->fieldlen = 5 + sip->countofrefered / 8;
+        xfseek(fip->file, sip->fieldlen - 1, SEEK_CUR, fip->filepath);
+    }
+    /* 7.2.5 Referred-to segment numbers */
+    if (sip->segnum <= 256)
+        sip->segnumwidth = 1;
+    else if (sip->segnum <= 65536)
+        sip->segnumwidth = 2;
+    else
+        sip->segnumwidth = 4;
+    for (i = 0; i < sip->countofrefered; i++) {
+        switch (sip->segnumwidth) {
+            case 1:
+                (void) ygetc(fip->file);
+                break;
+            case 2:
+                (void) read2bytes(fip->file);
+                break;
+            case 4:
+                (void) read4bytes(fip->file);
+                break;
+        }
+    }
+    /* 7.2.6 Segment page association */
+    if (sip->pageassocsizeflag)
+        sip->segpage = read4bytes(fip->file);
+    else
+        sip->segpage = ygetc(fip->file);
+    /* 7.2.7 Segment data length */
+    sip->segdatalen = read4bytes(fip->file);
+    sip->hdrend = (unsigned long) xftello(fip->file, fip->filepath);
+    /* ---- at end of segment header ---- */
+    return true;
+}
+
+@ @c
+static void checkseghdr(FILEINFO * fip, SEGINFO * sip);
+
+static void markpage0seg(FILEINFO * fip, unsigned long referedseg)
+{
+    PAGEINFO *pip;
+    SEGINFO *sip;
+    pip = fip->page0.first->d;
+    sip = find_seginfo(&(pip->segments), referedseg);
+    if (sip != NULL) {
+        if (!sip->refers && sip->countofrefered > 0)
+            checkseghdr(fip, sip);
+        sip->isrefered = true;
+    }
+}
+
+@ for writing, marks refered page0 segments, sets segpage > 0 to 1
+
+@c
+static void writeseghdr(PDF pdf, FILEINFO * fip, SEGINFO * sip)
+{
+    unsigned int i;
+    unsigned long referedseg = 0;
+    /* 7.2.2 Segment number */
+    /* 7.2.3 Segment header flags */
+    /* 7.2.4 Referred-to segment count and retention flags */
+    for (i = 0; i < 5 + sip->fieldlen; i++)
+        pdf_out(pdf, ygetc(fip->file));
+    /* 7.2.5 Referred-to segment numbers */
+    for (i = 0; i < sip->countofrefered; i++) {
+        switch (sip->segnumwidth) {
+            case 1:
+                referedseg = (unsigned long) ygetc(fip->file);
+                pdf_out(pdf, referedseg);
+                break;
+            case 2:
+                referedseg = read2bytes(fip->file);
+                pdf_out(pdf, (referedseg >> 8) & 0xff);
+                pdf_out(pdf, referedseg & 0xff);
+                break;
+            case 4:
+                referedseg = read4bytes(fip->file);
+                pdf_out(pdf, (referedseg >> 24) & 0xff);
+                pdf_out(pdf, (referedseg >> 16) & 0xff);
+                pdf_out(pdf, (referedseg >> 8) & 0xff);
+                pdf_out(pdf, referedseg & 0xff);
+                break;
+        }
+        if (fip->page0.last != NULL && !sip->refers)
+            markpage0seg(fip, referedseg);
+    }
+    if (sip->countofrefered > 0)
+        sip->refers = true;
+    /* 7.2.6 Segment page association */
+    if (sip->pageassocsizeflag)
+        for (i = 0; i < 3; i++) {
+            (void) ygetc(fip->file);
+            pdf_out(pdf, 0);
+        }
+    (void) ygetc(fip->file);
+    pdf_out(pdf, (unsigned char) ((sip->segpage > 0) ? 1 : 0));
+    /* 7.2.7 Segment data length */
+    for (i = 0; i < 4; i++)
+        pdf_out(pdf, ygetc(fip->file));
+    /* ---- at end of segment header ---- */
+}
+
+@ for recursive marking of refered page0 segments
+@c
+static void checkseghdr(FILEINFO * fip, SEGINFO * sip)
+{
+    unsigned int i;
+    unsigned long referedseg = 0;
+    /* 7.2.2 Segment number */
+    /* 7.2.3 Segment header flags */
+    /* 7.2.4 Referred-to segment count and retention flags */
+    xfseek(fip->file, 5 + sip->fieldlen, SEEK_CUR, fip->filepath);
+    /* 7.2.5 Referred-to segment numbers */
+    for (i = 0; i < sip->countofrefered; i++) {
+        switch (sip->segnumwidth) {
+            case 1:
+                referedseg = (unsigned long) ygetc(fip->file);
+                break;
+            case 2:
+                referedseg = read2bytes(fip->file);
+                break;
+            case 4:
+                referedseg = read4bytes(fip->file);
+                break;
+        }
+        if (!sip->refers)
+            markpage0seg(fip, referedseg);
+    }
+    if (sip->countofrefered > 0)
+        sip->refers = true;
+    /* 7.2.6 Segment page association */
+    /* 7.2.7 Segment data length */
+    if (sip->pageassocsizeflag)
+        xfseek(fip->file, 8, SEEK_CUR, fip->filepath);
+    else
+        xfseek(fip->file, 5, SEEK_CUR, fip->filepath);
+    /* ---- at end of segment header ---- */
+}
+
+@ @c
+static unsigned long findstreamstart(FILEINFO * fip)
+{
+    SEGINFO tmp;
+    assert(!fip->sequentialaccess);   /* D.2 Random-access organisation */
+    do                                /* find random-access stream start */
+        (void) readseghdr(fip, &tmp);
+    while (!tmp.endoffileflag);
+    fip->streamstart = tmp.hdrend;
+    readfilehdr(fip);
+    return fip->streamstart;
+}
+
+@ @c
+static void rd_jbig2_info(FILEINFO * fip)
+{
+    unsigned long seekdist = 0;    /* for sequential-access only */
+    unsigned long streampos = 0;   /* for random-access only */
+    unsigned long currentpage = 0;
+    boolean sipavail = false;
+    PAGEINFO *pip;
+    SEGINFO *sip = NULL;
+    LIST *plp, *slp;
+    fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE);
+    readfilehdr(fip);
+    if (!fip->sequentialaccess) /* D.2 Random-access organisation */
+        streampos = findstreamstart(fip);
+    while (true) {              /* loop over segments */
+        if (!sipavail) {
+            sip = xtalloc(1, SEGINFO);
+            sipavail = true;
+        }
+        init_seginfo(sip);
+        if (!readseghdr(fip, sip) || sip->endoffileflag)
+            break;
+        if (sip->segpage > 0) {
+            if (sip->segpage > (int) currentpage) {
+                plp = litem_append(&(fip->pages));
+                plp->last->d = new_pageinfo();
+                currentpage = (unsigned long) sip->segpage;
+            }
+            pip = fip->pages.last->d;
+        } else {
+            if (fip->page0.last == NULL) {
+                plp = litem_append(&(fip->page0));
+                plp->last->d = new_pageinfo();
+            }
+            pip = fip->page0.last->d;
+        }
+        if (!sip->endofpageflag) {
+            slp = litem_append(&(pip->segments));
+            slp->last->d = sip;
+            sipavail = false;
+        }
+        if (!fip->sequentialaccess)
+            sip->datastart = streampos;
+        else
+            sip->datastart = sip->hdrend;
+        sip->dataend = sip->datastart + sip->segdatalen;
+        if (!fip->sequentialaccess
+            && (sip->pageinfoflag || sip->endofstripeflag))
+            xfseeko(fip->file, (off_t) sip->datastart, SEEK_SET, fip->filepath);
+        seekdist = sip->segdatalen;
+        /* 7.4.8 Page information segment syntax */
+        if (sip->pageinfoflag) {
+            pip->pagenum = (unsigned long) sip->segpage;
+            pip->width = read4bytes(fip->file);
+            pip->height = read4bytes(fip->file);
+            pip->xres = read4bytes(fip->file);
+            pip->yres = read4bytes(fip->file);
+            pip->pagesegmentflags = (unsigned) ygetc(fip->file);
+            /* 7.4.8.6 Page striping information */
+            pip->stripinginfo = read2bytes(fip->file);
+            seekdist -= 19;
+        }
+        if (sip->endofstripeflag) {
+            pip->stripedheight = read4bytes(fip->file);
+            seekdist -= 4;
+        }
+        if (!fip->sequentialaccess
+            && (sip->pageinfoflag || sip->endofstripeflag))
+            xfseeko(fip->file, (off_t) sip->hdrend, SEEK_SET, fip->filepath);
+        if (!fip->sequentialaccess)
+            streampos += sip->segdatalen;
+        if (fip->sequentialaccess)
+            xfseeko(fip->file, (off_t) seekdist, SEEK_CUR, fip->filepath);
+        if (sip->endofpageflag && currentpage && (pip->stripinginfo >> 15))
+            pip->height = pip->stripedheight;
+    }
+    fip->phase = HAVEINFO;
+    if (sipavail)
+        xfree(sip);
+    xfclose(fip->file, fip->filepath);
+}
+
+@ @c
+static void wr_jbig2(PDF pdf, image_dict * idict, FILEINFO * fip,
+                     unsigned long page)
+{
+    LITEM *slip;
+    PAGEINFO *pip;
+    SEGINFO *sip;
+    unsigned long i;
+    if (page > 0) {
+        assert(idict != NULL);
+        pip = find_pageinfo(&(fip->pages), page);
+        assert(pip != NULL);
+        pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
+        pdf_begin_dict(pdf);
+        pdf_dict_add_name(pdf, "Type", "XObject");
+        pdf_dict_add_name(pdf, "Subtype", "Image");
+        pdf_dict_add_img_filename(pdf, idict);
+        pdf_dict_add_int(pdf, "Width", pip->width);
+        pdf_dict_add_int(pdf, "Height", pip->height);
+        pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
+        pdf_dict_add_int(pdf, "BitsPerComponent", 1);
+        pdf_dict_add_int(pdf, "Length", getstreamlen(pip->segments.first, true));
+        pdf_add_name(pdf, "Filter");
+        pdf_begin_array(pdf);
+        pdf_add_name(pdf, "JBIG2Decode");
+        pdf_end_array(pdf);
+        if (fip->page0.last != NULL) {
+            if (fip->pdfpage0objnum == 0) {
+                fip->pdfpage0objnum = (unsigned long) pdf_create_obj(pdf, obj_type_others, 0);
+            }
+            pdf_add_name(pdf, "DecodeParms");
+            pdf_begin_array(pdf);
+            pdf_begin_dict(pdf);
+            pdf_dict_add_ref(pdf, "JBIG2Globals", fip->pdfpage0objnum);
+            pdf_end_dict(pdf);
+            pdf_end_array(pdf);
+        }
+        pdf_end_dict(pdf);
+    } else {
+        assert(idict == NULL);
+        pip = find_pageinfo(&(fip->page0), page);
+        assert(pip != NULL);
+        pdf_begin_obj(pdf, (int) fip->pdfpage0objnum, OBJSTM_NEVER);
+        pdf_begin_dict(pdf);
+        pdf_dict_add_int(pdf, "Length", getstreamlen(pip->segments.first, false));
+        pdf_end_dict(pdf);
+    }
+    pdf_begin_stream(pdf);
+    fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE);
+    for (slip = pip->segments.first; slip != NULL; slip = slip->next) { /* loop over page segments */
+        sip = slip->d;
+        if (sip->isrefered || page > 0) {
+            xfseeko(fip->file, (off_t) sip->hdrstart, SEEK_SET, fip->filepath);
+            /* mark refered-to page 0 segments, change segpages > 1 to 1 */
+            writeseghdr(pdf, fip, sip);
+            xfseeko(fip->file, (off_t) sip->datastart, SEEK_SET, fip->filepath);
+            for (i = sip->datastart; i < sip->dataend; i++)
+                pdf_out(pdf, ygetc(fip->file));
+        }
+    }
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    xfclose(fip->file, fip->filepath);
+}
+
+@ @c
+boolean supported_jbig2(image_dict * idict)
+{
+    if (img_pdfmajorversion(idict) < 2 && img_pdfminorversion(idict) < 4) {
+        normal_error("readjbig2","you need to generate at least PDF 1.4");
+        return false;
+    } else {
+        return true;
+    }
+}
+
+@ @c
+void flush_jbig2_info(image_dict * idict)
+{
+    /* todo */
+}
+
+@ @c
+void read_jbig2_info(image_dict * idict)
+{
+    FILEINFO *fip, tmp;
+    PAGEINFO *pip;
+    img_type(idict) = IMG_TYPE_JBIG2; /* already set probably, see other read_... */
+    if (! supported_jbig2(idict)) {
+        /* already an error done */
+    }
+    if (img_pagenum(idict) < 1) {
+        normal_error("readjbig2","page must be > 0");
+    }
+    if (file_tree == NULL) {
+        file_tree = avl_create(comp_file_entry, NULL, &avl_xallocator);
+    }
+    tmp.filepath = img_filepath(idict);
+    fip = (FILEINFO *) avl_find(file_tree, &tmp);
+    if (fip == NULL) {
+        fip = new_fileinfo();
+        fip->filepath = xstrdup(img_filepath(idict));
+        avl_probe(file_tree, fip);
+    }
+    if (fip->phase == INITIAL) {
+        rd_jbig2_info(fip);
+        pages_maketree(&(fip->pages));
+        if (fip->page0.last != NULL) {
+            pages_maketree(&(fip->page0));
+            pip = fip->page0.first->d;
+            segments_maketree(&(pip->segments));
+        }
+    }
+    pip = find_pageinfo(&(fip->pages), (unsigned long) img_pagenum(idict));
+    if (pip == NULL) {
+        formatted_error("readjbig2","page %d not found in image file",(int) img_pagenum(idict));
+    }
+    img_totalpages(idict) = (int) fip->numofpages;
+    img_xsize(idict) = (int) pip->width;
+    img_ysize(idict) = (int) pip->height;
+    img_xres(idict) = (int) (pip->xres * 0.0254 + 0.5);
+    img_yres(idict) = (int) (pip->yres * 0.0254 + 0.5);
+    img_colordepth(idict) = 1;
+}
+
+@ @c
+void write_jbig2(PDF pdf, image_dict * idict)
+{
+    FILEINFO *fip, tmp;
+    PAGEINFO *pip;
+    assert(idict != NULL);
+    assert(file_tree != NULL);
+    tmp.filepath = img_filepath(idict);
+    fip = (FILEINFO *) avl_find(file_tree, &tmp);
+    assert(fip != NULL);
+    assert(fip->phase == HAVEINFO);     /* don't write before |rd_jbig2_info()| call */
+    pip = find_pageinfo(&(fip->pages), (unsigned long) img_pagenum(idict));
+    assert(pip != NULL);
+    wr_jbig2(pdf, idict, fip, pip->pagenum);
+    img_file(idict) = NULL;
+}
+
+@ @c
+void flush_jbig2_page0_objects(PDF pdf)
+{
+    FILEINFO *fip;
+    struct avl_traverser t;
+    if (file_tree != NULL) {
+        avl_t_init(&t, file_tree);
+        for (fip = avl_t_first(&t, file_tree); fip != NULL;
+             fip = avl_t_next(&t)) {
+            if (fip->page0.last != NULL)
+                wr_jbig2(pdf, NULL, fip, 0);    /* NULL: page0 */
+        }
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/image/writejbig2.c
+++ /dev/null
@@ -1,861 +0,0 @@
-/*
-
-writejbig2.c
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-Copyright 2006-2013 Taco Hoekwater <taco@luatex.org>
-Copyright 2003-2013 Hartmut Henkel <hartmut@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/>.
-
-*/
-
-/*tex
-
-This is experimental JBIG2 image support to pdfTeX. JBIG2 image decoding is part
-of Adobe PDF-1.4, and requires Acroread 5.0 or later.
-
-References
-==========
-
-* 14492 FCD: Information technology -- coded representation of picture
-and audio information -- lossy/lossless coding of bi-level images /
-JBIG committee, 1999 July 16. This JBIG2 Working Draft is available from
-http://www.jpeg.org/public/fcd14492.pdf. The references in the C-code
-correspond to the sections of this document.
-
-* PDF Reference, 5th edition, version 1.6, 1985--2005 Adobe Systems
-Incorporated. Available online:
-
-http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf
-
-News
-====
-
-31 May 2006: no need to wait for |endoffileflag| in sequential access
-organization.
-
-10 May 2006: |ygetc()| for some catching of broken JBIG2 files; modify to
-accept Example 3.4 from PDFRef 5th ed. with short end-of-file segment.
-
-09 May 2006: |pages_maketree()| and |segments_maketree()| by AVL tree,
-some cleaning up.
-
-06 May 2006: File list replaced by AVL tree; |new_fileinfo()|,
-|new_pageinfo()|.
-
-04 May 2006: Updated for pdftex-1.40-beta-20060213.
-
-08 Jan. 2003: Added |flushjbig2page0objects()| function. Now at the end
-of the pdfTeX run all pending page0 objects are written out.
-
-08 Jan. 2003: Release on private webpage.
-
-04 Jan. 2003: Completely rewritten. Now with some data structures.
-Rudimentary local file and image bookkeeping. Multiple image inclusion
-from one JBIG2 file. Only required page0 segments are marked for
-inclusion.
-
-13 Nov. 2002: pdfcrypting removed.
-
-08 Dec. 2002: bug in page 0 stream writing repaired.
-Strategy for multiple page inclusion from same JBIG2 file: When writing
-1st image, create fresh PDF object for page 0, and include any page
-0 segments from complete file (even if these segments are not needed
-for image). When writing next image, check by filename comparison if
-PDF object for page 0 of this JBIG2 file has already been written. This
-can only remember the file name for the direct predecessor JBIG2 image
-(but images of other types might come inbetween). If such page 0 PDF
-object exists, reference it. Else create fresh one.
-
-09 Dec. 2002: JBIG2 seg. page numbers > 0 are now set to 1, see PDF Ref.
-
-*/
-
-#undef DEBUG
-
-#include "ptexlib.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include "image/image.h"
-
-/*tex Table 7.3: Segment types */
-
-#define M_SymbolDictionary 0
-#define M_IntermediateTextRegion 4
-#define M_ImmediateTextRegion 6
-#define M_ImmediateLosslessTextRegion 7
-#define M_PatternDictionary 16
-#define M_IntermediateHalftoneRegion 20
-#define M_ImmediateHalftoneRegion 22
-#define M_ImmediateLosslessHalftoneRegion 23
-#define M_IntermediateGenericRegion 36
-#define M_ImmediateGenericRegion 38
-#define M_ImmediateLosslessGenericRegion 39
-#define M_IntermediateGenericRefinementRegion 40
-#define M_ImmediateGenericRefinementRegion 42
-#define M_ImmediateLosslessGenericRefinementRegion 43
-#define M_PageInformation 48
-#define M_EndOfPage 49
-#define M_EndOfStripe 50
-#define M_EndOfFile 51
-#define M_Profiles 52
-#define M_Tables 53
-#define M_Extension 62
-
-typedef enum { INITIAL, HAVEINFO, WRITEPDF } PHASE;
-
-typedef struct _LITEM {
-    struct _LITEM *prev;
-    struct _LITEM *next;
-    void *d;
-} LITEM;
-
-typedef struct _LIST {
-    LITEM *first;
-    LITEM *last;
-    struct avl_table *tree;
-} LIST;
-
-typedef struct _SEGINFO {
-    unsigned long segnum;
-    boolean isrefered;
-    boolean refers;
-    /*tex Set by |readseghdr|: */
-    unsigned int seghdrflags;
-    boolean pageassocsizeflag;
-    unsigned int reftosegcount;
-    unsigned int countofrefered;
-    unsigned int fieldlen;
-    unsigned int segnumwidth;
-    long segpage;
-    unsigned long segdatalen;
-    unsigned long hdrstart;
-    unsigned long hdrend;
-    unsigned long datastart;
-    unsigned long dataend;
-    /*tex Set by |checkseghdrflags|: */
-    boolean endofstripeflag;
-    boolean endofpageflag;
-    boolean pageinfoflag;
-    boolean endoffileflag;
-} SEGINFO;
-
-typedef struct _PAGEINFO {
-    LIST segments;
-    unsigned long pagenum;
-    unsigned int width;
-    unsigned int height;
-    unsigned int xres;
-    unsigned int yres;
-    unsigned int pagesegmentflags;
-    unsigned int stripinginfo;
-    unsigned int stripedheight;
-} PAGEINFO;
-
-typedef struct _FILEINFO {
-    FILE *file;
-    char *filepath;
-    long filesize;
-    /*tex Not including |page0|: */
-    LIST pages;
-    LIST page0;
-    /*tex Set by |readfilehdr| */
-    unsigned int filehdrflags;
-    boolean sequentialaccess;
-    unsigned long numofpages;
-    /*tex Set by |get_jbig2_info| */
-    unsigned long streamstart;
-    unsigned long pdfpage0objnum;
-    PHASE phase;
-} FILEINFO;
-
-static struct avl_table *file_tree = NULL;
-
-static int comp_file_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return strcmp(((const FILEINFO *) pa)->filepath,((const FILEINFO *) pb)->filepath);
-}
-
-static int comp_page_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return (int) (((const PAGEINFO *) pa)->pagenum - ((const PAGEINFO *) pb)->pagenum);
-}
-
-static int comp_segment_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return (int) (((const SEGINFO *) pa)->segnum - ((const SEGINFO *) pb)->segnum);
-}
-
-static int ygetc(FILE * stream)
-{
-    int c = getc(stream);
-    if (c < 0) {
-        if (c == EOF)
-            normal_error("readjbig2","premature end file");
-        else
-            normal_error("readjbig2","can't happen");
-    }
-    return c;
-}
-
-static void initlinkedlist(LIST * lp)
-{
-    lp->first = NULL;
-    lp->last = NULL;
-    lp->tree = NULL;
-}
-
-static LIST *litem_append(LIST * lp)
-{
-    LITEM *ip;
-    ip = xtalloc(1, LITEM);
-    if (lp->first == NULL) {
-        lp->first = ip;
-        ip->prev = NULL;
-    } else {
-        lp->last->next = ip;
-        ip->prev = lp->last;
-    }
-    lp->last = ip;
-    ip->next = NULL;
-    ip->d = NULL;
-    return lp;
-}
-
-static FILEINFO *new_fileinfo(void)
-{
-    FILEINFO *fip;
-    fip = xtalloc(1, FILEINFO);
-    fip->file = NULL;
-    fip->filepath = NULL;
-    fip->filesize = 0;
-    initlinkedlist(&(fip->pages));
-    initlinkedlist(&(fip->page0));
-    fip->filehdrflags = 0;
-    fip->sequentialaccess = false;
-    fip->numofpages = 0;
-    fip->streamstart = 0;
-    fip->pdfpage0objnum = 0;
-    fip->phase = INITIAL;
-    return fip;
-}
-
-static PAGEINFO *new_pageinfo(void)
-{
-    PAGEINFO *pip;
-    pip = xtalloc(1, PAGEINFO);
-    initlinkedlist(&(pip->segments));
-    pip->pagenum = 0;
-    pip->width = 0;
-    pip->height = 0;
-    pip->xres = 0;
-    pip->yres = 0;
-    pip->pagesegmentflags = 0;
-    pip->stripinginfo = 0;
-    pip->stripedheight = 0;
-    return pip;
-}
-
-static void init_seginfo(SEGINFO * sip)
-{
-    sip->segnum = 0;
-    sip->isrefered = false;
-    sip->refers = false;
-    sip->seghdrflags = 0;
-    sip->pageassocsizeflag = false;
-    sip->reftosegcount = 0;
-    sip->countofrefered = 0;
-    sip->fieldlen = 0;
-    sip->segnumwidth = 0;
-    sip->segpage = 0;
-    sip->segdatalen = 0;
-    sip->hdrstart = 0;
-    sip->hdrend = 0;
-    sip->datastart = 0;
-    sip->dataend = 0;
-    sip->endofstripeflag = false;
-    sip->endofpageflag = false;
-    sip->pageinfoflag = false;
-    sip->endoffileflag = false;
-}
-
-static void pages_maketree(LIST * plp)
-{
-    LITEM *ip;
-    void **aa;
-    assert(plp->tree == NULL);
-    plp->tree = avl_create(comp_page_entry, NULL, &avl_xallocator);
-    assert(plp->tree != NULL);
-    for (ip = plp->first; ip != NULL; ip = ip->next) {
-        aa = avl_probe(plp->tree, (PAGEINFO *) ip->d);
-        assert(aa != NULL);
-    }
-}
-
-static void segments_maketree(LIST * slp)
-{
-    LITEM *ip;
-    void **aa;
-    assert(slp->tree == NULL);
-    slp->tree = avl_create(comp_segment_entry, NULL, &avl_xallocator);
-    assert(slp->tree != NULL);
-    for (ip = slp->first; ip != NULL; ip = ip->next) {
-        aa = avl_probe(slp->tree, (SEGINFO *) ip->d);
-        assert(aa != NULL);
-    }
-}
-
-static PAGEINFO *find_pageinfo(LIST * plp, unsigned long pagenum)
-{
-    PAGEINFO tmp;
-    tmp.pagenum = pagenum;
-    assert(plp->tree != NULL);
-    return (PAGEINFO *) avl_find(plp->tree, &tmp);
-}
-
-static SEGINFO *find_seginfo(LIST * slp, unsigned long segnum)
-{
-    SEGINFO tmp;
-    tmp.segnum = segnum;
-    assert(slp->tree != NULL);
-    return (SEGINFO *) avl_find(slp->tree, &tmp);
-}
-
-unsigned int read2bytes(FILE * f)
-{
-    unsigned int c = (unsigned int) ygetc(f);
-    return (c << 8) + (unsigned int) ygetc(f);
-}
-
-unsigned int read4bytes(FILE * f)
-{
-    unsigned int l = read2bytes(f);
-    return (l << 16) + read2bytes(f);
-}
-
-static unsigned long getstreamlen(LITEM * slip, boolean refer)
-{
-    SEGINFO *sip;
-    unsigned long len = 0;
-    for (; slip != NULL; slip = slip->next) {
-        sip = slip->d;
-        if (refer || sip->isrefered)
-            len += sip->hdrend - sip->hdrstart + sip->dataend - sip->datastart;
-    }
-    return len;
-}
-
-static void readfilehdr(FILEINFO * fip)
-{
-    unsigned int i;
-    /*tex Annex D.4: File header syntax */
-    /*tex Annex D.4.1: ID string */
-    unsigned char jbig2_id[] = { 0x97, 'J', 'B', '2', 0x0d, 0x0a, 0x1a, 0x0a };
-    xfseek(fip->file, 0, SEEK_SET, fip->filepath);
-    for (i = 0; i < 8; i++)
-        if (ygetc(fip->file) != jbig2_id[i])
-            normal_error("readjbig2","ID string missing");
-    /*tex Annex D.4.2: File header flags */
-    fip->filehdrflags = (unsigned int) ygetc(fip->file);
-    fip->sequentialaccess = (fip->filehdrflags & 0x01) ? true : false;
-    if (fip->sequentialaccess) {
-        /*tex Annex D.1 vs. Annex D.2 */
-        xfseek(fip->file, 0, SEEK_END, fip->filepath);
-        fip->filesize = (long) xftello(fip->file, fip->filepath);
-        xfseek(fip->file, 9, SEEK_SET, fip->filepath);
-    }
-    /*tex Annex D.4.3: Number of pages */
-    if (( !(fip->filehdrflags >> 1)) & 0x01) {
-        /*tex The known number of pages: */
-        fip->numofpages = read4bytes(fip->file);
-    }
-    /*tex End of file header */
-}
-
-static void checkseghdrflags(SEGINFO * sip)
-{
-    sip->endofstripeflag = false;
-    sip->endofpageflag = false;
-    sip->pageinfoflag = false;
-    sip->endoffileflag = false;
-    /*tex Table 7.3: Segment types */
-    switch (sip->seghdrflags & 0x3f) {
-        case M_SymbolDictionary:
-        case M_IntermediateTextRegion:
-        case M_ImmediateTextRegion:
-        case M_ImmediateLosslessTextRegion:
-        case M_PatternDictionary:
-        case M_IntermediateHalftoneRegion:
-        case M_ImmediateHalftoneRegion:
-        case M_ImmediateLosslessHalftoneRegion:
-        case M_IntermediateGenericRegion:
-        case M_ImmediateGenericRegion:
-        case M_ImmediateLosslessGenericRegion:
-        case M_IntermediateGenericRefinementRegion:
-        case M_ImmediateGenericRefinementRegion:
-        case M_ImmediateLosslessGenericRefinementRegion:
-            break;
-        case M_PageInformation:
-            sip->pageinfoflag = true;
-            break;
-        case M_EndOfPage:
-            sip->endofpageflag = true;
-            break;
-        case M_EndOfStripe:
-            sip->endofstripeflag = true;
-            break;
-        case M_EndOfFile:
-            sip->endoffileflag = true;
-            break;
-        case M_Profiles:
-        case M_Tables:
-        case M_Extension:
-            break;
-        default:
-            normal_error("readjbig2","unknown segment type file");
-            break;
-    }
-}
-
-/*tex
-
-    For first reading of file; return value tells if header been read.
-
-*/
-
-static boolean readseghdr(FILEINFO * fip, SEGINFO * sip)
-{
-    unsigned int i;
-    sip->hdrstart = xftell(fip->file, fip->filepath);
-    if (fip->sequentialaccess && sip->hdrstart == (unsigned) fip->filesize) {
-        /*tex No endoffileflag is ok for sequential access. */
-        return false;
-    }
-    /*tex Table 7.2.2: Segment number */
-    sip->segnum = read4bytes(fip->file);
-    /*tex Table 7.2.3: Segment header flags */
-    sip->seghdrflags = (unsigned int) ygetc(fip->file);
-    checkseghdrflags(sip);
-    if (fip->sequentialaccess && sip->endoffileflag) {
-        /*
-            Accept shorter segment, makes it compliant with Example 3.4 of
-            PDFRef. 5th ed.
-        */
-        return true;
-    }
-    sip->pageassocsizeflag = ((sip->seghdrflags >> 6) & 0x01) ? true : false;
-    /*tex Table 7.2.4: Referred-to segment count and retention flags */
-    sip->reftosegcount = (unsigned int) ygetc(fip->file);
-    sip->countofrefered = sip->reftosegcount >> 5;
-    if (sip->countofrefered < 5)
-        sip->fieldlen = 1;
-    else {
-        sip->fieldlen = 5 + sip->countofrefered / 8;
-        xfseek(fip->file, sip->fieldlen - 1, SEEK_CUR, fip->filepath);
-    }
-    /*tex Table 7.2.5: Referred-to segment numbers */
-    if (sip->segnum <= 256)
-        sip->segnumwidth = 1;
-    else if (sip->segnum <= 65536)
-        sip->segnumwidth = 2;
-    else
-        sip->segnumwidth = 4;
-    for (i = 0; i < sip->countofrefered; i++) {
-        switch (sip->segnumwidth) {
-            case 1:
-                (void) ygetc(fip->file);
-                break;
-            case 2:
-                (void) read2bytes(fip->file);
-                break;
-            case 4:
-                (void) read4bytes(fip->file);
-                break;
-        }
-    }
-    /*tex Table 7.2.6: Segment page association */
-    if (sip->pageassocsizeflag)
-        sip->segpage = read4bytes(fip->file);
-    else
-        sip->segpage = ygetc(fip->file);
-    /*tex Table 7.2.7: Segment data length */
-    sip->segdatalen = read4bytes(fip->file);
-    sip->hdrend = (unsigned long) xftello(fip->file, fip->filepath);
-    /*tex End of segment header. */
-    return true;
-}
-
-static void checkseghdr(FILEINFO * fip, SEGINFO * sip);
-
-static void markpage0seg(FILEINFO * fip, unsigned long referedseg)
-{
-    PAGEINFO *pip;
-    SEGINFO *sip;
-    pip = fip->page0.first->d;
-    sip = find_seginfo(&(pip->segments), referedseg);
-    if (sip != NULL) {
-        if (!sip->refers && sip->countofrefered > 0)
-            checkseghdr(fip, sip);
-        sip->isrefered = true;
-    }
-}
-
-/*tex
-
-    For writing, marks refered page0 segments, sets segpage larger than
-    zero to one.
-
-*/
-
-static void writeseghdr(PDF pdf, FILEINFO * fip, SEGINFO * sip)
-{
-    unsigned int i;
-    unsigned long referedseg = 0;
-    /*tex Table 7.2.2: Segment number */
-    /*tex Table 7.2.3: Segment header flags */
-    /*tex Table 7.2.4: Referred-to segment count and retention flags */
-    for (i = 0; i < 5 + sip->fieldlen; i++)
-        pdf_out(pdf, ygetc(fip->file));
-    /*tex Table 7.2.5: Referred-to segment numbers */
-    for (i = 0; i < sip->countofrefered; i++) {
-        switch (sip->segnumwidth) {
-            case 1:
-                referedseg = (unsigned long) ygetc(fip->file);
-                pdf_out(pdf, referedseg);
-                break;
-            case 2:
-                referedseg = read2bytes(fip->file);
-                pdf_out(pdf, (referedseg >> 8) & 0xff);
-                pdf_out(pdf, referedseg & 0xff);
-                break;
-            case 4:
-                referedseg = read4bytes(fip->file);
-                pdf_out(pdf, (referedseg >> 24) & 0xff);
-                pdf_out(pdf, (referedseg >> 16) & 0xff);
-                pdf_out(pdf, (referedseg >> 8) & 0xff);
-                pdf_out(pdf, referedseg & 0xff);
-                break;
-        }
-        if (fip->page0.last != NULL && !sip->refers)
-            markpage0seg(fip, referedseg);
-    }
-    if (sip->countofrefered > 0)
-        sip->refers = true;
-    /*tex Table 7.2.6: Segment page association */
-    if (sip->pageassocsizeflag)
-        for (i = 0; i < 3; i++) {
-            (void) ygetc(fip->file);
-            pdf_out(pdf, 0);
-        }
-    (void) ygetc(fip->file);
-    pdf_out(pdf, (unsigned char) ((sip->segpage > 0) ? 1 : 0));
-    /*tex Table 7.2.7: Segment data length */
-    for (i = 0; i < 4; i++)
-        pdf_out(pdf, ygetc(fip->file));
-    /* End of segment header. */
-}
-
-/*tex
-
-    For recursive marking of refered page0 segments:
-
-*/
-
-static void checkseghdr(FILEINFO * fip, SEGINFO * sip)
-{
-    unsigned int i;
-    unsigned long referedseg = 0;
-    /*tex Table 7.2.2: Segment number */
-    /*tex Table 7.2.3: Segment header flags */
-    /*tex Table 7.2.4: Referred-to segment count and retention flags */
-    xfseek(fip->file, 5 + sip->fieldlen, SEEK_CUR, fip->filepath);
-    /*tex Table 7.2.5: Referred-to segment numbers */
-    for (i = 0; i < sip->countofrefered; i++) {
-        switch (sip->segnumwidth) {
-            case 1:
-                referedseg = (unsigned long) ygetc(fip->file);
-                break;
-            case 2:
-                referedseg = read2bytes(fip->file);
-                break;
-            case 4:
-                referedseg = read4bytes(fip->file);
-                break;
-        }
-        if (!sip->refers)
-            markpage0seg(fip, referedseg);
-    }
-    if (sip->countofrefered > 0)
-        sip->refers = true;
-    /*tex Table 7.2.6: Segment page association */
-    /*tex Table 7.2.7: Segment data length */
-    if (sip->pageassocsizeflag)
-        xfseek(fip->file, 8, SEEK_CUR, fip->filepath);
-    else
-        xfseek(fip->file, 5, SEEK_CUR, fip->filepath);
-    /*tex End of segment header. */
-}
-
-static unsigned long findstreamstart(FILEINFO * fip)
-{
-    SEGINFO tmp;
-    /*tex Table D.2: Random-access organisation */
-    do {
-        /*tex Find random-access stream start. */
-        (void) readseghdr(fip, &tmp);
-    } while (!tmp.endoffileflag);
-    fip->streamstart = tmp.hdrend;
-    readfilehdr(fip);
-    return fip->streamstart;
-}
-
-static void rd_jbig2_info(FILEINFO * fip)
-{
-    /*tex For sequential-access only: */
-    unsigned long seekdist = 0;
-    /*tex For random-access only: */
-    unsigned long streampos = 0;
-    unsigned long currentpage = 0;
-    boolean sipavail = false;
-    PAGEINFO *pip;
-    SEGINFO *sip = NULL;
-    LIST *plp, *slp;
-    fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE);
-    readfilehdr(fip);
-    if (!fip->sequentialaccess) {
-        /*tex Table D.2: Random-access organisation */
-        streampos = findstreamstart(fip);
-    }
-    while (true) {
-        /*tex Loop over segments: */
-        if (!sipavail) {
-            sip = xtalloc(1, SEGINFO);
-            sipavail = true;
-        }
-        init_seginfo(sip);
-        if (!readseghdr(fip, sip) || sip->endoffileflag)
-            break;
-        if (sip->segpage > 0) {
-            if (sip->segpage > (int) currentpage) {
-                plp = litem_append(&(fip->pages));
-                plp->last->d = new_pageinfo();
-                currentpage = (unsigned long) sip->segpage;
-            }
-            pip = fip->pages.last->d;
-        } else {
-            if (fip->page0.last == NULL) {
-                plp = litem_append(&(fip->page0));
-                plp->last->d = new_pageinfo();
-            }
-            pip = fip->page0.last->d;
-        }
-        if (!sip->endofpageflag) {
-            slp = litem_append(&(pip->segments));
-            slp->last->d = sip;
-            sipavail = false;
-        }
-        if (!fip->sequentialaccess)
-            sip->datastart = streampos;
-        else
-            sip->datastart = sip->hdrend;
-        sip->dataend = sip->datastart + sip->segdatalen;
-        if (!fip->sequentialaccess && (sip->pageinfoflag || sip->endofstripeflag))
-            xfseeko(fip->file, (off_t) sip->datastart, SEEK_SET, fip->filepath);
-        seekdist = sip->segdatalen;
-        /*tex Table 7.4.8: Page information segment syntax */
-        if (sip->pageinfoflag) {
-            pip->pagenum = (unsigned long) sip->segpage;
-            pip->width = read4bytes(fip->file);
-            pip->height = read4bytes(fip->file);
-            pip->xres = read4bytes(fip->file);
-            pip->yres = read4bytes(fip->file);
-            pip->pagesegmentflags = (unsigned) ygetc(fip->file);
-            /*tex Table 7.4.8.6: Page striping information */
-            pip->stripinginfo = read2bytes(fip->file);
-            seekdist -= 19;
-        }
-        if (sip->endofstripeflag) {
-            pip->stripedheight = read4bytes(fip->file);
-            seekdist -= 4;
-        }
-        if (!fip->sequentialaccess
-            && (sip->pageinfoflag || sip->endofstripeflag))
-            xfseeko(fip->file, (off_t) sip->hdrend, SEEK_SET, fip->filepath);
-        if (!fip->sequentialaccess)
-            streampos += sip->segdatalen;
-        if (fip->sequentialaccess)
-            xfseeko(fip->file, (off_t) seekdist, SEEK_CUR, fip->filepath);
-        if (sip->endofpageflag && currentpage && (pip->stripinginfo >> 15))
-            pip->height = pip->stripedheight;
-    }
-    fip->phase = HAVEINFO;
-    if (sipavail)
-        xfree(sip);
-    xfclose(fip->file, fip->filepath);
-}
-
-static void wr_jbig2(PDF pdf, image_dict * idict, FILEINFO * fip, unsigned long page)
-{
-    LITEM *slip;
-    PAGEINFO *pip;
-    SEGINFO *sip;
-    unsigned long i;
-    if (page > 0) {
-        assert(idict != NULL);
-        pip = find_pageinfo(&(fip->pages), page);
-        assert(pip != NULL);
-        pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
-        pdf_begin_dict(pdf);
-        pdf_dict_add_name(pdf, "Type", "XObject");
-        pdf_dict_add_name(pdf, "Subtype", "Image");
-        pdf_dict_add_img_filename(pdf, idict);
-        pdf_dict_add_int(pdf, "Width", pip->width);
-        pdf_dict_add_int(pdf, "Height", pip->height);
-        pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
-        pdf_dict_add_int(pdf, "BitsPerComponent", 1);
-        pdf_dict_add_int(pdf, "Length", getstreamlen(pip->segments.first, true));
-        pdf_add_name(pdf, "Filter");
-        pdf_begin_array(pdf);
-        pdf_add_name(pdf, "JBIG2Decode");
-        pdf_end_array(pdf);
-        if (fip->page0.last != NULL) {
-            if (fip->pdfpage0objnum == 0) {
-                fip->pdfpage0objnum = (unsigned long) pdf_create_obj(pdf, obj_type_others, 0);
-            }
-            pdf_add_name(pdf, "DecodeParms");
-            pdf_begin_array(pdf);
-            pdf_begin_dict(pdf);
-            pdf_dict_add_ref(pdf, "JBIG2Globals", fip->pdfpage0objnum);
-            pdf_end_dict(pdf);
-            pdf_end_array(pdf);
-        }
-        pdf_end_dict(pdf);
-    } else {
-        assert(idict == NULL);
-        pip = find_pageinfo(&(fip->page0), page);
-        assert(pip != NULL);
-        pdf_begin_obj(pdf, (int) fip->pdfpage0objnum, OBJSTM_NEVER);
-        pdf_begin_dict(pdf);
-        pdf_dict_add_int(pdf, "Length", getstreamlen(pip->segments.first, false));
-        pdf_end_dict(pdf);
-    }
-    pdf_begin_stream(pdf);
-    fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE);
-    for (slip = pip->segments.first; slip != NULL; slip = slip->next) {
-        /*tex Loop over page segments. */
-        sip = slip->d;
-        if (sip->isrefered || page > 0) {
-            xfseeko(fip->file, (off_t) sip->hdrstart, SEEK_SET, fip->filepath);
-            /*tex Mark refered-to page 0 segments, change segpages > 1 to 1. */
-            writeseghdr(pdf, fip, sip);
-            xfseeko(fip->file, (off_t) sip->datastart, SEEK_SET, fip->filepath);
-            for (i = sip->datastart; i < sip->dataend; i++)
-                pdf_out(pdf, ygetc(fip->file));
-        }
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    xfclose(fip->file, fip->filepath);
-}
-
-boolean supported_jbig2(image_dict * idict)
-{
-    if (img_pdfmajorversion(idict) < 2 && img_pdfminorversion(idict) < 4) {
-        normal_error("readjbig2","you need to generate at least PDF 1.4");
-        return false;
-    } else {
-        return true;
-    }
-}
-
-void flush_jbig2_info(image_dict * idict)
-{
-    /*tex Todo (or not). */
-}
-
-void read_jbig2_info(image_dict * idict)
-{
-    FILEINFO *fip, tmp;
-    PAGEINFO *pip;
-    /*tex Already set probably, see other |read_|. */
-    img_type(idict) = IMG_TYPE_JBIG2;
-    if (! supported_jbig2(idict)) {
-        /*tex Already an error seen? */
-    }
-    if (img_pagenum(idict) < 1) {
-        normal_error("readjbig2","page must be > 0");
-    }
-    if (file_tree == NULL) {
-        file_tree = avl_create(comp_file_entry, NULL, &avl_xallocator);
-    }
-    tmp.filepath = img_filepath(idict);
-    fip = (FILEINFO *) avl_find(file_tree, &tmp);
-    if (fip == NULL) {
-        fip = new_fileinfo();
-        fip->filepath = xstrdup(img_filepath(idict));
-        avl_probe(file_tree, fip);
-    }
-    if (fip->phase == INITIAL) {
-        rd_jbig2_info(fip);
-        pages_maketree(&(fip->pages));
-        if (fip->page0.last != NULL) {
-            pages_maketree(&(fip->page0));
-            pip = fip->page0.first->d;
-            segments_maketree(&(pip->segments));
-        }
-    }
-    pip = find_pageinfo(&(fip->pages), (unsigned long) img_pagenum(idict));
-    if (pip == NULL) {
-        formatted_error("readjbig2","page %d not found in image file",(int) img_pagenum(idict));
-    }
-    img_totalpages(idict) = (int) fip->numofpages;
-    img_xsize(idict) = (int) pip->width;
-    img_ysize(idict) = (int) pip->height;
-    img_xres(idict) = (int) (pip->xres * 0.0254 + 0.5);
-    img_yres(idict) = (int) (pip->yres * 0.0254 + 0.5);
-    img_colordepth(idict) = 1;
-}
-
-void write_jbig2(PDF pdf, image_dict * idict)
-{
-    FILEINFO *fip, tmp;
-    PAGEINFO *pip;
-    tmp.filepath = img_filepath(idict);
-    fip = (FILEINFO *) avl_find(file_tree, &tmp);
-    /*tex Don't write before |rd_jbig2_info()| call. */
-    pip = find_pageinfo(&(fip->pages), (unsigned long) img_pagenum(idict));
-    assert(pip != NULL);
-    wr_jbig2(pdf, idict, fip, pip->pagenum);
-    img_file(idict) = NULL;
-}
-
-void flush_jbig2_page0_objects(PDF pdf)
-{
-    FILEINFO *fip;
-    struct avl_traverser t;
-    if (file_tree != NULL) {
-        avl_t_init(&t, file_tree);
-        for (fip = avl_t_first(&t, file_tree); fip != NULL;
-             fip = avl_t_next(&t)) {
-            if (fip->page0.last != NULL)
-                /*tex |NULL|: page0 */
-                wr_jbig2(pdf, NULL, fip, 0);
-        }
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/image/writejp2.w
@@ -0,0 +1,297 @@
+% writejp2.w
+%
+% Copyright 2011-2013 Taco Hoekwater <taco@@luatex.org>
+% Copyright 2011-2013 Hartmut Henkel <hartmut@@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
+
+@ Basic JPEG~2000 image support. Section and Table references below:
+Information technology --- JPEG~2000 image coding system: Core coding system.
+ISO/IEC 15444-1, Second edition, 2004-09-15, file |15444-1annexi.pdf|.
+
+@c
+#include "ptexlib.h"
+#include <math.h>
+#include <assert.h>
+#include "image/image.h"
+#include "image/writejp2.h"
+#include "image/writejbig2.h"   /* read2bytes(), read4bytes() */
+
+/* Table 1.2 -- Defined boxes */
+#define BOX_JP   0x6A502020
+#define BOX_FTYP 0x66747970
+#define BOX_JP2H 0x6a703268
+#define BOX_IHDR 0x69686472
+#define BOX_BPCC 0x62706363
+#define BOX_COLR 0x636D6170
+#define BOX_CDEF 0x63646566
+#define BOX_RES  0x72657320
+#define BOX_RESC 0x72657363
+#define BOX_RESD 0x72657364
+#define BOX_JP2C 0x6A703263
+
+/* 1.4 Box definition */
+typedef struct {
+    uint64_t lbox;
+    unsigned int tbox;
+} hdr_struct;
+
+static uint64_t read8bytes(FILE * f)
+{
+    uint64_t l = read4bytes(f);
+    l = (l << 32) + read4bytes(f);
+    return l;
+}
+
+static hdr_struct read_boxhdr(image_dict * idict)
+{
+    hdr_struct hdr;
+    hdr.lbox = read4bytes(img_file(idict));
+    hdr.tbox = read4bytes(img_file(idict));
+    if (hdr.lbox == 1)
+        hdr.lbox = read8bytes(img_file(idict));
+    if (hdr.lbox == 0 && hdr.tbox != BOX_JP2C) {
+        normal_error("readjp2","LBox == 0");
+    }
+    return hdr;
+}
+
+/* 1.5.3.1 Image Header box */
+static void scan_ihdr(image_dict * idict)
+{
+    unsigned int height, width;
+    unsigned char bpc;
+    height = read4bytes(img_file(idict));
+    width = read4bytes(img_file(idict));
+    img_ysize(idict) = (int) height;
+    img_xsize(idict) = (int) width;
+    (void) read2bytes(img_file(idict)); /* nc */
+    bpc = (unsigned char) xgetc(img_file(idict));
+    img_colordepth(idict) = bpc + 1;
+    (void) xgetc(img_file(idict));      /* c */
+    (void) xgetc(img_file(idict));      /* unkc */
+    (void) xgetc(img_file(idict));      /* ipr */
+}
+
+/* 1.5.3.7.1 Capture Resolution box */
+/* 1.5.3.7.2 Default Display Resolution box */
+
+static void scan_resc_resd(image_dict * idict)
+{
+    unsigned int vr_n, vr_d, hr_n, hr_d;
+    unsigned char vr_e, hr_e;
+    double hr_, vr_;
+    vr_n = read2bytes(img_file(idict));
+    vr_d = read2bytes(img_file(idict));
+    hr_n = read2bytes(img_file(idict));
+    hr_d = read2bytes(img_file(idict));
+    vr_e = (unsigned char) xgetc(img_file(idict));
+    hr_e = (unsigned char) xgetc(img_file(idict));
+    hr_ = ((double) hr_n / hr_d) * exp(hr_e * log(10.0)) * 0.0254;
+    vr_ = ((double) vr_n / vr_d) * exp(vr_e * log(10.0)) * 0.0254;
+    img_xres(idict) = (int) (hr_ + 0.5);
+    img_yres(idict) = (int) (vr_ + 0.5);
+}
+
+/* 1.5.3.7 Resolution box (superbox) */
+
+static void scan_res(image_dict * idict, uint64_t epos_s)
+{
+    hdr_struct hdr;
+    uint64_t spos, epos;
+    epos = xftell(img_file(idict), img_filepath(idict));
+    while (1) {
+        spos = epos;
+        hdr = read_boxhdr(idict);
+        epos = spos + hdr.lbox;
+        switch (hdr.tbox) {
+            case (BOX_RESC):
+                /* arbitrarily: let BOX_RESD have precedence */
+                if (img_xres(idict) == 0 && img_yres(idict) == 0) {
+                    scan_resc_resd(idict);
+                    if (xftell(img_file(idict), img_filepath(idict)) != (long)epos)
+                        normal_error("readjp2","resc box size inconsistent");
+                }
+                break;
+            case (BOX_RESD):
+                scan_resc_resd(idict);
+                if (xftell(img_file(idict), img_filepath(idict)) != (long)epos)
+                    normal_error("readjp2","resd box size inconsistent");
+                break;
+            default:;
+        }
+        if (epos > epos_s)
+            normal_error("readjp2","res box size inconsistent");
+        if (epos == epos_s)
+            break;
+        xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
+    }
+}
+
+/* 1.5.3 JP2 Header box (superbox) */
+
+static boolean scan_jp2h(image_dict * idict, uint64_t epos_s)
+{
+    boolean ihdr_found = false;
+    hdr_struct hdr;
+    uint64_t spos, epos;
+    epos = xftell(img_file(idict), img_filepath(idict));
+    while (1) {
+        spos = epos;
+        hdr = read_boxhdr(idict);
+        epos = spos + hdr.lbox;
+        switch (hdr.tbox) {
+        case (BOX_IHDR):
+            scan_ihdr(idict);
+            if (xftell(img_file(idict), img_filepath(idict)) != (long)epos)
+                normal_error("readjp2","ihdr box size inconsistent");
+            ihdr_found = true;
+            break;
+        case (BOX_RES):
+            scan_res(idict, epos);
+            break;
+        default:;
+        }
+        if (epos > epos_s)
+            normal_error("readjp2","jp2h box size inconsistent");
+        if (epos == epos_s)
+            break;
+        xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
+    }
+    return ihdr_found;
+}
+
+static void close_and_cleanup_jp2(image_dict * idict)
+{
+    /* if one of then is not NULL we already cleaned up */
+    if (img_file(idict) != NULL) {
+        xfclose(img_file(idict), img_filepath(idict));
+        img_file(idict) = NULL;
+    }
+    if (img_jp2_ptr(idict) != NULL) {
+        xfree(img_jp2_ptr(idict));
+    }
+}
+
+void flush_jp2_info(image_dict * idict)
+{
+    close_and_cleanup_jp2(idict);
+}
+
+void read_jp2_info(image_dict * idict)
+{
+    boolean ihdr_found = false;
+    hdr_struct hdr;
+    uint64_t spos, epos;
+    if (img_type(idict) != IMG_TYPE_JP2) {
+        normal_error("readjp2","conflicting image dictionary");
+    }
+    if (img_file(idict) != NULL) {
+        normal_error("readjp2","image data already read");
+    }
+    img_totalpages(idict) = 1;
+    img_pagenum(idict) = 1;
+    img_xres(idict) = img_yres(idict) = 0;
+    img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE);
+    img_jp2_ptr(idict) = xtalloc(1, jp2_img_struct);
+    xfseek(img_file(idict), 0, SEEK_END, img_filepath(idict));
+    img_jp2_ptr(idict)->length = (int) xftell(img_file(idict), img_filepath(idict));
+    xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict));
+    if (sizeof(uint64_t) < 8) {
+        normal_error("readjp2","size problem");
+    }
+    spos = epos = 0;
+    /* 1.5.1 JPEG 2000 Signature box */
+    hdr = read_boxhdr(idict);
+    epos = spos + hdr.lbox;
+    xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
+    /* 1.5.2 File Type box */
+    spos = epos;
+    hdr = read_boxhdr(idict);
+    if (hdr.tbox != BOX_FTYP) {
+        normal_error("readjp2","missing ftyp box");
+    }
+    epos = spos + hdr.lbox;
+    xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
+    while (!ihdr_found) {
+        spos = epos;
+        hdr = read_boxhdr(idict);
+        epos = spos + hdr.lbox;
+        switch (hdr.tbox) {
+        case BOX_JP2H:
+            ihdr_found = scan_jp2h(idict, epos);
+            break;
+        case BOX_JP2C:
+            if (!ihdr_found)
+                normal_error("readjp2","no ihdr box found");
+            break;
+        default:;
+        }
+        xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
+    }
+    if (! img_keepopen(idict)) {
+        close_and_cleanup_jp2(idict);
+    }
+}
+
+static void reopen_jp2(image_dict * idict)
+{
+    int width, height, xres, yres;
+    width = img_xsize(idict);
+    height = img_ysize(idict);
+    xres = img_xres(idict);
+    yres = img_yres(idict);
+    /*
+        we need to make sure that the file kept open
+    */
+    img_keepopen(idict) = 1;
+    read_jp2_info(idict);
+    if (width != img_xsize(idict) || height != img_ysize(idict)
+            || xres != img_xres(idict) || yres != img_yres(idict)) {
+        normal_error("writejp2","image dimensions have changed");
+    }
+}
+
+void write_jp2(PDF pdf, image_dict * idict)
+{
+    long unsigned l;
+    if (img_file(idict) == NULL) {
+        reopen_jp2(idict);
+    }
+    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "XObject");
+    pdf_dict_add_name(pdf, "Subtype", "Image");
+    pdf_dict_add_img_filename(pdf, idict);
+    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0)
+        pdf_printf(pdf, "\n%s\n", img_attr(idict));
+    pdf_dict_add_int(pdf, "Width", (int) img_xsize(idict));
+    pdf_dict_add_int(pdf, "Height", (int) img_ysize(idict));
+    pdf_dict_add_int(pdf, "Length", (int) img_jp2_ptr(idict)->length);
+    pdf_dict_add_name(pdf, "Filter", "JPXDecode");
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    l = (long unsigned int) img_jp2_ptr(idict)->length;
+    xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict));
+    if (read_file_to_buf(pdf, img_file(idict), l) != l)
+        normal_error("writejp2","fread failed");
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    /* always */
+    close_and_cleanup_jp2(idict);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/image/writejp2.c
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
-
-writejp2.c
-
-Copyright 2011-2013 Taco Hoekwater <taco@luatex.org>
-Copyright 2011-2013 Hartmut Henkel <hartmut@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/>.
-
-*/
-
-/*tex
-
-    Basic JPEG~2000 image support. Section and Table references below:
-    Information technology --- JPEG~2000 image coding system: Core coding system.
-    ISO/IEC 15444-1, Second edition, 2004-09-15, file |15444-1annexi.pdf|.
-
-*/
-
-#include "ptexlib.h"
-#include <math.h>
-#include <assert.h>
-#include "image/image.h"
-#include "image/writejp2.h"
-#include "image/writejbig2.h"
-
-/*tex Table 1.2: Defined boxes */
-
-#define BOX_JP   0x6A502020
-#define BOX_FTYP 0x66747970
-#define BOX_JP2H 0x6a703268
-#define BOX_IHDR 0x69686472
-#define BOX_BPCC 0x62706363
-#define BOX_COLR 0x636D6170
-#define BOX_CDEF 0x63646566
-#define BOX_RES  0x72657320
-#define BOX_RESC 0x72657363
-#define BOX_RESD 0x72657364
-#define BOX_JP2C 0x6A703263
-
-/*tex Table 1.4: Box definition */
-
-typedef struct {
-    uint64_t lbox;
-    unsigned int tbox;
-} hdr_struct;
-
-static uint64_t read8bytes(FILE * f)
-{
-    uint64_t l = read4bytes(f);
-    l = (l << 32) + read4bytes(f);
-    return l;
-}
-
-static hdr_struct read_boxhdr(image_dict * idict)
-{
-    hdr_struct hdr;
-    hdr.lbox = read4bytes(img_file(idict));
-    hdr.tbox = read4bytes(img_file(idict));
-    if (hdr.lbox == 1)
-        hdr.lbox = read8bytes(img_file(idict));
-    if (hdr.lbox == 0 && hdr.tbox != BOX_JP2C) {
-        normal_error("readjp2","LBox == 0");
-    }
-    return hdr;
-}
-
-/*tex Table 1.5.3.1: Image Header box */
-
-static void scan_ihdr(image_dict * idict)
-{
-    unsigned int height, width;
-    unsigned char bpc;
-    height = read4bytes(img_file(idict));
-    width = read4bytes(img_file(idict));
-    img_ysize(idict) = (int) height;
-    img_xsize(idict) = (int) width;
-    (void) read2bytes(img_file(idict)); /* nc */
-    bpc = (unsigned char) xgetc(img_file(idict));
-    img_colordepth(idict) = bpc + 1;
-    (void) xgetc(img_file(idict));      /* c */
-    (void) xgetc(img_file(idict));      /* unkc */
-    (void) xgetc(img_file(idict));      /* ipr */
-}
-
-/*tex Table 1.5.3.7.1: Capture Resolution box */
-
-/*tex Table 1.5.3.7.2: Default Display Resolution box */
-
-static void scan_resc_resd(image_dict * idict)
-{
-    unsigned int vr_n, vr_d, hr_n, hr_d;
-    unsigned char vr_e, hr_e;
-    double hr_, vr_;
-    vr_n = read2bytes(img_file(idict));
-    vr_d = read2bytes(img_file(idict));
-    hr_n = read2bytes(img_file(idict));
-    hr_d = read2bytes(img_file(idict));
-    vr_e = (unsigned char) xgetc(img_file(idict));
-    hr_e = (unsigned char) xgetc(img_file(idict));
-    hr_ = ((double) hr_n / hr_d) * exp(hr_e * log(10.0)) * 0.0254;
-    vr_ = ((double) vr_n / vr_d) * exp(vr_e * log(10.0)) * 0.0254;
-    img_xres(idict) = (int) (hr_ + 0.5);
-    img_yres(idict) = (int) (vr_ + 0.5);
-}
-
-/*tex Table 1.5.3.7: Resolution box (superbox) */
-
-static void scan_res(image_dict * idict, uint64_t epos_s)
-{
-    hdr_struct hdr;
-    uint64_t spos, epos;
-    epos = xftell(img_file(idict), img_filepath(idict));
-    while (1) {
-        spos = epos;
-        hdr = read_boxhdr(idict);
-        epos = spos + hdr.lbox;
-        switch (hdr.tbox) {
-            case (BOX_RESC):
-                /*tex arbitrary: let BOX_RESD have precedence */
-                if (img_xres(idict) == 0 && img_yres(idict) == 0) {
-                    scan_resc_resd(idict);
-                    if (xftell(img_file(idict), img_filepath(idict)) != (long)epos)
-                        normal_error("readjp2","resc box size inconsistent");
-                }
-                break;
-            case (BOX_RESD):
-                scan_resc_resd(idict);
-                if (xftell(img_file(idict), img_filepath(idict)) != (long)epos)
-                    normal_error("readjp2","resd box size inconsistent");
-                break;
-            default:;
-        }
-        if (epos > epos_s)
-            normal_error("readjp2","res box size inconsistent");
-        if (epos == epos_s)
-            break;
-        xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
-    }
-}
-
-/*tex Table 1.5.3: JP2 Header box (superbox) */
-
-static boolean scan_jp2h(image_dict * idict, uint64_t epos_s)
-{
-    boolean ihdr_found = false;
-    hdr_struct hdr;
-    uint64_t spos, epos;
-    epos = xftell(img_file(idict), img_filepath(idict));
-    while (1) {
-        spos = epos;
-        hdr = read_boxhdr(idict);
-        epos = spos + hdr.lbox;
-        switch (hdr.tbox) {
-        case (BOX_IHDR):
-            scan_ihdr(idict);
-            if (xftell(img_file(idict), img_filepath(idict)) != (long)epos)
-                normal_error("readjp2","ihdr box size inconsistent");
-            ihdr_found = true;
-            break;
-        case (BOX_RES):
-            scan_res(idict, epos);
-            break;
-        default:;
-        }
-        if (epos > epos_s)
-            normal_error("readjp2","jp2h box size inconsistent");
-        if (epos == epos_s)
-            break;
-        xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
-    }
-    return ihdr_found;
-}
-
-static void close_and_cleanup_jp2(image_dict * idict)
-{
-    /*tex If one of then is not NULL we already cleaned up. */
-    if (img_file(idict) != NULL) {
-        xfclose(img_file(idict), img_filepath(idict));
-        img_file(idict) = NULL;
-    }
-    if (img_jp2_ptr(idict) != NULL) {
-        xfree(img_jp2_ptr(idict));
-    }
-}
-
-void flush_jp2_info(image_dict * idict)
-{
-    close_and_cleanup_jp2(idict);
-}
-
-void read_jp2_info(image_dict * idict)
-{
-    boolean ihdr_found = false;
-    hdr_struct hdr;
-    uint64_t spos, epos;
-    if (img_type(idict) != IMG_TYPE_JP2) {
-        normal_error("readjp2","conflicting image dictionary");
-    }
-    if (img_file(idict) != NULL) {
-        normal_error("readjp2","image data already read");
-    }
-    img_totalpages(idict) = 1;
-    img_pagenum(idict) = 1;
-    img_xres(idict) = img_yres(idict) = 0;
-    img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE);
-    img_jp2_ptr(idict) = xtalloc(1, jp2_img_struct);
-    xfseek(img_file(idict), 0, SEEK_END, img_filepath(idict));
-    img_jp2_ptr(idict)->length = (int) xftell(img_file(idict), img_filepath(idict));
-    xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict));
-    if (sizeof(uint64_t) < 8) {
-        normal_error("readjp2","size problem");
-    }
-    spos = epos = 0;
-    /*tex Table 1.5.1: JPEG 2000 Signature box */
-    hdr = read_boxhdr(idict);
-    epos = spos + hdr.lbox;
-    xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
-    /*tex Table 1.5.2: File Type box */
-    spos = epos;
-    hdr = read_boxhdr(idict);
-    if (hdr.tbox != BOX_FTYP) {
-        normal_error("readjp2","missing ftyp box");
-    }
-    epos = spos + hdr.lbox;
-    xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
-    while (!ihdr_found) {
-        spos = epos;
-        hdr = read_boxhdr(idict);
-        epos = spos + hdr.lbox;
-        switch (hdr.tbox) {
-        case BOX_JP2H:
-            ihdr_found = scan_jp2h(idict, epos);
-            break;
-        case BOX_JP2C:
-            if (!ihdr_found)
-                normal_error("readjp2","no ihdr box found");
-            break;
-        default:;
-        }
-        xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict));
-    }
-    if (! img_keepopen(idict)) {
-        close_and_cleanup_jp2(idict);
-    }
-}
-
-static void reopen_jp2(image_dict * idict)
-{
-    int width, height, xres, yres;
-    width = img_xsize(idict);
-    height = img_ysize(idict);
-    xres = img_xres(idict);
-    yres = img_yres(idict);
-    /*tex We need to make sure that the file kept open. */
-    img_keepopen(idict) = 1;
-    read_jp2_info(idict);
-    if (width != img_xsize(idict) || height != img_ysize(idict)
-            || xres != img_xres(idict) || yres != img_yres(idict)) {
-        normal_error("writejp2","image dimensions have changed");
-    }
-}
-
-void write_jp2(PDF pdf, image_dict * idict)
-{
-    long unsigned l;
-    if (img_file(idict) == NULL) {
-        reopen_jp2(idict);
-    }
-    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "XObject");
-    pdf_dict_add_name(pdf, "Subtype", "Image");
-    pdf_dict_add_img_filename(pdf, idict);
-    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0)
-        pdf_printf(pdf, "\n%s\n", img_attr(idict));
-    pdf_dict_add_int(pdf, "Width", (int) img_xsize(idict));
-    pdf_dict_add_int(pdf, "Height", (int) img_ysize(idict));
-    pdf_dict_add_int(pdf, "Length", (int) img_jp2_ptr(idict)->length);
-    pdf_dict_add_name(pdf, "Filter", "JPXDecode");
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    l = (long unsigned int) img_jp2_ptr(idict)->length;
-    xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict));
-    if (read_file_to_buf(pdf, img_file(idict), l) != l)
-        normal_error("writejp2","fread failed");
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    /*tex We always:*/
-    close_and_cleanup_jp2(idict);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/image/writejpg.w
@@ -0,0 +1,593 @@
+% writejpg.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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 <assert.h>
+#include "image/image.h"
+#include "image/writejpg.h"
+
+@ @c
+#define JPG_GRAY  1  /* Gray color space, use /DeviceGray    */
+#define JPG_RGB   3  /* RGB color space, use /DeviceRGB      */
+#define JPG_CMYK  4  /* CMYK color space, use /DeviceCMYK    */
+
+typedef enum {
+    M_SOF0  = 0xc0,  /* baseline DCT                         */
+    M_SOF1  = 0xc1,  /* extended sequential DCT              */
+    M_SOF2  = 0xc2,  /* progressive DCT                      */
+    M_SOF3  = 0xc3,  /* lossless (sequential)                */
+
+    M_SOF5  = 0xc5,  /* differential sequential DCT          */
+    M_SOF6  = 0xc6,  /* differential progressive DCT         */
+    M_SOF7  = 0xc7,  /* differential lossless (sequential)   */
+
+    M_JPG   = 0xc8,  /* reserved for JPEG extensions         */
+    M_SOF9  = 0xc9,  /* extended sequential DCT              */
+    M_SOF10 = 0xca,  /* progressive DCT                      */
+    M_SOF11 = 0xcb,  /* lossless (sequential)                */
+
+    M_SOF13 = 0xcd,  /* differential sequential DCT          */
+    M_SOF14 = 0xce,  /* differential progressive DCT         */
+    M_SOF15 = 0xcf,  /* differential lossless (sequential)   */
+
+    M_DHT   = 0xc4,  /* define Huffman table(s)              */
+
+    M_DAC   = 0xcc,  /* define arithmetic conditioning table */
+
+    M_RST0  = 0xd0,  /* restart                              */
+    M_RST1  = 0xd1,  /* restart                              */
+    M_RST2  = 0xd2,  /* restart                              */
+    M_RST3  = 0xd3,  /* restart                              */
+    M_RST4  = 0xd4,  /* restart                              */
+    M_RST5  = 0xd5,  /* restart                              */
+    M_RST6  = 0xd6,  /* restart                              */
+    M_RST7  = 0xd7,  /* restart                              */
+
+    M_SOI   = 0xd8,  /* start of image                       */
+    M_EOI   = 0xd9,  /* end of image                         */
+    M_SOS   = 0xda,  /* start of scan                        */
+    M_DQT   = 0xdb,  /* define quantization tables           */
+    M_DNL   = 0xdc,  /* define number of lines               */
+    M_DRI   = 0xdd,  /* define restart interval              */
+    M_DHP   = 0xde,  /* define hierarchical progression      */
+    M_EXP   = 0xdf,  /* expand reference image(s)            */
+
+    M_APP0  = 0xe0,  /* application marker, used for JFIF    */
+    M_APP1  = 0xe1,  /* application marker                   */
+    M_APP2  = 0xe2,  /* application marker                   */
+    M_APP3  = 0xe3,  /* application marker                   */
+    M_APP4  = 0xe4,  /* application marker                   */
+    M_APP5  = 0xe5,  /* application marker                   */
+    M_APP6  = 0xe6,  /* application marker                   */
+    M_APP7  = 0xe7,  /* application marker                   */
+    M_APP8  = 0xe8,  /* application marker                   */
+    M_APP9  = 0xe9,  /* application marker                   */
+    M_APP10 = 0xea,  /* application marker                   */
+    M_APP11 = 0xeb,  /* application marker                   */
+    M_APP12 = 0xec,  /* application marker                   */
+    M_APP13 = 0xed,  /* application marker                   */
+    M_APP14 = 0xee,  /* application marker, used by Adobe    */
+    M_APP15 = 0xef,  /* application marker                   */
+
+    M_JPG0  = 0xf0,  /* reserved for JPEG extensions         */
+    M_JPG13 = 0xfd,  /* reserved for JPEG extensions         */
+    M_COM   = 0xfe,  /* comment                              */
+
+    M_TEM   = 0x01,  /* temporary use                        */
+
+    M_ERROR = 0x100  /* dummy marker, internal use only      */
+} JPEG_MARKER;
+
+@ @c
+static unsigned int read_exif_bytes(unsigned char **p, int n, int b)
+{
+    unsigned int rval = 0;
+    unsigned char *pp = *p;
+    if (b) {
+        switch (n) {
+            case 4:
+                rval += *pp++; rval <<= 8;
+                rval += *pp++; rval <<= 8;
+            case 2:
+                rval += *pp++; rval <<= 8;
+                rval += *pp;
+                break;
+        }
+    } else {
+        pp += n;
+        switch (n) {
+            case 4:
+                rval += *--pp; rval <<= 8;
+                rval += *--pp; rval <<= 8;
+            case 2:
+                rval += *--pp; rval <<= 8;
+                rval += *--pp;
+                break;
+        }
+    }
+    *p += n;
+    return rval;
+}
+
+@ The Exif block can contain the data on the resolution in two forms:
+XResolution, YResolution and ResolutionUnit (tag 282, 283 and 296)
+as well as PixelPerUnitX, PixelPerUnitY and PixelUnit (tag 0x5111,
+0x5112 and 0x5110). Tags 282, 293 and 296 have the priority,
+with ResolutionUnit set to inch by default, then
+tag 0x5110, 0x5111 and 0x5112, where the only valid value for PixelUnit is 0.0254,
+and finally the given value xx and yy,
+choosen if the Exif x and y resolution are not strictly positive.
+
+
+@ @c
+static void read_APP1_Exif (FILE *fp, unsigned short length, int *xx, int *yy, int *or)
+{
+    /* this doesn't save the data, just reads the tags we need */
+    /* based on info from http://www.exif.org/Exif2-2.PDF */
+    unsigned char *buffer = (unsigned char *)xmalloc(length);
+    unsigned char *p, *rp;
+    unsigned char *tiff_header;
+    char bigendian;
+    int i;
+    int num_fields, tag, type;
+    int value = 0;/* silence uninitialized warnings */
+    unsigned int num = 0;
+    unsigned int den = 0;
+    boolean found_x = false;
+    boolean found_y = false;
+    int tempx = 0;
+    int tempy = 0;
+    double xres = 0;
+    double yres = 0;
+    double res_unit = 1.0;
+    unsigned int xres_ms = 0;
+    unsigned int yres_ms = 0;
+    double res_unit_ms = 0;
+    boolean found_x_ms = false;
+    boolean found_y_ms = false;
+    boolean found_res= false;
+
+    int orientation = 1;
+    size_t ret_len;
+    ret_len = fread(buffer, length, 1, fp);
+    if (ret_len != 1)
+        goto err ;
+    p = buffer;
+    while ((p < buffer + length) && (*p == 0))
+        ++p;
+    tiff_header = p;
+    if ((*p == 'M') && (*(p+1) == 'M'))
+        bigendian = 1;
+    else if ((*p == 'I') && (*(p+1) == 'I'))
+        bigendian = 0;
+    else
+        goto err;
+    p += 2;
+    i = read_exif_bytes(&p, 2, bigendian);
+    if (i != 42)
+        goto err;
+    i = read_exif_bytes(&p, 4, bigendian);
+    p = tiff_header + i;
+    num_fields = read_exif_bytes(&p, 2, bigendian);
+    while (num_fields-- > 0) {
+        tag = read_exif_bytes(&p, 2, bigendian);
+        type = read_exif_bytes(&p, 2, bigendian);
+        read_exif_bytes(&p, 4, bigendian);
+        switch (type) {
+            case 1: /* byte */
+                value = *p++;
+                p += 3;
+                break;
+            case 3: /* unsigned short */
+            case 8: /* signed short */
+                value = read_exif_bytes(&p, 2, bigendian);
+                p += 2;
+                break;
+            case 4: /* unsigned long */
+            case 9: /* signed long */
+                value = read_exif_bytes(&p, 4, bigendian);
+                break;
+            case 5: /* rational */
+            case 10: /* srational */
+                value = read_exif_bytes(&p, 4, bigendian);
+                rp = tiff_header + value;
+                num = read_exif_bytes(&rp, 4, bigendian);
+                den = read_exif_bytes(&rp, 4, bigendian);
+                break;
+            case 7: /* undefined */
+                value = *p++;
+                p += 3;
+                break;
+            case 2: /* ascii */
+            default:
+                p += 4;
+                break;
+        }
+        switch (tag) {
+            case 274: /* orientation */
+                orientation = value;
+                break;
+            case 282: /* x res */
+                if (den != 0) {
+                    xres = num / den;
+                    found_x = true;
+		}
+                break;
+            case 283: /* y res */
+                if (den != 0) {
+                    yres = num / den;
+                    found_y = true ;
+		}
+                break;
+            case 296: /* res unit */
+                switch (value) {
+                    case 2:
+                        res_unit = 1.0;
+                        break;
+                    case 3:
+                        res_unit = 2.54;
+                        break;
+                    default:
+                        res_unit = 0;
+                        break;
+                }
+	   case 0x5110: /* PixelUnit */
+	        switch (value) {
+                    case 1:
+		        res_unit_ms = 0.0254; /* Unit is meter */
+			break;
+		    default:
+  		        res_unit_ms = 0;
+		}
+	   case 0x5111: /* PixelPerUnitX */
+                found_x_ms = true ;
+	   	xres_ms = value;
+		break;
+	   case 0x5112: /* PixelPerUnitY */
+                found_y_ms = true ;
+		yres_ms = value ;
+		break;
+           }
+
+
+    }
+    if (found_x && found_y && res_unit>0) {
+     found_res = true;
+     tempx = (int)(xres * res_unit+0.5);
+     tempy = (int)(yres * res_unit+0.5);
+    } else if (found_x_ms && found_y_ms && res_unit_ms==0.0254) {
+     found_res = true;
+     tempx = (int)(xres_ms * res_unit_ms+0.5);
+     tempy = (int)(yres_ms * res_unit_ms+0.5);
+    }
+    if (found_res) {
+      if (tempx>0 && tempy>0) {
+       if ((tempx!=(*xx) || tempy!=(*yy)) && (*xx!=0 && (*yy!=0) )  ) {
+        formatted_warning("readjpg","Exif resolution  %ddpi x %ddpi differs from the input resolution %ddpi x %ddpi",tempx,tempy,*xx,*yy);
+       }
+       if (tempx==1 || tempy==1) {
+        formatted_warning("readjpg","Exif resolution %ddpi x %ddpi looks weird", tempx, tempy);
+       }
+       *xx = tempx;
+       *yy = tempy;
+     }else {
+       formatted_warning("readjpg","Bad Exif resolution  %ddpi x %ddpi (zero or negative value of a signed integer)",tempx,tempy);
+     }
+    }
+
+    *or = orientation;
+
+err:
+    free(buffer);
+    return;
+}
+
+/*
+
+    Contrary to pdf where several parallel usage can happen (epdf, tex, lua) with
+    bitmaps we care less about keeping files open. So, we can keep files open in
+    the img lib but then they are closed after inclusion anyway.
+
+*/
+
+@ @c
+static void close_and_cleanup_jpg(image_dict * idict)
+{
+    /* if one of then is not NULL we already cleaned up */
+    if (img_file(idict) != NULL) {
+        xfclose(img_file(idict), img_filepath(idict));
+        img_file(idict) = NULL;
+    }
+    if (img_jpg_ptr(idict) != NULL) {
+        xfree(img_jpg_ptr(idict));
+    }
+}
+
+@ @c
+void flush_jpg_info(image_dict * idict)
+{
+    close_and_cleanup_jpg(idict);
+}
+
+@ The jpeg images are scanned for resolution, colorspace, depth, dimensions and
+orientation. We need to look at the exif blob for that. The original version did
+a quick test for jfif and exif but there can be more blobs later on. The current
+approach is to run over the linked list of blobs which is somewhat less efficient
+but not noticeable.
+
+@ @c
+void read_jpg_info(image_dict * idict)
+{
+    int i, position, units = 0;
+    unsigned short length;
+    int okay = 0 ;
+    FILE *fp = img_file(idict);
+    if (img_type(idict) != IMG_TYPE_JPG) {
+        normal_error("readjpg","conflicting image dictionary");
+    }
+    if (fp != NULL) {
+        normal_error("readjpg","image data already read");
+    }
+    fp = xfopen(img_filepath(idict), FOPEN_RBIN_MODE);
+    img_totalpages(idict) = 1;
+    img_pagenum(idict) = 1;
+    img_xres(idict) = img_yres(idict) = 0;
+    img_file(idict) = fp;
+    if (fp == NULL) {
+        normal_error("readjpg","unable to read image file");
+    }
+    img_jpg_ptr(idict) = xtalloc(1, jpg_img_struct);
+    xfseek(fp, 0, SEEK_END, img_filepath(idict));
+    img_jpg_ptr(idict)->length = xftell(fp, img_filepath(idict));
+    xfseek(fp, 0, SEEK_SET, img_filepath(idict));
+    if ((unsigned int) read2bytes(fp) != 0xFFD8) {
+        normal_error("readjpg","no header found");
+    }
+    xfseek(fp, 0, SEEK_SET, img_filepath(idict));
+    while (1) {
+        if (feof(fp)) {
+            if (okay) {
+                break ;
+            } else {
+                normal_error("readjpg","premature file end");
+            }
+        } else if (fgetc(fp) != 0xFF) {
+            if (okay) {
+                break ;
+            } else {
+                normal_error("readjpg","no marker found");
+            }
+        }
+        i = xgetc(fp);
+        position = ftell(fp);
+        length = 0 ;
+        switch (i) {
+            case M_SOF3:  /* lossless */
+            case M_SOF5:
+            case M_SOF6:
+            case M_SOF7:  /* lossless */
+            case M_SOF9:
+            case M_SOF10:
+            case M_SOF11: /* lossless */
+            case M_SOF13:
+            case M_SOF14:
+            case M_SOF15: /* lossless */
+                formatted_error("readjpg","unsupported compression SOF_%d", i - M_SOF0);
+                break;
+            case M_SOF2:
+                if (img_pdfmajorversion(idict) < 2 && img_pdfminorversion(idict) <= 2) {
+                    normal_error("readjpg","progressive DCT with PDF-1.2 is not permitted");
+                }
+            case M_SOF0:
+            case M_SOF1:
+                length = (int) read2bytes(fp); /* read segment length  */
+                img_colordepth(idict) = xgetc(fp);
+                img_ysize(idict) = (int) read2bytes(fp);
+                img_xsize(idict) = (int) read2bytes(fp);
+                img_jpg_color(idict) = xgetc(fp);
+                switch (img_jpg_color(idict)) {
+                    case JPG_GRAY:
+                        img_procset(idict) |= PROCSET_IMAGE_B;
+                        break;
+                    case JPG_RGB:
+                        img_procset(idict) |= PROCSET_IMAGE_C;
+                        break;
+                    case JPG_CMYK:
+                        img_procset(idict) |= PROCSET_IMAGE_C;
+                        break;
+                    default:
+                        formatted_error("readjpg","unsupported color space %i", (int) img_jpg_color(idict));
+                }
+                okay = 1 ;
+                break ;
+            case M_APP0:
+                {
+                    char app_sig[32];
+                    length = (int) read2bytes(fp);
+                    if (length > 6) {
+                        if (fread(app_sig, sizeof(char), 5, fp) != 5)
+                            return;
+                        if (!memcmp(app_sig, "JFIF\000", 5)) {
+                            units = (int) read2bytes(fp);    /*skip two bytes, compiler is also happy*/
+                            units = xgetc(fp);
+                            img_xres(idict) = (int) read2bytes(fp);
+                            img_yres(idict) = (int) read2bytes(fp);
+                            switch (units) {
+                                case 1:
+                                    /* pixels per inch */
+                                    if ((img_xres(idict) == 1) || (img_yres(idict) == 1)) {
+                                        formatted_warning("readjpg","unusual resolution of %ddpi by %ddpi", img_xres(idict), img_yres(idict));
+                                    }
+                                    break;
+                                case 2:
+                                    /* pixels per cm */
+                                    img_xres(idict) = (int) ((double) img_xres(idict) * 2.54);
+                                    img_yres(idict) = (int) ((double) img_yres(idict) * 2.54);
+                                    break;
+                                default:
+                                    img_xres(idict) = img_yres(idict) = 0;
+                                    break;
+                                }
+                            }
+                        /* if either xres or yres is 0 but the other isn't, set it to the value of the other */
+                    }
+                }
+                break;
+            case M_APP1:
+                {
+                    char app_sig[32];
+                    length = (int) read2bytes(fp);
+                    if (length > 7) {
+                        if (fread(app_sig, sizeof(char), 5, fp) != 5)
+                            return;
+                        if (!memcmp(app_sig, "Exif\000", 5)) {
+                            int xxres = img_xres(idict);
+                            int yyres = img_yres(idict);
+                            int orientation = img_orientation(idict);
+                            read_APP1_Exif(fp, length - 7, &xxres, &yyres, &orientation);
+                            img_xres(idict) = xxres;
+                            img_yres(idict) = yyres;
+                            img_orientation(idict) = orientation;
+                        }
+                    }
+                }
+                break;
+            /* ignore markers without parameters */
+            case M_SOI:
+            case M_EOI:
+            case M_TEM:
+            case M_RST0:
+            case M_RST1:
+            case M_RST2:
+            case M_RST3:
+            case M_RST4:
+            case M_RST5:
+            case M_RST6:
+            case M_RST7:
+                break;
+            default:
+                /* skip variable length markers */
+                length = (int) read2bytes(fp);
+                break;
+        }
+        /*
+            printf("marker %X : %i %i\n",i,position,length);
+        */
+        if (length > 0) {
+            xfseek(fp, position + length, SEEK_SET, img_filepath(idict));
+        }
+    }
+    /* moved */
+    xfseek(fp, 0, SEEK_SET, img_filepath(idict));
+    if (! img_keepopen(idict)) {
+        close_and_cleanup_jpg(idict);
+    }
+    /* */
+    if (okay){
+        if ((img_xres(idict) == 0) && (img_yres(idict) != 0)) {
+            img_xres(idict) = img_yres(idict);
+        }
+        if ((img_yres(idict) == 0) && (img_xres(idict) != 0)) {
+            img_yres(idict) = img_xres(idict);
+        }
+    } else {
+        normal_error("readjpg","unknown fatal error");
+    }
+}
+
+@ @c
+static void reopen_jpg(image_dict * idict)
+{
+    int width = img_xsize(idict);
+    int height = img_ysize(idict);
+    int xres = img_xres(idict);
+    int yres = img_yres(idict);
+    /*
+        we need to make sure that the file kept open
+    */
+    img_keepopen(idict) = 1;
+    read_jpg_info(idict);
+    if (width != img_xsize(idict) || height != img_ysize(idict) || xres != img_xres(idict) || yres != img_yres(idict)) {
+        normal_error("writejpg","image dimensions have changed");
+    }
+}
+
+@ @c
+void write_jpg(PDF pdf, image_dict * idict)
+{
+    size_t l;
+    if (img_file(idict) == NULL) {
+        reopen_jpg(idict);
+    }
+    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "XObject");
+    pdf_dict_add_name(pdf, "Subtype", "Image");
+    pdf_dict_add_img_filename(pdf, idict);
+    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) {
+        pdf_printf(pdf, "\n%s\n", img_attr(idict));
+    }
+    pdf_dict_add_int(pdf, "Width", (int) img_xsize(idict));
+    pdf_dict_add_int(pdf, "Height", (int) img_ysize(idict));
+    pdf_dict_add_int(pdf, "BitsPerComponent", (int) img_colordepth(idict));
+    pdf_dict_add_int(pdf, "Length", (int) img_jpg_ptr(idict)->length);
+    if (img_colorspace(idict) != 0) {
+        pdf_dict_add_ref(pdf, "ColorSpace", (int) img_colorspace(idict));
+    } else {
+        switch (img_jpg_color(idict)) {
+            case JPG_GRAY:
+                pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
+                break;
+            case JPG_RGB:
+                pdf_dict_add_name(pdf, "ColorSpace", "DeviceRGB");
+                break;
+            case JPG_CMYK:
+                pdf_dict_add_name(pdf, "ColorSpace", "DeviceCMYK");
+                pdf_add_name(pdf, "Decode");
+                pdf_begin_array(pdf);
+                pdf_add_int(pdf, 1);
+                pdf_add_int(pdf, 0);
+                pdf_add_int(pdf, 1);
+                pdf_add_int(pdf, 0);
+                pdf_add_int(pdf, 1);
+                pdf_add_int(pdf, 0);
+                pdf_add_int(pdf, 1);
+                pdf_add_int(pdf, 0);
+                pdf_end_array(pdf);
+                break;
+            default:
+                formatted_error("writejpg","unsupported JPEG color space %i", (int) img_jpg_color(idict));
+        }
+    }
+    pdf_dict_add_name(pdf, "Filter", "DCTDecode");
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    l = (size_t) img_jpg_ptr(idict)->length;
+    xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict));
+    if (read_file_to_buf(pdf, img_file(idict), l) != l) {
+        normal_error("writejpg","fread failed");
+    }
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    /* always */
+    close_and_cleanup_jpg(idict);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/image/writejpg.c
+++ /dev/null
@@ -1,609 +0,0 @@
-/*
-
-writejpg.w
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <assert.h>
-#include "image/image.h"
-#include "image/writejpg.h"
-
-#define JPG_GRAY  1  /* Gray color space, use /DeviceGray    */
-#define JPG_RGB   3  /* RGB color space, use /DeviceRGB      */
-#define JPG_CMYK  4  /* CMYK color space, use /DeviceCMYK    */
-
-typedef enum {
-    M_SOF0  = 0xc0,  /* baseline DCT                         */
-    M_SOF1  = 0xc1,  /* extended sequential DCT              */
-    M_SOF2  = 0xc2,  /* progressive DCT                      */
-    M_SOF3  = 0xc3,  /* lossless (sequential)                */
-
-    M_SOF5  = 0xc5,  /* differential sequential DCT          */
-    M_SOF6  = 0xc6,  /* differential progressive DCT         */
-    M_SOF7  = 0xc7,  /* differential lossless (sequential)   */
-
-    M_JPG   = 0xc8,  /* reserved for JPEG extensions         */
-    M_SOF9  = 0xc9,  /* extended sequential DCT              */
-    M_SOF10 = 0xca,  /* progressive DCT                      */
-    M_SOF11 = 0xcb,  /* lossless (sequential)                */
-
-    M_SOF13 = 0xcd,  /* differential sequential DCT          */
-    M_SOF14 = 0xce,  /* differential progressive DCT         */
-    M_SOF15 = 0xcf,  /* differential lossless (sequential)   */
-
-    M_DHT   = 0xc4,  /* define Huffman table(s)              */
-
-    M_DAC   = 0xcc,  /* define arithmetic conditioning table */
-
-    M_RST0  = 0xd0,  /* restart                              */
-    M_RST1  = 0xd1,  /* restart                              */
-    M_RST2  = 0xd2,  /* restart                              */
-    M_RST3  = 0xd3,  /* restart                              */
-    M_RST4  = 0xd4,  /* restart                              */
-    M_RST5  = 0xd5,  /* restart                              */
-    M_RST6  = 0xd6,  /* restart                              */
-    M_RST7  = 0xd7,  /* restart                              */
-
-    M_SOI   = 0xd8,  /* start of image                       */
-    M_EOI   = 0xd9,  /* end of image                         */
-    M_SOS   = 0xda,  /* start of scan                        */
-    M_DQT   = 0xdb,  /* define quantization tables           */
-    M_DNL   = 0xdc,  /* define number of lines               */
-    M_DRI   = 0xdd,  /* define restart interval              */
-    M_DHP   = 0xde,  /* define hierarchical progression      */
-    M_EXP   = 0xdf,  /* expand reference image(s)            */
-
-    M_APP0  = 0xe0,  /* application marker, used for JFIF    */
-    M_APP1  = 0xe1,  /* application marker                   */
-    M_APP2  = 0xe2,  /* application marker                   */
-    M_APP3  = 0xe3,  /* application marker                   */
-    M_APP4  = 0xe4,  /* application marker                   */
-    M_APP5  = 0xe5,  /* application marker                   */
-    M_APP6  = 0xe6,  /* application marker                   */
-    M_APP7  = 0xe7,  /* application marker                   */
-    M_APP8  = 0xe8,  /* application marker                   */
-    M_APP9  = 0xe9,  /* application marker                   */
-    M_APP10 = 0xea,  /* application marker                   */
-    M_APP11 = 0xeb,  /* application marker                   */
-    M_APP12 = 0xec,  /* application marker                   */
-    M_APP13 = 0xed,  /* application marker                   */
-    M_APP14 = 0xee,  /* application marker, used by Adobe    */
-    M_APP15 = 0xef,  /* application marker                   */
-
-    M_JPG0  = 0xf0,  /* reserved for JPEG extensions         */
-    M_JPG13 = 0xfd,  /* reserved for JPEG extensions         */
-    M_COM   = 0xfe,  /* comment                              */
-
-    M_TEM   = 0x01,  /* temporary use                        */
-
-    M_ERROR = 0x100  /* dummy marker, internal use only      */
-} JPEG_MARKER;
-
-static unsigned int read_exif_bytes(unsigned char **p, int n, int b)
-{
-    unsigned int rval = 0;
-    unsigned char *pp = *p;
-    if (b) {
-        switch (n) {
-            case 4:
-                rval += *pp++; rval <<= 8;
-                rval += *pp++; rval <<= 8;
-            case 2:
-                rval += *pp++; rval <<= 8;
-                rval += *pp;
-                break;
-        }
-    } else {
-        pp += n;
-        switch (n) {
-            case 4:
-                rval += *--pp; rval <<= 8;
-                rval += *--pp; rval <<= 8;
-            case 2:
-                rval += *--pp; rval <<= 8;
-                rval += *--pp;
-                break;
-        }
-    }
-    *p += n;
-    return rval;
-}
-
-/*tex
-
-    The Exif block can contain the data on the resolution in two forms:
-    XResolution, YResolution and ResolutionUnit (tag 282, 283 and 296) as well as
-    PixelPerUnitX, PixelPerUnitY and PixelUnit (tag 0x5111, 0x5112 and 0x5110).
-    Tags 282, 293 and 296 have the priority, with ResolutionUnit set to inch by
-    default, then tag 0x5110, 0x5111 and 0x5112, where the only valid value for
-    PixelUnit is 0.0254, and finally the given value xx and yy, choosen if the
-    Exif x and y resolution are not strictly positive.
-
-    The next one doesn't save the data, just reads the tags we need based on info
-    from \typ {http://www.exif.org/Exif2-2.PDF}.
-
-*/
-
-static void read_APP1_Exif (FILE *fp, unsigned short length, int *xx, int *yy, int *or)
-{
-    unsigned char *buffer = (unsigned char *)xmalloc(length);
-    unsigned char *p, *rp;
-    unsigned char *tiff_header;
-    char bigendian;
-    int i;
-    int num_fields, tag, type;
-    /*tex silence uninitialized warnings */
-    int value = 0;
-    unsigned int num = 0;
-    unsigned int den = 0;
-    boolean found_x = false;
-    boolean found_y = false;
-    int tempx = 0;
-    int tempy = 0;
-    double xres = 0;
-    double yres = 0;
-    double res_unit = 1.0;
-    unsigned int xres_ms = 0;
-    unsigned int yres_ms = 0;
-    double res_unit_ms = 0;
-    boolean found_x_ms = false;
-    boolean found_y_ms = false;
-    boolean found_res= false;
-    int orientation = 1;
-    size_t ret_len;
-    ret_len = fread(buffer, length, 1, fp);
-    if (ret_len != 1)
-        goto err ;
-    p = buffer;
-    while ((p < buffer + length) && (*p == 0))
-        ++p;
-    tiff_header = p;
-    if ((*p == 'M') && (*(p+1) == 'M'))
-        bigendian = 1;
-    else if ((*p == 'I') && (*(p+1) == 'I'))
-        bigendian = 0;
-    else
-        goto err;
-    p += 2;
-    i = read_exif_bytes(&p, 2, bigendian);
-    if (i != 42)
-        goto err;
-    i = read_exif_bytes(&p, 4, bigendian);
-    p = tiff_header + i;
-    num_fields = read_exif_bytes(&p, 2, bigendian);
-    while (num_fields-- > 0) {
-        tag = read_exif_bytes(&p, 2, bigendian);
-        type = read_exif_bytes(&p, 2, bigendian);
-        read_exif_bytes(&p, 4, bigendian);
-        switch (type) {
-            case 1:
-                /*tex byte */
-                value = *p++;
-                p += 3;
-                break;
-            case 3:
-                /*tex unsigned short */
-            case 8:
-                /*tex signed short */
-                value = read_exif_bytes(&p, 2, bigendian);
-                p += 2;
-                break;
-            case 4:
-                /*tex unsigned long */
-            case 9:
-                /*tex signed long */
-                value = read_exif_bytes(&p, 4, bigendian);
-                break;
-            case 5:
-                /*tex rational */
-            case 10:
-                /*tex srational */
-                value = read_exif_bytes(&p, 4, bigendian);
-                rp = tiff_header + value;
-                num = read_exif_bytes(&rp, 4, bigendian);
-                den = read_exif_bytes(&rp, 4, bigendian);
-                break;
-            case 7:
-                /*tex undefined */
-                value = *p++;
-                p += 3;
-                break;
-            case 2:
-                /*tex ascii */
-            default:
-                p += 4;
-                break;
-        }
-        switch (tag) {
-            case 274:
-                /*tex orientation */
-                orientation = value;
-                break;
-            case 282:
-                /*tex x res */
-                if (den != 0) {
-                    xres = num / den;
-                    found_x = true;
-                }
-                break;
-            case 283:
-                /*tex y res */
-                if (den != 0) {
-                    yres = num / den;
-                    found_y = true ;
-                }
-                break;
-            case 296:
-                /*tex res unit */
-                switch (value) {
-                    case 2:
-                        res_unit = 1.0;
-                        break;
-                    case 3:
-                        res_unit = 2.54;
-                        break;
-                    default:
-                        res_unit = 0;
-                        break;
-                }
-                break;
-            case 0x5110:
-                /*tex PixelUnit */
-                switch (value) {
-                    case 1:
-                        res_unit_ms = 0.0254; /* Unit is meter */
-                        break;
-                    default:
-                        res_unit_ms = 0;
-                }
-                break;
-           case 0x5111:
-               /*tex PixelPerUnitX */
-                found_x_ms = true ;
-                xres_ms = value;
-                break;
-           case 0x5112:
-               /*tex PixelPerUnitY */
-                found_y_ms = true ;
-                yres_ms = value ;
-                break;
-        }
-    }
-    if (found_x && found_y && res_unit>0) {
-        found_res = true;
-        tempx = (int)(xres * res_unit+0.5);
-        tempy = (int)(yres * res_unit+0.5);
-    } else if (found_x_ms && found_y_ms && res_unit_ms==0.0254) {
-        found_res = true;
-        tempx = (int)(xres_ms * res_unit_ms+0.5);
-        tempy = (int)(yres_ms * res_unit_ms+0.5);
-    }
-    if (found_res) {
-        if (tempx>0 && tempy>0) {
-            if ((tempx!=(*xx) || tempy!=(*yy)) && (*xx!=0 && (*yy!=0) )  ) {
-                formatted_warning("readjpg","Exif resolution %ddpi x %ddpi differs from the input resolution %ddpi x %ddpi",tempx,tempy,*xx,*yy);
-            }
-            if (tempx==1 || tempy==1) {
-                formatted_warning("readjpg","Exif resolution %ddpi x %ddpi looks weird", tempx, tempy);
-            }
-            *xx = tempx;
-            *yy = tempy;
-        } else {
-            formatted_warning("readjpg","Bad Exif resolution %ddpi x %ddpi (zero or negative value of a signed integer)",tempx,tempy);
-        }
-    }
-    *or = orientation;
-err:
-    free(buffer);
-    return;
-}
-
-/*tex
-
-    Contrary to \PDF\ where several parallel usage can happen (\PDF, |TEX, \LUA)
-    with bitmaps we care less about keeping files open. So, we can keep files
-    open in the img lib but then they are closed after inclusion anyway.
-
-*/
-
-static void close_and_cleanup_jpg(image_dict * idict)
-{
-    /*tex if one of then is not NULL we already cleaned up */
-    if (img_file(idict) != NULL) {
-        xfclose(img_file(idict), img_filepath(idict));
-        img_file(idict) = NULL;
-    }
-    if (img_jpg_ptr(idict) != NULL) {
-        xfree(img_jpg_ptr(idict));
-    }
-}
-
-void flush_jpg_info(image_dict * idict)
-{
-    close_and_cleanup_jpg(idict);
-}
-
-/*tex
-
-    The jpeg images are scanned for resolution, colorspace, depth, dimensions and
-    orientation. We need to look at the exif blob for that. The original version
-    did a quick test for jfif and exif but there can be more blobs later on. The
-    current approach is to run over the linked list of blobs which is somewhat
-    less efficient but not noticeable.
-
-*/
-
-void read_jpg_info(image_dict * idict)
-{
-    int i, position, units = 0;
-    unsigned short length;
-    int okay = 0 ;
-    FILE *fp = img_file(idict);
-    if (img_type(idict) != IMG_TYPE_JPG) {
-        normal_error("readjpg","conflicting image dictionary");
-    }
-    if (fp != NULL) {
-        normal_error("readjpg","image data already read");
-    }
-    fp = xfopen(img_filepath(idict), FOPEN_RBIN_MODE);
-    img_totalpages(idict) = 1;
-    img_pagenum(idict) = 1;
-    img_xres(idict) = img_yres(idict) = 0;
-    img_file(idict) = fp;
-    if (fp == NULL) {
-        normal_error("readjpg","unable to read image file");
-    }
-    img_jpg_ptr(idict) = xtalloc(1, jpg_img_struct);
-    xfseek(fp, 0, SEEK_END, img_filepath(idict));
-    img_jpg_ptr(idict)->length = xftell(fp, img_filepath(idict));
-    xfseek(fp, 0, SEEK_SET, img_filepath(idict));
-    if ((unsigned int) read2bytes(fp) != 0xFFD8) {
-        normal_error("readjpg","no header found");
-    }
-    xfseek(fp, 0, SEEK_SET, img_filepath(idict));
-    while (1) {
-        if (feof(fp)) {
-            if (okay) {
-                break ;
-            } else {
-                normal_error("readjpg","premature file end");
-            }
-        } else if (fgetc(fp) != 0xFF) {
-            if (okay) {
-                break ;
-            } else {
-                normal_error("readjpg","no marker found");
-            }
-        }
-        i = xgetc(fp);
-        position = ftell(fp);
-        length = 0 ;
-        switch (i) {
-            case M_SOF3:
-                /*tex lossless */
-            case M_SOF5:
-            case M_SOF6:
-            case M_SOF7:
-                /*tex lossless */
-            case M_SOF9:
-            case M_SOF10:
-            case M_SOF11:
-                /*tex lossless */
-            case M_SOF13:
-            case M_SOF14:
-            case M_SOF15:
-                /*tex lossless */
-                formatted_error("readjpg","unsupported compression SOF_%d", i - M_SOF0);
-                break;
-            case M_SOF2:
-                if (img_pdfmajorversion(idict) < 2 && img_pdfminorversion(idict) <= 2) {
-                    normal_error("readjpg","progressive DCT with PDF-1.2 is not permitted");
-                }
-            case M_SOF0:
-            case M_SOF1:
-                /*tex read segment length  */
-                length = (int) read2bytes(fp);
-                img_colordepth(idict) = xgetc(fp);
-                img_ysize(idict) = (int) read2bytes(fp);
-                img_xsize(idict) = (int) read2bytes(fp);
-                img_jpg_color(idict) = xgetc(fp);
-                switch (img_jpg_color(idict)) {
-                    case JPG_GRAY:
-                        img_procset(idict) |= PROCSET_IMAGE_B;
-                        break;
-                    case JPG_RGB:
-                        img_procset(idict) |= PROCSET_IMAGE_C;
-                        break;
-                    case JPG_CMYK:
-                        img_procset(idict) |= PROCSET_IMAGE_C;
-                        break;
-                    default:
-                        formatted_error("readjpg","unsupported color space %i", (int) img_jpg_color(idict));
-                }
-                okay = 1 ;
-                break ;
-            case M_APP0:
-                {
-                    char app_sig[32];
-                    length = (int) read2bytes(fp);
-                    if (length > 6) {
-                        if (fread(app_sig, sizeof(char), 5, fp) != 5)
-                            return;
-                        if (!memcmp(app_sig, "JFIF\000", 5)) {
-                            /*tex skip two bytes, compiler is also happy*/
-                            units = (int) read2bytes(fp);
-                            units = xgetc(fp);
-                            img_xres(idict) = (int) read2bytes(fp);
-                            img_yres(idict) = (int) read2bytes(fp);
-                            switch (units) {
-                                case 1:
-                                    /*tex pixels per inch */
-                                    if ((img_xres(idict) == 1) || (img_yres(idict) == 1)) {
-                                        formatted_warning("readjpg","unusual resolution of %ddpi by %ddpi", img_xres(idict), img_yres(idict));
-                                    }
-                                    break;
-                                case 2:
-                                    /*tex pixels per cm */
-                                    img_xres(idict) = (int) ((double) img_xres(idict) * 2.54);
-                                    img_yres(idict) = (int) ((double) img_yres(idict) * 2.54);
-                                    break;
-                                default:
-                                    img_xres(idict) = img_yres(idict) = 0;
-                                    break;
-                                }
-                            }
-                        /*tex
-                            If either xres or yres is 0 but the other isn't, set
-                            it to the value of the other.
-                        */
-                    }
-                }
-                break;
-            case M_APP1:
-                {
-                    char app_sig[32];
-                    length = (int) read2bytes(fp);
-                    if (length > 7) {
-                        if (fread(app_sig, sizeof(char), 5, fp) != 5)
-                            return;
-                        if (!memcmp(app_sig, "Exif\000", 5)) {
-                            int xxres = img_xres(idict);
-                            int yyres = img_yres(idict);
-                            int orientation = img_orientation(idict);
-                            read_APP1_Exif(fp, length - 7, &xxres, &yyres, &orientation);
-                            img_xres(idict) = xxres;
-                            img_yres(idict) = yyres;
-                            img_orientation(idict) = orientation;
-                        }
-                    }
-                }
-                break;
-            /*tex ignore markers without parameters */
-            case M_SOI:
-            case M_EOI:
-            case M_TEM:
-            case M_RST0:
-            case M_RST1:
-            case M_RST2:
-            case M_RST3:
-            case M_RST4:
-            case M_RST5:
-            case M_RST6:
-            case M_RST7:
-                break;
-            default:
-                /*tex skip variable length markers */
-                length = (int) read2bytes(fp);
-                break;
-        }
-        if (length > 0) {
-            xfseek(fp, position + length, SEEK_SET, img_filepath(idict));
-        }
-    }
-    xfseek(fp, 0, SEEK_SET, img_filepath(idict));
-    if (! img_keepopen(idict)) {
-        close_and_cleanup_jpg(idict);
-    }
-    if (okay){
-        if ((img_xres(idict) == 0) && (img_yres(idict) != 0)) {
-            img_xres(idict) = img_yres(idict);
-        }
-        if ((img_yres(idict) == 0) && (img_xres(idict) != 0)) {
-            img_yres(idict) = img_xres(idict);
-        }
-    } else {
-        normal_error("readjpg","unknown fatal error");
-    }
-}
-
-static void reopen_jpg(image_dict * idict)
-{
-    int width = img_xsize(idict);
-    int height = img_ysize(idict);
-    int xres = img_xres(idict);
-    int yres = img_yres(idict);
-    /*tex
-        we need to make sure that the file kept open
-    */
-    img_keepopen(idict) = 1;
-    read_jpg_info(idict);
-    if (width != img_xsize(idict) || height != img_ysize(idict) || xres != img_xres(idict) || yres != img_yres(idict)) {
-        normal_error("writejpg","image dimensions have changed");
-    }
-}
-
-void write_jpg(PDF pdf, image_dict * idict)
-{
-    size_t l;
-    if (img_file(idict) == NULL) {
-        reopen_jpg(idict);
-    }
-    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "XObject");
-    pdf_dict_add_name(pdf, "Subtype", "Image");
-    pdf_dict_add_img_filename(pdf, idict);
-    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) {
-        pdf_printf(pdf, "\n%s\n", img_attr(idict));
-    }
-    pdf_dict_add_int(pdf, "Width", (int) img_xsize(idict));
-    pdf_dict_add_int(pdf, "Height", (int) img_ysize(idict));
-    pdf_dict_add_int(pdf, "BitsPerComponent", (int) img_colordepth(idict));
-    pdf_dict_add_int(pdf, "Length", (int) img_jpg_ptr(idict)->length);
-    if (img_colorspace(idict) != 0) {
-        pdf_dict_add_ref(pdf, "ColorSpace", (int) img_colorspace(idict));
-    } else {
-        switch (img_jpg_color(idict)) {
-            case JPG_GRAY:
-                pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
-                break;
-            case JPG_RGB:
-                pdf_dict_add_name(pdf, "ColorSpace", "DeviceRGB");
-                break;
-            case JPG_CMYK:
-                pdf_dict_add_name(pdf, "ColorSpace", "DeviceCMYK");
-                pdf_add_name(pdf, "Decode");
-                pdf_begin_array(pdf);
-                pdf_add_int(pdf, 1);
-                pdf_add_int(pdf, 0);
-                pdf_add_int(pdf, 1);
-                pdf_add_int(pdf, 0);
-                pdf_add_int(pdf, 1);
-                pdf_add_int(pdf, 0);
-                pdf_add_int(pdf, 1);
-                pdf_add_int(pdf, 0);
-                pdf_end_array(pdf);
-                break;
-            default:
-                formatted_error("writejpg","unsupported JPEG color space %i", (int) img_jpg_color(idict));
-        }
-    }
-    pdf_dict_add_name(pdf, "Filter", "DCTDecode");
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    l = (size_t) img_jpg_ptr(idict)->length;
-    xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict));
-    if (read_file_to_buf(pdf, img_file(idict), l) != l) {
-        normal_error("writejpg","fread failed");
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    close_and_cleanup_jpg(idict);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/image/writepng.w
@@ -0,0 +1,686 @@
+% writepng.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% 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 <assert.h>
+#include "image/image.h"
+#include "image/writepng.h"
+
+@ @c
+static int transparent_page_group = -1;
+
+static void close_and_cleanup_png(image_dict * idict)
+{
+    /* if one of then is not NULL we already cleaned up */
+    if (img_file(idict) != NULL) {
+        xfclose(img_file(idict), img_filepath(idict));
+        img_file(idict) = NULL;
+    }
+    if (img_png_ptr(idict) != NULL) {
+        png_destroy_read_struct(&(img_png_png_ptr(idict)), &(img_png_info_ptr(idict)), NULL);
+        xfree(img_png_ptr(idict));
+    }
+}
+
+@ @c
+void flush_png_info(image_dict * idict)
+{
+    close_and_cleanup_png(idict);
+}
+
+@ @c
+static void warn(png_structp png_ptr, png_const_charp msg)
+{
+    (void)png_ptr; (void)msg; /* Make compiler happy */
+}
+
+void read_png_info(image_dict * idict)
+{
+    png_structp png_p;
+    png_infop info_p;
+    if (img_type(idict) != IMG_TYPE_PNG) {
+        normal_error("readpng","conflicting image dictionary");
+    }
+    if (img_file(idict) != NULL) {
+        normal_error("readpng","image data already read");
+    }
+    img_totalpages(idict) = 1;
+    img_pagenum(idict) = 1;
+    img_xres(idict) = img_yres(idict) = 0;
+    img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE);
+    img_png_ptr(idict) = xtalloc(1, png_img_struct);
+    if ((png_p = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, warn)) == NULL) {
+        normal_error("readpng","png_create_read_struct() failed");
+    }
+    img_png_png_ptr(idict) = png_p;
+    if ((info_p = png_create_info_struct(png_p)) == NULL) {
+        normal_error("readpng","png_create_info_struct() failed");
+    }
+    img_png_info_ptr(idict) = info_p;
+    if (setjmp(png_jmpbuf(png_p))) {
+        normal_error("readpng","internal error");
+    }
+#if PNG_LIBPNG_VER >= 10603
+    /* ignore possibly incorrect CMF bytes */
+    png_set_option(png_p, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
+#endif
+    png_init_io(png_p, img_file(idict));
+    png_read_info(png_p, info_p);
+    /* resolution support */
+    img_xsize(idict) = (int) png_get_image_width(png_p, info_p);
+    img_ysize(idict) = (int) png_get_image_height(png_p, info_p);
+    if (png_get_valid(png_p, info_p, PNG_INFO_pHYs)) {
+        img_xres(idict) = round(0.0254 * (double) png_get_x_pixels_per_meter(png_p, info_p));
+        img_yres(idict) = round(0.0254 * (double) png_get_y_pixels_per_meter(png_p, info_p));
+    }
+    switch (png_get_color_type(png_p, info_p)) {
+    case PNG_COLOR_TYPE_PALETTE:
+        img_procset(idict) |= PROCSET_IMAGE_C | PROCSET_IMAGE_I;
+        break;
+    case PNG_COLOR_TYPE_GRAY:
+    case PNG_COLOR_TYPE_GRAY_ALPHA:
+        img_procset(idict) |= PROCSET_IMAGE_B;
+        break;
+    case PNG_COLOR_TYPE_RGB:
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+        img_procset(idict) |= PROCSET_IMAGE_C;
+        break;
+    default:
+        formatted_error("readpng","unsupported type of color_type '%i'",(int) png_get_color_type(png_p, info_p));
+    }
+    img_colordepth(idict) = png_get_bit_depth(png_p, info_p);
+    /*
+        So we can optionally keep open a file in img.
+    */
+    if (! img_keepopen(idict)) {
+        close_and_cleanup_png(idict);
+    }
+}
+
+@ @c
+#define write_gray_pixel_16(r)       \
+    if (j % 4 == 0 || j % 4 == 1)    \
+        pdf_quick_out(pdf, *r++);    \
+    else                             \
+        smask[smask_ptr++] = *r++    \
+
+#define write_gray_pixel_8(r)        \
+    if (j % 2 == 0)                  \
+        pdf_quick_out(pdf, *r++);    \
+    else                             \
+        smask[smask_ptr++] = *r++
+
+#define write_rgb_pixel_16(r)        \
+    if (!(j % 8 == 6 || j % 8 == 7)) \
+        pdf_quick_out(pdf, *r++);    \
+    else                             \
+        smask[smask_ptr++] = *r++
+
+#define write_rgb_pixel_8(r)         \
+    if (j % 4 != 3)                  \
+        pdf_quick_out(pdf, *r++);    \
+    else                             \
+        smask[smask_ptr++] = *r++
+
+#define write_simple_pixel(r)    pdf_quick_out(pdf,*r++)
+
+#define write_noninterlaced(outmac)                                   \
+    for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { \
+        png_read_row(png_p, row, NULL);                               \
+        r = row;                                                      \
+        k = (size_t) png_get_rowbytes(png_p, info_p);                 \
+        while (k > 0) {                                               \
+            l = (k > pdf->buf->size) ? pdf->buf->size : k;            \
+            pdf_room(pdf, l);                                         \
+            for (j = 0; j < l; j++) {                                 \
+                outmac;                                               \
+            }                                                         \
+            k -= l;                                                   \
+        }                                                             \
+    }
+
+#define write_interlaced(outmac)                                      \
+    for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { \
+        row = rows[i];                                                \
+        k = (size_t) png_get_rowbytes(png_p, info_p);                 \
+        while (k > 0) {                                               \
+            l = (k > pdf->buf->size) ? pdf->buf->size : k;            \
+            pdf_room(pdf, l);                                         \
+            for (j = 0; j < l; j++) {                                 \
+                outmac;                                               \
+            }                                                         \
+            k -= l;                                                   \
+        }                                                             \
+        xfree(rows[i]);                                               \
+    }
+
+@ @c
+static void write_palette_streamobj(PDF pdf, int palette_objnum, png_colorp palette, int num_palette)
+{
+    int i;
+    if (palette_objnum == 0)
+        return;
+    assert(palette != NULL);
+    pdf_begin_obj(pdf, palette_objnum, OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    for (i = 0; i < num_palette; i++) {
+        pdf_room(pdf, 3);
+        pdf_quick_out(pdf, palette[i].red);
+        pdf_quick_out(pdf, palette[i].green);
+        pdf_quick_out(pdf, palette[i].blue);
+    }
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+}
+
+@ @c
+static void write_smask_streamobj(PDF pdf, image_dict * idict, int smask_objnum, png_bytep smask, int smask_size)
+{
+    int i;
+    png_structp png_p = img_png_png_ptr(idict);
+    png_infop info_p = img_png_info_ptr(idict);
+    png_byte bitdepth = png_get_bit_depth(png_p, info_p);
+    pdf_begin_obj(pdf, smask_objnum, OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "XObject");
+    pdf_dict_add_name(pdf, "Subtype", "Image");
+    pdf_dict_add_img_filename(pdf, idict);
+    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0)
+        pdf_printf(pdf, "\n%s\n", img_attr(idict));
+    pdf_dict_add_int(pdf, "Width", (int) png_get_image_width(png_p, info_p));
+    pdf_dict_add_int(pdf, "Height", (int) png_get_image_height(png_p, info_p));
+    pdf_dict_add_int(pdf, "BitsPerComponent", (bitdepth == 16 ? 8 : bitdepth));
+    pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    for (i = 0; i < smask_size; i++) {
+        if (i % 8 == 0)
+            pdf_room(pdf, 8);
+        pdf_quick_out(pdf, smask[i]);
+        if (bitdepth == 16)
+            i++;
+    }
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+}
+
+@ @c
+static void write_png_gray(PDF pdf, image_dict * idict)
+{
+    int i;
+    size_t j, k, l;
+    png_structp png_p = img_png_png_ptr(idict);
+    png_infop info_p = img_png_info_ptr(idict);
+    png_bytep row, r, *rows;
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) {
+        row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
+        write_noninterlaced(write_simple_pixel(r));
+        xfree(row);
+    } else {
+        if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) {
+            formatted_warning("pngwrite","large interlaced bitmap might cause out of memory");
+        }
+        rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep);
+        for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) {
+            rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
+        }
+        png_read_image(png_p, rows);
+        write_interlaced(write_simple_pixel(row));
+        xfree(rows);
+    }
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+}
+
+@ @c
+static void write_png_gray_alpha(PDF pdf, image_dict * idict)
+{
+    int i;
+    size_t j, k, l;
+    png_structp png_p = img_png_png_ptr(idict);
+    png_infop info_p = img_png_info_ptr(idict);
+    png_bytep row, r, *rows;
+    int smask_objnum = 0;
+    png_bytep smask;
+    int smask_ptr = 0;
+    int smask_size = 0;
+    smask_objnum = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_dict_add_ref(pdf, "SMask", (int) smask_objnum);
+    smask_size = (int) ((png_get_rowbytes(png_p, info_p) / 2) * png_get_image_height(png_p, info_p));
+    smask = xtalloc((unsigned) smask_size, png_byte);
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) {
+        row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
+        if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) {
+            write_noninterlaced(write_gray_pixel_16(r));
+        } else {
+            write_noninterlaced(write_gray_pixel_8(r));
+        }
+        xfree(row);
+    } else {
+        if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) {
+            formatted_warning("pngwrite","large interlaced bitmap might cause out of memory");
+        }
+        rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep);
+        for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) {
+            rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
+        }
+        png_read_image(png_p, rows);
+        if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) {
+            write_interlaced(write_gray_pixel_16(row));
+        } else {
+            write_interlaced(write_gray_pixel_8(row));
+        }
+        xfree(rows);
+    }
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    write_smask_streamobj(pdf, idict, smask_objnum, smask, smask_size);
+    xfree(smask);
+}
+
+@ @c
+static void write_png_rgb_alpha(PDF pdf, image_dict * idict)
+{
+    int i;
+    size_t j, k, l;
+    png_structp png_p = img_png_png_ptr(idict);
+    png_infop info_p = img_png_info_ptr(idict);
+    png_bytep row, r, *rows;
+    int smask_objnum = 0;
+    png_bytep smask;
+    int smask_ptr = 0;
+    int smask_size = 0;
+    smask_objnum = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_dict_add_ref(pdf, "SMask", (int) smask_objnum);
+    smask_size = (int) ((png_get_rowbytes(png_p, info_p) / 4) * png_get_image_height(png_p, info_p));
+    smask = xtalloc((unsigned) smask_size, png_byte);
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) {
+        row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
+        if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) {
+            write_noninterlaced(write_rgb_pixel_16(r));
+        } else {
+            write_noninterlaced(write_rgb_pixel_8(r));
+        }
+        xfree(row);
+    } else {
+        if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) {
+            formatted_warning("pngwrite","large interlaced bitmap might cause out of memory");
+        }
+        rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep);
+        for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) {
+            rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
+        }
+        png_read_image(png_p, rows);
+        if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) {
+            write_interlaced(write_rgb_pixel_16(row));
+        } else {
+            write_interlaced(write_rgb_pixel_8(row));
+        }
+        xfree(rows);
+    }
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    write_smask_streamobj(pdf, idict, smask_objnum, smask, smask_size);
+    xfree(smask);
+}
+
+@ The |copy_png| code is cheerfully gleaned from Thomas Merz' PDFlib,
+file |p_png.c| ``SPNG - Simple PNG''.
+The goal is to use pdf's native FlateDecode support, if that is possible.
+Only a subset of the png files allows this, but for these it greatly
+improves inclusion speed.
+
+In the ``PNG Copy'' mode only the IDAT chunks are copied;
+all other chunks from the PNG file are discarded.
+If there are any other chunks in the PNG file,
+which might influence the visual appearance of the image,
+or if image processing like gamma change is requested,
+the ``PNG Copy'' function must be skipped; therefore the lengthy tests.
+
+@c
+static int spng_getint(FILE * f)
+{
+    unsigned char buf[4];
+    if (fread(buf, 1, 4, f) != 4) {
+        normal_error("writepng", "reading chunk type failed");
+    }
+    return ((((((int) buf[0] << 8) + buf[1]) << 8) + buf[2]) << 8) + buf[3];
+}
+
+#define SPNG_CHUNK_IDAT 0x49444154
+#define SPNG_CHUNK_IEND 0x49454E44
+
+static void copy_png(PDF pdf, image_dict * idict)
+{
+    int type, streamlength = 0, idat = 0;
+    size_t len;
+    boolean endflag = false;
+    FILE *f;
+    png_structp png_p;
+    png_infop info_p;
+    assert(idict != NULL);
+    png_p = img_png_png_ptr(idict);
+    info_p = img_png_info_ptr(idict);
+    f = (FILE *) png_get_io_ptr(png_p);
+    /* 1st pass to find overall stream /Length */
+    if (fseek(f, 8, SEEK_SET) != 0)
+        normal_error("writepng", "fseek in file failed");
+    do {
+        len = spng_getint(f);
+        type = spng_getint(f);
+        switch (type) {
+        case SPNG_CHUNK_IEND:
+            endflag = true;
+            break;
+        case SPNG_CHUNK_IDAT:
+            streamlength += len;
+        default:
+            if (fseek(f, len + 4, SEEK_CUR) != 0) {
+                normal_error("writepng", "fseek in file failed");
+            }
+        }
+    } while (endflag == false);
+    pdf_dict_add_int(pdf, "Length", streamlength);
+    pdf_dict_add_name(pdf, "Filter", "FlateDecode");
+    pdf_add_name(pdf, "DecodeParms");
+    pdf_begin_dict(pdf);
+    pdf_dict_add_int(pdf, "Colors", png_get_color_type(png_p,info_p) == PNG_COLOR_TYPE_RGB ? 3 : 1);
+    pdf_dict_add_int(pdf, "Columns", png_get_image_width(png_p, info_p));
+    pdf_dict_add_int(pdf, "BitsPerComponent", png_get_bit_depth(png_p, info_p));
+    pdf_dict_add_int(pdf, "Predictor", 10);
+    pdf_end_dict(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    assert(pdf->zip_write_state == NO_ZIP); /* the PNG stream is already compressed */
+    /* 2nd pass to copy data */
+    endflag = false;
+    if (fseek(f, 8, SEEK_SET) != 0)
+        normal_error("writepng", "fseek in file failed");
+    do {
+        len = spng_getint(f);
+        type = spng_getint(f);
+        switch (type) {
+        case SPNG_CHUNK_IDAT:
+            /* do copy */
+            if (idat == 2) {
+                normal_error("writepng", "IDAT chunk sequence broken");
+            }
+            idat = 1;
+            if (read_file_to_buf(pdf, f, len) != len) {
+                normal_error("writepng", "fread failed");
+            } else if (fseek(f, 4, SEEK_CUR) != 0) {
+                normal_error("writepng", "fseek in file failed");
+            }
+            break;
+        case SPNG_CHUNK_IEND:
+            /* done */
+            endflag = true;
+            break;
+        default:
+            if (idat == 1)
+                idat = 2;
+            if (fseek(f, len + 4, SEEK_CUR) != 0)
+                normal_error("writepng", "fseek in file failed");
+        }
+    } while (endflag == false);
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+}
+
+@ @c
+static void reopen_png(image_dict * idict)
+{
+    int width, height, xres, yres;
+    width = img_xsize(idict);   /* do consistency check */
+    height = img_ysize(idict);
+    xres = img_xres(idict);
+    yres = img_yres(idict);
+    /*
+        we need to ake sure that the file kept open
+    */
+    img_keepopen(idict) = 1;
+    read_png_info(idict);
+    if (width != img_xsize(idict) || height != img_ysize(idict) || xres != img_xres(idict) || yres != img_yres(idict)) {
+        normal_error("writepng", "image dimensions have changed");
+    }
+}
+
+@ @c
+static boolean last_png_needs_page_group;
+
+void write_png(PDF pdf, image_dict * idict)
+{
+#ifndef PNG_FP_1
+    /* for libpng < 1.5.0 */
+#  define PNG_FP_1    100000
+#endif
+    int num_palette, palette_objnum = 0;
+    boolean png_copy = true;
+    double gamma = 0.0;
+    png_fixed_point int_file_gamma = 0;
+    png_structp png_p;
+    png_infop info_p;
+    png_colorp palette;
+    assert(idict != NULL);
+    last_png_needs_page_group = false;
+    if (img_file(idict) == NULL)
+        reopen_png(idict);
+    assert(img_png_ptr(idict) != NULL);
+    png_p = img_png_png_ptr(idict);
+    info_p = img_png_info_ptr(idict);
+    /* simple transparency support */
+    if (png_get_valid(png_p, info_p, PNG_INFO_tRNS)) {
+        png_set_tRNS_to_alpha(png_p);
+        png_copy = false;
+    }
+    /* alpha channel support */
+    if (pdf->minor_version < 4
+        && png_get_color_type(png_p, info_p) | PNG_COLOR_MASK_ALPHA) {
+        png_set_strip_alpha(png_p);
+        png_copy = false;
+    }
+    /* 16 bit depth support */
+    if (pdf->minor_version < 5)
+        pdf->image_hicolor = 0;
+    if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor == 0)) {
+        png_set_strip_16(png_p);
+        png_copy = false;
+    }
+    /* gamma support */
+    if (png_get_valid(png_p, info_p, PNG_INFO_gAMA)) {
+        png_get_gAMA(png_p, info_p, &gamma);
+        png_get_gAMA_fixed(png_p, info_p, &int_file_gamma);
+    }
+    if (pdf->image_apply_gamma) {
+        if (png_get_valid(png_p, info_p, PNG_INFO_gAMA))
+            png_set_gamma(png_p, (pdf->gamma / 1000.0), gamma);
+        else
+            png_set_gamma(png_p, (pdf->gamma / 1000.0),
+                          (1000.0 / pdf->image_gamma));
+        png_copy = false;
+    }
+    /* reset structure */
+    (void) png_set_interlace_handling(png_p);
+    png_read_update_info(png_p, info_p);
+    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "XObject");
+    pdf_dict_add_name(pdf, "Subtype", "Image");
+    pdf_dict_add_img_filename(pdf, idict);
+    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) {
+        pdf_printf(pdf, "\n%s\n", img_attr(idict));
+    }
+    pdf_dict_add_int(pdf, "Width", (int) png_get_image_width(png_p, info_p));
+    pdf_dict_add_int(pdf, "Height", (int) png_get_image_height(png_p, info_p));
+    pdf_dict_add_int(pdf, "BitsPerComponent", (int) png_get_bit_depth(png_p, info_p));
+    if (img_colorspace(idict) != 0) {
+        pdf_dict_add_ref(pdf, "ColorSpace", (int) img_colorspace(idict));
+    } else {
+        switch (png_get_color_type(png_p, info_p)) {
+            case PNG_COLOR_TYPE_PALETTE:
+                png_get_PLTE(png_p, info_p, &palette, &num_palette);
+                palette_objnum = pdf_create_obj(pdf, obj_type_others, 0);
+                pdf_add_name(pdf, "ColorSpace");
+                pdf_begin_array(pdf);
+                pdf_add_name(pdf, "Indexed");
+                pdf_add_name(pdf, "DeviceRGB");     /* base; PDFRef. 4.5.5 */
+                pdf_add_int(pdf, (int) (num_palette - 1));  /* hival */
+                pdf_add_ref(pdf, (int) palette_objnum);     /* lookup */
+                pdf_end_array(pdf);
+                break;
+            case PNG_COLOR_TYPE_GRAY:
+            case PNG_COLOR_TYPE_GRAY_ALPHA:
+                pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
+                break;
+            case PNG_COLOR_TYPE_RGB:
+            case PNG_COLOR_TYPE_RGB_ALPHA:
+                pdf_dict_add_name(pdf, "ColorSpace", "DeviceRGB");
+                break;
+            default:
+                formatted_error("writepng", "unsupported color_type '%i'", png_get_color_type(png_p, info_p));
+        }
+    }
+    if (png_copy
+        && pdf->minor_version > 1
+        && png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE
+        && (png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_GRAY
+         || png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_RGB)
+        && !pdf->image_apply_gamma
+        && (!png_get_valid(png_p, info_p, PNG_INFO_gAMA) || int_file_gamma == PNG_FP_1)
+        && !png_get_valid(png_p, info_p, PNG_INFO_cHRM)
+        && !png_get_valid(png_p, info_p, PNG_INFO_iCCP)
+        && !png_get_valid(png_p, info_p, PNG_INFO_sBIT)
+        && !png_get_valid(png_p, info_p, PNG_INFO_sRGB)
+        && !png_get_valid(png_p, info_p, PNG_INFO_bKGD)
+        && !png_get_valid(png_p, info_p, PNG_INFO_hIST)
+        && !png_get_valid(png_p, info_p, PNG_INFO_tRNS)
+        && !png_get_valid(png_p, info_p, PNG_INFO_sPLT)) {
+        copy_png(pdf, idict);
+    } else {
+        if (img_errorlevel(idict) > 1) {
+            if (!png_copy)
+                normal_warning("pngcopy","failed");
+            if (!(pdf->minor_version > 1))
+                formatted_warning("pngcopy","skipped because minorversion is '%d'", pdf->minor_version);
+            if (!(png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE))
+                normal_warning("pngcopy","skipped because of interlacing");
+            if (!((png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_GRAY)
+               || (png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_RGB)))
+                normal_warning("pngcopy","skipped because of colortype");
+            if (pdf->image_apply_gamma)
+                normal_warning("pngcopy","skipped because of gamma (1)");
+            if (!(!png_get_valid(png_p, info_p, PNG_INFO_gAMA) || int_file_gamma == PNG_FP_1))
+                normal_warning("pngcopy","skipped because of gamma (2)");
+            if (png_get_valid(png_p, info_p, PNG_INFO_cHRM))
+                normal_warning("pngcopy","skipped because of cHRM");
+            if (png_get_valid(png_p, info_p, PNG_INFO_iCCP))
+                normal_warning("pngcopy","skipped because of iCCP");
+            if (png_get_valid(png_p, info_p, PNG_INFO_sBIT))
+                normal_warning("pngcopy","skipped because of sBIT");
+            if (png_get_valid(png_p, info_p, PNG_INFO_sRGB))
+                normal_warning("pngcopy","skipped because of sRGB");
+            if (png_get_valid(png_p, info_p, PNG_INFO_bKGD))
+                normal_warning("pngcopy","skipped because of bKGD");
+            if (png_get_valid(png_p, info_p, PNG_INFO_hIST))
+                normal_warning("pngcopy","skipped because of hIST");
+            if (png_get_valid(png_p, info_p, PNG_INFO_tRNS))
+                normal_warning("pngcopy","skipped because of tRNS");
+            if (png_get_valid(png_p, info_p, PNG_INFO_sPLT))
+                normal_warning("pngcopy","skipped because of sPLT");
+        }
+        switch (png_get_color_type(png_p, info_p)) {
+            case PNG_COLOR_TYPE_PALETTE:
+            case PNG_COLOR_TYPE_GRAY:
+            case PNG_COLOR_TYPE_RGB:
+                write_png_gray(pdf, idict);
+                break;
+            case PNG_COLOR_TYPE_GRAY_ALPHA:
+                if (pdf->minor_version >= 4) {
+                    write_png_gray_alpha(pdf, idict);
+                    last_png_needs_page_group = true;
+                } else
+                    write_png_gray(pdf, idict);
+                break;
+            case PNG_COLOR_TYPE_RGB_ALPHA:
+                if (pdf->minor_version >= 4) {
+                    write_png_rgb_alpha(pdf, idict);
+                    last_png_needs_page_group = true;
+                } else
+                    write_png_gray(pdf, idict);
+                break;
+            default:
+                assert(0);
+        }
+    }
+    write_palette_streamobj(pdf, palette_objnum, palette, num_palette);
+    /* always */
+    close_and_cleanup_png(idict);
+}
+
+@ @c
+static boolean transparent_page_group_was_written = false;
+
+@ Called after the xobject generated by |write_png| has been finished; used to
+write out additional objects
+
+@c
+void write_additional_png_objects(PDF pdf)
+{
+    (void) pdf;
+    (void) transparent_page_group;
+    (void) transparent_page_group_was_written;
+    return;
+    /* this interferes with current macro-based usage and cannot be configured */
+#if 0
+    if (last_png_needs_page_group) {
+        if (!transparent_page_group_was_written && transparent_page_group > 1) {
+            /* create new group object */
+            transparent_page_group_was_written = true;
+            pdf_begin_obj(pdf, transparent_page_group, 2);
+            if (pdf->compress_level == 0) {
+                pdf_puts(pdf, "%PTEX Group needed for transparent pngs\n");
+            }
+            pdf_begin_dict(pdf);
+            pdf_dict_add_name(pdf, "Type", "Group");
+            pdf_dict_add_name(pdf, "S", "Transparency");
+            pdf_dict_add_name(pdf, "CS", "DeviceRGB");
+            pdf_dict_add_bool(pdf, "I", 1);
+            pdf_dict_add_bool(pdf, "K", 1);
+            pdf_end_dict(pdf);
+            pdf_end_obj(pdf);
+        }
+    }
+#endif
+}
--- texlive-bin.orig/texk/web2c/luatexdir/image/writepng.c
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
-
-writepng.c
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <assert.h>
-#include "image/image.h"
-#include "image/writepng.h"
-
-static void close_and_cleanup_png(image_dict * idict)
-{
-    if (img_file(idict) != NULL) {
-        xfclose(img_file(idict), img_filepath(idict));
-        img_file(idict) = NULL;
-    }
-    if (img_png_ptr(idict) != NULL) {
-        png_destroy_read_struct(&(img_png_png_ptr(idict)), &(img_png_info_ptr(idict)), NULL);
-        xfree(img_png_ptr(idict));
-    }
-}
-
-void flush_png_info(image_dict * idict)
-{
-    close_and_cleanup_png(idict);
-}
-
-/*tex A dummy function: */
-
-static void warn(png_structp png_ptr, png_const_charp msg)
-{
-    (void)png_ptr; (void)msg;
-}
-
-void read_png_info(image_dict * idict)
-{
-    png_structp png_p;
-    png_infop info_p;
-    if (img_type(idict) != IMG_TYPE_PNG) {
-        normal_error("readpng","conflicting image dictionary");
-    }
-    if (img_file(idict) != NULL) {
-        normal_error("readpng","image data already read");
-    }
-    img_totalpages(idict) = 1;
-    img_pagenum(idict) = 1;
-    img_xres(idict) = img_yres(idict) = 0;
-    img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE);
-    img_png_ptr(idict) = xtalloc(1, png_img_struct);
-    if ((png_p = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, warn)) == NULL) {
-        normal_error("readpng","png_create_read_struct() failed");
-    }
-    img_png_png_ptr(idict) = png_p;
-    if ((info_p = png_create_info_struct(png_p)) == NULL) {
-        normal_error("readpng","png_create_info_struct() failed");
-    }
-    img_png_info_ptr(idict) = info_p;
-    if (setjmp(png_jmpbuf(png_p))) {
-        normal_error("readpng","internal error");
-    }
-#if PNG_LIBPNG_VER >= 10603
-    /*tex ignore possibly incorrect CMF bytes */
-    png_set_option(png_p, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
-#endif
-    png_init_io(png_p, img_file(idict));
-    png_read_info(png_p, info_p);
-    /*tex resolution support */
-    img_xsize(idict) = (int) png_get_image_width(png_p, info_p);
-    img_ysize(idict) = (int) png_get_image_height(png_p, info_p);
-    if (png_get_valid(png_p, info_p, PNG_INFO_pHYs)) {
-        img_xres(idict) = round(0.0254 * (double) png_get_x_pixels_per_meter(png_p, info_p));
-        img_yres(idict) = round(0.0254 * (double) png_get_y_pixels_per_meter(png_p, info_p));
-    }
-    switch (png_get_color_type(png_p, info_p)) {
-    case PNG_COLOR_TYPE_PALETTE:
-        img_procset(idict) |= PROCSET_IMAGE_C | PROCSET_IMAGE_I;
-        break;
-    case PNG_COLOR_TYPE_GRAY:
-    case PNG_COLOR_TYPE_GRAY_ALPHA:
-        img_procset(idict) |= PROCSET_IMAGE_B;
-        break;
-    case PNG_COLOR_TYPE_RGB:
-    case PNG_COLOR_TYPE_RGB_ALPHA:
-        img_procset(idict) |= PROCSET_IMAGE_C;
-        break;
-    default:
-        formatted_error("readpng","unsupported type of color_type '%i'",(int) png_get_color_type(png_p, info_p));
-    }
-    img_colordepth(idict) = png_get_bit_depth(png_p, info_p);
-    /*tex So we can optionally keep open a file in |img|. */
-    if (! img_keepopen(idict)) {
-        close_and_cleanup_png(idict);
-    }
-}
-
-#define write_gray_pixel_16(r)       \
-    if (j % 4 == 0 || j % 4 == 1)    \
-        pdf_quick_out(pdf, *r++);    \
-    else                             \
-        smask[smask_ptr++] = *r++    \
-
-#define write_gray_pixel_8(r)        \
-    if (j % 2 == 0)                  \
-        pdf_quick_out(pdf, *r++);    \
-    else                             \
-        smask[smask_ptr++] = *r++
-
-#define write_rgb_pixel_16(r)        \
-    if (!(j % 8 == 6 || j % 8 == 7)) \
-        pdf_quick_out(pdf, *r++);    \
-    else                             \
-        smask[smask_ptr++] = *r++
-
-#define write_rgb_pixel_8(r)         \
-    if (j % 4 != 3)                  \
-        pdf_quick_out(pdf, *r++);    \
-    else                             \
-        smask[smask_ptr++] = *r++
-
-#define write_simple_pixel(r)    pdf_quick_out(pdf,*r++)
-
-#define write_noninterlaced(outmac)                                   \
-    for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { \
-        png_read_row(png_p, row, NULL);                               \
-        r = row;                                                      \
-        k = (size_t) png_get_rowbytes(png_p, info_p);                 \
-        while (k > 0) {                                               \
-            l = (k > pdf->buf->size) ? pdf->buf->size : k;            \
-            pdf_room(pdf, l);                                         \
-            for (j = 0; j < l; j++) {                                 \
-                outmac;                                               \
-            }                                                         \
-            k -= l;                                                   \
-        }                                                             \
-    }
-
-#define write_interlaced(outmac)                                      \
-    for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { \
-        row = rows[i];                                                \
-        k = (size_t) png_get_rowbytes(png_p, info_p);                 \
-        while (k > 0) {                                               \
-            l = (k > pdf->buf->size) ? pdf->buf->size : k;            \
-            pdf_room(pdf, l);                                         \
-            for (j = 0; j < l; j++) {                                 \
-                outmac;                                               \
-            }                                                         \
-            k -= l;                                                   \
-        }                                                             \
-        xfree(rows[i]);                                               \
-    }
-
-static void write_palette_streamobj(PDF pdf, int palette_objnum, png_colorp palette, int num_palette)
-{
-    int i;
-    if (palette_objnum == 0)
-        return;
-    assert(palette != NULL);
-    pdf_begin_obj(pdf, palette_objnum, OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    for (i = 0; i < num_palette; i++) {
-        pdf_room(pdf, 3);
-        pdf_quick_out(pdf, palette[i].red);
-        pdf_quick_out(pdf, palette[i].green);
-        pdf_quick_out(pdf, palette[i].blue);
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-}
-
-static void write_smask_streamobj(PDF pdf, image_dict * idict, int smask_objnum, png_bytep smask, int smask_size)
-{
-    int i;
-    png_structp png_p = img_png_png_ptr(idict);
-    png_infop info_p = img_png_info_ptr(idict);
-    png_byte bitdepth = png_get_bit_depth(png_p, info_p);
-    pdf_begin_obj(pdf, smask_objnum, OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "XObject");
-    pdf_dict_add_name(pdf, "Subtype", "Image");
-    pdf_dict_add_img_filename(pdf, idict);
-    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0)
-        pdf_printf(pdf, "\n%s\n", img_attr(idict));
-    pdf_dict_add_int(pdf, "Width", (int) png_get_image_width(png_p, info_p));
-    pdf_dict_add_int(pdf, "Height", (int) png_get_image_height(png_p, info_p));
-    pdf_dict_add_int(pdf, "BitsPerComponent", (bitdepth == 16 ? 8 : bitdepth));
-    pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    for (i = 0; i < smask_size; i++) {
-        if (i % 8 == 0)
-            pdf_room(pdf, 8);
-        pdf_quick_out(pdf, smask[i]);
-        if (bitdepth == 16)
-            i++;
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-}
-
-static void write_png_gray(PDF pdf, image_dict * idict)
-{
-    int i;
-    size_t j, k, l;
-    png_structp png_p = img_png_png_ptr(idict);
-    png_infop info_p = img_png_info_ptr(idict);
-    png_bytep row, r, *rows;
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) {
-        row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
-        write_noninterlaced(write_simple_pixel(r));
-        xfree(row);
-    } else {
-        if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) {
-            formatted_warning("pngwrite","large interlaced bitmap might cause out of memory");
-        }
-        rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep);
-        for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) {
-            rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
-        }
-        png_read_image(png_p, rows);
-        write_interlaced(write_simple_pixel(row));
-        xfree(rows);
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-}
-
-static void write_png_gray_alpha(PDF pdf, image_dict * idict)
-{
-    int i;
-    size_t j, k, l;
-    png_structp png_p = img_png_png_ptr(idict);
-    png_infop info_p = img_png_info_ptr(idict);
-    png_bytep row, r, *rows;
-    int smask_objnum = 0;
-    png_bytep smask;
-    int smask_ptr = 0;
-    int smask_size = 0;
-    smask_objnum = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_dict_add_ref(pdf, "SMask", (int) smask_objnum);
-    smask_size = (int) ((png_get_rowbytes(png_p, info_p) / 2) * png_get_image_height(png_p, info_p));
-    smask = xtalloc((unsigned) smask_size, png_byte);
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) {
-        row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
-        if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) {
-            write_noninterlaced(write_gray_pixel_16(r));
-        } else {
-            write_noninterlaced(write_gray_pixel_8(r));
-        }
-        xfree(row);
-    } else {
-        if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) {
-            formatted_warning("pngwrite","large interlaced bitmap might cause out of memory");
-        }
-        rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep);
-        for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) {
-            rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
-        }
-        png_read_image(png_p, rows);
-        if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) {
-            write_interlaced(write_gray_pixel_16(row));
-        } else {
-            write_interlaced(write_gray_pixel_8(row));
-        }
-        xfree(rows);
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    write_smask_streamobj(pdf, idict, smask_objnum, smask, smask_size);
-    xfree(smask);
-}
-
-static void write_png_rgb_alpha(PDF pdf, image_dict * idict)
-{
-    int i;
-    size_t j, k, l;
-    png_structp png_p = img_png_png_ptr(idict);
-    png_infop info_p = img_png_info_ptr(idict);
-    png_bytep row, r, *rows;
-    int smask_objnum = 0;
-    png_bytep smask;
-    int smask_ptr = 0;
-    int smask_size = 0;
-    smask_objnum = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_dict_add_ref(pdf, "SMask", (int) smask_objnum);
-    smask_size = (int) ((png_get_rowbytes(png_p, info_p) / 4) * png_get_image_height(png_p, info_p));
-    smask = xtalloc((unsigned) smask_size, png_byte);
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) {
-        row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
-        if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) {
-            write_noninterlaced(write_rgb_pixel_16(r));
-        } else {
-            write_noninterlaced(write_rgb_pixel_8(r));
-        }
-        xfree(row);
-    } else {
-        if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) {
-            formatted_warning("pngwrite","large interlaced bitmap might cause out of memory");
-        }
-        rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep);
-        for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) {
-            rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte);
-        }
-        png_read_image(png_p, rows);
-        if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) {
-            write_interlaced(write_rgb_pixel_16(row));
-        } else {
-            write_interlaced(write_rgb_pixel_8(row));
-        }
-        xfree(rows);
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    write_smask_streamobj(pdf, idict, smask_objnum, smask, smask_size);
-    xfree(smask);
-}
-
-/*tex
-
-The |copy_png| code is cheerfully gleaned from Thomas Merz' PDFlib, file
-|p_png.c| ``SPNG - Simple PNG''. The goal is to use pdf's native FlateDecode
-support, if that is possible. Only a subset of the png files allows this, but for
-these it greatly improves inclusion speed.
-
-In the ``PNG Copy'' mode only the IDAT chunks are copied; all other chunks from
-the PNG file are discarded. If there are any other chunks in the PNG file, which
-might influence the visual appearance of the image, or if image processing like
-gamma change is requested, the ``PNG Copy'' function must be skipped; therefore
-the lengthy tests.
-
-*/
-
-static int spng_getint(FILE * f)
-{
-    unsigned char buf[4];
-    if (fread(buf, 1, 4, f) != 4) {
-        normal_error("writepng", "reading chunk type failed");
-    }
-    return ((((((int) buf[0] << 8) + buf[1]) << 8) + buf[2]) << 8) + buf[3];
-}
-
-#define SPNG_CHUNK_IDAT 0x49444154
-#define SPNG_CHUNK_IEND 0x49454E44
-
-static void copy_png(PDF pdf, image_dict * idict)
-{
-    int type, streamlength = 0, idat = 0;
-    size_t len;
-    boolean endflag = false;
-    FILE *f;
-    png_structp png_p;
-    png_infop info_p;
-    assert(idict != NULL);
-    png_p = img_png_png_ptr(idict);
-    info_p = img_png_info_ptr(idict);
-    f = (FILE *) png_get_io_ptr(png_p);
-    /*tex 1st pass to find overall stream /Length */
-    if (fseek(f, 8, SEEK_SET) != 0)
-        normal_error("writepng", "fseek in file failed");
-    do {
-        len = spng_getint(f);
-        type = spng_getint(f);
-        switch (type) {
-        case SPNG_CHUNK_IEND:
-            endflag = true;
-            break;
-        case SPNG_CHUNK_IDAT:
-            streamlength += len;
-        default:
-            if (fseek(f, len + 4, SEEK_CUR) != 0) {
-                normal_error("writepng", "fseek in file failed");
-            }
-        }
-    } while (endflag == false);
-    pdf_dict_add_int(pdf, "Length", streamlength);
-    pdf_dict_add_name(pdf, "Filter", "FlateDecode");
-    pdf_add_name(pdf, "DecodeParms");
-    pdf_begin_dict(pdf);
-    pdf_dict_add_int(pdf, "Colors", png_get_color_type(png_p,info_p) == PNG_COLOR_TYPE_RGB ? 3 : 1);
-    pdf_dict_add_int(pdf, "Columns", png_get_image_width(png_p, info_p));
-    pdf_dict_add_int(pdf, "BitsPerComponent", png_get_bit_depth(png_p, info_p));
-    pdf_dict_add_int(pdf, "Predictor", 10);
-    pdf_end_dict(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    /*tex 2nd pass to copy data */
-    endflag = false;
-    if (fseek(f, 8, SEEK_SET) != 0)
-        normal_error("writepng", "fseek in file failed");
-    do {
-        len = spng_getint(f);
-        type = spng_getint(f);
-        switch (type) {
-        case SPNG_CHUNK_IDAT:
-            /*tex do copy */
-            if (idat == 2) {
-                normal_error("writepng", "IDAT chunk sequence broken");
-            }
-            idat = 1;
-            if (read_file_to_buf(pdf, f, len) != len) {
-                normal_error("writepng", "fread failed");
-            } else if (fseek(f, 4, SEEK_CUR) != 0) {
-                normal_error("writepng", "fseek in file failed");
-            }
-            break;
-        case SPNG_CHUNK_IEND:
-            /*tex done */
-            endflag = true;
-            break;
-        default:
-            if (idat == 1)
-                idat = 2;
-            if (fseek(f, len + 4, SEEK_CUR) != 0)
-                normal_error("writepng", "fseek in file failed");
-        }
-    } while (endflag == false);
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-}
-
-static void reopen_png(image_dict * idict)
-{
-    int width, height, xres, yres;
-    /*tex A consistency check: */
-    width = img_xsize(idict);
-    height = img_ysize(idict);
-    xres = img_xres(idict);
-    yres = img_yres(idict);
-    /*tex We need to ake sure that the file kept open. */
-    img_keepopen(idict) = 1;
-    read_png_info(idict);
-    if (width != img_xsize(idict) || height != img_ysize(idict) || xres != img_xres(idict) || yres != img_yres(idict)) {
-        normal_error("writepng", "image dimensions have changed");
-    }
-}
-
-void write_png(PDF pdf, image_dict * idict)
-{
-#ifndef PNG_FP_1
-    /*tex for libpng < 1.5.0 */
-#  define PNG_FP_1    100000
-#endif
-    int num_palette, palette_objnum = 0;
-    boolean png_copy = true;
-    double gamma = 0.0;
-    png_fixed_point int_file_gamma = 0;
-    png_structp png_p;
-    png_infop info_p;
-    png_colorp palette;
-    assert(idict != NULL);
-    if (img_file(idict) == NULL)
-        reopen_png(idict);
-    assert(img_png_ptr(idict) != NULL);
-    png_p = img_png_png_ptr(idict);
-    info_p = img_png_info_ptr(idict);
-    /*tex simple transparency support */
-    if (png_get_valid(png_p, info_p, PNG_INFO_tRNS)) {
-        png_set_tRNS_to_alpha(png_p);
-        png_copy = false;
-    }
-    /*tex alpha channel support */
-    if (pdf->minor_version < 4
-        && png_get_color_type(png_p, info_p) | PNG_COLOR_MASK_ALPHA) {
-        png_set_strip_alpha(png_p);
-        png_copy = false;
-    }
-    /*tex 16 bit depth support */
-    if (pdf->minor_version < 5)
-        pdf->image_hicolor = 0;
-    if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor == 0)) {
-        png_set_strip_16(png_p);
-        png_copy = false;
-    }
-    /*tex gamma support */
-    if (png_get_valid(png_p, info_p, PNG_INFO_gAMA)) {
-        png_get_gAMA(png_p, info_p, &gamma);
-        png_get_gAMA_fixed(png_p, info_p, &int_file_gamma);
-    }
-    if (pdf->image_apply_gamma) {
-        if (png_get_valid(png_p, info_p, PNG_INFO_gAMA))
-            png_set_gamma(png_p, (pdf->gamma / 1000.0), gamma);
-        else
-            png_set_gamma(png_p, (pdf->gamma / 1000.0), (1000.0 / pdf->image_gamma));
-        png_copy = false;
-    }
-    /*tex reset structure */
-    (void) png_set_interlace_handling(png_p);
-    png_read_update_info(png_p, info_p);
-    pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "XObject");
-    pdf_dict_add_name(pdf, "Subtype", "Image");
-    pdf_dict_add_img_filename(pdf, idict);
-    if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) {
-        pdf_printf(pdf, "\n%s\n", img_attr(idict));
-    }
-    pdf_dict_add_int(pdf, "Width", (int) png_get_image_width(png_p, info_p));
-    pdf_dict_add_int(pdf, "Height", (int) png_get_image_height(png_p, info_p));
-    pdf_dict_add_int(pdf, "BitsPerComponent", (int) png_get_bit_depth(png_p, info_p));
-    if (img_colorspace(idict) != 0) {
-        pdf_dict_add_ref(pdf, "ColorSpace", (int) img_colorspace(idict));
-    } else {
-        switch (png_get_color_type(png_p, info_p)) {
-            case PNG_COLOR_TYPE_PALETTE:
-                png_get_PLTE(png_p, info_p, &palette, &num_palette);
-                palette_objnum = pdf_create_obj(pdf, obj_type_others, 0);
-                pdf_add_name(pdf, "ColorSpace");
-                pdf_begin_array(pdf);
-                pdf_add_name(pdf, "Indexed");
-                pdf_add_name(pdf, "DeviceRGB");
-                /*tex hival */
-                pdf_add_int(pdf, (int) (num_palette - 1));
-                /*tex lookup */
-                pdf_add_ref(pdf, (int) palette_objnum);
-                pdf_end_array(pdf);
-                break;
-            case PNG_COLOR_TYPE_GRAY:
-            case PNG_COLOR_TYPE_GRAY_ALPHA:
-                pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray");
-                break;
-            case PNG_COLOR_TYPE_RGB:
-            case PNG_COLOR_TYPE_RGB_ALPHA:
-                pdf_dict_add_name(pdf, "ColorSpace", "DeviceRGB");
-                break;
-            default:
-                formatted_error("writepng", "unsupported color_type '%i'", png_get_color_type(png_p, info_p));
-        }
-    }
-    if (png_copy
-        && pdf->minor_version > 1
-        && png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE
-        && (png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_GRAY
-         || png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_RGB)
-        && !pdf->image_apply_gamma
-        && (!png_get_valid(png_p, info_p, PNG_INFO_gAMA) || int_file_gamma == PNG_FP_1)
-        && !png_get_valid(png_p, info_p, PNG_INFO_cHRM)
-        && !png_get_valid(png_p, info_p, PNG_INFO_iCCP)
-        && !png_get_valid(png_p, info_p, PNG_INFO_sBIT)
-        && !png_get_valid(png_p, info_p, PNG_INFO_sRGB)
-        && !png_get_valid(png_p, info_p, PNG_INFO_bKGD)
-        && !png_get_valid(png_p, info_p, PNG_INFO_hIST)
-        && !png_get_valid(png_p, info_p, PNG_INFO_tRNS)
-        && !png_get_valid(png_p, info_p, PNG_INFO_sPLT)) {
-        copy_png(pdf, idict);
-    } else {
-        if (img_errorlevel(idict) > 1) {
-            if (!png_copy)
-                normal_warning("pngcopy","failed");
-            if (!(pdf->minor_version > 1))
-                formatted_warning("pngcopy","skipped because minorversion is '%d'", pdf->minor_version);
-            if (!(png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE))
-                normal_warning("pngcopy","skipped because of interlacing");
-            if (!((png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_GRAY)
-               || (png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_RGB)))
-                normal_warning("pngcopy","skipped because of colortype");
-            if (pdf->image_apply_gamma)
-                normal_warning("pngcopy","skipped because of gamma (1)");
-            if (!(!png_get_valid(png_p, info_p, PNG_INFO_gAMA) || int_file_gamma == PNG_FP_1))
-                normal_warning("pngcopy","skipped because of gamma (2)");
-            if (png_get_valid(png_p, info_p, PNG_INFO_cHRM))
-                normal_warning("pngcopy","skipped because of cHRM");
-            if (png_get_valid(png_p, info_p, PNG_INFO_iCCP))
-                normal_warning("pngcopy","skipped because of iCCP");
-            if (png_get_valid(png_p, info_p, PNG_INFO_sBIT))
-                normal_warning("pngcopy","skipped because of sBIT");
-            if (png_get_valid(png_p, info_p, PNG_INFO_sRGB))
-                normal_warning("pngcopy","skipped because of sRGB");
-            if (png_get_valid(png_p, info_p, PNG_INFO_bKGD))
-                normal_warning("pngcopy","skipped because of bKGD");
-            if (png_get_valid(png_p, info_p, PNG_INFO_hIST))
-                normal_warning("pngcopy","skipped because of hIST");
-            if (png_get_valid(png_p, info_p, PNG_INFO_tRNS))
-                normal_warning("pngcopy","skipped because of tRNS");
-            if (png_get_valid(png_p, info_p, PNG_INFO_sPLT))
-                normal_warning("pngcopy","skipped because of sPLT");
-        }
-        switch (png_get_color_type(png_p, info_p)) {
-            case PNG_COLOR_TYPE_PALETTE:
-            case PNG_COLOR_TYPE_GRAY:
-            case PNG_COLOR_TYPE_RGB:
-                write_png_gray(pdf, idict);
-                break;
-            case PNG_COLOR_TYPE_GRAY_ALPHA:
-                if (pdf->minor_version >= 4) {
-                    write_png_gray_alpha(pdf, idict);
-                } else
-                    write_png_gray(pdf, idict);
-                break;
-            case PNG_COLOR_TYPE_RGB_ALPHA:
-                if (pdf->minor_version >= 4) {
-                    write_png_rgb_alpha(pdf, idict);
-                } else
-                    write_png_gray(pdf, idict);
-                break;
-            default:
-                assert(0);
-        }
-    }
-    write_palette_streamobj(pdf, palette_objnum, palette, num_palette);
-    /*tex always */
-    close_and_cleanup_png(idict);
-}
-
-
-/*tex
-
-    Called after the xobject generated by |write_png| has been finished; used to
-    write out additional objects.
-
-*/
-
-void write_additional_png_objects(PDF pdf)
-{
-    (void) pdf;
-    return;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/lang/hnjalloc.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-
-hnjalloc.w
-
-LibHnj is dual licensed under LGPL and MPL. Boilerplate for both licenses
-follows.
-
-LibHnj - a library for high quality hyphenation and justification Copyright (C)
-1998 Raph Levien, (C) 2001 ALTLinux, Moscow
-
-This library is free software; you can redistribute it and/or modify it under the
-terms of the GNU Library General Public License as published by the Free Software
-Foundation; either version 2 of the License, or (at your option) any later
-version.
-
-This library 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 Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public License along
-with this library; if not, write to the Free Software Foundation, Inc., 59 Temple
-Place - Suite 330, Boston, MA 02111-1307 USA.
-
-The contents of this file are subject to the Mozilla Public License Version 1.0
-(the "MPL"); you may not use this file except in compliance with the MPL. You may
-obtain a copy of the MPL at http://www.mozilla.org/MPL/
-
-Software distributed under the MPL is distributed on an "AS IS" basis, WITHOUT
-WARRANTY OF ANY KIND, either express or implied. See the MPL for the specific
-language governing rights and limitations under the MPL.
-
-*/
-
-/*tex The wrappers for malloc */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include "lang/hnjalloc.h"
-
-void *hnj_malloc(int size)
-{
-    void *p;
-
-    p = malloc((size_t) size);
-    if (p == NULL) {
-        fprintf(stderr, "can't allocate %d bytes\n", size);
-        exit(1);
-    }
-    return p;
-}
-
-void *hnj_realloc(void *p, int size)
-{
-    p = realloc(p, (size_t) size);
-    if (p == NULL) {
-        fprintf(stderr, "can't allocate %d bytes\n", size);
-        exit(1);
-    }
-    return p;
-}
-
-void hnj_free(void *p)
-{
-    free(p);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lang/hnjalloc.w
@@ -0,0 +1,69 @@
+% hnjalloc.w
+%
+% LibHnj is dual licensed under LGPL and MPL. Boilerplate for both
+% licenses follows.
+%
+%
+% LibHnj - a library for high quality hyphenation and justification
+% Copyright (C) 1998 Raph Levien, (C) 2001 ALTLinux, Moscow
+%
+% This library is free software; you can redistribute it and/or
+% modify it under the terms of the GNU Library General Public
+% License as published by the Free Software Foundation; either
+% version 2 of the License, or (at your option) any later version.
+%
+% This library 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
+% Library General Public License for more details.
+%
+% You should have received a copy of the GNU Library General Public
+% License along with this library; if not, write to the
+% Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+% Boston, MA  02111-1307  USA.
+%
+%
+%
+% The contents of this file are subject to the Mozilla Public License
+% Version 1.0 (the "MPL"); you may not use this file except in
+% compliance with the MPL.  You may obtain a copy of the MPL at
+% http://www.mozilla.org/MPL/
+%
+% Software distributed under the MPL is distributed on an "AS IS" basis,
+% WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL
+% for the specific language governing rights and limitations under the
+% MPL.
+
+@ wrappers for malloc
+@c
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "lang/hnjalloc.h"
+
+void *hnj_malloc(int size)
+{
+    void *p;
+
+    p = malloc((size_t) size);
+    if (p == NULL) {
+        fprintf(stderr, "can't allocate %d bytes\n", size);
+        exit(1);
+    }
+    return p;
+}
+
+void *hnj_realloc(void *p, int size)
+{
+    p = realloc(p, (size_t) size);
+    if (p == NULL) {
+        fprintf(stderr, "can't allocate %d bytes\n", size);
+        exit(1);
+    }
+    return p;
+}
+
+void hnj_free(void *p)
+{
+    free(p);
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lang/hyphen.w
@@ -0,0 +1,875 @@
+% hyphen.w
+%
+% Libhnj is dual licensed under LGPL and MPL. Boilerplate for both
+% licenses follows.
+%
+%
+% LibHnj - a library for high quality hyphenation and justification
+% Copyright (C) 1998 Raph Levien,
+% 	     (C) 2001 ALTLinux, Moscow (http://www.alt-linux.org),
+%           (C) 2001 Peter Novodvorsky (nidd@@cs.msu.su)
+%
+% This library is free software; you can redistribute it and/or
+% modify it under the terms of the GNU Library General Public
+% License as published by the Free Software Foundation; either
+% version 2 of the License, or (at your option) any later version.
+%
+% This library 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
+% Library General Public License for more details.
+%
+% You should have received a copy of the GNU Library General Public
+% License along with this library; if not, write to the
+% Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+% Boston, MA  02111-1307  USA.
+%
+%
+%
+% The contents of this file are subject to the Mozilla Public License
+% Version 1.0 (the "MPL"); you may not use this file except in
+% compliance with the MPL.  You may obtain a copy of the MPL at
+% http://www.mozilla.org/MPL/
+%
+% Software distributed under the MPL is distributed on an "AS IS" basis,
+% WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL
+% for the specific language governing rights and limitations under the
+% MPL.
+
+
+@ @c
+
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+#include <stdlib.h>             /* for NULL, malloc */
+#include <stdio.h>              /* for fprintf */
+#include <string.h>             /* for strdup */
+#include <stdlib.h>             /* for malloc used by substring inclusion */
+
+#define MAXPATHS 40960
+
+#ifdef UNX
+#  include <unistd.h>           /* for exit */
+#endif
+
+#include <kpathsea/c-ctype.h>
+
+#define noVERBOSE
+
+#include "lang/hnjalloc.h"
+
+@ TODO: should be moved to separate library
+
+@c
+static unsigned char *hnj_strdup(const unsigned char *s)
+{
+    unsigned char *new;
+    size_t l;
+
+    l = strlen((const char *) s);
+    new = hnj_malloc((int) l + 1);
+    memcpy(new, s, l);
+    new[l] = 0;
+    return new;
+}
+
+@* Type definitions.
+
+@ a little bit of a hash table implementation. This simply maps strings
+   to state numbers
+
+@c
+typedef struct _HashTab HashTab;
+typedef struct _HashEntry HashEntry;
+typedef struct _HashIter HashIter;
+typedef union _HashVal HashVal;
+
+/* A cheap, but effective, hack. */
+#define HASH_SIZE 31627
+
+struct _HashTab {
+    HashEntry *entries[HASH_SIZE];
+};
+
+union _HashVal {
+    int state;
+    char *hyppat;
+};
+
+struct _HashEntry {
+    HashEntry *next;
+    unsigned char *key;
+    HashVal u;
+};
+
+struct _HashIter {
+    HashEntry **e;
+    HashEntry *cur;
+    int ndx;
+};
+
+@ State machine
+
+@c
+typedef struct _HyphenState HyphenState;
+typedef struct _HyphenTrans HyphenTrans;
+#define MAX_CHARS 256
+#define MAX_NAME 20
+
+struct _HyphenDict {
+    int num_states;
+    int pat_length;
+    char cset[MAX_NAME];
+    HyphenState *states;
+    HashTab *patterns;
+    HashTab *merged;
+    HashTab *state_num;
+};
+
+struct _HyphenState {
+    char *match;
+    /*char *repl; */
+    /*signed char replindex; */
+    /*signed char replcut; */
+    int fallback_state;
+    int num_trans;
+    HyphenTrans *trans;
+};
+
+struct _HyphenTrans {
+    int uni_ch;
+    int new_state;
+};
+
+
+@ Combine two right-aligned number patterns, 04000 + 020 becomes 04020
+
+@c
+static char *combine(char *expr, const char *subexpr)
+{
+    size_t l1 = strlen(expr);
+    size_t l2 = strlen(subexpr);
+    size_t off = l1 - l2;
+    unsigned j;
+    /* this works also for utf8 sequences because the substring is identical
+     to the last substring-length bytes of expr except for the (single byte)
+     hyphenation encoders
+     */
+    for (j = 0; j < l2; j++) {
+        if (expr[off + j] < subexpr[j])
+            expr[off + j] = subexpr[j];
+    }
+    return expr;
+}
+
+
+@ ORIGINAL CODE
+@c
+static HashIter *new_HashIter(HashTab * h)
+{
+    HashIter *i = hnj_malloc(sizeof(HashIter));
+    i->e = h->entries;
+    i->cur = NULL;
+    i->ndx = -1;
+    return i;
+}
+
+
+static int nextHashStealPattern(HashIter * i, unsigned char **word, char **pattern)
+{
+    while (i->cur == NULL) {
+        if (i->ndx >= HASH_SIZE - 1)
+            return 0;
+        i->cur = i->e[++i->ndx];
+    }
+    *word = i->cur->key;
+    *pattern = i->cur->u.hyppat;
+    i->cur->u.hyppat = NULL;
+    i->cur = i->cur->next;
+    return 1;
+}
+
+
+static int nextHash(HashIter * i, unsigned char **word)
+{
+    while (i->cur == NULL) {
+        if (i->ndx >= HASH_SIZE - 1)
+            return 0;
+        i->cur = i->e[++i->ndx];
+    }
+    *word = i->cur->key;
+    i->cur = i->cur->next;
+    return 1;
+}
+
+
+static int eachHash(HashIter * i, unsigned char **word, char **pattern)
+{
+    while (i->cur == NULL) {
+        if (i->ndx >= HASH_SIZE - 1)
+            return 0;
+        i->cur = i->e[++i->ndx];
+    }
+    *word = i->cur->key;
+    *pattern = i->cur->u.hyppat;
+    i->cur = i->cur->next;
+    return 1;
+}
+
+
+static void delete_HashIter(HashIter * i)
+{
+    hnj_free(i);
+}
+
+
+@ a |char*| hash function from ASU - adapted from Gtk+
+
+@c
+static unsigned int hnj_string_hash(const unsigned char *s)
+{
+    const unsigned char *p;
+    unsigned int h = 0, g;
+
+    for (p = s; *p != '\0'; p += 1) {
+        h = (h << 4) + *p;
+        if ((g = (h & 0xf0000000))) {
+            h = h ^ (g >> 24);
+            h = h ^ g;
+        }
+    }
+    return h /* \% M */ ;
+}
+
+
+@ assumes that key is not already present!
+
+@c
+static void state_insert(HashTab * hashtab, unsigned char *key, int state)
+{
+    int i;
+    HashEntry *e;
+
+    i = (int) (hnj_string_hash(key) % HASH_SIZE);
+    e = hnj_malloc(sizeof(HashEntry));
+    e->next = hashtab->entries[i];
+    e->key = key;
+    e->u.state = state;
+    hashtab->entries[i] = e;
+}
+
+
+@ assumes that key is not already present!
+
+@c
+static void hyppat_insert(HashTab * hashtab, unsigned char *key, char *hyppat)
+{
+    int i;
+    HashEntry *e;
+
+    i = (int) (hnj_string_hash(key) % HASH_SIZE);
+    for (e = hashtab->entries[i]; e; e = e->next) {
+        if (strcmp((char *) e->key, (char *) key) == 0) {
+            if (e->u.hyppat) {
+                if (hyppat
+                    && strcmp((char *) e->u.hyppat, (char *) hyppat) != 0) {
+                    print_err("Conflicting pattern ignored");
+                    error();
+                }
+                hnj_free(e->u.hyppat);
+            }
+            e->u.hyppat = hyppat;
+            hnj_free(key);
+            return;
+        }
+    }
+    e = hnj_malloc(sizeof(HashEntry));
+    e->next = hashtab->entries[i];
+    e->key = key;
+    e->u.hyppat = hyppat;
+    hashtab->entries[i] = e;
+}
+
+
+@ return state if found, otherwise $-1$
+
+@c
+static int state_lookup(HashTab * hashtab, const unsigned char *key)
+{
+    int i;
+    HashEntry *e;
+
+    i = (int) (hnj_string_hash(key) % HASH_SIZE);
+    for (e = hashtab->entries[i]; e; e = e->next) {
+        if (!strcmp((const char *) key, (const char *) e->key)) {
+            return e->u.state;
+        }
+    }
+    return -1;
+}
+
+
+@ return state if found, otherwise $-1$
+
+@c
+static char *hyppat_lookup(HashTab * hashtab, const unsigned char *chars, int l)
+{
+    int i;
+    HashEntry *e;
+    unsigned char key[256];     /* should be ample */
+    strncpy((char *) key, (const char *) chars, (size_t) l);
+    key[l] = 0;
+    i = (int) (hnj_string_hash(key) % HASH_SIZE);
+    for (e = hashtab->entries[i]; e; e = e->next) {
+        if (!strcmp((char *) key, (char *) e->key)) {
+            return e->u.hyppat;
+        }
+    }
+    return NULL;
+}
+
+
+@ Get the state number, allocating a new state if necessary.
+
+@c
+static int hnj_get_state(HyphenDict * dict,
+                         const unsigned char *str, int *state_num)
+{
+    *state_num = state_lookup(dict->state_num, str);
+
+    if (*state_num >= 0)
+        return *state_num;
+
+    state_insert(dict->state_num, hnj_strdup(str), dict->num_states);
+    /* predicate is true if |dict->num_states| is a power of two */
+    if (!(dict->num_states & (dict->num_states - 1))) {
+        dict->states = hnj_realloc(dict->states,
+                                   (int) ((dict->num_states << 1) *
+                                          (int) sizeof(HyphenState)));
+    }
+    dict->states[dict->num_states].match = NULL;
+    dict->states[dict->num_states].fallback_state = -1;
+    dict->states[dict->num_states].num_trans = 0;
+    dict->states[dict->num_states].trans = NULL;
+    return dict->num_states++;
+}
+
+
+@ Add a transition from state1 to state2 through ch - assumes that the
+   transition does not already exist
+
+@c
+static void hnj_add_trans(HyphenDict * dict, int state1, int state2, int uni_ch)
+{
+    int num_trans;
+    /* TH: this test was a bit too strict, it is quite normal for old
+       patterns to have chars in the range 0-31 or 127-159 (inclusive).
+       To ease the transition, let's only disallow NUL for now
+       (this is probably a requirement of the code anyway).
+     */
+    if (uni_ch == 0) {
+        char errmsg[256]; /* temp hack ... we will have a formatted error */
+        snprintf(errmsg, 255, "character out of bounds: u%04x", uni_ch);
+        errmsg[255] = '\0';
+        normal_error("hyphenation",errmsg); /* todo */
+    }
+    num_trans = dict->states[state1].num_trans;
+    if (num_trans == 0) {
+        dict->states[state1].trans = hnj_malloc(sizeof(HyphenTrans));
+    } else {
+        /* TH: The old version did
+           } else if (!(num_trans & (num_trans - 1))) {
+             ... hnj_realloc(dict->states[state1].trans,
+                                                 (int) ((num_trans << 1) *
+                                                        sizeof(HyphenTrans)));
+           but that is incredibly nasty when adding patters one-at-a-time.
+           Controlled growth would be nicer than the current +1, but if
+           noone complains, this is good enough ;)
+         */
+        dict->states[state1].trans = hnj_realloc(dict->states[state1].trans,
+                                                 (int) ((num_trans + 1) *
+                                                        sizeof(HyphenTrans)));
+    }
+    dict->states[state1].trans[num_trans].uni_ch = uni_ch;
+    dict->states[state1].trans[num_trans].new_state = state2;
+    dict->states[state1].num_trans++;
+}
+
+
+#ifdef VERBOSE
+
+static unsigned char *get_state_str(int state)
+{
+    int i;
+    HashEntry *e;
+
+    for (i = 0; i < HASH_SIZE; i++)
+        for (e = global->entries[i]; e; e = e->next)
+            if (e->u.state == state)
+                return e->key;
+    return NULL;
+}
+#endif
+
+
+@ I've changed the semantics a bit here: |hnj_hyphen_load| used to
+   operate on a file, but now the argument is a string buffer.
+
+@c
+static const unsigned char *next_pattern(size_t * length,
+                                         const unsigned char **buf)
+{
+    const unsigned char *here, *rover = *buf;
+    while (*rover && isspace(*rover))
+        rover++;
+    here = rover;
+    while (*rover) {
+        if (isspace(*rover)) {
+            *length = (size_t) (rover - here);
+            *buf = rover;
+            return here;
+        }
+        rover++;
+    }
+    *length = (size_t) (rover - here);
+    *buf = rover;
+    return *length ? here : NULL;       /* zero sensed */
+}
+
+static void init_hash(HashTab ** h)
+{
+    int i;
+    if (*h)
+        return;
+    *h = hnj_malloc(sizeof(HashTab));
+    for (i = 0; i < HASH_SIZE; i++)
+        (*h)->entries[i] = NULL;
+}
+
+
+static void clear_state_hash(HashTab ** h)
+{
+    int i;
+    if (*h == NULL)
+        return;
+    for (i = 0; i < HASH_SIZE; i++) {
+        HashEntry *e, *next;
+        for (e = (*h)->entries[i]; e; e = next) {
+            next = e->next;
+            hnj_free(e->key);
+            hnj_free(e);
+        }
+    }
+    hnj_free(*h);
+    *h = NULL;
+}
+
+
+static void clear_hyppat_hash(HashTab ** h)
+{
+    int i;
+    if (*h == NULL)
+        return;
+    for (i = 0; i < HASH_SIZE; i++) {
+        HashEntry *e, *next;
+        for (e = (*h)->entries[i]; e; e = next) {
+            next = e->next;
+            hnj_free(e->key);
+            if (e->u.hyppat)
+                hnj_free(e->u.hyppat);
+            hnj_free(e);
+        }
+    }
+    hnj_free(*h);
+    *h = NULL;
+}
+
+
+static void init_dict(HyphenDict * dict)
+{
+    dict->num_states = 1;
+    dict->pat_length = 0;
+    dict->states = hnj_malloc(sizeof(HyphenState));
+    dict->states[0].match = NULL;
+    dict->states[0].fallback_state = -1;
+    dict->states[0].num_trans = 0;
+    dict->states[0].trans = NULL;
+    dict->patterns = NULL;
+    dict->merged = NULL;
+    dict->state_num = NULL;
+    init_hash(&dict->patterns);
+}
+
+
+static void clear_dict(HyphenDict * dict)
+{
+    int state_num;
+    for (state_num = 0; state_num < dict->num_states; state_num++) {
+        HyphenState *hstate = &dict->states[state_num];
+        if (hstate->match)
+            hnj_free(hstate->match);
+        if (hstate->trans)
+            hnj_free(hstate->trans);
+    }
+    hnj_free(dict->states);
+    clear_hyppat_hash(&dict->patterns);
+    clear_hyppat_hash(&dict->merged);
+    clear_state_hash(&dict->state_num);
+}
+
+
+
+HyphenDict *hnj_hyphen_new(void)
+{
+    HyphenDict *dict = hnj_malloc(sizeof(HyphenDict));
+    init_dict(dict);
+    return dict;
+}
+
+
+void hnj_hyphen_clear(HyphenDict * dict)
+{
+    clear_dict(dict);
+    init_dict(dict);
+}
+
+
+void hnj_hyphen_free(HyphenDict * dict)
+{
+    clear_dict(dict);
+    hnj_free(dict);
+}
+
+unsigned char *hnj_serialize(HyphenDict * dict)
+{
+    HashIter *v;
+    unsigned char *word;
+    char *pattern;
+    unsigned char *buf = hnj_malloc(dict->pat_length);
+    unsigned char *cur = buf;
+    v = new_HashIter(dict->patterns);
+    while (eachHash(v, &word, &pattern)) {
+        int i = 0, e = 0;
+        while (word[e + i]) {
+            if (pattern[i] != '0')
+                *cur++ = (unsigned char) pattern[i];
+            *cur++ = word[e + i++];
+            while (is_utf8_follow(word[e + i]))
+                *cur++ = word[i + e++];
+        }
+        if (pattern[i] != '0')
+            *cur++ = (unsigned char) pattern[i];
+        *cur++ = ' ';
+    }
+    delete_HashIter(v);
+    *cur = 0;
+    return buf;
+}
+
+
+void hnj_free_serialize(unsigned char *c)
+{
+    hnj_free(c);
+}
+
+
+@ hyphenation pattern:
+
+signed bytes
+
+0 indicates end (actually any negative number)
+
+: prio(1+),startpos,length,len1,[replace],len2,[replace]
+
+most basic example is:
+
+p n 0 0 0
+
+for a hyphenation point between characters
+
+
+@c
+void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f)
+{
+    int state_num, last_state;
+    int ch;
+    int found;
+    HashEntry *e;
+    HashIter *v;
+    unsigned char *word;
+    char *pattern;
+    size_t l = 0;
+
+    const unsigned char *format;
+    const unsigned char *begin = f;
+    unsigned char *pat;
+    char *org;
+    while ((format = next_pattern(&l, &f)) != NULL) {
+        int i, j, e1;
+        if (l>=255) {
+           help1("Individual patterns should not be longer than 254 bytes total.");
+           print_err("Pattern of enormous length ignored");
+           error();
+           continue;
+        }
+#if 0
+           printf("%s\n",format);
+           char* repl = strnchr(format, '/',l);
+           int replindex = 0;
+           int replcut = 0;
+           if (repl) {
+           int clen = l-(repl-format);
+           l = repl-format;
+           char * index = strnchr(repl + 1, ',',clen);
+           if (index) {
+           char * index2 = strnchr(index + 1, ',',clen-(index-repl));
+           if (index2) {
+           replindex = (signed char) atoi(index + 1) - 1;
+           replcut = (signed char) atoi(index2 + 1);
+           }
+           } else {
+           hnj_strchomp(repl + 1);
+           replindex = 0;
+           replcut = strlen(buf);
+           }
+           repl = hnj_strdup(repl + 1);
+           }
+#endif
+        for (i = 0, j = 0, e1 = 0; (unsigned) i < l; i++) {
+            if (format[i] >= '0' && format[i] <= '9')
+                j++;
+            if (is_utf8_follow(format[i]))
+                e1++;
+        }
+        /* |l-e1|   => number of {\it characters} not {\it bytes} */
+        /* |l-j|   => number of pattern bytes */
+        /* |l-e1-j| => number of pattern characters */
+        pat = (unsigned char *) malloc((1 + l - (size_t) j));
+        org = (char *) malloc((size_t) (2 + l - (size_t) e1 - (size_t) j));
+        /* remove hyphenation encoders (digits) from pat */
+        org[0] = '0';
+        for (i = 0, j = 0, e1 = 0; (unsigned) i < l; i++) {
+            unsigned char c = format[i];
+            if (is_utf8_follow(c)) {
+                pat[j + e1++] = c;
+            } else if (c < '0' || c > '9') {
+                pat[e1 + j++] = c;
+                org[j] = '0';
+            } else {
+                org[j] = (char) c;
+            }
+        }
+        pat[e1 + j] = 0;
+        org[j + 1] = 0;
+        hyppat_insert(dict->patterns, pat, org);
+    }
+    dict->pat_length += (int) ((f - begin) + 2);        /* 2 for spurious spaces */
+    init_hash(&dict->merged);
+    v = new_HashIter(dict->patterns);
+    while (nextHash(v, &word)) {
+        int wordsize = (int) strlen((char *) word);
+        int j1, l1;
+        for (l1 = 1; l1 <= wordsize; l1++) {
+            if (is_utf8_follow(word[l1]))
+                continue;       /* Do not clip an utf8 sequence */
+            for (j1 = 1; j1 <= l1; j1++) {
+                char *subpat_pat;
+                int i1 = l1 - j1;
+                if (is_utf8_follow(word[i1]))
+                    continue;   /* Do not start halfway an utf8 sequence */
+                if ((subpat_pat =
+                     hyppat_lookup(dict->patterns, word + i1, j1)) != NULL) {
+                    char *newpat_pat;
+                    if ((newpat_pat =
+                         hyppat_lookup(dict->merged, word, l1)) == NULL) {
+                        char *neworg;
+                        unsigned char *newword =
+                            (unsigned char *) malloc((size_t) (l1 + 1));
+                        int e1 = 0;
+                        strncpy((char *) newword, (char *) word, (size_t) l1);
+                        newword[l1] = 0;
+                        for (i1 = 0; i1 < l1; i1++)
+                            if (is_utf8_follow(newword[i1]))
+                                e1++;
+                        neworg = malloc((size_t) (l1 + 2 - e1));
+                        sprintf(neworg, "%0*d", l1 + 1 - e1, 0);  /* fill with right amount of '0' */
+                        hyppat_insert(dict->merged, newword, combine(neworg, subpat_pat));
+                    } else {
+                        combine(newpat_pat, subpat_pat);
+                    }
+                }
+            }
+        }
+    }
+    delete_HashIter(v);
+
+    init_hash(&dict->state_num);
+    state_insert(dict->state_num, hnj_strdup((const unsigned char *) ""), 0);
+    v = new_HashIter(dict->merged);
+    while (nextHashStealPattern(v, &word, &pattern)) {
+        static unsigned char mask[] = { 0x3F, 0x1F, 0xF, 0x7 };
+        int j1 = (int) strlen((char *) word);
+#ifdef VERBOSE
+        printf("word %s pattern %s, j = %d\n", word, pattern, j1);
+#endif
+        state_num = hnj_get_state(dict, word, &found);
+        dict->states[state_num].match = pattern;
+
+        /* now, put in the prefix transitions */
+        while (found < 0) {
+            j1--;
+            last_state = state_num;
+            ch = word[j1];
+            if (ch >= 0x80) {
+                int m;
+                int i1 = 1;
+                while (is_utf8_follow(word[j1 - i1]))
+                    i1++;
+                ch = word[j1 - i1] & mask[i1];
+                m = j1 - i1;
+                while (i1--) {
+                    ch = (ch << 6) + (0x3F & word[j1 - i1]);
+                }
+                j1 = m;
+            }
+            word[j1] = '\0';
+            state_num = hnj_get_state(dict, word, &found);
+            hnj_add_trans(dict, state_num, last_state, ch);
+        }
+    }
+    delete_HashIter(v);
+    clear_hyppat_hash(&dict->merged);
+
+    /* put in the fallback states */
+    {
+    int i, j = 0;
+    for (i = 0; i < HASH_SIZE; i++) {
+        for (e = dict->state_num->entries[i]; e; e = e->next) {
+            /* do not do state==0 otherwise things get confused */
+            if (e->u.state) {
+                for (j = 1; 1; j++) {
+                    state_num = state_lookup(dict->state_num, e->key + j);
+                    if (state_num >= 0)
+                        break;
+                }
+                dict->states[e->u.state].fallback_state = state_num;
+            }
+        }
+    }
+#ifdef VERBOSE
+    for (i = 0; i < HASH_SIZE; i++) {
+        for (e = dict->state_num->entries[i]; e; e = e->next) {
+            printf("%d string %s state %d, fallback=%d\n",
+                i, e->key, e->u.state, dict->states[e->u.state].fallback_state);
+            for (j = 0; j < dict->states[e->u.state].num_trans; j++) {
+                printf(" u%4x->%d\n",
+                   (int) dict->states[e->u.state].trans[j].uni_ch,
+                   dict->states[e->u.state].trans[j].new_state);
+            }
+        }
+    }
+#endif
+    }
+    clear_state_hash(&dict->state_num);
+}
+
+@ @c
+extern halfword insert_syllable_discretionary(halfword t, lang_variables * lan);
+
+void hnj_hyphen_hyphenate(HyphenDict * dict,
+                          halfword first1,
+                          halfword last1,
+                          int length,
+                          halfword left, halfword right, lang_variables * lan)
+{
+    int char_num;
+    halfword here;
+    int state = 0;
+    /* +2 for dots at each end, +1 for points /outside/ characters */
+    int ext_word_len = length + 2;
+    int hyphen_len = ext_word_len + 1;
+    char *hyphens = hnj_malloc(hyphen_len + 1);
+
+    /* Add a '.' to beginning and end to facilitate matching */
+    vlink(begin_point) = first1;
+    vlink(end_point) = vlink(last1);
+    vlink(last1) = end_point;
+
+    for (char_num = 0; char_num < hyphen_len; char_num++) {
+        hyphens[char_num] = '0';
+    }
+    hyphens[hyphen_len] = 0;
+
+    /* now, run the finite state machine */
+    for (char_num = 0, here = begin_point; here != vlink(end_point);
+         here = vlink(here)) {
+
+        int ch;
+        if (here == begin_point || here == end_point) {
+            ch = '.';
+        } else {
+            ch = get_hj_code(char_lang(here),character(here));
+            if (ch <= 32) {
+                ch = character(here);
+            }
+        }
+        while (state != -1) {
+#if 0
+            printf("%*s%s%c",char_num-strlen(get_state_str(state)),"",get_state_str(state),(char)ch);
+#endif
+            HyphenState *hstate = &dict->states[state];
+            int k;
+            for (k = 0; k < hstate->num_trans; k++) {
+                if (hstate->trans[k].uni_ch == ch) {
+                    char *match;
+                    state = hstate->trans[k].new_state;
+#if 0
+                    printf(" state %d\n",state);
+#endif
+                    match = dict->states[state].match;
+                    if (match) {
+                        /* +2 because:
+                         1 string length is one bigger than offset
+                         1 hyphenation starts before first character
+                         */
+                        int offset = (int) (char_num + 2 - (int) strlen(match));
+#if 0
+                        printf("%*s%s\n", offset,"", match);
+#endif
+                        int m;
+                        for (m = 0; match[m]; m++) {
+                            if (hyphens[offset + m] < match[m])
+                                hyphens[offset + m] = match[m];
+                        }
+                    }
+                    goto try_next_letter;
+                }
+            }
+            state = hstate->fallback_state;
+#if 0
+            printf(" back to %d\n", state);
+#endif
+        }
+        /* nothing worked, let's go to the next character */
+        state = 0;
+      try_next_letter:;
+        char_num++;
+    }
+
+    /* restore the correct pointers */
+    vlink(last1) = vlink(end_point);
+
+    /* pattern is \.{\^.\^w\^o\^r\^d\^.\^}   |word_len|=4, |ext_word_len|=6, |hyphens|=7
+     * check      \.{    \^ \^ \^    }   so drop first two and stop after |word_len-1|
+     */
+    for (here = first1, char_num = 2; here != left; here = vlink(here))
+        char_num++;
+    for (; here != right; here = vlink(here)) {
+        if (hyphens[char_num] & 1)
+            here = insert_syllable_discretionary(here, lan);
+        char_num++;
+    }
+    hnj_free(hyphens);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/lang/hyphen.c
+++ /dev/null
@@ -1,788 +0,0 @@
-/*
-
-hyphen.c
-
-This file is derived from libhnj which is is dual licensed under LGPL and MPL.
-Boilerplate for both licenses follows.
-
-LibHnj - a library for high quality hyphenation and justification
-
-(C) 1998 Raph Levien,
-(C) 2001 ALTLinux, Moscow (http://www.alt-linux.org),
-(C) 2001 Peter Novodvorsky (nidd@cs.msu.su)
-
-This library is free software; you can redistribute it and/or modify it under the
-terms of the GNU Library General Public License as published by the Free Software
-Foundation; either version 2 of the License, or (at your option) any later
-version.
-
-This library 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 Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public License along
-with this library; if not, write to the Free Software Foundation, Inc., 59 Temple
-Place - Suite 330, Boston, MA 02111-1307 USA.
-
-The contents of this file are subject to the Mozilla Public License Version 1.0
-(the "MPL"); you may not use this file except in compliance with the MPL. You may
-obtain a copy of the MPL at http://www.mozilla.org/MPL/
-
-Software distributed under the MPL is distributed on an "AS IS" basis, WITHOUT
-WARRANTY OF ANY KIND, either express or implied. See the MPL for the specific
-language governing rights and limitations under the MPL.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-#include <stdlib.h>   /* for NULL, malloc */
-#include <stdio.h>    /* for fprintf */
-#include <string.h>   /* for strdup */
-#include <stdlib.h>   /* for malloc used by substring inclusion */
-
-#define MAXPATHS 40960
-
-#ifdef UNX
-#  include <unistd.h> /* for exit */
-#endif
-
-#include <kpathsea/c-ctype.h>
-
-#define noVERBOSE
-
-#include "lang/hnjalloc.h"
-
-/*tex This could be moved to separate library. */
-
-static unsigned char *hnj_strdup(const unsigned char *s)
-{
-    unsigned char *new;
-    size_t l;
-
-    l = strlen((const char *) s);
-    new = hnj_malloc((int) l + 1);
-    memcpy(new, s, l);
-    new[l] = 0;
-    return new;
-}
-
-/*tex
-
-    First some type definitions and a little bit of a hash table implementation.
-    This simply maps strings to state numbers
-
-*/
-
-typedef struct _HashTab   HashTab;
-typedef struct _HashEntry HashEntry;
-typedef struct _HashIter  HashIter;
-typedef union  _HashVal   HashVal;
-
-/*tex A cheap, but effective, hack. */
-
-#define HASH_SIZE 31627
-
-struct _HashTab {
-    HashEntry *entries[HASH_SIZE];
-};
-
-union _HashVal {
-    int state;
-    char *hyppat;
-};
-
-struct _HashEntry {
-    HashEntry *next;
-    unsigned char *key;
-    HashVal u;
-};
-
-struct _HashIter {
-    HashEntry **e;
-    HashEntry *cur;
-    int ndx;
-};
-
-/* The state state machine. */
-
-typedef struct _HyphenState HyphenState;
-typedef struct _HyphenTrans HyphenTrans;
-
-#define MAX_CHARS 256
-#define MAX_NAME   20
-
-struct _HyphenDict {
-    int num_states;
-    int pat_length;
-    char cset[MAX_NAME];
-    HyphenState *states;
-    HashTab *patterns;
-    HashTab *merged;
-    HashTab *state_num;
-};
-
-struct _HyphenState {
-    char *match;
-    /*tex Removed:
-
-        \starttyping
-        char *repl;
-        signed char replindex;
-        signed char replcut;
-        \stoptyping
-    */
-    int fallback_state;
-    int num_trans;
-    HyphenTrans *trans;
-};
-
-struct _HyphenTrans {
-    int uni_ch;
-    int new_state;
-};
-
-/*tex
-
-    Combine two right-aligned number patterns, 04000 + 020 becomes 04020. This
-    works also for utf8 sequences because the substring is identical to the last
-    substring-length bytes of expr except for the (single byte) hyphenation
-    encoders
-
-*/
-
-static char *combine(char *expr, const char *subexpr)
-{
-    size_t l1 = strlen(expr);
-    size_t l2 = strlen(subexpr);
-    size_t off = l1 - l2;
-    unsigned j;
-    for (j = 0; j < l2; j++) {
-        if (expr[off + j] < subexpr[j])
-            expr[off + j] = subexpr[j];
-    }
-    return expr;
-}
-
-/*tex Some original code: */
-
-static HashIter *new_HashIter(HashTab * h)
-{
-    HashIter *i = hnj_malloc(sizeof(HashIter));
-    i->e = h->entries;
-    i->cur = NULL;
-    i->ndx = -1;
-    return i;
-}
-
-static int nextHashStealPattern(HashIter * i, unsigned char **word, char **pattern)
-{
-    while (i->cur == NULL) {
-        if (i->ndx >= HASH_SIZE - 1)
-            return 0;
-        i->cur = i->e[++i->ndx];
-    }
-    *word = i->cur->key;
-    *pattern = i->cur->u.hyppat;
-    i->cur->u.hyppat = NULL;
-    i->cur = i->cur->next;
-    return 1;
-}
-
-
-static int nextHash(HashIter * i, unsigned char **word)
-{
-    while (i->cur == NULL) {
-        if (i->ndx >= HASH_SIZE - 1)
-            return 0;
-        i->cur = i->e[++i->ndx];
-    }
-    *word = i->cur->key;
-    i->cur = i->cur->next;
-    return 1;
-}
-
-static int eachHash(HashIter * i, unsigned char **word, char **pattern)
-{
-    while (i->cur == NULL) {
-        if (i->ndx >= HASH_SIZE - 1)
-            return 0;
-        i->cur = i->e[++i->ndx];
-    }
-    *word = i->cur->key;
-    *pattern = i->cur->u.hyppat;
-    i->cur = i->cur->next;
-    return 1;
-}
-
-static void delete_HashIter(HashIter * i)
-{
-    hnj_free(i);
-}
-
-/*tex
-
-A |char*| hash function from ASU, adapted from |Gtk+|:
-
-*/
-
-static unsigned int hnj_string_hash(const unsigned char *s)
-{
-    const unsigned char *p;
-    unsigned int h = 0, g;
-
-    for (p = s; *p != '\0'; p += 1) {
-        h = (h << 4) + *p;
-        if ((g = (h & 0xf0000000))) {
-            h = h ^ (g >> 24);
-            h = h ^ g;
-        }
-    }
-    return h;
-}
-
-/*tex This assumes that key is not already present! */
-
-static void state_insert(HashTab * hashtab, unsigned char *key, int state)
-{
-    int i;
-    HashEntry *e;
-    i = (int) (hnj_string_hash(key) % HASH_SIZE);
-    e = hnj_malloc(sizeof(HashEntry));
-    e->next = hashtab->entries[i];
-    e->key = key;
-    e->u.state = state;
-    hashtab->entries[i] = e;
-}
-
-/*tex This also assumes that key is not already present! */
-
-static void hyppat_insert(HashTab * hashtab, unsigned char *key, char *hyppat)
-{
-    int i;
-    HashEntry *e;
-    i = (int) (hnj_string_hash(key) % HASH_SIZE);
-    for (e = hashtab->entries[i]; e; e = e->next) {
-        if (strcmp((char *) e->key, (char *) key) == 0) {
-            if (e->u.hyppat) {
-                if (hyppat && strcmp((char *) e->u.hyppat, (char *) hyppat) != 0) {
-                    normal_warning("hyphenation","a conflicting pattern has been ignored");
-                }
-                hnj_free(e->u.hyppat);
-            }
-            e->u.hyppat = hyppat;
-            hnj_free(key);
-            return;
-        }
-    }
-    e = hnj_malloc(sizeof(HashEntry));
-    e->next = hashtab->entries[i];
-    e->key = key;
-    e->u.hyppat = hyppat;
-    hashtab->entries[i] = e;
-}
-
-/*tex We return |state| if found, otherwise |-1|. */
-
-static int state_lookup(HashTab * hashtab, const unsigned char *key)
-{
-    int i;
-    HashEntry *e;
-    i = (int) (hnj_string_hash(key) % HASH_SIZE);
-    for (e = hashtab->entries[i]; e; e = e->next) {
-        if (!strcmp((const char *) key, (const char *) e->key)) {
-            return e->u.state;
-        }
-    }
-    return -1;
-}
-
-/*tex We return |state| if found, otherwise |-1|. */
-
-static char *hyppat_lookup(HashTab * hashtab, const unsigned char *chars, int l)
-{
-    int i;
-    HashEntry *e;
-    /*tex The 256 should be enough. */
-    unsigned char key[256];
-    strncpy((char *) key, (const char *) chars, (size_t) l);
-    key[l] = 0;
-    i = (int) (hnj_string_hash(key) % HASH_SIZE);
-    for (e = hashtab->entries[i]; e; e = e->next) {
-        if (!strcmp((char *) key, (char *) e->key)) {
-            return e->u.hyppat;
-        }
-    }
-    return NULL;
-}
-
-/*tex Get the state number, allocating a new state if necessary. */
-
-static int hnj_get_state(HyphenDict * dict, const unsigned char *str, int *state_num)
-{
-    *state_num = state_lookup(dict->state_num, str);
-    if (*state_num >= 0)
-        return *state_num;
-    state_insert(dict->state_num, hnj_strdup(str), dict->num_states);
-    /*tex The predicate is true if |dict->num_states| is a power of two: */
-    if (!(dict->num_states & (dict->num_states - 1))) {
-        dict->states = hnj_realloc(dict->states,
-           (int) ((dict->num_states << 1) * (int) sizeof(HyphenState)));
-    }
-    dict->states[dict->num_states].match = NULL;
-    dict->states[dict->num_states].fallback_state = -1;
-    dict->states[dict->num_states].num_trans = 0;
-    dict->states[dict->num_states].trans = NULL;
-    return dict->num_states++;
-}
-
-/*tex
-
-    Add a transition from state1 to state2 through ch - assumes that the
-    transition does not already exist.
-
-*/
-
-static void hnj_add_trans(HyphenDict * dict, int state1, int state2, int uni_ch)
-{
-    int num_trans;
-    /*
-        TH: this test was a bit too strict, it is quite normal for old patterns
-        to have chars in the range 0-31 or 127-159 (inclusive). To ease the
-        transition, let's only disallow |NUL| for now, which probably is a
-        requirement of the code anyway.
-    */
-    if (uni_ch == 0) {
-        formatted_error("hyphenation","a character is out of bounds: u%04x", uni_ch);
-    }
-    num_trans = dict->states[state1].num_trans;
-    if (num_trans == 0) {
-        dict->states[state1].trans = hnj_malloc(sizeof(HyphenTrans));
-    } else {
-        /*
-            TH: The old version did:
-
-            \starttyping
-            } else if (!(num_trans & (num_trans - 1))) {
-                ... =hnj_realloc(dict->states[state1].trans,
-                    (int) ((num_trans << 1) * sizeof(HyphenTrans)));
-            \stoptyping
-
-            but that is incredibly nasty when adding patters one-at-a-time.
-            Controlled growth would be nicer than the current +1, but if no one
-            complains, and no one did in a decade, this is good enough.
-
-        */
-        dict->states[state1].trans = hnj_realloc(dict->states[state1].trans,
-            (int) ((num_trans + 1) * sizeof(HyphenTrans)));
-    }
-    dict->states[state1].trans[num_trans].uni_ch = uni_ch;
-    dict->states[state1].trans[num_trans].new_state = state2;
-    dict->states[state1].num_trans++;
-}
-
-/*tex
-
-    We did change the semantics a bit here: |hnj_hyphen_load| used to operate on
-    a file, but now the argument is a string buffer.
-
-*/
-
-static const unsigned char *next_pattern(size_t * length, const unsigned char **buf)
-{
-    const unsigned char *here, *rover = *buf;
-    while (*rover && isspace(*rover))
-        rover++;
-    here = rover;
-    while (*rover) {
-        if (isspace(*rover)) {
-            *length = (size_t) (rover - here);
-            *buf = rover;
-            return here;
-        }
-        rover++;
-    }
-    *length = (size_t) (rover - here);
-    *buf = rover;
-    /*tex Zero sensed: */
-    return *length ? here : NULL;
-}
-
-static void init_hash(HashTab ** h)
-{
-    int i;
-    if (*h) {
-        return;
-    }
-    *h = hnj_malloc(sizeof(HashTab));
-    for (i = 0; i < HASH_SIZE; i++) {
-        (*h)->entries[i] = NULL;
-    }
-}
-
-static void clear_state_hash(HashTab ** h)
-{
-    int i;
-    if (*h == NULL) {
-        return;
-    }
-    for (i = 0; i < HASH_SIZE; i++) {
-        HashEntry *e, *next;
-        for (e = (*h)->entries[i]; e; e = next) {
-            next = e->next;
-            hnj_free(e->key);
-            hnj_free(e);
-        }
-    }
-    hnj_free(*h);
-    *h = NULL;
-}
-
-static void clear_hyppat_hash(HashTab ** h)
-{
-    int i;
-    if (*h == NULL) {
-        return;
-    }
-    for (i = 0; i < HASH_SIZE; i++) {
-        HashEntry *e, *next;
-        for (e = (*h)->entries[i]; e; e = next) {
-            next = e->next;
-            hnj_free(e->key);
-            if (e->u.hyppat) {
-                hnj_free(e->u.hyppat);
-            }
-            hnj_free(e);
-        }
-    }
-    hnj_free(*h);
-    *h = NULL;
-}
-
-static void init_dict(HyphenDict * dict)
-{
-    dict->num_states = 1;
-    dict->pat_length = 0;
-    dict->states = hnj_malloc(sizeof(HyphenState));
-    dict->states[0].match = NULL;
-    dict->states[0].fallback_state = -1;
-    dict->states[0].num_trans = 0;
-    dict->states[0].trans = NULL;
-    dict->patterns = NULL;
-    dict->merged = NULL;
-    dict->state_num = NULL;
-    init_hash(&dict->patterns);
-}
-
-static void clear_dict(HyphenDict * dict)
-{
-    int state_num;
-    for (state_num = 0; state_num < dict->num_states; state_num++) {
-        HyphenState *hstate = &dict->states[state_num];
-        if (hstate->match)
-            hnj_free(hstate->match);
-        if (hstate->trans)
-            hnj_free(hstate->trans);
-    }
-    hnj_free(dict->states);
-    clear_hyppat_hash(&dict->patterns);
-    clear_hyppat_hash(&dict->merged);
-    clear_state_hash(&dict->state_num);
-}
-
-HyphenDict *hnj_hyphen_new(void)
-{
-    HyphenDict *dict = hnj_malloc(sizeof(HyphenDict));
-    init_dict(dict);
-    return dict;
-}
-
-void hnj_hyphen_clear(HyphenDict * dict)
-{
-    clear_dict(dict);
-    init_dict(dict);
-}
-
-void hnj_hyphen_free(HyphenDict * dict)
-{
-    clear_dict(dict);
-    hnj_free(dict);
-}
-
-unsigned char *hnj_serialize(HyphenDict * dict)
-{
-    HashIter *v;
-    unsigned char *word;
-    char *pattern;
-    unsigned char *buf = hnj_malloc(dict->pat_length);
-    unsigned char *cur = buf;
-    v = new_HashIter(dict->patterns);
-    while (eachHash(v, &word, &pattern)) {
-        int i = 0, e = 0;
-        while (word[e + i]) {
-            if (pattern[i] != '0')
-                *cur++ = (unsigned char) pattern[i];
-            *cur++ = word[e + i++];
-            while (is_utf8_follow(word[e + i]))
-                *cur++ = word[i + e++];
-        }
-        if (pattern[i] != '0')
-            *cur++ = (unsigned char) pattern[i];
-        *cur++ = ' ';
-    }
-    delete_HashIter(v);
-    *cur = 0;
-    return buf;
-}
-
-void hnj_free_serialize(unsigned char *c)
-{
-    hnj_free(c);
-}
-
-/*tex
-
-    In hyphenation patterns we use signed bytes where |0|, or actually any
-    negative number, indicates end:
-
-    \starttyping
-    prio(1+),startpos,length,len1,[replace],len2,[replace]
-    \starttyping
-
-    A basic example is:
-
-    \starttyping
-    p n 0 0 0
-    \starttyping
-
-    for a hyphenation point between characters.
-
-*/
-
-void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f)
-{
-    int state_num, last_state;
-    int ch;
-    int found;
-    HashEntry *e;
-    HashIter *v;
-    unsigned char *word;
-    char *pattern;
-    size_t l = 0;
-    const unsigned char *format;
-    const unsigned char *begin = f;
-    unsigned char *pat;
-    char *org;
-    while ((format = next_pattern(&l, &f)) != NULL) {
-        int i, j, e1;
-        if (l>=255) {
-           normal_warning("hyphenation","a pattern of more than 254 bytes ignored");
-           continue;
-        }
-        for (i = 0, j = 0, e1 = 0; (unsigned) i < l; i++) {
-            if (format[i] >= '0' && format[i] <= '9')
-                j++;
-            if (is_utf8_follow(format[i]))
-                e1++;
-        }
-        /*tex
-            Here |l-e1| is the number of {\em characters} not {\em bytes}, |l-j|
-            the number of pattern bytes and |l-e1-j| the number of pattern
-            characters.
-        */
-        pat = (unsigned char *) malloc((1 + l - (size_t) j));
-        org = (char *) malloc((size_t) (2 + l - (size_t) e1 - (size_t) j));
-        /*tex Remove hyphenation encoders (digits) from pat. */
-        org[0] = '0';
-        for (i = 0, j = 0, e1 = 0; (unsigned) i < l; i++) {
-            unsigned char c = format[i];
-            if (is_utf8_follow(c)) {
-                pat[j + e1++] = c;
-            } else if (c < '0' || c > '9') {
-                pat[e1 + j++] = c;
-                org[j] = '0';
-            } else {
-                org[j] = (char) c;
-            }
-        }
-        pat[e1 + j] = 0;
-        org[j + 1] = 0;
-        hyppat_insert(dict->patterns, pat, org);
-    }
-    /*tex We add 2 bytes for spurious spaces. */
-    dict->pat_length += (int) ((f - begin) + 2);
-    init_hash(&dict->merged);
-    v = new_HashIter(dict->patterns);
-    while (nextHash(v, &word)) {
-        int wordsize = (int) strlen((char *) word);
-        int j1, l1;
-        for (l1 = 1; l1 <= wordsize; l1++) {
-            if (is_utf8_follow(word[l1])) {
-                /*tex Do not clip an utf8 sequence. */
-                continue;
-            }
-            for (j1 = 1; j1 <= l1; j1++) {
-                char *subpat_pat;
-                int i1 = l1 - j1;
-                if (is_utf8_follow(word[i1])) {
-                    /*tex Do not start halfway an utf8 sequence. */
-                    continue;
-                }
-                if ((subpat_pat =
-                     hyppat_lookup(dict->patterns, word + i1, j1)) != NULL) {
-                    char *newpat_pat;
-                    if ((newpat_pat =
-                         hyppat_lookup(dict->merged, word, l1)) == NULL) {
-                        char *neworg;
-                        unsigned char *newword =
-                            (unsigned char *) malloc((size_t) (l1 + 1));
-                        int e1 = 0;
-                        strncpy((char *) newword, (char *) word, (size_t) l1);
-                        newword[l1] = 0;
-                        for (i1 = 0; i1 < l1; i1++)
-                            if (is_utf8_follow(newword[i1]))
-                                e1++;
-                        neworg = malloc((size_t) (l1 + 2 - e1));
-                        /*tex Fill with right amount of zeros: */
-                        sprintf(neworg, "%0*d", l1 + 1 - e1, 0);
-                        hyppat_insert(dict->merged, newword, combine(neworg, subpat_pat));
-                    } else {
-                        combine(newpat_pat, subpat_pat);
-                    }
-                }
-            }
-        }
-    }
-    delete_HashIter(v);
-    init_hash(&dict->state_num);
-    state_insert(dict->state_num, hnj_strdup((const unsigned char *) ""), 0);
-    v = new_HashIter(dict->merged);
-    while (nextHashStealPattern(v, &word, &pattern)) {
-        static unsigned char mask[] = { 0x3F, 0x1F, 0xF, 0x7 };
-        int j1 = (int) strlen((char *) word);
-        state_num = hnj_get_state(dict, word, &found);
-        dict->states[state_num].match = pattern;
-        /*tex Now, put in the prefix transitions. */
-        while (found < 0) {
-            j1--;
-            last_state = state_num;
-            ch = word[j1];
-            if (ch >= 0x80) {
-                int m;
-                int i1 = 1;
-                while (is_utf8_follow(word[j1 - i1]))
-                    i1++;
-                ch = word[j1 - i1] & mask[i1];
-                m = j1 - i1;
-                while (i1--) {
-                    ch = (ch << 6) + (0x3F & word[j1 - i1]);
-                }
-                j1 = m;
-            }
-            word[j1] = '\0';
-            state_num = hnj_get_state(dict, word, &found);
-            hnj_add_trans(dict, state_num, last_state, ch);
-        }
-    }
-    delete_HashIter(v);
-    clear_hyppat_hash(&dict->merged);
-    /*tex Put in the fallback states. */
-    {
-        int i, j = 0;
-        for (i = 0; i < HASH_SIZE; i++) {
-            for (e = dict->state_num->entries[i]; e; e = e->next) {
-                /*tex Do not do |state==0| otherwise things get confused. */
-                if (e->u.state) {
-                    for (j = 1; 1; j++) {
-                        state_num = state_lookup(dict->state_num, e->key + j);
-                        if (state_num >= 0)
-                            break;
-                    }
-                    dict->states[e->u.state].fallback_state = state_num;
-                }
-            }
-        }
-    }
-    clear_state_hash(&dict->state_num);
-}
-
-extern halfword insert_syllable_discretionary(halfword t, lang_variables * lan);
-
-void hnj_hyphen_hyphenate(HyphenDict * dict, halfword first1, halfword last1,
-    int length, halfword left, halfword right, lang_variables * lan)
-{
-    int char_num;
-    halfword here;
-    int state = 0;
-    /*tex +2 for dots at each end, +1 for points outside characters. */
-    int ext_word_len = length + 2;
-    int hyphen_len = ext_word_len + 1;
-    char *hyphens = hnj_malloc(hyphen_len + 1);
-    /*tex Add a '.' to beginning and end to facilitate matching. */
-    vlink(begin_point) = first1;
-    vlink(end_point) = vlink(last1);
-    vlink(last1) = end_point;
-    for (char_num = 0; char_num < hyphen_len; char_num++) {
-        hyphens[char_num] = '0';
-    }
-    hyphens[hyphen_len] = 0;
-    /*tex Now, run the finite state machine. */
-    for (char_num = 0, here = begin_point; here != vlink(end_point); here = vlink(here)) {
-        int ch;
-        if (here == begin_point || here == end_point) {
-            ch = '.';
-        } else {
-            ch = get_hj_code(char_lang(here),character(here));
-            if (ch <= 32) {
-                ch = character(here);
-            }
-        }
-        while (state != -1) {
-            HyphenState *hstate = &dict->states[state];
-            int k;
-            for (k = 0; k < hstate->num_trans; k++) {
-                if (hstate->trans[k].uni_ch == ch) {
-                    char *match;
-                    state = hstate->trans[k].new_state;
-                    match = dict->states[state].match;
-                    if (match) {
-                        /*tex
-
-                            We add +2 because 1 string length is one bigger than offset
-                            and 1 hyphenation starts before first character.
-                        */
-                        int offset = (int) (char_num + 2 - (int) strlen(match));
-                        int m;
-                        for (m = 0; match[m]; m++) {
-                            if (hyphens[offset + m] < match[m])
-                                hyphens[offset + m] = match[m];
-                        }
-                    }
-                    goto try_next_letter;
-                }
-            }
-            state = hstate->fallback_state;
-        }
-        /*tex Nothing worked, let's go to the next character. */
-        state = 0;
-    try_next_letter:;
-        char_num++;
-    }
-    /*tex Restore the correct pointers. */
-    vlink(last1) = vlink(end_point);
-    /*tex
-
-        Pattern is \.{\^.\^w\^o\^r\^d\^.\^} and |word_len|=4, |ext_word_len|=6,
-        |hyphens|=7; check \.{ \^ \^ \^ } and therefore drop first two and stop
-        after |word_len-1|
-     */
-    for (here = first1, char_num = 2; here != left; here = vlink(here))
-        char_num++;
-    for (; here != right; here = vlink(here)) {
-        if (hyphens[char_num] & 1)
-            here = insert_syllable_discretionary(here, lan);
-        char_num++;
-    }
-    hnj_free(hyphens);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lang/texlang.w
@@ -0,0 +1,1213 @@
+% texlang.w
+%
+% Copyright 2006-2012 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 <string.h>
+#include "lua/luatex-api.h"
+
+@ Low-level helpers
+
+@ @c
+#define unVERBOSE
+
+#define MAX_TEX_LANGUAGES  16384
+
+static struct tex_language *tex_languages[MAX_TEX_LANGUAGES] = { NULL };
+
+static int next_lang_id = 0;
+
+struct tex_language *new_language(int n)
+{
+    struct tex_language *lang;
+    unsigned l;
+    if (n >= 0) {
+        l = (unsigned) n;
+        if (l != (MAX_TEX_LANGUAGES - 1))
+            if (next_lang_id <= n)
+                next_lang_id = n + 1;
+    } else {
+        while (tex_languages[next_lang_id] != NULL)
+            next_lang_id++;
+        l = (unsigned) next_lang_id++;
+    }
+    if (l < (MAX_TEX_LANGUAGES - 1) && tex_languages[l] == NULL) {
+        lang = xmalloc(sizeof(struct tex_language));
+        tex_languages[l] = lang;
+        lang->id = (int) l;
+        lang->exceptions = 0;
+        lang->patterns = NULL;
+        lang->pre_hyphen_char = '-';
+        lang->post_hyphen_char = 0;
+        lang->pre_exhyphen_char = 0;
+        lang->post_exhyphen_char = 0;
+        lang->hyphenation_min = -1;
+        if (saving_hyph_codes_par) {
+            /* for now, we might just use specific value for whatever task */
+            hj_codes_from_lc_codes(l);
+        }
+        return lang;
+    } else {
+        return NULL;
+    }
+}
+
+struct tex_language *get_language(int n)
+{
+    if (n >= 0 && n < MAX_TEX_LANGUAGES) {
+        if (tex_languages[n] != NULL) {
+            return tex_languages[n];
+        } else {
+            return new_language(n);
+        }
+    } else {
+        return NULL;
+    }
+}
+
+@ @c
+void set_pre_hyphen_char(int n, int v)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l != NULL)
+        l->pre_hyphen_char = (int) v;
+}
+
+void set_post_hyphen_char(int n, int v)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l != NULL)
+        l->post_hyphen_char = (int) v;
+}
+
+void set_pre_exhyphen_char(int n, int v)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l != NULL)
+        l->pre_exhyphen_char = (int) v;
+}
+
+void set_post_exhyphen_char(int n, int v)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l != NULL)
+        l->post_exhyphen_char = (int) v;
+}
+
+int get_pre_hyphen_char(int n)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l == NULL)
+        return -1;
+    return (int) l->pre_hyphen_char;
+}
+
+int get_post_hyphen_char(int n)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l == NULL)
+        return -1;
+    return (int) l->post_hyphen_char;
+}
+
+int get_pre_exhyphen_char(int n)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l == NULL)
+        return -1;
+    return (int) l->pre_exhyphen_char;
+}
+
+int get_post_exhyphen_char(int n)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l == NULL)
+        return -1;
+    return (int) l->post_exhyphen_char;
+}
+
+void set_hyphenation_min(int n, int v)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l != NULL)
+        l->hyphenation_min = (int) v;
+}
+
+int get_hyphenation_min(int n)
+{
+    struct tex_language *l = get_language((int) n);
+    if (l == NULL)
+        return -1;
+    return (int) l->hyphenation_min;
+}
+
+void load_patterns(struct tex_language *lang, const unsigned char *buff)
+{
+    if (lang == NULL || buff == NULL || strlen((const char *) buff) == 0)
+        return;
+    if (lang->patterns == NULL) {
+        lang->patterns = hnj_hyphen_new();
+    }
+    hnj_hyphen_load(lang->patterns, buff);
+}
+
+void clear_patterns(struct tex_language *lang)
+{
+    if (lang == NULL)
+        return;
+    if (lang->patterns != NULL) {
+        hnj_hyphen_clear(lang->patterns);
+    }
+}
+
+void load_tex_patterns(int curlang, halfword head)
+{
+    char *s = tokenlist_to_cstring(head, 1, NULL);
+    load_patterns(get_language(curlang), (unsigned char *) s);
+}
+
+@ @c
+#define STORE_CHAR(l,x) do { \
+    unsigned xx = get_hj_code(l,x); \
+    if (!xx || xx <= 32) { \
+        xx = x; \
+    } \
+    uindex = uni2string(uindex, xx); \
+} while (0)
+
+@ Cleans one word which is returned in |cleaned|, returns the new offset into
+|buffer|
+
+@c
+const char *clean_hyphenation(int id, const char *buff, char **cleaned)
+{
+    int items = 0;
+    unsigned char word[MAX_WORD_LEN + 1];     /* work buffer for bytes */
+    unsigned uword[MAX_WORD_LEN + 1] = { 0 }; /* work buffer for unicode */
+    int u = 0;                                /* unicode buffer value */
+    int i = 0;                                /* index into buffer */
+    char *uindex = (char *)word;
+    const char *s = buff;
+
+    while (*s && !isspace((unsigned char)*s)) {
+        word[i++] = (unsigned)*s;
+        s++;
+        if ((s-buff)>MAX_WORD_LEN) {
+            /* todo: this is too strict, should count unicode, not bytes */
+            *cleaned = NULL;
+            tex_error("exception too long", NULL);
+            return s;
+        }
+    }
+    /* now convert the input to unicode */
+    word[i] = '\0';
+    utf2uni_strcpy(uword, (const char *)word);
+    /* build the new word string */
+    i = 0;
+    while (uword[i]>0) {
+        u = uword[i++];
+        if (u == '-') {
+            /* skip */
+        } else if (u == '=') {
+            STORE_CHAR(id,'-');
+        } else if (u == '{') {
+            u = uword[i++];
+            items = 0;
+            while (u && u != '}') {
+                u = uword[i++];
+            }
+            if (u == '}') {
+                items++;
+                u = uword[i++];
+            }
+            while (u && u != '}') {
+                u = uword[i++];
+            }
+            if (u == '}') {
+                items++;
+                u = uword[i++];
+            }
+            if (u == '{') {
+                u = uword[i++];
+            }
+            while (u && u != '}') {
+                STORE_CHAR(id,u);
+                u = uword[i++];
+            }
+            if (u == '}') {
+                items++;
+            }
+            if (items != 3) {
+                *cleaned = NULL;
+                tex_error("exception syntax error", NULL);
+                return s;
+            }
+        } else {
+            STORE_CHAR(id,u);
+        }
+    }
+    *uindex = '\0';
+    *cleaned = xstrdup((char *) word);
+    return s;
+}
+
+@ @c
+void load_hyphenation(struct tex_language *lang, const unsigned char *buff)
+{
+    const char *s;
+    const char *value;
+    char *cleaned;
+    int id ;
+    if (lang == NULL)
+        return;
+    if (lang->exceptions == 0) {
+        lua_newtable(Luas);
+        lang->exceptions = luaL_ref(Luas, LUA_REGISTRYINDEX);
+    }
+    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lang->exceptions);
+    s = (const char *) buff;
+    id = lang->id;
+    while (*s) {
+        while (isspace((unsigned char)*s))
+            s++;
+        if (*s) {
+            value = s;
+            s = clean_hyphenation(id, s, &cleaned);
+            if (cleaned != NULL) {
+                if ((s - value) > 0) {
+                    lua_pushstring(Luas, cleaned);
+                    lua_pushlstring(Luas, value, (size_t) (s - value));
+                    lua_rawset(Luas, -3);
+                }
+                free(cleaned);
+            } else {
+#ifdef VERBOSE
+                formatted_warning("hyphenation","skipping invalid hyphenation exception: %s", value);
+#endif
+            }
+        }
+    }
+}
+
+void clear_hyphenation(struct tex_language *lang)
+{
+    if (lang == NULL)
+        return;
+    if (lang->exceptions != 0) {
+        luaL_unref(Luas, LUA_REGISTRYINDEX, lang->exceptions);
+        lang->exceptions = 0;
+    }
+}
+
+void load_tex_hyphenation(int curlang, halfword head)
+{
+    char *s = tokenlist_to_cstring(head, 1, NULL);
+    load_hyphenation(get_language(curlang), (unsigned char *) s);
+}
+
+@ @c
+static halfword insert_discretionary(halfword t, halfword pre, halfword post, halfword replace, int penalty)
+{
+    halfword g;
+    int f;
+    halfword d = new_node(disc_node, syllable_disc);
+    int attr = node_attr(t) ;
+    disc_penalty(d) = penalty;
+    if (t == replace) {
+        /* prev disc next-next */
+        try_couple_nodes(d, vlink(t));
+        try_couple_nodes(alink(t), d);
+        alink(t) = null;
+        vlink(t) = null;
+        replace = t ;
+    } else {
+        /* prev disc next */
+        try_couple_nodes(d, vlink(t));
+        couple_nodes(t, d);
+    }
+    if (replace != null) {
+        f = font(replace);
+    } else {
+        /* For compound words following explicit hyphens. */
+        f = get_cur_font();
+    }
+    for (g = pre; g != null; g = vlink(g)) {
+        font(g) = f;
+        if (attr != null) {
+            delete_attribute_ref(node_attr(g));
+            node_attr(g) = attr;
+            attr_list_ref(attr) += 1;
+        }
+    }
+    for (g = post; g != null; g = vlink(g)) {
+        font(g) = f;
+        if (attr != null) {
+            delete_attribute_ref(node_attr(g));
+            node_attr(g) = attr;
+            attr_list_ref(attr) += 1;
+        }
+    }
+    if (attr != null) {
+        for (g = replace; g != null; g = vlink(g)) {
+            delete_attribute_ref(node_attr(g));
+            node_attr(g) = attr;
+            attr_list_ref(attr) += 1;
+        }
+        delete_attribute_ref(node_attr(d));
+        node_attr(d) = attr;
+        attr_list_ref(attr) += 1;
+    }
+    set_disc_field(pre_break(d), pre);
+    set_disc_field(post_break(d), post);
+    set_disc_field(no_break(d), replace);
+    return d;
+}
+
+halfword insert_syllable_discretionary(halfword t, lang_variables * lan)
+{
+    halfword g, n;
+    n = new_node(disc_node, syllable_disc);
+    disc_penalty(n) = hyphen_penalty_par;
+    couple_nodes(n, vlink(t));
+    couple_nodes(t, n);
+    delete_attribute_ref(node_attr(n));
+    if (node_attr(t) != null) {
+        node_attr(n) = node_attr(t);
+        attr_list_ref(node_attr(t))++;
+    } else {
+        node_attr(n) = null;
+    }
+    if (lan->pre_hyphen_char > 0) {
+        g = raw_glyph_node();
+        set_to_character(g);
+        character(g) = lan->pre_hyphen_char;
+        font(g) = font(t);
+        lang_data(g) = lang_data(t);
+        if (node_attr(t) != null) {
+            node_attr(g) = node_attr(t);
+            attr_list_ref(node_attr(t))++;
+        }
+        set_disc_field(pre_break(n), g);
+    }
+
+    if (lan->post_hyphen_char > 0) {
+        t = vlink(n);
+        g = raw_glyph_node();
+        set_to_character(g);
+        character(g) = lan->post_hyphen_char;
+        font(g) = font(t);
+        lang_data(g) = lang_data(t);
+        if (node_attr(t) != null) {
+            node_attr(g) = node_attr(t);
+            attr_list_ref(node_attr(t)) += 1;
+        }
+        set_disc_field(post_break(n), g);
+    }
+    return n;
+}
+
+@ @c
+static halfword insert_character(halfword t, int c)
+{
+    halfword p;
+    p = new_node(glyph_node, 0);
+    set_to_character(p);
+    character(p) = c;
+    if (t != null) {
+        couple_nodes(t, p);
+    }
+    return p;
+}
+
+static halfword compound_word_break(halfword t, int clang)
+{
+    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);
+    } else {
+        pre = insert_character(null,ex_hyphen_char_par);
+    }
+    if (post_exhyphen_char > 0)
+        pos = insert_character(null,post_exhyphen_char);
+    disc = insert_discretionary(t,pre,pos,t,ex_hyphen_penalty_par);
+    subtype(disc) = automatic_disc;
+    set_automatic_disc_penalty(disc);
+    return disc;
+}
+
+@ @c
+void set_disc_field(halfword f, halfword t)
+{
+    if (t != null) {
+        /*
+            couple_nodes(f, t); // better not expose f as prev pointer
+        */
+        vlink(f) = t ;
+        alink(t) = null ;
+        tlink(f) = tail_of_list(t);
+    } else {
+        vlink(f) = null;
+        tlink(f) = null;
+    }
+}
+
+@ @c
+static char *hyphenation_exception(int exceptions, char *w)
+{
+    char *ret = NULL;
+    lua_checkstack(Luas, 2);
+    lua_rawgeti(Luas, LUA_REGISTRYINDEX, exceptions);
+    if (lua_istable(Luas, -1)) {   /* ?? */
+        lua_pushstring(Luas, w);   /* word table */
+        lua_rawget(Luas, -2);
+        if (lua_type(Luas, -1) == LUA_TSTRING) {
+            ret = xstrdup(lua_tostring(Luas, -1));
+        }
+        lua_pop(Luas, 2);
+    } else {
+        lua_pop(Luas, 1);
+    }
+    return ret;
+}
+
+@ @c
+char *exception_strings(struct tex_language *lang)
+{
+    const char *value;
+    size_t size = 0, current = 0;
+    size_t l = 0;
+    char *ret = NULL;
+    if (lang->exceptions == 0)
+        return NULL;
+    lua_checkstack(Luas, 2);
+    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lang->exceptions);
+    if (lua_istable(Luas, -1)) {
+        /* iterate and join */
+        lua_pushnil(Luas);         /* first key */
+        while (lua_next(Luas, -2) != 0) {
+            value = lua_tolstring(Luas, -1, &l);
+            if (current + 2 + l > size) {
+                ret = xrealloc(ret, (unsigned) ((size + size / 5) + current + l + 1024));
+                size = (size + size / 5) + current + l + 1024;
+            }
+            *(ret + current) = ' ';
+            strcpy(ret + current + 1, value);
+            current += l + 1;
+            lua_pop(Luas, 1);
+        }
+    }
+    return ret;
+}
+
+@ the sequence from |wordstart| to |r| can contain only normal characters it
+could be faster to modify a halfword pointer and return an integer
+
+@c
+static halfword find_exception_part(unsigned int *j, unsigned int *uword, int len)
+{
+    halfword g = null, gg = null;
+    register unsigned i = *j;
+    i++;                        /* this puts uword[i] on the |{| */
+    while (i < (unsigned) len && uword[i + 1] != '}') {
+        if (g == null) {
+            gg = new_char(0, (int) uword[i + 1]);
+            g = gg;
+        } else {
+            halfword s = new_char(0, (int) uword[i + 1]);
+            couple_nodes(g, s);
+            g = vlink(g);
+        }
+        i++;
+    }
+    *j = ++i;
+    return gg;
+}
+
+static int count_exception_part(unsigned int *j, unsigned int *uword, int len)
+{
+    int ret = 0;
+    register unsigned i = *j;
+    i++;                        /* this puts uword[i] on the |{| */
+    while (i < (unsigned) len && uword[i + 1] != '}') {
+        ret++;
+        i++;
+    }
+    *j = ++i;
+    return ret;
+}
+
+@ @c
+static const char *PAT_ERROR[] = {
+    "Exception discretionaries should contain three pairs of braced items.",
+    "No intervening spaces are allowed.",
+    NULL
+};
+
+/*
+    The exceptions are taken as-is: no min values are taken into account. One can
+    add normal patterns on-the-fly if needed.
+*/
+
+static void do_exception(halfword wordstart, halfword r, char *replacement)
+{
+    unsigned i;
+    halfword t;
+    unsigned len;
+    int clang;
+    lang_variables langdata;
+    unsigned uword[MAX_WORD_LEN + 1] = { 0 };
+    utf2uni_strcpy(uword, replacement);
+    len = u_length(uword);
+    i = 0;
+    t = wordstart;
+    clang = char_lang(wordstart);
+    langdata.pre_hyphen_char = get_pre_hyphen_char(clang);
+    langdata.post_hyphen_char = get_post_hyphen_char(clang);
+
+    for (i = 0; i < len; i++) {
+        if (uword[i + 1] == '-') {      /* a hyphen follows */
+            while (vlink(t) != r && (type(t) != glyph_node || !is_simple_character(t)))
+                t = vlink(t);
+            if (vlink(t) == r)
+                break;
+            insert_syllable_discretionary(t, &langdata);
+            t = vlink(t);       /* skip the new disc */
+        } else if (uword[i + 1] == '=') {
+            /* do nothing ? */
+            t = vlink(t);
+        } else if (uword[i + 1] == '{') {
+            halfword gg, hh, replace = null;
+            int repl;
+            gg = find_exception_part(&i, uword, (int) len);
+            if (i == len || uword[i + 1] != '{') {
+                tex_error("broken pattern 1", PAT_ERROR);
+            }
+            hh = find_exception_part(&i, uword, (int) len);
+            if (i == len || uword[i + 1] != '{') {
+                tex_error("broken pattern 2", PAT_ERROR);
+            }
+            repl = count_exception_part(&i, uword, (int) len);
+            if (i == len) {
+                tex_error("broken pattern 3", PAT_ERROR);
+            }
+            /*i++;  *//* jump over the last right brace */
+            if (vlink(t) == r)
+                break;
+            if (repl > 0) {
+                halfword q = t;
+                replace = vlink(q);
+                while (repl > 0 && q != null) {
+                    q = vlink(q);
+                    if (type(q) == glyph_node) {
+                        repl--;
+                    }
+                }
+                try_couple_nodes(t, vlink(q));
+                vlink(q) = null;
+            }
+            t = insert_discretionary(t, gg, hh, replace, hyphen_penalty_par);
+            t = vlink(t);       /* skip the new disc */
+        } else {
+            t = vlink(t);
+        }
+    }
+}
+
+@ This is a documentation section from the pascal web file. It is not true any
+more, but I do not have time right now to rewrite it -- Taco
+
+When the line-breaking routine is unable to find a feasible sequence of
+breakpoints, it makes a second pass over the paragraph, attempting to hyphenate
+the hyphenatable words. The goal of hyphenation is to insert discretionary
+material into the paragraph so that there are more potential places to break.
+
+The general rules for hyphenation are somewhat complex and technical, because we
+want to be able to hyphenate words that are preceded or followed by punctuation
+marks, and because we want the rules to work for languages other than English. We
+also must contend with the fact that hyphens might radically alter the ligature
+and kerning structure of a word.
+
+A sequence of characters will be considered for hyphenation only if it belongs to
+a ``potentially hyphenatable part'' of the current paragraph. This is a sequence
+of nodes $p_0p_1\ldots p_m$ where $p_0$ is a glue node, $p_1\ldots p_{m-1}$ are
+either character or ligature or whatsit or implicit kern nodes, and $p_m$ is a
+glue or penalty or insertion or adjust or mark or whatsit or explicit kern node.
+(Therefore hyphenation is disabled by boxes, math formulas, and discretionary
+nodes already inserted by the user.) The ligature nodes among $p_1\ldots p_{m-1}$
+are effectively expanded into the original non-ligature characters; the kern
+nodes and whatsits are ignored. Each character |c| is now classified as either a
+nonletter (if |lc_code(c)=0|), a lowercase letter (if |lc_code(c)=c|), or an
+uppercase letter (otherwise); an uppercase letter is treated as if it were
+|lc_code(c)| for purposes of hyphenation. The characters generated by $p_1\ldots
+p_{m-1}$ may begin with nonletters; let $c_1$ be the first letter that is not in
+the middle of a ligature. Whatsit nodes preceding $c_1$ are ignored; a whatsit
+found after $c_1$ will be the terminating node $p_m$. All characters that do not
+have the same font as $c_1$ will be treated as nonletters. The |hyphen_char| for
+that font must be between 0 and 255, otherwise hyphenation will not be attempted.
+\TeX\ looks ahead for as many consecutive letters $c_1\ldots c_n$ as possible;
+however, |n| must be less than 64, so a character that would otherwise be
+$c_{64}$ is effectively not a letter. Furthermore $c_n$ must not be in the middle
+of a ligature. In this way we obtain a string of letters $c_1\ldots c_n$ that are
+generated by nodes $p_a\ldots p_b$, where |1<=a<=b+1<=m|. If |n>=l_hyf+r_hyf|,
+this string qualifies for hyphenation; however, |uc_hyph| must be positive, if
+$c_1$ is uppercase.
+
+The hyphenation process takes place in three stages. First, the candidate
+sequence $c_1\ldots c_n$ is found; then potential positions for hyphens are
+determined by referring to hyphenation tables; and finally, the nodes $p_a\ldots
+p_b$ are replaced by a new sequence of nodes that includes the discretionary
+breaks found.
+
+Fortunately, we do not have to do all this calculation very often, because of the
+way it has been taken out of \TeX's inner loop. For example, when the second
+edition of the author's 700-page book {\sl Seminumerical Algorithms} was typeset
+by \TeX, only about 1.2 hyphenations needed to be @^Knuth, Donald Ervin@> tried
+per paragraph, since the line breaking algorithm needed to use two passes on only
+about 5 per cent of the paragraphs.
+
+When a word been set up to contain a candidate for hyphenation, \TeX\ first looks
+to see if it is in the user's exception dictionary. If not, hyphens are inserted
+based on patterns that appear within the given word, using an algorithm due to
+Frank~M. Liang. @^Liang, Franklin Mark@>
+
+@ This is incompatible with TEX because the first word of a paragraph can be
+hyphenated, but most european users seem to agree that prohibiting hyphenation
+there was not the best idea ever.
+
+@c
+/*
+    More strict: \hyphenationbounds
+
+    0 = not strict
+    1 = strict start
+    2 = strict end
+    3 = strict start and strict end
+
+    \parindent0pt \hsize=1.1cm
+    12-34-56 \par
+    12-34-\hbox{56} \par
+    12-34-\vrule width 1em height 1.5ex \par
+    12-\hbox{34}-56 \par
+    12-\vrule width 1em height 1.5ex-56 \par
+    \hjcode`\1=`\1 \hjcode`\2=`\2 \hjcode`\3=`\3 \hjcode`\4=`\4 \vskip.5cm
+    12-34-56 \par
+    12-34-\hbox{56} \par
+    12-34-\vrule width 1em height 1.5ex \par
+    12-\hbox{34}-56 \par
+    12-\vrule width 1em height 1.5ex-56 \par
+
+*/
+
+/*
+    We only accept an explicit hyphen when there is a preceding glyph and we skip a sequence of
+    explicit hyphens as that normally indicates a -- or --- ligature in which case we can in a
+    worse case usage get bad node lists later on due to messed up ligature building as these
+    dashes are ligatures in base fonts. This is a side effect of the separating the hyphenation,
+    ligaturing and kerning steps. A test is cmr with ------.
+
+    A font handler can collapse successive hyphens but it's not nice to put the burden there. A
+    somewhat messy border case is ---- but in LuaTeX we don't treat -- and --- special. Also,
+    traditional TeX will break a line at -foo but this can be disabled by setting the automatic
+    mode to 1.
+
+*/
+
+static halfword find_next_wordstart(halfword r, halfword first_language, halfword strict_bound)
+{
+    register int l;
+    register int start_ok = 1;
+    int mathlevel = 1;
+    int chr ;
+    halfword t ;
+    while (r != null) {
+        switch (type(r)) {
+        case boundary_node:
+            if (subtype(r) == word_boundary) {
+                start_ok = 1;
+            }
+            break;
+        case hlist_node: /* new > 0.95 */
+        case vlist_node: /* new > 0.95 */
+        case rule_node:  /* new > 0.95 */
+        case dir_node:
+        case whatsit_node:
+            if (strict_bound == 1 || strict_bound == 3) {
+                start_ok = 0;
+            }
+            break;
+        case glue_node:
+            start_ok = 1;
+            break;
+        case math_node:
+            while (mathlevel > 0) {
+                r = vlink(r);
+                if (r == null)
+                    return r;
+                if (type(r) == math_node) {
+                    if (subtype(r) == before) {
+                        mathlevel++;
+                    } else {
+                        mathlevel--;
+                    }
+                }
+            }
+            break;
+        case glyph_node:
+            if (is_simple_character(r)) {
+                chr = character(r) ;
+                if (chr == ex_hyphen_char_par) {
+                    t = vlink(r) ;
+                    if ((automatic_hyphen_mode_par == 0) && (t != null) && (type(t) == glyph_node) && (character(t) != ex_hyphen_char_par)) {
+                        /* we have no word yet and the next character is a non hyphen */
+                        r = compound_word_break(r, char_lang(r));
+                    } else {
+                        /* we jump over the sequence of hyphens */
+                        while ((t != null) && (type(t) == glyph_node) && (character(t) == ex_hyphen_char_par)) {
+                            r = t ;
+                            t = vlink(r) ;
+                        }
+                        if (t == null) {
+                            /* we reached the end of the list so we have no word start */
+                            return null;
+                        }
+                    }
+                    /* we need a restart */
+                    start_ok = 0;
+                } else if (start_ok && (char_lang(r)>=first_language) && ((l = get_hj_code(char_lang(r),chr)) > 0)) {
+                    if (char_uchyph(r) || l == chr || l <= 32) {
+                        return r;
+                    } else {
+                        start_ok = 0;
+                    }
+                } else {
+                    /* go on */
+                }
+            }
+            break;
+        default:
+            start_ok = 0;
+            break;
+        }
+        r = vlink(r);
+    }
+    return r;
+}
+
+@ @c
+static int valid_wordend(halfword s, halfword strict_bound)
+{
+    register halfword r = s;
+    register int clang = char_lang(s);
+    if (r == null)
+        return 1;
+    while ( (r != null) &&
+           (    (type(r) == glyph_node && is_simple_character(r) && clang == char_lang(r))
+             || (type(r) == kern_node && (subtype(r) == normal))
+            )
+           ) {
+        r = vlink(r);
+    }
+    if (r == null || (type(r) == glyph_node && is_simple_character(r) && clang != char_lang(r))
+                  ||  type(r) == glue_node
+                  ||  type(r) == penalty_node
+                  || (type(r) == kern_node && (subtype(r) == explicit_kern || /* so why not italic correction ? */
+                                               subtype(r) == italic_kern   ||
+                                               subtype(r) == accent_kern   ))
+                  ||  ((type(r) == hlist_node   || /* new > 0.95 */
+                        type(r) == vlist_node   || /* new > 0.95 */
+                        type(r) == rule_node    || /* new > 0.95 */
+                        type(r) == dir_node     || /* new > 0.97 */
+                        type(r) == whatsit_node ||
+                        type(r) == ins_node     || /* yes or no strict test */
+                        type(r) == adjust_node     /* yes or no strict test */
+                       ) && ! (strict_bound == 2 || strict_bound == 3))
+                  ||  type(r) == boundary_node
+        )
+        return 1;
+    return 0;
+}
+
+@ @c
+void hnj_hyphenation(halfword head, halfword tail)
+{
+    int lchar, i;
+    struct tex_language *lang;
+    lang_variables langdata;
+    char utf8word[(4 * MAX_WORD_LEN) + 1] = { 0 };
+    int wordlen = 0;
+    char *hy = utf8word;
+    char *replacement = NULL;
+    boolean explicit_hyphen = false;
+    halfword first_language = first_valid_language_par;
+    halfword strict_bound = hyphenation_bounds_par;
+    halfword s, r = head, wordstart = null, save_tail1 = null, left = null, right = null;
+
+    /*
+        This first movement assures two things:
+
+        \item{a)} that we won't waste lots of time on something that has been handled already
+        (in that case, none of the glyphs match |simple_character|).
+
+        \item{b)} that the first word can be hyphenated. if the movement was not explicit,
+        then the indentation at the start of a paragraph list would make |find_next_wordstart()|
+        look too far ahead.
+    */
+
+    while (r != null && (type(r) != glyph_node || !is_simple_character(r))) {
+        r = vlink(r);
+    }
+    /*
+        This will make |r| a glyph node with subtype character.
+    */
+    r = find_next_wordstart(r,first_language,strict_bound);
+    if (r == null)
+        return;
+
+    assert(tail != null);
+    save_tail1 = vlink(tail);
+    s = new_penalty(0,word_penalty);
+    couple_nodes(tail, s);
+
+    while (r != null) { /* could be while(1), but let's be paranoid */
+        int clang, lhmin, rhmin, hmin;
+        halfword hyf_font;
+        halfword end_word = r;
+        wordstart = r;
+        assert(is_simple_character(wordstart));
+        hyf_font = font(wordstart);
+        if (hyphen_char(hyf_font) < 0) {
+            /* For backward compatibility: */
+            hyf_font = 0;
+        }
+        clang = char_lang(wordstart);
+        lhmin = char_lhmin(wordstart);
+        rhmin = char_rhmin(wordstart);
+        hmin = get_hyphenation_min(clang);
+        langdata.pre_hyphen_char = get_pre_hyphen_char(clang);
+        langdata.post_hyphen_char = get_post_hyphen_char(clang);
+        while (    r != null
+                && type(r) == glyph_node
+                && is_simple_character(r)
+                && clang == char_lang(r)
+                && (    (     (clang >= first_language)
+                           && (lchar = get_hj_code(clang,character(r))) > 0
+                        )
+                     || (     character(r) == ex_hyphen_char_par
+                           && (lchar = ex_hyphen_char_par)
+                        )
+                   )
+              ) {
+            if (character(r) == ex_hyphen_char_par) {
+                explicit_hyphen = true;
+                break;
+            }
+            wordlen++;
+            if (lchar <= 32) {
+                if (lchar == 32) {
+                    lchar = 0 ;
+                }
+                if (wordlen <= lhmin) {
+                    lhmin = lhmin - lchar + 1 ;
+                    if (lhmin < 0)
+                        lhmin = 1;
+                }
+                if (wordlen >= rhmin) {
+                    rhmin = rhmin - lchar + 1 ;
+                    if (rhmin < 0)
+                        rhmin = 1;
+                }
+                hmin = hmin - lchar + 1 ;
+                if (hmin < 0)
+                    rhmin = 1;
+                lchar = character(r) ;
+            }
+            hy = uni2string(hy, (unsigned) lchar);
+            end_word = r;
+            r = vlink(r);
+        }
+        if (explicit_hyphen == true) {
+            /* we are not at the start, so we only need to look ahead */
+            halfword t = vlink(r) ;
+            if ((automatic_hyphen_mode_par == 0 || automatic_hyphen_mode_par == 1) && (t != null) && ((type(t) == glyph_node) && (character(t) != ex_hyphen_char_par))) {
+                /* we have a word already but the next character may not be a hyphen too */
+                r = compound_word_break(r, char_lang(r));
+            } else {
+                /* we jump over the sequence of hyphens */
+                while ((t != null) && (type(t) == glyph_node) && (character(t) == ex_hyphen_char_par)) {
+                    r = t ;
+                    t = vlink(r) ;
+                }
+                if (t == null) {
+                    /* we reached the end of the list and will quit the loop later */
+                    r = null;
+                }
+            }
+        } else if (     valid_wordend(r,strict_bound)
+              && clang >= first_language
+              && wordlen >= lhmin + rhmin
+              && (hmin <= 0 || wordlen >= hmin)
+              && (hyf_font != 0)
+              && (lang = tex_languages[clang]) != NULL
+           ) {
+            *hy = 0;
+            if (lang->exceptions != 0 && (replacement = hyphenation_exception(lang->exceptions, utf8word)) != NULL) {
+                /* handle the exception and go on to the next word */
+                do_exception(wordstart, r, replacement);
+                free(replacement);
+            } else if (lang->patterns != NULL) {
+                left = wordstart;
+                for (i = lhmin; i > 1; i--) {
+                    left = vlink(left);
+                    while (!is_simple_character(left)) {
+                        left = vlink(left);
+                    }
+                    /* if (!left) break; */
+                    /* what is left overruns right .. a bit messy */
+                }
+                right = r;
+                for (i = rhmin; i > 0; i--) {
+                    right = alink(right);
+                    while (!is_simple_character(right)) {
+                        right = alink(right);
+                    }
+                    /* if (!right) break; */
+                    /* what is right overruns left .. a bit messy */
+                }
+                (void) hnj_hyphen_hyphenate(lang->patterns, wordstart, end_word, wordlen, left, right, &langdata);
+            }
+        }
+        explicit_hyphen = false;
+        wordlen = 0;
+        hy = utf8word;
+        if (r == null)
+            break;
+        r = find_next_wordstart(r,first_language,strict_bound);
+    }
+    flush_node(vlink(tail));
+    vlink(tail) = save_tail1;
+}
+
+@ @c
+void new_hyphenation(halfword head, halfword tail)
+{
+    register int callback_id = 0;
+    if (head == null || vlink(head) == null)
+        return;
+    fix_node_list(head);
+    callback_id = callback_defined(hyphenate_callback);
+    if (callback_id > 0) {
+        if (!get_callback(Luas, callback_id)) {
+            lua_pop(Luas, 2);
+            return;
+        }
+        nodelist_to_lua(Luas, head);
+        nodelist_to_lua(Luas, tail);
+        if (lua_pcall(Luas, 2, 0, 0) != 0) {
+            formatted_warning("hyphenation","bad specification: %s",lua_tostring(Luas, -1));
+            lua_pop(Luas, 2);
+            lua_error(Luas);
+            return;
+        }
+        lua_pop(Luas, 1);
+    } else if (callback_id == 0) {
+        hnj_hyphenation(head, tail);
+    }
+}
+
+@ Dumping and undumping languages.
+
+@c
+#define dump_string(a)                \
+  if (a!=NULL) {                      \
+      x = (int)strlen(a)+1;           \
+    dump_int(x);  dump_things(*a, x); \
+  } else {                            \
+    x = 0; dump_int(x);               \
+  }
+
+static void dump_one_language(int i)
+{
+    char *s = NULL;
+    int x = 0;
+    struct tex_language *lang;
+    lang = tex_languages[i];
+    dump_int(lang->id);
+    dump_int(lang->pre_hyphen_char);
+    dump_int(lang->post_hyphen_char);
+    dump_int(lang->pre_exhyphen_char);
+    dump_int(lang->post_exhyphen_char);
+    dump_int(lang->hyphenation_min);
+    if (lang->patterns != NULL) {
+        s = (char *) hnj_serialize(lang->patterns);
+    }
+    dump_string(s);
+    if (s != NULL) {
+        free(s);
+        s = NULL;
+    }
+    if (lang->exceptions != 0)
+        s = exception_strings(lang);
+    dump_string(s);
+    if (s != NULL) {
+        free(s);
+    }
+    free(lang);
+}
+
+void dump_language_data(void)
+{
+    int i;
+    dump_int(next_lang_id);
+    for (i = 0; i < next_lang_id; i++) {
+        if (tex_languages[i]) {
+            dump_int(1);
+            dump_one_language(i);
+        } else {
+            dump_int(0);
+        }
+    }
+}
+
+static void undump_one_language(int i)
+{
+    char *s = NULL;
+    int x = 0;
+    struct tex_language *lang = get_language(i);
+    undump_int(x);
+    lang->id = x;
+    undump_int(x);
+    lang->pre_hyphen_char = x;
+    undump_int(x);
+    lang->post_hyphen_char = x;
+    undump_int(x);
+    lang->pre_exhyphen_char = x;
+    undump_int(x);
+    lang->post_exhyphen_char = x;
+    undump_int(x);
+    lang->hyphenation_min = x;
+    /* patterns */
+    undump_int(x);
+    if (x > 0) {
+        s = xmalloc((unsigned) x);
+        undump_things(*s, x);
+        load_patterns(lang, (unsigned char *) s);
+        free(s);
+    }
+    /* exceptions */
+    undump_int(x);
+    if (x > 0) {
+        s = xmalloc((unsigned) x);
+        undump_things(*s, x);
+        load_hyphenation(lang, (unsigned char *) s);
+        free(s);
+    }
+}
+
+void undump_language_data(void)
+{
+    int i, x, numlangs;
+    undump_int(numlangs);
+    next_lang_id = numlangs;
+    for (i = 0; i < numlangs; i++) {
+        undump_int(x);
+        if (x == 1) {
+            undump_one_language(i);
+        }
+    }
+}
+
+@ When \TeX\ has scanned `\.{\\hyphenation}', it calls on a procedure named
+|new_hyph_exceptions| to do the right thing.
+
+@c
+void new_hyph_exceptions(void)
+{                               /* enters new exceptions */
+    (void) scan_toks(false, true);
+    load_tex_hyphenation(language_par, def_ref);
+    flush_list(def_ref);
+}
+
+@ Similarly, when \TeX\ has scanned `\.{\\patterns}', it calls on a procedure named
+|new_patterns|.
+
+@c
+void new_patterns(void)
+{
+    (void) scan_toks(false, true);
+    load_tex_patterns(language_par, def_ref);
+    flush_list(def_ref);
+}
+
+@ `\.{\\prehyphenchar}', sets the |pre_break| character, and `\.{\\posthyphenchar}' the
+|post_break| character. Their respective defaults are ascii hyphen ("-") and zero (nul).
+
+@c
+void new_pre_hyphen_char(void)
+{
+    scan_optional_equals();
+    scan_int();
+    set_pre_hyphen_char(language_par, cur_val);
+}
+
+void new_post_hyphen_char(void)
+{
+    scan_optional_equals();
+    scan_int();
+    set_post_hyphen_char(language_par, cur_val);
+}
+
+@ `\.{\\preexhyphenchar}', sets the |pre_break| character, and `\.{\\postexhyphenchar}' the
+|post_break| character. Their defaults are both zero (nul).
+
+@c
+void new_pre_exhyphen_char(void)
+{
+    scan_optional_equals();
+    scan_int();
+    set_pre_exhyphen_char(language_par, cur_val);
+}
+
+void new_post_exhyphen_char(void)
+{
+    scan_optional_equals();
+    scan_int();
+    set_post_exhyphen_char(language_par, cur_val);
+}
+
+void new_hyphenation_min(void)
+{
+    scan_optional_equals();
+    scan_int();
+    set_hyphenation_min(language_par, cur_val);
+}
+
+void new_hj_code(void)
+{
+    int i ;
+    scan_int();
+    i = cur_val;
+    scan_optional_equals();
+    scan_int();
+    set_hj_code(language_par, i, cur_val, -1);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/lang/texlang.c
+++ /dev/null
@@ -1,1359 +0,0 @@
-/*
-
-texlang.w
-
-Copyright 2006-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <string.h>
-#include "lua/luatex-api.h"
-
-/*tex Low-level helpers */
-
-#define unVERBOSE
-
-#define MAX_TEX_LANGUAGES  16384
-
-static struct tex_language *tex_languages[MAX_TEX_LANGUAGES] = { NULL };
-
-static int next_lang_id = 0;
-
-struct tex_language *new_language(int n)
-{
-    struct tex_language *lang;
-    unsigned l;
-    if (n >= 0) {
-        l = (unsigned) n;
-        if (l != (MAX_TEX_LANGUAGES - 1))
-            if (next_lang_id <= n)
-                next_lang_id = n + 1;
-    } else {
-        while (tex_languages[next_lang_id] != NULL)
-            next_lang_id++;
-        l = (unsigned) next_lang_id++;
-    }
-    if (l < (MAX_TEX_LANGUAGES - 1) && tex_languages[l] == NULL) {
-        lang = xmalloc(sizeof(struct tex_language));
-        tex_languages[l] = lang;
-        lang->id = (int) l;
-        lang->exceptions = 0;
-        lang->patterns = NULL;
-        lang->pre_hyphen_char = '-';
-        lang->post_hyphen_char = 0;
-        lang->pre_exhyphen_char = 0;
-        lang->post_exhyphen_char = 0;
-        lang->hyphenation_min = -1;
-        if (saving_hyph_codes_par) {
-            /*tex
-                For now, we might just use specific value for whatever task.
-            */
-            hj_codes_from_lc_codes(l);
-        }
-        return lang;
-    } else {
-        return NULL;
-    }
-}
-
-struct tex_language *get_language(int n)
-{
-    if (n >= 0 && n < MAX_TEX_LANGUAGES) {
-        if (tex_languages[n] != NULL) {
-            return tex_languages[n];
-        } else {
-            return new_language(n);
-        }
-    } else {
-        return NULL;
-    }
-}
-
-void set_pre_hyphen_char(int n, int v)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l != NULL)
-        l->pre_hyphen_char = (int) v;
-}
-
-void set_post_hyphen_char(int n, int v)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l != NULL)
-        l->post_hyphen_char = (int) v;
-}
-
-void set_pre_exhyphen_char(int n, int v)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l != NULL)
-        l->pre_exhyphen_char = (int) v;
-}
-
-void set_post_exhyphen_char(int n, int v)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l != NULL)
-        l->post_exhyphen_char = (int) v;
-}
-
-int get_pre_hyphen_char(int n)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l == NULL)
-        return -1;
-    return (int) l->pre_hyphen_char;
-}
-
-int get_post_hyphen_char(int n)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l == NULL)
-        return -1;
-    return (int) l->post_hyphen_char;
-}
-
-int get_pre_exhyphen_char(int n)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l == NULL)
-        return -1;
-    return (int) l->pre_exhyphen_char;
-}
-
-int get_post_exhyphen_char(int n)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l == NULL)
-        return -1;
-    return (int) l->post_exhyphen_char;
-}
-
-void set_hyphenation_min(int n, int v)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l != NULL)
-        l->hyphenation_min = (int) v;
-}
-
-int get_hyphenation_min(int n)
-{
-    struct tex_language *l = get_language((int) n);
-    if (l == NULL)
-        return -1;
-    return (int) l->hyphenation_min;
-}
-
-void load_patterns(struct tex_language *lang, const unsigned char *buff)
-{
-    if (lang == NULL || buff == NULL || strlen((const char *) buff) == 0)
-        return;
-    if (lang->patterns == NULL) {
-        lang->patterns = hnj_hyphen_new();
-    }
-    hnj_hyphen_load(lang->patterns, buff);
-}
-
-void clear_patterns(struct tex_language *lang)
-{
-    if (lang == NULL)
-        return;
-    if (lang->patterns != NULL) {
-        hnj_hyphen_clear(lang->patterns);
-    }
-}
-
-void load_tex_patterns(int curlang, halfword head)
-{
-    char *s = tokenlist_to_cstring(head, 1, NULL);
-    load_patterns(get_language(curlang), (unsigned char *) s);
-}
-
-#define STORE_CHAR(l,x) do { \
-    unsigned xx = get_hj_code(l,x); \
-    if (!xx || xx <= 32) { \
-        xx = x; \
-    } \
-    uindex = uni2string(uindex, xx); \
-} while (0)
-
-/*
-
-    This cleans one word which is returned in |cleaned|, returns the new offset
-    into |buffer|.
-
-*/
-
-const char *clean_hyphenation(int id, const char *buff, char **cleaned)
-{
-    int items = 0;
-    /*tex Work buffer for bytes: */
-    unsigned char word[MAX_WORD_LEN + 1];
-    /*tex Work buffer for \UNICODE: */
-    unsigned uword[MAX_WORD_LEN + 1] = { 0 };
-    /*tex The \UNICODE\ buffer value: */
-    int u = 0;
-    /*tex The index into buffer: */
-    int i = 0;
-    char *uindex = (char *)word;
-    const char *s = buff;
-
-    while (*s && !isspace((unsigned char)*s)) {
-        word[i++] = (unsigned)*s;
-        s++;
-        if ((s-buff)>MAX_WORD_LEN) {
-            /*tex Todo: this is too strict, should count \UNICODE, not bytes. */
-            *cleaned = NULL;
-            tex_error("exception too long", NULL);
-            return s;
-        }
-    }
-    /*tex Now convert the input to \UNICODE. */
-    word[i] = '\0';
-    utf2uni_strcpy(uword, (const char *)word);
-    /*tex Build the new word string. */
-    i = 0;
-    while (uword[i]>0) {
-        u = uword[i++];
-        if (u == '-') {
-            /*tex Skip. */
-        } else if (u == '=') {
-            STORE_CHAR(id,'-');
-        } else if (u == '{') {
-            u = uword[i++];
-            items = 0;
-            while (u && u != '}') {
-                u = uword[i++];
-            }
-            if (u == '}') {
-                items++;
-                u = uword[i++];
-            }
-            while (u && u != '}') {
-                u = uword[i++];
-            }
-            if (u == '}') {
-                items++;
-                u = uword[i++];
-            }
-            if (u == '{') {
-                u = uword[i++];
-            }
-            while (u && u != '}') {
-                STORE_CHAR(id,u);
-                u = uword[i++];
-            }
-            if (u == '}') {
-                items++;
-            }
-            if (items != 3) {
-                *cleaned = NULL;
-                tex_error("exception syntax error", NULL);
-                return s;
-            }
-            if (uword[i] == '[' && uword[i+1] >= '0' && uword[i+1] <= '9' && uword[i+2] == ']') {
-                i += 3;
-            }
-        } else {
-            STORE_CHAR(id,u);
-        }
-    }
-    *uindex = '\0';
-    *cleaned = xstrdup((char *) word);
-    return s;
-}
-
-void load_hyphenation(struct tex_language *lang, const unsigned char *buff)
-{
-    const char *s;
-    const char *value;
-    char *cleaned;
-    int id ;
-    if (lang == NULL)
-        return;
-    if (lang->exceptions == 0) {
-        lua_newtable(Luas);
-        lang->exceptions = luaL_ref(Luas, LUA_REGISTRYINDEX);
-    }
-    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lang->exceptions);
-    s = (const char *) buff;
-    id = lang->id;
-    while (*s) {
-        while (isspace((unsigned char)*s))
-            s++;
-        if (*s) {
-            value = s;
-            s = clean_hyphenation(id, s, &cleaned);
-            if (cleaned != NULL) {
-                if ((s - value) > 0) {
-                    lua_pushstring(Luas, cleaned);
-                    lua_pushlstring(Luas, value, (size_t) (s - value));
-                    lua_rawset(Luas, -3);
-                }
-                free(cleaned);
-            } else {
-#ifdef VERBOSE
-                formatted_warning("hyphenation","skipping invalid hyphenation exception: %s", value);
-#endif
-            }
-        }
-    }
-}
-
-void clear_hyphenation(struct tex_language *lang)
-{
-    if (lang == NULL)
-        return;
-    if (lang->exceptions != 0) {
-        luaL_unref(Luas, LUA_REGISTRYINDEX, lang->exceptions);
-        lang->exceptions = 0;
-    }
-}
-
-void load_tex_hyphenation(int curlang, halfword head)
-{
-    char *s = tokenlist_to_cstring(head, 1, NULL);
-    load_hyphenation(get_language(curlang), (unsigned char *) s);
-}
-
-static halfword insert_discretionary(halfword t, halfword pre, halfword post, halfword replace, int penalty)
-{
-    halfword g;
-    int f;
-    halfword d = new_node(disc_node, syllable_disc);
-    int attr = node_attr(t) ;
-    disc_penalty(d) = penalty;
-    if (t == replace) {
-        /*tex We have |prev disc next-next|. */
-        try_couple_nodes(d, vlink(t));
-        try_couple_nodes(alink(t), d);
-        alink(t) = null;
-        vlink(t) = null;
-        replace = t ;
-    } else {
-        /*tex We have |prev disc next|. */
-        try_couple_nodes(d, vlink(t));
-        couple_nodes(t, d);
-    }
-    if (replace != null) {
-        f = font(replace);
-    } else {
-        /*tex For compound words following explicit hyphens. */
-        f = get_cur_font();
-    }
-    for (g = pre; g != null; g = vlink(g)) {
-        font(g) = f;
-        if (attr != null) {
-            delete_attribute_ref(node_attr(g));
-            node_attr(g) = attr;
-            attr_list_ref(attr) += 1;
-        }
-    }
-    for (g = post; g != null; g = vlink(g)) {
-        font(g) = f;
-        if (attr != null) {
-            delete_attribute_ref(node_attr(g));
-            node_attr(g) = attr;
-            attr_list_ref(attr) += 1;
-        }
-    }
-    if (attr != null) {
-        for (g = replace; g != null; g = vlink(g)) {
-            delete_attribute_ref(node_attr(g));
-            node_attr(g) = attr;
-            attr_list_ref(attr) += 1;
-        }
-        delete_attribute_ref(node_attr(d));
-        node_attr(d) = attr;
-        attr_list_ref(attr) += 1;
-    }
-    set_disc_field(pre_break(d), pre);
-    set_disc_field(post_break(d), post);
-    set_disc_field(no_break(d), replace);
-    return d;
-}
-
-halfword insert_syllable_discretionary(halfword t, lang_variables * lan)
-{
-    halfword g, n;
-    n = new_node(disc_node, syllable_disc);
-    disc_penalty(n) = hyphen_penalty_par;
-    couple_nodes(n, vlink(t));
-    couple_nodes(t, n);
-    delete_attribute_ref(node_attr(n));
-    if (node_attr(t) != null) {
-        node_attr(n) = node_attr(t);
-        attr_list_ref(node_attr(t))++;
-    } else {
-        node_attr(n) = null;
-    }
-    if (lan->pre_hyphen_char > 0) {
-        g = raw_glyph_node();
-        set_to_character(g);
-        character(g) = lan->pre_hyphen_char;
-        font(g) = font(t);
-        lang_data(g) = lang_data(t);
-        if (node_attr(t) != null) {
-            node_attr(g) = node_attr(t);
-            attr_list_ref(node_attr(t))++;
-        }
-        set_disc_field(pre_break(n), g);
-    }
-    if (lan->post_hyphen_char > 0) {
-        t = vlink(n);
-        g = raw_glyph_node();
-        set_to_character(g);
-        character(g) = lan->post_hyphen_char;
-        font(g) = font(t);
-        lang_data(g) = lang_data(t);
-        if (node_attr(t) != null) {
-            node_attr(g) = node_attr(t);
-            attr_list_ref(node_attr(t)) += 1;
-        }
-        set_disc_field(post_break(n), g);
-    }
-    return n;
-}
-
-static halfword insert_character(halfword t, int c)
-{
-    halfword p;
-    p = new_node(glyph_node, 0);
-    set_to_character(p);
-    character(p) = c;
-    if (t != null) {
-        couple_nodes(t, p);
-    }
-    return p;
-}
-
-static halfword compound_word_break(halfword t, int clang)
-{
-    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);
-    } else {
-        pre = insert_character(null,ex_hyphen_char_par);
-    }
-    if (post_exhyphen_char > 0)
-        pos = insert_character(null,post_exhyphen_char);
-    disc = insert_discretionary(t,pre,pos,t,ex_hyphen_penalty_par);
-    subtype(disc) = automatic_disc;
-    set_automatic_disc_penalty(disc);
-    return disc;
-}
-
-void set_disc_field(halfword f, halfword t)
-{
-    if (t != null) {
-        /*tex
-            No |couple_nodes(f, t);| as we can better not expose |f| as |prev|
-            pointer.
-        */
-        vlink(f) = t ;
-        alink(t) = null ;
-        tlink(f) = tail_of_list(t);
-    } else {
-        vlink(f) = null;
-        tlink(f) = null;
-    }
-}
-
-static char *hyphenation_exception(int exceptions, char *w)
-{
-    char *ret = NULL;
-    lua_checkstack(Luas, 2);
-    lua_rawgeti(Luas, LUA_REGISTRYINDEX, exceptions);
-    if (lua_istable(Luas, -1)) {
-        /*tex Word table: */
-        lua_pushstring(Luas, w);
-        lua_rawget(Luas, -2);
-        if (lua_type(Luas, -1) == LUA_TSTRING) {
-            ret = xstrdup(lua_tostring(Luas, -1));
-        }
-        lua_pop(Luas, 2);
-    } else {
-        lua_pop(Luas, 1);
-    }
-    return ret;
-}
-
-char *exception_strings(struct tex_language *lang)
-{
-    const char *value;
-    size_t size = 0, current = 0;
-    size_t l = 0;
-    char *ret = NULL;
-    if (lang->exceptions == 0)
-        return NULL;
-    lua_checkstack(Luas, 2);
-    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lang->exceptions);
-    if (lua_istable(Luas, -1)) {
-        /*tex Iterate and join. */
-        lua_pushnil(Luas);
-        while (lua_next(Luas, -2) != 0) {
-            value = lua_tolstring(Luas, -1, &l);
-            if (current + 2 + l > size) {
-                ret = xrealloc(ret, (unsigned) ((size + size / 5) + current + l + 1024));
-                size = (size + size / 5) + current + l + 1024;
-            }
-            *(ret + current) = ' ';
-            strcpy(ret + current + 1, value);
-            current += l + 1;
-            lua_pop(Luas, 1);
-        }
-    }
-    return ret;
-}
-
-/*tex
-
-    The sequence from |wordstart| to |r| can contain only normal characters it
-    could be faster to modify a halfword pointer and return an integer
-
-*/
-
-static halfword find_exception_part(unsigned int *j, unsigned int *uword, int len)
-{
-    halfword g = null, gg = null;
-    register unsigned i = *j;
-    /*tex This puts uword[i] on the |{|. */
-    i++;
-    while (i < (unsigned) len && uword[i + 1] != '}') {
-        if (g == null) {
-            gg = new_char(0, (int) uword[i + 1]);
-            g = gg;
-        } else {
-            halfword s = new_char(0, (int) uword[i + 1]);
-            couple_nodes(g, s);
-            g = vlink(g);
-        }
-        i++;
-    }
-    *j = ++i;
-    return gg;
-}
-
-static int count_exception_part(unsigned int *j, unsigned int *uword, int len)
-{
-    int ret = 0;
-    register unsigned i = *j;
-    /*tex This puts uword[i] on the |{|. */
-    i++;
-    while (i < (unsigned) len && uword[i + 1] != '}') {
-        ret++;
-        i++;
-    }
-    *j = ++i;
-    return ret;
-}
-
-static const char *PAT_ERROR[] = {
-    "Exception discretionaries should contain three pairs of braced items.",
-    "No intervening spaces are allowed.",
-    NULL
-};
-
-/*tex
-
-    The exceptions are taken as-is: no min values are taken into account. One can
-    add normal patterns on-the-fly if needed.
-
-*/
-
-static void do_exception(halfword wordstart, halfword r, char *replacement)
-{
-    unsigned i;
-    halfword t, pen;
-    unsigned len;
-    int clang;
-    lang_variables langdata;
-    unsigned uword[MAX_WORD_LEN + 1] = { 0 };
-    utf2uni_strcpy(uword, replacement);
-    len = u_length(uword);
-    i = 0;
-    t = wordstart;
-    clang = char_lang(wordstart);
-    langdata.pre_hyphen_char = get_pre_hyphen_char(clang);
-    langdata.post_hyphen_char = get_post_hyphen_char(clang);
-    for (i = 0; i < len; i++) {
-        if (uword[i + 1] == 0 ) {
-            /*tex We ran out of the exception pattern. */
-            break;
-        } else if (uword[i + 1] == '-') {
-            /*tex A hyphen follows. */
-            if (vlink(t) == r)
-                break;
-            insert_syllable_discretionary(t, &langdata);
-            t = vlink(t);       /* skip the new disc */
-        } else if (uword[i + 1] == '=') {
-            /*tex We skip a disc. */
-            t = vlink(t);
-        } else if (uword[i + 1] == '{') {
-            /*tex We ran into an exception |{}{}{}| or |{}{}{}[]|. */
-            halfword gg, hh, replace = null;
-            int repl;
-            /*tex |pre| */
-            gg = find_exception_part(&i, uword, (int) len);
-            if (i == len || uword[i + 1] != '{') {
-                tex_error("broken pattern 1", PAT_ERROR);
-            }
-            /*tex |post| */
-            hh = find_exception_part(&i, uword, (int) len);
-            if (i == len || uword[i + 1] != '{') {
-                tex_error("broken pattern 2", PAT_ERROR);
-            }
-            /*tex |replace| */
-            repl = count_exception_part(&i, uword, (int) len);
-            if (i == len) {
-                tex_error("broken pattern 3", PAT_ERROR);
-            }
-            /*tex Play safe. */
-            if (vlink(t) == r)
-                break;
-            /*tex Let's deal with an (optional) replacement. */
-            if (repl > 0) {
-                /*tex Assemble the replace stream. */
-                halfword q = t;
-                replace = vlink(q);
-                while (repl > 0 && q != null) {
-                    q = vlink(q);
-                    if (type(q) == glyph_node || type(q) == disc_node) {
-                        repl--;
-                    } else {
-                        break ;
-                    }
-                }
-                /*tex Remove it from the main stream */
-                try_couple_nodes(t, vlink(q));
-                /*tex and finish it in the replace. */
-                vlink(q) = null;
-                /*tex Sanitize the replace stream (we could use the flattener instead). */
-                q = replace ;
-                while (q != null) {
-                    halfword n = vlink(q);
-                    if (type(q) == disc_node) {
-                        /*tex Beware: the replacement starts after the no_break pointer. */
-                        halfword r = vlink(no_break(q));
-                        vlink(no_break(q)) = null;
-                        alink(r) = null ;
-                        /*tex Insert the replacement glyph. */
-                        if (q == replace) {
-                            replace = r;
-                        } else {
-                            try_couple_nodes(alink(q),r);
-                        }
-                        /*tex Append the glyph (one). */
-                        try_couple_nodes(r,n);
-                        /*tex Flush the disc. */
-                        flush_node(q);
-                    }
-                    q = n ;
-                }
-            }
-            /*tex Let's check if we have a penalty spec. */
-            if (((i+3) < len) && uword[i+1] == '[' && uword[i+2] >= '0' && uword[i+2] <= '9' && uword[i+3] == ']') {
-                if (exception_penalty_par > 0) {
-                    if (exception_penalty_par > 100000) {
-                        pen = (uword[i+2] - '0') * exception_penalty_par ;
-                    } else {
-                        pen = exception_penalty_par;
-                    }
-                } else {
-                    pen = hyphen_penalty_par;
-                }
-                i += 3;
-            } else {
-                pen = hyphen_penalty_par;
-            }
-            /*tex And now we insert a disc node. */
-            t = insert_discretionary(t, gg, hh, replace, pen);
-            /*tex We skip the new disc node. */
-            t = vlink(t);
-            /*tex check if we have two exceptions in a row */
-            if (uword[i + 1] == '{') {
-                i--;
-            }
-        } else {
-            t = vlink(t);
-        }
-        /*tex Again we play safe. */
-        if (t == null || vlink(t) == r) {
-            break;
-        }
-    }
-}
-
-/*tex
-
-    This is a documentation section from the pascal web file. It is not true any
-    more, but I (Taco) do not have time right now to rewrite it.
-
-    When the line-breaking routine is unable to find a feasible sequence of
-    breakpoints, it makes a second pass over the paragraph, attempting to
-    hyphenate the hyphenatable words. The goal of hyphenation is to insert
-    discretionary material into the paragraph so that there are more potential
-    places to break.
-
-    The general rules for hyphenation are somewhat complex and technical, because
-    we want to be able to hyphenate words that are preceded or followed by
-    punctuation marks, and because we want the rules to work for languages other
-    than English. We also must contend with the fact that hyphens might radically
-    alter the ligature and kerning structure of a word.
-
-    A sequence of characters will be considered for hyphenation only if it
-    belongs to a ``potentially hyphenatable part'' of the current paragraph. This
-    is a sequence of nodes $p_0p_1\ldots p_m$ where $p_0$ is a glue node,
-    $p_1\ldots p_{m-1}$ are either character or ligature or whatsit or implicit
-    kern nodes, and $p_m$ is a glue or penalty or insertion or adjust or mark or
-    whatsit or explicit kern node. (Therefore hyphenation is disabled by boxes,
-    math formulas, and discretionary nodes already inserted by the user.) The
-    ligature nodes among $p_1\ldots p_{m-1}$ are effectively expanded into the
-    original non-ligature characters; the kern nodes and whatsits are ignored.
-    Each character |c| is now classified as either a nonletter (if
-    |lc_code(c)=0|), a lowercase letter (if |lc_code(c)=c|), or an uppercase
-    letter (otherwise); an uppercase letter is treated as if it were |lc_code(c)|
-    for purposes of hyphenation. The characters generated by $p_1\ldots p_{m-1}$
-    may begin with nonletters; let $c_1$ be the first letter that is not in the
-    middle of a ligature. Whatsit nodes preceding $c_1$ are ignored; a whatsit
-    found after $c_1$ will be the terminating node $p_m$. All characters that do
-    not have the same font as $c_1$ will be treated as nonletters. The
-    |hyphen_char| for that font must be between 0 and 255, otherwise hyphenation
-    will not be attempted. \TeX\ looks ahead for as many consecutive letters
-    $c_1\ldots c_n$ as possible; however, |n| must be less than 64, so a
-    character that would otherwise be $c_{64}$ is effectively not a letter.
-    Furthermore $c_n$ must not be in the middle of a ligature. In this way we
-    obtain a string of letters $c_1\ldots c_n$ that are generated by nodes
-    $p_a\ldots p_b$, where |1<=a<=b+1<=m|. If |n>=l_hyf+r_hyf|, this string
-    qualifies for hyphenation; however, |uc_hyph| must be positive, if $c_1$ is
-    uppercase.
-
-    The hyphenation process takes place in three stages. First, the candidate
-    sequence $c_1\ldots c_n$ is found; then potential positions for hyphens are
-    determined by referring to hyphenation tables; and finally, the nodes
-    $p_a\ldots p_b$ are replaced by a new sequence of nodes that includes the
-    discretionary breaks found.
-
-    Fortunately, we do not have to do all this calculation very often, because of
-    the way it has been taken out of \TeX's inner loop. For example, when the
-    second edition of the author's 700-page book {\sl Seminumerical Algorithms}
-    was typeset by \TeX, only about 1.2 hyphenations needed to be tried per
-    paragraph, since the line breaking algorithm needed to use two passes on only
-    about 5 per cent of the paragraphs.
-
-    When a word been set up to contain a candidate for hyphenation, \TeX\ first
-    looks to see if it is in the user's exception dictionary. If not, hyphens are
-    inserted based on patterns that appear within the given word, using an
-    algorithm due to Frank~M. Liang.
-
-    This is incompatible with \TEX\ because the first word of a paragraph can be
-    hyphenated, but most European users seem to agree that prohibiting
-    hyphenation there was not the best idea ever.
-
-    We have some variants available that are controlled by the paremeter
-    \type {\hyphenationbounds}:
-
-    \starttabulate
-    \NC \type {0} \NC not strict \NC \NR
-    \NC \type {1} \NC strict start \NC \NR
-    \NC \type {2} \NC strict end \NC \NR
-    \NC \type {3} \NC strict start and strict end \NC \NR
-    \stoptabulate
-
-    \startbuffer
-    \parindent0pt \hsize=1.1cm
-    12-34-56 \par
-    12-34-\hbox{56} \par
-    12-34-\vrule width 1em height 1.5ex \par
-    12-\hbox{34}-56 \par
-    12-\vrule width 1em height 1.5ex-56 \par
-    \hjcode`\1=`\1 \hjcode`\2=`\2 \hjcode`\3=`\3 \hjcode`\4=`\4 \vskip.5cm
-    12-34-56 \par
-    12-34-\hbox{56} \par
-    12-34-\vrule width 1em height 1.5ex \par
-    12-\hbox{34}-56 \par
-    12-\vrule width 1em height 1.5ex-56 \par
-    \stopbuffer
-
-    \typebuffer
-
-    \startpacked \getbuffer \stopbuffer
-
-    We only accept an explicit hyphen when there is a preceding glyph and we skip
-    a sequence of explicit hyphens as that normally indicates a \type {--} or
-    \type {---} ligature in which case we can in a worse case usage get bad node
-    lists later on due to messed up ligature building as these dashes are
-    ligatures in base fonts. This is a side effect of the separating the
-    hyphenation, ligaturing and kerning steps. A test is cmr with \type {------}.
-
-    A font handler can collapse successive hyphens but it's not nice to put the
-    burden there. A somewhat messy border case is \type {----} but in \LUATEX\ we
-    don't treat \type {--} and \type {---} special. Also, traditional \TEX\ will
-    break a line at \type {-foo} but this can be disabled by setting the
-    automatic mode to \type {1}.
-
-*/
-
-static halfword find_next_wordstart(halfword r, halfword first_language, halfword strict_bound)
-{
-    register int l;
-    register int start_ok = 1;
-    int mathlevel = 1;
-    int chr ;
-    halfword t ;
-    while (r != null) {
-        switch (type(r)) {
-            case boundary_node:
-                if (subtype(r) == word_boundary) {
-                    start_ok = 1;
-                }
-                break;
-            case hlist_node:
-            case vlist_node:
-            case rule_node:
-            case dir_node:
-            case whatsit_node:
-                if (strict_bound == 1 || strict_bound == 3) {
-                    start_ok = 0;
-                }
-                break;
-            case glue_node:
-                start_ok = 1;
-                break;
-            case math_node:
-                while (mathlevel > 0) {
-                    r = vlink(r);
-                    if (r == null)
-                        return r;
-                    if (type(r) == math_node) {
-                        if (subtype(r) == before) {
-                            mathlevel++;
-                        } else {
-                            mathlevel--;
-                        }
-                    }
-                }
-                break;
-            case glyph_node:
-                if (is_simple_character(r)) {
-                    chr = character(r) ;
-                    if (chr == ex_hyphen_char_par) {
-                        t = vlink(r) ;
-                        if ((automatic_hyphen_mode_par == 0) && (t != null) && (type(t) == glyph_node) && (character(t) != ex_hyphen_char_par)) {
-                            /*tex We have no word yet and the next character is a non hyphen. */
-                            r = compound_word_break(r, char_lang(r));
-                        } else {
-                            /*tex We jump over the sequence of hyphens. */
-                            while ((t != null) && (type(t) == glyph_node) && (character(t) == ex_hyphen_char_par)) {
-                                r = t ;
-                                t = vlink(r) ;
-                            }
-                            if (t == null) {
-                                /*tex We reached the end of the list so we have no word start. */
-                                return null;
-                            }
-                        }
-                        /*tex We need a restart. */
-                        start_ok = 0;
-                    } else if (start_ok && (char_lang(r)>=first_language) && ((l = get_hj_code(char_lang(r),chr)) > 0)) {
-                        if (char_uchyph(r) || l == chr || l <= 32) {
-                            return r;
-                        } else {
-                            start_ok = 0;
-                        }
-                    } else {
-                        /*tex We go on. */
-                    }
-                }
-                break;
-            default:
-                start_ok = 0;
-                break;
-        }
-        r = vlink(r);
-    }
-    return r;
-}
-
-static int valid_wordend(halfword s, halfword strict_bound)
-{
-    register halfword r = s;
-    register int clang = char_lang(s);
-    if (r == null)
-        return 1;
-    while ( (r != null) &&
-           (    (type(r) == glyph_node && is_simple_character(r) && clang == char_lang(r))
-             || (type(r) == kern_node && (subtype(r) == normal))
-            )
-           ) {
-        r = vlink(r);
-    }
-    if (r == null || (type(r) == glyph_node && is_simple_character(r) && clang != char_lang(r))
-                  ||  type(r) == glue_node
-                  ||  type(r) == penalty_node
-                  || (type(r) == kern_node && (subtype(r) == explicit_kern ||
-                                               subtype(r) == italic_kern   ||
-                                               subtype(r) == accent_kern   ))
-                  ||  ((type(r) == hlist_node   ||
-                        type(r) == vlist_node   ||
-                        type(r) == rule_node    ||
-                        type(r) == dir_node     ||
-                        type(r) == whatsit_node ||
-                        type(r) == ins_node     ||
-                        type(r) == adjust_node
-                       ) && ! (strict_bound == 2 || strict_bound == 3))
-                  ||  type(r) == boundary_node
-        )
-        return 1;
-    return 0;
-}
-
-void hnj_hyphenation(halfword head, halfword tail)
-{
-    int lchar, i;
-    struct tex_language *lang;
-    lang_variables langdata;
-    char utf8word[(4 * MAX_WORD_LEN) + 1] = { 0 };
-    int wordlen = 0;
-    char *hy = utf8word;
-    char *replacement = NULL;
-    boolean explicit_hyphen = false;
-    boolean valid_word = false;
-    halfword first_language = first_valid_language_par;
-    halfword strict_bound = hyphenation_bounds_par;
-    halfword s, r = head, wordstart = null, save_tail1 = null, left = null, right = null;
-    halfword expstart = null;
-    boolean compound_hyphen = compound_hyphen_mode_par;
-    /*tex
-        This first movement assures two things:
-
-        \startitemize
-            \startitem
-                That we won't waste lots of time on something that has been
-                handled already (in that case, none of the glyphs match
-                |simple_character|).
-            \stopitem
-            \startitem
-                That the first word can be hyphenated. if the movement was not
-                explicit, then the indentation at the start of a paragraph list
-                would make |find_next_wordstart()| look too far ahead.
-            \stopitem
-        \stopitemize
-    */
-    while (r != null && (type(r) != glyph_node || !is_simple_character(r))) {
-        r = vlink(r);
-    }
-    /*tex
-        This will make |r| a glyph node with subtype character.
-    */
-    r = find_next_wordstart(r,first_language,strict_bound);
-    if (r == null)
-        return;
-    assert(tail != null);
-    save_tail1 = vlink(tail);
-    s = new_penalty(0,word_penalty);
-    couple_nodes(tail, s);
-    while (r != null) {
-        /*tex This could be |while(1)|, but let's be paranoid: */
-        int clang, lhmin, rhmin, hmin;
-        halfword hyf_font;
-        halfword end_word = r;
-        wordstart = r;
-        assert(is_simple_character(wordstart));
-        hyf_font = font(wordstart);
-        if (hyphen_char(hyf_font) < 0) {
-            /*tex For backward compatibility we set: */
-            hyf_font = 0;
-        }
-        clang = char_lang(wordstart);
-        lhmin = char_lhmin(wordstart);
-        rhmin = char_rhmin(wordstart);
-        hmin = get_hyphenation_min(clang);
-        langdata.pre_hyphen_char = get_pre_hyphen_char(clang);
-        langdata.post_hyphen_char = get_post_hyphen_char(clang);
-        while (    r != null
-                && type(r) == glyph_node
-                && is_simple_character(r)
-                && clang == char_lang(r)
-                && (    (     (clang >= first_language)
-                           && (lchar = get_hj_code(clang,character(r))) > 0
-                        )
-                     || (     character(r) == ex_hyphen_char_par
-                           && (lchar = ex_hyphen_char_par)
-                        )
-                   )
-              ) {
-            if (character(r) == ex_hyphen_char_par) {
-                explicit_hyphen = true;
-                break;
-            }
-            wordlen++;
-            if (lchar <= 32) {
-                if (lchar == 32) {
-                    lchar = 0 ;
-                }
-                if (wordlen <= lhmin) {
-                    lhmin = lhmin - lchar + 1 ;
-                    if (lhmin < 0)
-                        lhmin = 1;
-                }
-                if (wordlen >= rhmin) {
-                    rhmin = rhmin - lchar + 1 ;
-                    if (rhmin < 0)
-                        rhmin = 1;
-                }
-                hmin = hmin - lchar + 1 ;
-                if (hmin < 0)
-                    rhmin = 1;
-                lchar = character(r) ;
-            }
-            hy = uni2string(hy, (unsigned) lchar);
-            end_word = r;
-            r = vlink(r);
-        }
-        if (explicit_hyphen == true) {
-            /*tex we are not at the start, so we only need to look ahead */
-            halfword t = vlink(r) ;
-            if ((automatic_hyphen_mode_par == 0 || automatic_hyphen_mode_par == 1) && (t != null) && ((type(t) == glyph_node) && (character(t) != ex_hyphen_char_par))) {
-                /*tex we have a word already but the next character may not be a hyphen too */
-                r = compound_word_break(r, char_lang(r));
-                if (compound_hyphen) {
-                    if (expstart == null) {
-                        expstart = wordstart;
-                    }
-                    explicit_hyphen = false;
-                    hy = uni2string(hy, '-');
-                    r = t;
-                    continue;
-                }
-            } else {
-                /*tex we jump over the sequence of hyphens */
-               while ((t != null) && (type(t) == glyph_node) && (character(t) == ex_hyphen_char_par)) {
-                    r = t ;
-                    t = vlink(r) ;
-                }
-                if (t == null) {
-                    /*tex we reached the end of the list and will quit the loop later */
-                    r = null;
-                }
-            }
-        } else if (     valid_wordend(r,strict_bound)
-              && clang >= first_language
-              && wordlen >= lhmin + rhmin
-              && (hmin <= 0 || wordlen >= hmin)
-              && (hyf_font != 0)
-              && (lang = tex_languages[clang]) != NULL
-           ) {
-            *hy = 0;
-            /*tex
-                this is messy and nasty: we can have a word with a - in it which
-                is why we have two branches
-            */
-            if (lang->exceptions != 0 && (replacement = hyphenation_exception(lang->exceptions, utf8word)) != NULL) {
-                /*tex handle the exception and go on to the next word */
-                if (expstart == null) {
-                    do_exception(wordstart, r, replacement);
-                } else {
-                    do_exception(expstart,r,replacement);
-                }
-                free(replacement);
-            } else if (expstart != null) {
-                /*tex We're done already */
-            } else if (lang->patterns != NULL) {
-                /*tex A safeguard, not needed but doesn't hurt either: */
-                valid_word = true;
-                left = wordstart;
-                for (i = lhmin; i > 1; i--) {
-                    left = vlink(left);
-                    if (left == null) {
-                        valid_word = false;
-                        break ;
-                    }
-                    while (!is_simple_character(left)) {
-                        left = vlink(left);
-                        if (left == null) {
-                            valid_word = false;
-                            break ;
-                        }
-                    }
-                }
-                if (valid_word) {
-                    right = r;
-                    if (right == left) {
-                        valid_word = false;
-                        break ;
-                    }
-                    for (i = rhmin; i > 0; i--) {
-                        right = alink(right);
-                        if (right == null || right == left) {
-                            valid_word = false;
-                            break ;
-                        }
-                        while (!is_simple_character(right)) {
-                            right = alink(right);
-                            if (right == null || right == left) {
-                                valid_word = false;
-                                break ;
-                            }
-                        }
-                    }
-                    if (valid_word && expstart == null) {
-                        (void) hnj_hyphen_hyphenate(lang->patterns, wordstart, end_word, wordlen, left, right, &langdata);
-                    } else {
-                        /*tex nothing yet */
-                    }
-                }
-            }
-        }
-        expstart = null ;
-        explicit_hyphen = false;
-        wordlen = 0;
-        hy = utf8word;
-        if (r == null)
-            break;
-        r = find_next_wordstart(r,first_language,strict_bound);
-    }
-    flush_node(vlink(tail));
-    vlink(tail) = save_tail1;
-}
-
-void new_hyphenation(halfword head, halfword tail)
-{
-    int i, top;
-    register int callback_id = 0;
-    if (head == null || vlink(head) == null)
-        return;
-    fix_node_list(head);
-    callback_id = callback_defined(hyphenate_callback);
-    if (callback_id > 0) {
-        top = lua_gettop(Luas);
-        if (!get_callback(Luas, callback_id)) {
-            lua_settop(Luas, top);
-            return;
-        }
-        nodelist_to_lua(Luas, head);
-        nodelist_to_lua(Luas, tail);
-        if ((i=lua_pcall(Luas, 2, 0, 0)) != 0) {
-            formatted_warning("hyphenation","bad specification: %s",lua_tostring(Luas, -1));
-            lua_settop(Luas, top);
-            luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-            return;
-        }
-        lua_settop(Luas, top);
-    } else if (callback_id == 0) {
-        hnj_hyphenation(head, tail);
-    }
-}
-
-/*tex
-
-    Dumping and undumping languages:
-
-*/
-
-#define dump_string(a)                \
-  if (a!=NULL) {                      \
-      x = (int)strlen(a)+1;           \
-    dump_int(x);  dump_things(*a, x); \
-  } else {                            \
-    x = 0; dump_int(x);               \
-  }
-
-static void dump_one_language(int i)
-{
-    char *s = NULL;
-    int x = 0;
-    struct tex_language *lang;
-    lang = tex_languages[i];
-    dump_int(lang->id);
-    dump_int(lang->pre_hyphen_char);
-    dump_int(lang->post_hyphen_char);
-    dump_int(lang->pre_exhyphen_char);
-    dump_int(lang->post_exhyphen_char);
-    dump_int(lang->hyphenation_min);
-    if (lang->patterns != NULL) {
-        s = (char *) hnj_serialize(lang->patterns);
-    }
-    dump_string(s);
-    if (s != NULL) {
-        free(s);
-        s = NULL;
-    }
-    if (lang->exceptions != 0)
-        s = exception_strings(lang);
-    dump_string(s);
-    if (s != NULL) {
-        free(s);
-    }
-    free(lang);
-}
-
-void dump_language_data(void)
-{
-    int i;
-    dump_int(next_lang_id);
-    for (i = 0; i < next_lang_id; i++) {
-        if (tex_languages[i]) {
-            dump_int(1);
-            dump_one_language(i);
-        } else {
-            dump_int(0);
-        }
-    }
-}
-
-static void undump_one_language(int i)
-{
-    char *s = NULL;
-    int x = 0;
-    struct tex_language *lang = get_language(i);
-    undump_int(x);
-    lang->id = x;
-    undump_int(x);
-    lang->pre_hyphen_char = x;
-    undump_int(x);
-    lang->post_hyphen_char = x;
-    undump_int(x);
-    lang->pre_exhyphen_char = x;
-    undump_int(x);
-    lang->post_exhyphen_char = x;
-    undump_int(x);
-    lang->hyphenation_min = x;
-    /*tex patterns */
-    undump_int(x);
-    if (x > 0) {
-        s = xmalloc((unsigned) x);
-        undump_things(*s, x);
-        load_patterns(lang, (unsigned char *) s);
-        free(s);
-    }
-    /*tex exceptions */
-    undump_int(x);
-    if (x > 0) {
-        s = xmalloc((unsigned) x);
-        undump_things(*s, x);
-        load_hyphenation(lang, (unsigned char *) s);
-        free(s);
-    }
-}
-
-void undump_language_data(void)
-{
-    int i, x, numlangs;
-    undump_int(numlangs);
-    next_lang_id = numlangs;
-    for (i = 0; i < numlangs; i++) {
-        undump_int(x);
-        if (x == 1) {
-            undump_one_language(i);
-        }
-    }
-}
-
-/*tex
-
-    When \TeX\ has scanned `\.{\\hyphenation}', it calls on a procedure named
-    |new_hyph_exceptions| to do the right thing.
-
-*/
-
-void new_hyph_exceptions(void)
-{
-    (void) scan_toks(false, true);
-    load_tex_hyphenation(language_par, def_ref);
-    flush_list(def_ref);
-}
-
-/*
-    Similarly, when \TeX\ has scanned `\.{\\patterns}', it calls on a procedure
-    named |new_patterns|.
-
-*/
-
-void new_patterns(void)
-{
-    (void) scan_toks(false, true);
-    load_tex_patterns(language_par, def_ref);
-    flush_list(def_ref);
-}
-
-/*tex
-
-    `\.{\\prehyphenchar}', sets the |pre_break| character, and
-    `\.{\\posthyphenchar}' the |post_break| character. Their respective defaults
-    are ascii hyphen ("-") and zero (nul).
-
-*/
-
-void new_pre_hyphen_char(void)
-{
-    scan_optional_equals();
-    scan_int();
-    set_pre_hyphen_char(language_par, cur_val);
-}
-
-void new_post_hyphen_char(void)
-{
-    scan_optional_equals();
-    scan_int();
-    set_post_hyphen_char(language_par, cur_val);
-}
-
-/*tex
-
-    `\.{\\preexhyphenchar}', sets the |pre_break| character, and
-    `\.{\\postexhyphenchar}' the |post_break| character. Their defaults are both
-    zero (nul).
-
-*/
-
-void new_pre_exhyphen_char(void)
-{
-    scan_optional_equals();
-    scan_int();
-    set_pre_exhyphen_char(language_par, cur_val);
-}
-
-void new_post_exhyphen_char(void)
-{
-    scan_optional_equals();
-    scan_int();
-    set_post_exhyphen_char(language_par, cur_val);
-}
-
-void new_hyphenation_min(void)
-{
-    scan_optional_equals();
-    scan_int();
-    set_hyphenation_min(language_par, cur_val);
-}
-
-void new_hj_code(void)
-{
-    int i ;
-    scan_int();
-    i = cur_val;
-    scan_optional_equals();
-    scan_int();
-    set_hj_code(language_par, i, cur_val, -1);
-}
--- texlive-bin.orig/texk/web2c/luatexdir/lua/helpers.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-/*tex
-
-We might move here some helpers common to code that are now in weird places,
-probably organized in texhelpers, luahelpers, pdfhelpers.
-
-*/
--- /dev/null
+++ texlive-bin/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... */
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lua/llfslibext.c
@@ -0,0 +1,171 @@
+/* llfslibext.c
+   
+   Copyright 2010-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/>. */
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+#include <kpathsea/c-stat.h>
+#include <kpathsea/c-dir.h>
+#include <time.h>
+
+
+#ifdef _WIN32
+#  include <windows.h>
+#else
+#endif
+
+#ifdef _WIN32
+
+static int get_short_name(lua_State * L)
+{
+    long length = 0;
+    TCHAR *buffer = NULL;
+    const char *lpszPath = luaL_checkstring(L, 1);
+    length = GetShortPathName(lpszPath, NULL, 0);
+    if (length == 0) {
+        lua_pushnil(L);
+        lua_pushfstring(L, "operating system error: %d", (int) GetLastError());
+        return 2;
+    }
+    buffer = (TCHAR *) xmalloc(length * sizeof(TCHAR));
+    length = GetShortPathName(lpszPath, buffer, length);
+    if (length == 0) {
+        lua_pushnil(L);
+        lua_pushfstring(L, "operating system error: %d", (int) GetLastError());
+        return 2;
+    }
+    lua_pushlstring(L, (const char *) buffer, (size_t) length);
+    return 1;
+}
+
+static int read_link(lua_State * L)
+{
+    lua_pushboolean(L, 0);
+    lua_pushliteral(L, "readlink not supported on this platform");
+    return 2;
+}
+
+#else
+
+static int pusherror(lua_State * L, const char *info)
+{
+    lua_pushnil(L);
+    if (info == NULL)
+        lua_pushstring(L, strerror(errno));
+    else
+        lua_pushfstring(L, "%s: %s", info, strerror(errno));
+    lua_pushinteger(L, errno);
+    return 3;
+}
+
+static int Preadlink(lua_State * L)
+{
+/** readlink(path) */
+    const char *path = luaL_checkstring(L, 1);
+    char *b = NULL;
+    int allocated = 128;
+    int n;
+    while (1) {
+        b = malloc(allocated);
+        if (!b)
+            return pusherror(L, path);
+        n = readlink(path, b, allocated);
+        if (n == -1) {
+            free(b);
+            return pusherror(L, path);
+        }
+        if (n < allocated)
+            break;
+        /* Not enough room, try bigger */
+        allocated *= 2;
+        free(b);
+    }
+    lua_pushlstring(L, b, n);
+    free(b);
+    return 1;
+}
+
+
+static int read_link(lua_State * L)
+{
+    return Preadlink(L);
+}
+
+static int get_short_name(lua_State * L __attribute__ ((unused)))
+{
+    /* simply do nothing */
+    return 1;
+}
+#endif
+
+
+/*
+** Get file information
+*/
+static int file_is_directory(lua_State * L)
+{
+    struct stat info;
+    const char *file = luaL_checkstring(L, 1);
+
+    if (stat(file, &info)) {
+        lua_pushnil(L);
+        lua_pushfstring(L, "cannot obtain information from file `%s'", file);
+        return 2;
+    }
+    if (S_ISDIR(info.st_mode))
+        lua_pushboolean(L, 1);
+    else
+        lua_pushboolean(L, 0);
+
+    return 1;
+}
+
+static int file_is_file(lua_State * L)
+{
+    struct stat info;
+    const char *file = luaL_checkstring(L, 1);
+
+    if (stat(file, &info)) {
+        lua_pushnil(L);
+        lua_pushfstring(L, "cannot obtain information from file `%s'", file);
+        return 2;
+    }
+    if (S_ISREG(info.st_mode))
+        lua_pushboolean(L, 1);
+    else
+        lua_pushboolean(L, 0);
+
+    return 1;
+}
+
+
+void open_lfslibext(lua_State * L)
+{
+
+    lua_getglobal(L, "lfs");
+    lua_pushcfunction(L, file_is_directory);
+    lua_setfield(L, -2, "isdir");
+    lua_pushcfunction(L, file_is_file);
+    lua_setfield(L, -2, "isfile");
+    lua_pushcfunction(L, read_link);
+    lua_setfield(L, -2, "readlink");
+    lua_pushcfunction(L, get_short_name);
+    lua_setfield(L, -2, "shortname");
+    lua_pop(L, 1);              /* pop the table */
+}
--- texlive-bin.orig/texk/web2c/luatexdir/lua/lpdfelib.c
+++ /dev/null
@@ -1,1666 +0,0 @@
-/*tex
-
-    This file will host the encapsulated \PDF\ support code used for inclusion
-    and access from \LUA.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    We need to avoid collision with some defines in |cpascal.h|.
-
-*/
-
-#undef lpdfelib_orig_input
-#undef lpdfelib_orig_output
-
-#ifdef input
-#define lpdfelib_orig_input input
-#undef input
-#endif
-
-#ifdef output
-#define lpdfelib_orig_output output
-#undef output
-#endif
-
-#include "luapplib/pplib.h"
-
-#include "image/epdf.h"
-
-#ifdef lpdfelib_orig_input
-#define input  lpdfelib_orig_input
-#undef lpdfelib_orig_input
-#endif
-
-#ifdef lpdfelib_orig_output
-#define output  lpdfelib_orig_output
-#undef lpdfelib_orig_output
-#endif
-
-#include "lua/luatex-api.h"
-
-/*tex
-
-    We start with some housekeeping. Dictionaries, arrays, streams and references
-    get userdata, while strings, names, integers, floats and booleans become regular
-    \LUA\ objects. We need to define a few metatable identifiers too.
-
-*/
-
-#define PDFE_METATABLE            "luatex.pdfe"
-#define PDFE_METATABLE_DICTIONARY "luatex.pdfe.dictionary"
-#define PDFE_METATABLE_ARRAY      "luatex.pdfe.array"
-#define PDFE_METATABLE_STREAM     "luatex.pdfe.stream"
-#define PDFE_METATABLE_REFERENCE  "luatex.pdfe.reference"
-
-typedef struct {
-    ppdoc *document;
-    boolean open;
-    boolean isfile;
-    char *memstream;
-    int pages;
-    int index;
-} pdfe_document ;
-
-typedef struct {
-    ppdict *dictionary;
-    ppref *ref;
-} pdfe_dictionary;
-
-typedef struct {
-    pparray *array;
-    ppref *ref;
-} pdfe_array;
-
-typedef struct {
-    ppstream *stream;
-    ppref *ref;
-    int decode;
-    int open;
-} pdfe_stream;
-
-typedef struct {
-    ppref *reference;
-} pdfe_reference;
-
-/*tex
-
-    We need to check if we have the right userdata. A similar warning is issued
-    when encounter a problem. We don't exit.
-
-*/
-
-static void pdfe_invalid_object_warning(const char * detail)
-{
-    formatted_warning("pdfe lib","lua <pdfe %s> expected",detail);
-}
-
-static pdfe_document *check_isdocument(lua_State * L, int n)
-{
-    pdfe_document *p = (pdfe_document *)lua_touserdata(L, n);
-    if (p != NULL && lua_getmetatable(L, n)) {
-        lua_get_metatablelua(luatex_pdfe);
-        if (!lua_rawequal(L, -1, -2)) {
-            p = NULL;
-        }
-        lua_pop(L, 2);
-        if (p != NULL) {
-            return p;
-        }
-    }
-    pdfe_invalid_object_warning("document");
-    return NULL;
-}
-
-static pdfe_dictionary *check_isdictionary(lua_State * L, int n)
-{
-    pdfe_dictionary *p = (pdfe_dictionary *)lua_touserdata(L, n);
-    if (p != NULL && lua_getmetatable(L, n)) {
-        lua_get_metatablelua(luatex_pdfe_dictionary);
-        if (!lua_rawequal(L, -1, -2)) {
-            p = NULL;
-        }
-        lua_pop(L, 2);
-        if (p != NULL) {
-            return p;
-        }
-    }
-    pdfe_invalid_object_warning("dictionary");
-    return NULL;
-}
-
-static pdfe_array *check_isarray(lua_State * L, int n)
-{
-    pdfe_array *p = (pdfe_array *)lua_touserdata(L, n);
-    if (p != NULL && lua_getmetatable(L, n)) {
-        lua_get_metatablelua(luatex_pdfe_array);
-        if (!lua_rawequal(L, -1, -2)) {
-            p = NULL;
-        }
-        lua_pop(L, 2);
-        if (p != NULL) {
-            return p;
-        }
-    }
-    pdfe_invalid_object_warning("array");
-    return NULL;
-}
-
-static pdfe_stream *check_isstream(lua_State * L, int n)
-{
-    pdfe_stream *p = (pdfe_stream *)lua_touserdata(L, n);
-    if (p != NULL && lua_getmetatable(L, n)) {
-        lua_get_metatablelua(luatex_pdfe_stream);
-        if (!lua_rawequal(L, -1, -2)) {
-            p = NULL;
-        }
-        lua_pop(L, 2);
-        if (p != NULL) {
-            return p;
-        }
-    }
-    pdfe_invalid_object_warning("stream");
-    return NULL;
-}
-
-static pdfe_reference *check_isreference(lua_State * L, int n)
-{
-    pdfe_reference *p = (pdfe_reference *)lua_touserdata(L, n);
-    if (p != NULL && lua_getmetatable(L, n)) {
-        lua_get_metatablelua(luatex_pdfe_reference);
-        if (!lua_rawequal(L, -1, -2)) {
-            p = NULL;
-        }
-        lua_pop(L, 2);
-        if (p != NULL) {
-            return p;
-        }
-    }
-    pdfe_invalid_object_warning("reference");
-    return NULL;
-}
-
-/*tex
-
-    Reporting the type of a userdata is just a sequence of tests till we find the
-    right one. We return nothing is it is no pdfe type.
-
-    \starttyping
-    t = pdfe.type(<pdfe document|dictionary|array|reference|stream>)
-    \stoptyping
-
-*/
-
-#define check_type(field,meta,name) do { \
-    lua_get_metatablelua(luatex_##meta); \
-    if (lua_rawequal(L, -1, -2)) { \
-        lua_pushstring(L,name); \
-        return 1; \
-    } \
-    lua_pop(L, 1); \
-} while (0)
-
-static int pdfelib_type(lua_State * L)
-{
-    void *p = lua_touserdata(L, 1);
-    if (p != NULL && lua_getmetatable(L, 1)) {
-        check_type(document,  pdfe,           "pdfe");
-        check_type(dictionary,pdfe_dictionary,"pdfe.dictionary");
-        check_type(array,     pdfe_array,     "pdfe.array");
-        check_type(reference, pdfe_reference, "pdfe.reference");
-        check_type(stream,    pdfe_stream,    "pdfe.stream");
-    }
-    return 0;
-}
-
-/*tex
-
-    The \type {tostring} metamethods are similar and report a pdfe type plus a
-    pointer value, as is rather usual in \LUA.
-
-*/
-
-#define define_to_string(field,what) \
-static int pdfelib_tostring_##field(lua_State * L) { \
-    pdfe_##field *p = check_is##field(L, 1); \
-    if (p != NULL) { \
-        lua_pushfstring(L, "<" what " %p>", (ppdoc *) p->field); \
-        return 1; \
-    } \
-    return 0; \
-}
-
-define_to_string(document,  "pdfe")
-define_to_string(dictionary,"pdfe.dictionary")
-define_to_string(array,     "pdfe.array")
-define_to_string(reference, "pdfe.reference")
-define_to_string(stream,    "pdfe.stream")
-
-/*tex
-
-    The pushers look rather similar. We have two variants, one that just pushes
-    the object, and another that also pushes some extra information.
-
-*/
-
-#define pdfe_push_dictionary do { \
-    pdfe_dictionary *d = (pdfe_dictionary *)lua_newuserdata(L, sizeof(pdfe_dictionary));	\
-    luaL_getmetatable(L, PDFE_METATABLE_DICTIONARY); \
-    lua_setmetatable(L, -2); \
-    d->dictionary = dictionary; \
-} while(0)
-
-static int pushdictionary(lua_State * L, ppdict *dictionary)
-{
-    if (dictionary != NULL) {
-        pdfe_push_dictionary;
-        lua_pushinteger(L,dictionary->size);
-        return 2;
-    }
-    return 0;
-}
-
-static int pushdictionaryonly(lua_State * L, ppdict *dictionary)
-{
-    if (dictionary != NULL) {
-        pdfe_push_dictionary;
-        return 1;
-    }
-    return 0;
-}
-
-#define pdfe_push_array do { \
-    pdfe_array *a = (pdfe_array *)lua_newuserdata(L, sizeof(pdfe_array));	\
-    luaL_getmetatable(L, PDFE_METATABLE_ARRAY); \
-    lua_setmetatable(L, -2); \
-    a->array = array; \
-  } while (0)
-
-static int pusharray(lua_State * L, pparray * array)
-{
-    if (array != NULL) {
-        pdfe_push_array;
-        lua_pushinteger(L,array->size);
-        return 2;
-    }
-    return 0;
-}
-
-static int pusharrayonly(lua_State * L, pparray * array)
-{
-    if (array != NULL) {
-        pdfe_push_array;
-        return 1;
-    }
-    return 0;
-}
-
-#define pdfe_push_stream do { \
-    pdfe_stream *s = (pdfe_stream *)lua_newuserdata(L, sizeof(pdfe_stream));	\
-    luaL_getmetatable(L, PDFE_METATABLE_STREAM); \
-    lua_setmetatable(L, -2); \
-    s->stream = stream; \
-    s->open = 0; \
-    s->decode = 0; \
-} while(0)
-
-static int pushstream(lua_State * L, ppstream * stream)
-{
-    if (stream != NULL) {
-        pdfe_push_stream;
-        if (pushdictionary(L, stream->dict) > 0)
-            return 3;
-        else
-            return 1;
-    }
-    return 0;
-}
-
-static int pushstreamonly(lua_State * L, ppstream * stream)
-{
-    if (stream != NULL) {
-        pdfe_push_stream;
-        if (pushdictionaryonly(L, stream->dict) > 0)
-            return 2;
-        else
-            return 1;
-    }
-    return 0;
-}
-
-#define pdfe_push_reference do { \
-    pdfe_reference *r = (pdfe_reference *)lua_newuserdata(L, sizeof(pdfe_reference));	\
-    luaL_getmetatable(L, PDFE_METATABLE_REFERENCE); \
-    lua_setmetatable(L, -2); \
-    r->reference = reference; \
-  } while (0)
-
-static int pushreference(lua_State * L, ppref * reference)
-{
-    if (reference != NULL) {
-        pdfe_push_reference;
-        lua_pushinteger(L,reference->number);
-        return 2;
-    }
-    return 0;
-}
-
-/*tex
-
-    The next function checks for the type and then pushes the matching data on
-    the stack.
-
-    \starttabulate[|c|l|l|l|]
-    \BC type       \BC meaning    \BC value            \BC detail \NC \NR
-    \NC \type {0}  \NC none       \NC nil              \NC \NC \NR
-    \NC \type {1}  \NC null       \NC nil              \NC \NC \NR
-    \NC \type {2}  \NC boolean    \NC boolean          \NC \NC \NR
-    \NC \type {3}  \NC boolean    \NC integer          \NC \NC \NR
-    \NC \type {4}  \NC number     \NC float            \NC \NC \NR
-    \NC \type {5}  \NC name       \NC string           \NC \NC \NR
-    \NC \type {6}  \NC string     \NC string           \NC type \NC \NR
-    \NC \type {7}  \NC array      \NC arrayobject      \NC size \NC \NR
-    \NC \type {8}  \NC dictionary \NC dictionaryobject \NC size \NC \NR
-    \NC \type {9}  \NC stream     \NC streamobject     \NC dictionary size \NC \NR
-    \NC \type {10} \NC reference  \NC integer          \NC \NC \NR
-    \LL
-    \stoptabulate
-
-    A name and string can be distinguished by the extra type value that a string
-    has.
-
-*/
-
-static int pushvalue(lua_State * L, ppobj *object)
-{
-    switch (object->type) {
-        case PPNONE:
-        case PPNULL:
-            lua_pushnil(L);
-            return 1;
-            break;
-        case PPBOOL:
-            lua_pushboolean(L,object->integer);
-            return 1;
-            break;
-        case PPINT:
-            lua_pushinteger(L, object-> integer);
-            return 1;
-            break;
-        case PPNUM:
-            lua_pushnumber(L, object->number);
-            return 1;
-            break;
-        case PPNAME:
-            lua_pushstring(L, (const char *) ppname_decoded(object->name));
-            return 1;
-            break;
-        case PPSTRING:
-            lua_pushlstring(L,(const char *) object->string, ppstring_size((void *)object->string));
-            lua_pushboolean(L, ppstring_hex((void *)object->string));
-            return 2;
-            break;
-        case PPARRAY:
-            return pusharray(L, object->array);
-            break;
-        case PPDICT:
-            return pushdictionary(L, object->dict);
-            break;
-        case PPSTREAM:
-            return pushstream(L, object->stream);
-            break;
-        case PPREF:
-            return pushreference(L, object->ref);
-            break;
-    }
-    return 0;
-}
-
-/*tex
-
-    We need to start someplace when we traverse a document's tree. There are
-    three places:
-
-    \starttyping
-    catalogdictionary = getcatalog(documentobject)
-    trailerdictionary = gettrailer(documentobject)
-    infodictionary    = getinfo   (documentobject)
-    \stoptyping
-
-*/
-
-static int pdfelib_getcatalog(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL)
-        return 0;
-    return pushdictionaryonly(L,ppdoc_catalog(p->document));
-}
-
-static int pdfelib_gettrailer(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL)
-        return 0;
-    return pushdictionaryonly(L,ppdoc_trailer(p->document));
-}
-
-static int pdfelib_getinfo(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL)
-        return 0;
-    return pushdictionaryonly(L,ppdoc_info(p->document));
-}
-
-/*tex
-
-    We have three more helpers.
-
-    \starttyping
-    [key,] type, value, detail = getfromdictionary(dictionaryobject,name|index)
-           type, value, detail = getfromarray     (arrayobject,index)
-    [key,] type, value, detail = getfromstream    (streamobject,name|index)
-    \stoptyping
-
-*/
-
-static int pdfelib_getfromarray(lua_State * L)
-{
-    pdfe_array *a = check_isarray(L, 1);
-    if (a != NULL) {
-        int index = luaL_checkint(L, 2) - 1;
-        if (index < a->array->size) {
-            ppobj *object = pparray_at(a->array,index);
-            lua_pushinteger(L,(int) object->type);
-            return 1 + pushvalue(L,object);
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getfromdictionary(lua_State * L)
-{
-    pdfe_dictionary *d = check_isdictionary(L, 1);
-    if (d != NULL) {
-        if (lua_type(L,2) == LUA_TSTRING) {
-            const char *name = luaL_checkstring(L, 2);
-            ppobj *object = ppdict_get_obj(d->dictionary,name);
-            if (object != NULL) {
-                lua_pushinteger(L,(int) object->type);
-                return 1 + pushvalue(L,object);
-            }
-        } else {
-            int index = luaL_checkint(L, 2) - 1;
-            if (index < d->dictionary->size) {
-                ppobj *object = ppdict_at(d->dictionary,index);
-                ppname key = ppdict_key(d->dictionary,index);
-                lua_pushstring(L,(const char *) key);
-                lua_pushinteger(L,(int) object->type);
-                return 2 + pushvalue(L,object);
-            }
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getfromstream(lua_State * L)
-{
-    pdfe_stream *s = (pdfe_stream *)lua_touserdata(L, 1);
-    if (s != NULL) {
-        ppdict *d = s->stream->dict;
-        if (lua_type(L,2) == LUA_TSTRING) {
-            const char *name = luaL_checkstring(L, 2);
-            ppobj *object = ppdict_get_obj(d,name);
-            if (object != NULL) {
-                lua_pushinteger(L,(int) object->type);
-                return 1 + pushvalue(L,object);
-            }
-        } else {
-            int index = luaL_checkint(L, 2) - 1;
-            if (index < d->size) {
-                ppobj *object = ppdict_at(d,index);
-                ppname key = ppdict_key(d,index);
-                lua_pushstring(L,(const char *) key);
-                lua_pushinteger(L,(int) object->type);
-                return 2 + pushvalue(L,object);
-            }
-        }
-    }
-    return 0;
-}
-
-/*tex
-
-    An indexed table with all entries in an array can be fetched with::
-
-    \starttyping
-    t = arraytotable(arrayobject)
-    \stoptyping
-
-    An hashed table with all entries in an dictionary can be fetched with::
-
-    \starttyping
-    t = dictionarytotable(arrayobject)
-    \stoptyping
-
-*/
-
-static void pdfelib_totable(lua_State * L, ppobj * object, int flat)
-{
-    int n = pushvalue(L,object);
-    if (flat && n < 2) {
-        return;
-    }
-    /* [value] [extra] [more] */
-    lua_createtable(L,n+1,0);
-    if (n == 1) {
-        /* value { nil, nil } */
-        lua_insert(L,-2);
-        /* { nil, nil } value */
-        lua_rawseti(L,-2,2);
-        /* { nil , value } */
-    } else if (n == 2) {
-        /* value extra { nil, nil, nil } */
-        lua_insert(L,-3);
-        /* { nil, nil, nil } value extra */
-        lua_rawseti(L,-3,3);
-        /* { nil, nil, extra } value */
-        lua_rawseti(L,-2,2);
-        /* { nil, value, extra } */
-    } else if (n == 3) {
-        /* value extra more { nil, nil, nil, nil } */
-        lua_insert(L,-4);
-        /* { nil, nil, nil, nil, nil } value extra more */
-        lua_rawseti(L,-4,4);
-        /* { nil, nil, nil, more } value extra */
-        lua_rawseti(L,-3,3);
-        /* { nil, nil, extra, more } value */
-        lua_rawseti(L,-2,2);
-        /* { nil, value, extra, more } */
-    }
-    lua_pushinteger(L,(int) object->type);
-    /* { nil, [value], [extra], [more] } type */
-    lua_rawseti(L,-2,1);
-    /* { type, [value], [extra], [more] } */
-    return;
-}
-
-static int pdfelib_arraytotable(lua_State * L)
-{
-    pdfe_array *a = check_isarray(L, 1);
-    if (a != NULL) {
-        int flat = lua_isboolean(L,2);
-        int i = 0;
-        lua_createtable(L,i,0);
-        /* table */
-        for (i=0;i<a->array->size;i++) {
-            ppobj *object = pparray_at(a->array,i);
-            pdfelib_totable(L,object,flat);
-            /* table { type, [value], [extra], [more] } */
-            lua_rawseti(L,-2,i+1);
-            /* table[i] = { type, [value], [extra], [more] } */
-        }
-        return 1;
-    }
-    return 0;
-}
-
-static int pdfelib_dictionarytotable(lua_State * L)
-{
-    pdfe_dictionary *d = check_isdictionary(L, 1);
-    if (d != NULL) {
-        int flat = lua_isboolean(L,2);
-        int i = 0;
-        lua_createtable(L,0,i);
-        /* table */
-        for (i=0;i<d->dictionary->size;i++) {
-            ppobj *object = ppdict_at(d->dictionary,i);
-            ppname key = ppdict_key(d->dictionary,i);
-            lua_pushstring(L,(const char *) key);
-            /* table key */
-            pdfelib_totable(L,object,flat);
-            /* table key { type, [value], [extra], [more] } */
-            lua_rawset(L,-3);
-            /* table[key] = { type, [value], [extra] } */
-        }
-        return 1;
-    }
-    return 0;
-}
-
-/*tex
-
-    All pages are collected with:
-
-    \starttyping
-    { { dict, size, objnum }, ... } = pagestotable(document)
-    \stoptyping
-
-*/
-
-static int pdfelib_pagestotable(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p != NULL) {
-        ppdoc *d = p->document;
-        ppref *r = NULL;
-        int i = 0;
-        lua_createtable(L,ppdoc_page_count(d),0);
-        /* pages[1..n] */
-        for (r = ppdoc_first_page(d), i = 1; r != NULL; r = ppdoc_next_page(d), ++i) {
-            lua_createtable(L,3,0);
-            pushdictionary(L,ppref_obj(r)->dict);
-            /* table dictionary n */
-            lua_rawseti(L,-3,2);
-            /* table dictionary */
-            lua_rawseti(L,-2,1);
-            /* table */
-            lua_pushinteger(L,r->number);
-            /* table reference */
-            lua_rawseti(L,-2,3);
-            /* table */
-            lua_rawseti(L,-2,i);
-            /* pages[i] = { dictionary, size, objnum } */
-        }
-        return 1;
-    }
-    return 0;
-}
-
-/*tex
-
-    Streams can be fetched on one go:
-
-    \starttyping
-    string, n = readwholestream(streamobject,decode)
-    \stoptyping
-
-*/
-
-static int pdfelib_readwholestream(lua_State * L)
-{
-    pdfe_stream *s = check_isstream(L, 1);
-    if (s != NULL) {
-        uint8_t *b = NULL;
-        int decode = 0;
-        size_t n = 0;
-        if (s->open > 0) {
-            ppstream_done(s->stream);
-            s->open = 0;
-            s->decode = 0;
-        }
-        if (lua_gettop(L) > 1 && lua_isboolean(L, 2)) {
-            decode = lua_toboolean(L, 2);
-        }
-        b = ppstream_all(s->stream,&n,decode);
-        lua_pushlstring(L, (const char *) b, n);
-        lua_pushinteger(L, (int) n);
-        ppstream_done(s->stream);
-        return 2;
-    }
-    return 0;
-}
-
-/*tex
-
-    Alternatively streams can be fetched stepwise:
-
-    okay = openstream(streamobject,[decode])
-    string, n = readfromstream(streamobject)
-    closestream(streamobject)
-
-*/
-
-static int pdfelib_openstream(lua_State * L)
-{
-    pdfe_stream *s = check_isstream(L, 1);
-    if (s != NULL) {
-        if (s->open == 0) {
-            if (lua_gettop(L) > 1) {
-                s->decode = lua_isboolean(L, 2);
-            }
-            s->open = 1;
-        }
-        lua_pushboolean(L,1);
-        return 1;
-    }
-    return 0;
-}
-
-static int pdfelib_closestream(lua_State * L)
-{
-    pdfe_stream *s = check_isstream(L, 1);
-    if (s != NULL) {
-        if (s->open >0) {
-            ppstream_done(s->stream);
-            s->open = 0;
-            s->decode = 0;
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_readfromstream(lua_State * L)
-{
-    pdfe_stream *s = check_isstream(L, 1);
-    if (s != NULL) {
-        size_t n = 0;
-        uint8_t *d = NULL;
-        if (s->open == 1) {
-            d = ppstream_first(s->stream,&n,s->decode);
-            s->open = 2;
-        } else if (s->open == 2) {
-            d = ppstream_next(s->stream,&n);
-        } else {
-            return 0;
-        }
-        lua_pushlstring(L, (const char *) d, n);
-        lua_pushinteger(L, (int) n);
-        return 2;
-    }
-    return 0;
-}
-
-/*tex
-
-    There are two methods for opening a document: files and strings.
-
-    \starttyping
-    documentobject = open(filename)
-    documentobject = new(string,length)
-    \stoptyping
-
-    Closing happens with:
-
-    \starttyping
-    close(documentobject)
-    \stoptyping
-
-    When the \type {new} function gets a peudo filename as third argument,
-    no user data will be created but the stream is accessible as image.
-
-*/
-
-static int pdfelib_open(lua_State * L)
-{
-    const char *filename = luaL_checkstring(L, 1);
-    ppdoc *d = ppdoc_load(filename);
-    if (d == NULL) {
-        formatted_warning("pdfe lib","no valid pdf file '%s'",filename);
-    } else {
-        pdfe_document *p = (pdfe_document *) lua_newuserdata(L, sizeof(pdfe_document));
-        luaL_getmetatable(L, PDFE_METATABLE);
-        lua_setmetatable(L, -2);
-        p->document = d;
-        p->open = true;
-        p->isfile = true;
-        p->memstream = NULL;
-        return 1;
-    }
-    return 0;
-}
-
-static int pdfelib_new(lua_State * L)
-{
-    const char *docstream = NULL;
-    char *memstream = NULL ;
-    unsigned long long streamsize;
-    switch (lua_type(L, 1)) {
-        case LUA_TSTRING:
-            /* stream as Lua string */
-            docstream = luaL_checkstring(L, 1);
-            break;
-        case LUA_TLIGHTUSERDATA:
-            /* stream as sequence of bytes */
-            docstream = (const char *) lua_touserdata(L, 1);
-            break;
-        default:
-            luaL_error(L, "bad <pdfe> argument: string or lightuserdata expected");
-            break;
-    }
-    if (docstream == NULL) {
-        luaL_error(L, "bad <pdfe> document");
-    }
-    /* size of the stream */
-    streamsize = (unsigned long long) luaL_checkint(L, 2);
-    memstream = xmalloc((unsigned) (streamsize + 1));
-    if (! memstream) {
-        luaL_error(L, "no room for <pdfe> stream");
-    }
-    memcpy(memstream, docstream, (streamsize + 1));
-    memstream[streamsize]='\0';
-    if (lua_gettop(L) == 2) {
-        /* we stay at the lua end */
-        ppdoc *d = ppdoc_mem(memstream, streamsize);
-        if (d == NULL) {
-            normal_warning("pdfe lib","no valid pdf mem stream");
-        } else {
-            pdfe_document *p = (pdfe_document *) lua_newuserdata(L, sizeof(pdfe_document));
-            luaL_getmetatable(L, PDFE_METATABLE);
-            lua_setmetatable(L, -2);
-            p->document = d;
-            p->open = true;
-            p->isfile = false;
-            p->memstream = memstream;
-            return 1;
-        }
-    } else {
-        /* pseudo file name */
-        PdfDocument *pdf_doc;
-        const char *file_id = luaL_checkstring(L, 3);
-        if (file_id == NULL) {
-            luaL_error(L, "<pdfe> stream has an invalid id");
-        }
-        if (strlen(file_id) > STREAM_FILE_ID_LEN ) {
-            /* a limit to the length of the string */
-            luaL_error(L, "<pdfe> stream has a too long id");
-        }
-        pdf_doc = refMemStreamPdfDocument(memstream, streamsize, file_id);
-        if (pdf_doc != NULL) {
-            lua_pushstring(L,pdf_doc->file_path);
-            return 1;
-        } else {
-            /* pplib does this: xfree(memstream); */
-        }
-    }
-    return 0;
-}
-
-/*
-
-    There is no garbage collection needed as the library itself manages the
-    objects. Normally objects don't take much space. Streams use buffers so (I
-    assume) that they are not persistent. The only collector is in the parent
-    object (the document).
-
-*/
-
-static int pdfelib_free(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p != NULL && p->open) {
-        if (p->document != NULL) {
-            ppdoc_free(p->document);
-            p->document = NULL;
-        }
-        if (p->memstream != NULL) {
-         /* pplib does this: xfree(p->memstream); */
-            p->memstream = NULL;
-        }
-        p->open = false;
-    }
-    return 0;
-}
-
-static int pdfelib_close(lua_State * L)
-{
-    return pdfelib_free(L);
-}
-
-/*tex
-
-    A document is can be uncrypted with:
-
-    \starttyping
-    status = unencrypt(documentobject,user,owner)
-    \stoptyping
-
-    Instead of a password \type {nil} can be passed, so there are three possible
-    useful combinations.
-
-*/
-
-static int pdfelib_unencrypt(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p != NULL) {
-        size_t u = 0;
-        size_t o = 0;
-        const char* user = NULL;
-        const char* owner = NULL;
-        int top = lua_gettop(L);
-        if (top > 1) {
-            if (lua_type(L,2) == LUA_TSTRING) {
-                user = lua_tolstring(L, 2, &u);
-            } else {
-                /*tex we're not too picky but normally it will be nil or false */
-            }
-            if (top > 2) {
-                if (lua_type(L,3) == LUA_TSTRING) {
-                    owner = lua_tolstring(L, 3, &o);
-                } else {
-                    /*tex we're not too picky but normally it will be nil or false */
-                }
-            }
-            lua_pushinteger(L, (int) ppdoc_crypt_pass(p->document,user,u,owner,o));
-            return 1;
-        }
-    }
-    lua_pushinteger(L, (int) PPCRYPT_FAIL);
-    return 1;
-}
-
-/*tex
-
-    There are a couple of ways to get information about the document:
-
-    \starttyping
-    n             = getsize       (documentobject)
-    major, minor  = getversion    (documentobject)
-    status        = getstatus     (documentobject)
-    n             = getnofobjects (documentobject)
-    n             = getnofpages   (documentobject)
-    bytes, waste  = getmemoryusage(documentobject)
-    \stoptyping
-
-*/
-
-static int pdfelib_getsize(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL)
-        return 0;
-    lua_pushinteger(L,(int) ppdoc_file_size(p->document));
-    return 1;
-}
-
-
-static int pdfelib_getversion(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL) {
-        return 0;
-    } else {
-        int minor;
-        int major = ppdoc_version_number(p->document, &minor);
-        lua_pushinteger(L,(int) major);
-        lua_pushinteger(L,(int) minor);
-        return 2;
-    }
-}
-
-static int pdfelib_getstatus(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL)
-        return 0;
-    lua_pushinteger(L,(int) ppdoc_crypt_status(p->document));
-    return 1;
-}
-
-static int pdfelib_getnofobjects(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL)
-        return 0;
-    lua_pushinteger(L,(int) ppdoc_objects(p->document));
-    return 1;
-}
-
-static int pdfelib_getnofpages(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL)
-        return 0;
-    lua_pushinteger(L,(int) ppdoc_page_count(p->document));
-    return 1;
-}
-
-static int pdfelib_getmemoryusage(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p != NULL) {
-        size_t w = 0;
-        size_t m = ppdoc_memory(p->document,&w);
-        lua_pushinteger(L,(int) m);
-        lua_pushinteger(L,(int) w);
-        return 2;
-    }
-    return 0;
-}
-
-/*
-    A specific page dictionary can be filtered with the next command. So, there
-    is no need to parse the document page tree (with these \type {kids} arrays).
-
-    \starttyping
-    dictionaryobject = getpage(documentobject,pagenumber)
-    \stoptyping
-
-*/
-
-static int pushpage(lua_State * L, ppdoc * d, int page)
-{
-    if (page <= 0 || page > ppdoc_page_count(d)) {
-        return 0;
-    } else {
-        ppref *pp = ppdoc_page(d,page);
-        return pushdictionaryonly(L, ppref_obj(pp)->dict);
-    }
-}
-
-static int pdfelib_getpage(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL) {
-        return 0;
-    } else {
-        return pushpage(L, p->document, luaL_checkint(L, 2));
-    }
-}
-
-static int pushpages(lua_State * L, ppdoc * d)
-{
-    int i = 0;
-    ppref *r;
-    lua_createtable(L,ppdoc_page_count(d),0);
-    /* pages[1..n] */
-    for (r = ppdoc_first_page(d), i = 1; r != NULL; r = ppdoc_next_page(d), ++i) {
-        pushdictionaryonly(L,ppref_obj(r)->dict);
-        lua_rawseti(L,-2,i);
-    }
-    return 1 ;
-}
-
-static int pdfelib_getpages(lua_State * L)
-{
-    pdfe_document *p = check_isdocument(L, 1);
-    if (p == NULL) {
-        return 0;
-    } else {
-        return pushpages(L, p->document);
-    }
-}
-
-/*tex
-
-    The boundingbox (\type {MediaBox) and similar boxes can be available in a
-    (page) doctionary but also in a parent object. Therefore a helper is
-    available that does the (backtracked) lookup.
-
-    \starttyping
-    { lx, ly, rx, ry } = getbox(dictionaryobject)
-    \stoptyping
-
-*/
-
-static int pdfelib_getbox(lua_State * L)
-{
-    if (lua_gettop(L) > 1 && lua_type(L,2) == LUA_TSTRING) {
-        pdfe_dictionary *p = check_isdictionary(L, 1);
-        if (p != NULL) {
-            const char *key = lua_tostring(L,2);
-            pprect box;
-            pprect *r;
-            box.lx = box.rx = box.ly = box.ry = 0;
-            r = ppdict_get_box(p->dictionary,key,&box);
-            if (r != NULL) {
-                lua_createtable(L,4,0);
-                lua_pushnumber(L,r->lx);
-                lua_rawseti(L,-2,1);
-                lua_pushnumber(L,r->ly);
-                lua_rawseti(L,-2,2);
-                lua_pushnumber(L,r->rx);
-                lua_rawseti(L,-2,3);
-                lua_pushnumber(L,r->ry);
-                lua_rawseti(L,-2,4);
-                return 1;
-            }
-        }
-    }
-    return 0;
-}
-
-/*tex
-
-    This one is needed when you use the detailed getters and run into an
-    object reference. The regular getters resolve this automatically.
-
-    \starttyping
-    [dictionary|array|stream]object = getfromreference(referenceobject)
-    \stoptyping
-
-*/
-
-static int pdfelib_getfromreference(lua_State * L)
-{
-    pdfe_reference *r = check_isreference(L, 1);
-    if (r != NULL) {
-        ppobj *o = ppref_obj(r->reference);
-        lua_pushinteger(L,o->type);
-        return 1 + pushvalue(L,o);
-    }
-    return 0;
-}
-
-/*tex
-
-    Here are some convenient getters:
-
-    \starttyping
-    <string>         = getstring    (array|dict|ref,index|key)
-    <integer>        = getinteger   (array|dict|ref,index|key)
-    <number>         = getnumber    (array|dict|ref,index|key)
-    <boolan>         = getboolean   (array|dict|ref,index|key)
-    <string>         = getname      (array|dict|ref,index|key)
-    <dictionary>     = getdictionary(array|dict|ref,index|key)
-    <array>          = getarray     (array|dict|ref,index|key)
-    <stream>, <dict> = getstream    (array|dict|ref,index|key)
-    \stoptyping
-
-    We report issues when reasonable but are silent when it makes sense. We don't
-    error on this because we expect the user code to act reasonable on a return
-    value.
-
-*/
-
-#define pdfelib_get_value_check_1 do { \
-    if (p == NULL) { \
-        if (t == LUA_TSTRING) { \
-            normal_warning("pdfe lib","lua <pdfe dictionary> expected"); \
-        } else if (t == LUA_TNUMBER) { \
-            normal_warning("pdfe lib","lua <pdfe array> expected"); \
-        } else { \
-          normal_warning("pdfe lib","invalid arguments"); \
-        } \
-        return 0; \
-    } else if (! lua_getmetatable(L, 1)) { \
-        normal_warning("pdfe lib","first argument should be a <pde array> or <pde dictionary>"); \
-    } \
-} while (0)
-
-#define pdfelib_get_value_check_2 \
-    normal_warning("pdfe lib","second argument should be integer or string");
-
-/*tex
-
-    The direct fetcher returns the result or |NULL| when there is nothing
-    found.
-
-*/
-
-#define pdfelib_get_value_direct(get_d,get_a) do {                      \
-    int t = lua_type(L,2);                                              \
-    void *p = lua_touserdata(L, 1);                                     \
-    pdfelib_get_value_check_1;                                          \
-    if (t == LUA_TSTRING) {                                             \
-        const char *key = lua_tostring(L,-2);                           \
-        lua_get_metatablelua(luatex_pdfe_dictionary);                   \
-        if (lua_rawequal(L, -1, -2)) {                                  \
-            value = get_d(((pdfe_dictionary *) p)->dictionary, key);    \
-        } else {                                                        \
-            lua_pop(L,1);                                               \
-            lua_get_metatablelua(luatex_pdfe_reference);                \
-            if (lua_rawequal(L, -1, -2)) {                              \
-                ppobj * o = ppref_obj((ppref *) (((pdfe_reference *) p)->reference)); \
-                if (o != NULL && o->type == PPDICT) {                   \
-                    value = get_d((ppdict *)o->dict, key);              \
-                }                                                       \
-            }                                                           \
-        }                                                               \
-    } else if (t == LUA_TNUMBER) {                                      \
-        size_t index = lua_tointeger(L,-2);                             \
-        lua_get_metatablelua(luatex_pdfe_array);                        \
-        if (lua_rawequal(L, -1, -2)) {                                  \
-            value = get_a(((pdfe_array *) p)->array, index);            \
-        } else {                                                        \
-            lua_pop(L,1);                                               \
-            lua_get_metatablelua(luatex_pdfe_reference);                \
-            if (lua_rawequal(L, -1, -2)) {                              \
-                ppobj * o = ppref_obj((ppref *) (((pdfe_reference *) p)->reference)); \
-                if (o != NULL && o->type == PPARRAY) {                  \
-                    value = get_a((pparray *) o->array, index);         \
-                }                                                       \
-            }                                                           \
-        }                                                               \
-    } else {                                                            \
-        pdfelib_get_value_check_2;                                      \
-    }                                                                   \
-} while (0)
-
-/*tex
-
-    The indirect fetcher passes a pointer to the target variable and returns
-    success state.
-
-*/
-
-#define pdfelib_get_value_indirect(get_d,get_a) do {                        \
-    int t = lua_type(L,2);                                                  \
-    void *p = lua_touserdata(L, 1);                                         \
-    pdfelib_get_value_check_1;                                              \
-    if (t == LUA_TSTRING) {                                                 \
-        const char *key = lua_tostring(L,-2);                               \
-        lua_get_metatablelua(luatex_pdfe_dictionary);                       \
-        if (lua_rawequal(L, -1, -2)) {                                      \
-            okay = get_d(((pdfe_dictionary *) p)->dictionary, key, &value); \
-        } else {                                                            \
-            lua_pop(L,1);                                                   \
-            lua_get_metatablelua(luatex_pdfe_reference);                    \
-            if (lua_rawequal(L, -1, -2)) {                                  \
-                ppobj * o = ppref_obj((ppref *) (((pdfe_reference *) p)->reference)); \
-                if (o != NULL && o->type == PPDICT)                         \
-                    okay = get_d(o->dict, key, &value);                     \
-            }                                                               \
-        }                                                                   \
-    } else if (t == LUA_TNUMBER) {                                          \
-        size_t index = lua_tointeger(L,-2);                                 \
-        lua_get_metatablelua(luatex_pdfe_array);                            \
-        if (lua_rawequal(L, -1, -2)) {                                      \
-            okay = get_a(((pdfe_array *) p)->array, index, &value);         \
-        } else {                                                            \
-            lua_pop(L,1);                                                   \
-            lua_get_metatablelua(luatex_pdfe_reference);                    \
-            if (lua_rawequal(L, -1, -2)) {                                  \
-                ppobj * o = ppref_obj((ppref *) (((pdfe_reference *) p)->reference)); \
-                if (o != NULL && o->type == PPARRAY)                        \
-                    okay = get_a(o->array, index, &value);                  \
-            }                                                               \
-        }                                                                   \
-    } else {                                                                \
-        pdfelib_get_value_check_2;                                          \
-    }                                                                       \
-} while (0)
-
-static int pdfelib_getstring(lua_State * L)
-{
-    if (lua_gettop(L) > 1) {
-        ppstring value = NULL;
-        pdfelib_get_value_direct(ppdict_rget_string,pparray_rget_string);
-        if (value != NULL) {
-            lua_pushstring(L,(const char *) value);
-            return 1;
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getinteger(lua_State * L)
-{
-    if (lua_gettop(L) > 1) {
-        ppint value = 0;
-        int okay = 0;
-        pdfelib_get_value_indirect(ppdict_rget_int,pparray_rget_int);
-        if (okay) {
-            lua_pushinteger(L,(int) value);
-            return 1;
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getnumber(lua_State * L)
-{
-    if (lua_gettop(L) > 1) {
-        ppnum value = 0;
-        int okay = 0;
-        pdfelib_get_value_indirect(ppdict_rget_num,pparray_rget_num);
-        if (okay) {
-            lua_pushnumber(L,value);
-            return 1;
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getboolean(lua_State * L)
-{
-    if (lua_gettop(L) > 1) {
-        int value = 0;
-        int okay = 0;
-        pdfelib_get_value_indirect(ppdict_rget_bool,pparray_rget_bool);
-        if (okay) {
-            lua_pushboolean(L,value);
-            return 1;
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getname(lua_State * L)
-{
-    if (lua_gettop(L) > 1) {
-        ppname value = NULL;
-        pdfelib_get_value_direct(ppdict_rget_name,pparray_rget_name);
-        if (value != NULL) {
-            lua_pushstring(L,(const char *) ppname_decoded(value));
-            return 1;
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getdictionary(lua_State * L)
-{
-    if (lua_gettop(L) > 1) {
-        ppdict * value = NULL;
-        pdfelib_get_value_direct(ppdict_rget_dict,pparray_rget_dict);
-        if (value != NULL) {
-            return pushdictionaryonly(L,value);
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getarray(lua_State * L)
-{
-    if (lua_gettop(L) > 1) {
-        pparray * value = NULL;
-        pdfelib_get_value_direct(ppdict_rget_array,pparray_rget_array);
-        if (value != NULL) {
-            return pusharrayonly(L,value);
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_getstream(lua_State * L)
-{
-    if (lua_gettop(L) > 1) {
-        ppobj * value = NULL;
-        pdfelib_get_value_direct(ppdict_rget_obj,pparray_rget_obj);
-        if (value != NULL && value->type == PPSTREAM) {
-            return pushstreamonly(L,(ppstream *) value->stream);
-        }
-    }
-    return 0;
-}
-
-/*tex
-
-    The generic pushed that does a similar job as the previous getters acts upon
-    the type.
-
-*/
-
-static int pdfelib_pushvalue(lua_State * L, ppobj *object)
-{
-    switch (object->type) {
-        case PPNONE:
-        case PPNULL:
-            lua_pushnil(L);
-            break;
-        case PPBOOL:
-            lua_pushboolean(L, object->integer);
-            break;
-        case PPINT:
-            lua_pushinteger(L, object->integer);
-            break;
-        case PPNUM:
-            lua_pushnumber(L, object->number);
-            break;
-        case PPNAME:
-            lua_pushstring(L, (const char *) ppname_decoded(object->name));
-            break;
-        case PPSTRING:
-            lua_pushlstring(L,(const char *) object->string, ppstring_size((void *)object->string));
-            break;
-        case PPARRAY:
-            return pusharrayonly(L, object->array);
-            break;
-        case PPDICT:
-            return pushdictionary(L, object->dict);
-            break;
-        case PPSTREAM:
-            return pushstream(L, object->stream);
-            break;
-        case PPREF:
-            pushreference(L, object->ref);
-            break;
-        default:
-            lua_pushnil(L);
-            break;
-    }
-    return 1;
-}
-
-/*tex
-
-    Finally we arrived at the acessors for the userdata objects. The use
-    previously defined helpers.
-
-*/
-
-static int pdfelib_access(lua_State * L)
-{
-    if (lua_type(L,2) == LUA_TSTRING) {
-        pdfe_document *p = (pdfe_document *)lua_touserdata(L, 1);
-        const char *s = lua_tostring(L,2);
-        if (lua_key_eq(s,catalog) || lua_key_eq(s,Catalog)) {
-            return pushdictionaryonly(L,ppdoc_catalog(p->document));
-        } else if (lua_key_eq(s,info) || lua_key_eq(s,Info)) {
-            return pushdictionaryonly(L,ppdoc_info(p->document));
-        } else if (lua_key_eq(s,trailer) || lua_key_eq(s,Trailer)) {
-            return pushdictionaryonly(L,ppdoc_trailer(p->document));
-        } else if (lua_key_eq(s,pages) || lua_key_eq(s,Pages)) {
-            return pushpages(L,p->document);
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_array_access(lua_State * L)
-{
-    if (lua_type(L,2) == LUA_TNUMBER) {
-        pdfe_array *p = (pdfe_array *)lua_touserdata(L, 1);
-        ppint index = lua_tointeger(L,2) - 1;
-        ppobj *o = pparray_rget_obj(p->array,index);
-        if (o != NULL) {
-            return pdfelib_pushvalue(L,o);
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_dictionary_access(lua_State * L)
-{
-    pdfe_dictionary *p = (pdfe_dictionary *)lua_touserdata(L, 1);
-    if (lua_type(L,2) == LUA_TSTRING) {
-        const char *key = lua_tostring(L,2);
-        ppobj *o = ppdict_rget_obj(p->dictionary,key);
-        if (o != NULL) {
-            return pdfelib_pushvalue(L,o);
-        }
-    } else if (lua_type(L,2) == LUA_TNUMBER) {
-        ppint index = lua_tointeger(L,2) - 1;
-        ppobj *o = ppdict_at(p->dictionary,index);
-        if (o != NULL) {
-            return pdfelib_pushvalue(L,o);
-        }
-    }
-    return 0;
-}
-
-static int pdfelib_stream_access(lua_State * L)
-{
-    pdfe_stream *p = (pdfe_stream *)lua_touserdata(L, 1);
-    if (lua_type(L,2) == LUA_TSTRING) {
-        const char *key = lua_tostring(L,2);
-        ppobj *o = ppdict_rget_obj(p->stream->dict,key);
-        if (o != NULL) {
-            return pdfelib_pushvalue(L,o);
-        }
-    } else if (lua_type(L,2) == LUA_TNUMBER) {
-        ppint index = lua_tointeger(L,2) - 1;
-        ppobj *o = ppdict_at(p->stream->dict,index);
-        if (o != NULL) {
-            return pdfelib_pushvalue(L,o);
-        }
-    }
-    return 0;
-}
-
-/*tex
-
-    The length metamethods are defined last.
-
-*/
-
-static int pdfelib_array_size(lua_State * L)
-{
-    pdfe_array *p = (pdfe_array *)lua_touserdata(L, 1);
-    lua_pushinteger(L,p->array->size);
-    return 1;
-}
-
-static int pdfelib_dictionary_size(lua_State * L)
-{
-    pdfe_dictionary *p = (pdfe_dictionary *)lua_touserdata(L, 1);
-    lua_pushinteger(L,p->dictionary->size);
-    return 1;
-}
-
-static int pdfelib_stream_size(lua_State * L)
-{
-    pdfe_stream *p = (pdfe_stream *)lua_touserdata(L, 1);
-    lua_pushinteger(L,p->stream->dict->size);
-    return 1;
-}
-
-/*tex
-
-    We now initialize the main interface. We might aa few more
-    informational helpers but this is it.
-
-*/
-
-static const struct luaL_Reg pdfelib[] = {
-    /* management */
-    { "type",                    pdfelib_type },
-    { "open",                    pdfelib_open },
-    { "new",                     pdfelib_new },
-    { "close",                   pdfelib_close },
-    { "unencrypt",               pdfelib_unencrypt },
-    /* statistics */
-    { "getversion",              pdfelib_getversion },
-    { "getstatus",               pdfelib_getstatus },
-    { "getsize",                 pdfelib_getsize },
-    { "getnofobjects",           pdfelib_getnofobjects },
-    { "getnofpages",             pdfelib_getnofpages },
-    { "getmemoryusage",          pdfelib_getmemoryusage },
-    /* getters */
-    { "getcatalog",              pdfelib_getcatalog },
-    { "gettrailer",              pdfelib_gettrailer },
-    { "getinfo",                 pdfelib_getinfo },
-    { "getpage",                 pdfelib_getpage },
-    { "getpages",                pdfelib_getpages },
-    { "getbox",                  pdfelib_getbox },
-    { "getfromreference",        pdfelib_getfromreference },
-    { "getfromdictionary",       pdfelib_getfromdictionary },
-    { "getfromarray",            pdfelib_getfromarray },
-    { "getfromstream",           pdfelib_getfromstream },
-    /* collectors */
-    { "dictionarytotable",       pdfelib_dictionarytotable },
-    { "arraytotable",            pdfelib_arraytotable },
-    { "pagestotable",            pdfelib_pagestotable },
-    /* more getters */
-    { "getstring",               pdfelib_getstring },
-    { "getinteger",              pdfelib_getinteger },
-    { "getnumber",               pdfelib_getnumber },
-    { "getboolean",              pdfelib_getboolean },
-    { "getname",                 pdfelib_getname },
-    { "getdictionary",           pdfelib_getdictionary },
-    { "getarray",                pdfelib_getarray },
-    { "getstream",               pdfelib_getstream },
-    /* streams */
-    { "readwholestream",         pdfelib_readwholestream },
-    /* not really needed */
-    { "openstream",              pdfelib_openstream },
-    { "readfromstream",          pdfelib_readfromstream },
-    { "closestream",             pdfelib_closestream },
-    /* done */
-    { NULL,                      NULL}
-};
-
-/*tex
-
-    The user data metatables are defined as follows. Watch how only the
-    document needs a garbage collector.
-
-*/
-
-static const struct luaL_Reg pdfelib_m[] = {
-    { "__tostring", pdfelib_tostring_document },
-    { "__gc",       pdfelib_free },
-    { "__index",    pdfelib_access },
-    { NULL,         NULL}
-};
-
-static const struct luaL_Reg pdfelib_m_dictionary[] = {
-    { "__tostring", pdfelib_tostring_dictionary },
-    { "__index",    pdfelib_dictionary_access },
-    { "__len",      pdfelib_dictionary_size },
-    { NULL,         NULL}
-};
-
-static const struct luaL_Reg pdfelib_m_array[] = {
-    { "__tostring", pdfelib_tostring_array },
-    { "__index",    pdfelib_array_access },
-    { "__len",      pdfelib_array_size },
-    { NULL,         NULL}
-};
-
-static const struct luaL_Reg pdfelib_m_stream[] = {
-    { "__tostring", pdfelib_tostring_stream },
-    { "__index",    pdfelib_stream_access },
-    { "__len",      pdfelib_stream_size },
-    { "__call",     pdfelib_readwholestream },
-    { NULL,         NULL}
-};
-
-static const struct luaL_Reg pdfelib_m_reference[] = {
-    { "__tostring", pdfelib_tostring_reference },
-    { NULL,         NULL}
-};
-
-/*tex
-
-    Finally we hav earrived at the main initialiser that will be called as part
-    of \LUATEX's initializer.
-
-*/
-
-/*tex
-
-    Here we hook in the error handler.
-
-*/
-
-static void pdfelib_message(const char *message, void *alien)
-{
-    normal_warning("pdfe",message);
-}
-
-int luaopen_pdfe(lua_State * L)
-{
-    /*tex First the four userdata object get their metatables defined. */
-
-    luaL_newmetatable(L, PDFE_METATABLE_DICTIONARY);
-    luaL_openlib(L, NULL, pdfelib_m_dictionary, 0);
-
-    luaL_newmetatable(L, PDFE_METATABLE_ARRAY);
-    luaL_openlib(L, NULL, pdfelib_m_array, 0);
-
-    luaL_newmetatable(L, PDFE_METATABLE_STREAM);
-    luaL_openlib(L, NULL, pdfelib_m_stream, 0);
-
-    luaL_newmetatable(L, PDFE_METATABLE_REFERENCE);
-    luaL_openlib(L, NULL, pdfelib_m_reference, 0);
-
-    /*tex Then comes the main (document) metatable: */
-
-    luaL_newmetatable(L, PDFE_METATABLE);
-    luaL_openlib(L, NULL, pdfelib_m, 0);
-
-    /*tex Last the library opens up itself to the world. */
-
-    luaL_openlib(L, "pdfe", pdfelib, 0);
-
-    pplog_callback(pdfelib_message, stderr);
-
-    return 1;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/lua/lpdfscannerlib.c
+++ /dev/null
@@ -1,1110 +0,0 @@
-/* lpdfscannerlib.c
-
-   Copyright 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/>.
-
-*/
-
-/*tex
-
-    The scanner can read from a string or stream. Streams can be given directly as
-    |ppstream| object or as a |pparray| of streams. Here is an example of usage:
-
-    \starttyping
-    local operatortable = { }
-
-    operatortable.Do = function(scanner,info)
-        local resources = info.resources
-        if resources then
-            local val     = scanner:pop()
-            local name    = val[2]
-            local xobject = resources.XObject
-            print(info.space .. "Uses XObject " .. name)
-            local resources = xobject.Resources
-            if resources then
-                local newinfo =  {
-                    space     = info.space .. "  ",
-                    resources = resources,
-                }
-                pdfscanner.scan(entry, operatortable, newinfo)
-            end
-        end
-    end
-
-    local function Analyze(filename)
-        local doc = pdfe.open(filename)
-        if doc then
-            local pages = doc.Pages
-            for i=1,#pages do
-                local page = pages[i]
-                local info = {
-                  space     = "  " ,
-                  resources = page.Resources,
-                }
-                print("Page " .. i)
-                pdfscanner.scan(page.Contents,operatortable,info)
-                pdfscanner.scan(page.Contents(),operatortable,info)
-            end
-        end
-    end
-
-    Analyze("foo.pdf")
-    \stoptyping
-
-*/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <assert.h>
-#include <math.h>
-
-#include <lua.h>
-#include <lauxlib.h>
-#include <lualib.h>
-
-#include "luapplib/pplib.h"
-
-#include <lua/luatex-api.h>
-
-#define SCANNER "pdfscanner"
-
-#define MAXOPERANDS 1000
-
-typedef enum {
-    pdf_integer = 1,
-    pdf_real,
-    pdf_boolean,
-    pdf_name,
-    pdf_operator,
-    pdf_string,
-    pdf_startarray,
-    pdf_stoparray,
-    pdf_startdict,
-    pdf_stopdict,
-} pdf_token_type;
-
-typedef struct Token {
-    pdf_token_type type;
-    double value;
-    char *string;
-} Token;
-
-typedef struct ObjectList {
-    struct ObjectList *next;
-    ppstream *stream;
-} ObjectList;
-
-typedef struct scannerdata {
-    int _ininlineimage;
-    int _nextoperand;
-    Token **_operandstack;
-    ppstream *_stream;
-    ObjectList *_streams;
-    const char *buffer;
-    size_t position;
-    size_t size;
-    int uses_stream;
-} scannerdata;
-
-#define PDFE_METATABLE_ARRAY  "luatex.pdfe.array"
-#define PDFE_METATABLE_STREAM "luatex.pdfe.stream"
-
-typedef struct {
-    void *d;
-    /*tex reference to |PdfDocument|, or |NULL| */
-    void *pd;
-    /*tex counter to detect |PDFDoc| change */
-    unsigned long pc;
-} udstruct;
-
-static void clear_operand_stack(scannerdata * self, int from);
-static Token *_parseToken(scannerdata * self, int c);
-static void push_token(lua_State * L, scannerdata * self);
-
-static void *priv_xmalloc(size_t size)
-{
-    void *new_mem = (void *) malloc(size);
-    if (new_mem == NULL) {
-        luaL_error(Luas, "no room for <pdfscanned> stream");
-    }
-    return new_mem;
-}
-
-static void *priv_xrealloc(void *old_ptr, size_t size)
-{
-    void *new_mem = (void *) realloc(old_ptr, size);
-    if (new_mem == NULL) {
-        luaL_error(Luas, "no room for <pdfscanned> stream");
-    }
-    return new_mem;
-}
-
-#define xreallocarray(ptr,type,size) ((type*)priv_xrealloc(ptr,(size+1)*sizeof(type)))
-
-#define INITBUFSIZE 64
-
-#define define_buffer(a) \
-  char *a = (char *)priv_xmalloc (INITBUFSIZE); \
-  int a##_size = INITBUFSIZE; \
-  int a##index = 0; \
-  memset (a,0,INITBUFSIZE)
-
-#define check_overflow(a, wsize) do { \
-    if (wsize >= a##_size) { \
-        int nsize = a##_size + a##_size / 4; \
-        a = (char *) xreallocarray(a, char, (unsigned) nsize); \
-        memset (a+a##_size, 0, a##_size / 4); \
-        a##_size = nsize; \
-    } \
-} while (0)
-
-
-static scannerdata *scanner_push(lua_State * L)
-{
-    scannerdata *a = (scannerdata *) lua_newuserdata(L, sizeof(scannerdata));
-    luaL_getmetatable(L, SCANNER);
-    lua_setmetatable(L, -2);
-    return a;
-}
-
-static scannerdata *scanner_check(lua_State * L, int index)
-{
-    scannerdata *bar;
-    luaL_checktype(L, index, LUA_TUSERDATA);
-    bar = (scannerdata *) luaL_checkudata(L, index, SCANNER);
-    if (bar == NULL)
-        luaL_argerror(L, index, SCANNER " expected");
-    return bar;
-}
-
-static void free_token(Token * token)
-{
-    if (token->string) {
-        free(token->string);
-    }
-    free(token);
-}
-
-static void clear_operand_stack(scannerdata * self, int from)
-{
-    int i = self->_nextoperand - 1;
-    while (i >= from) {
-        if (self->_operandstack[i]) {
-            free_token(self->_operandstack[i]);
-            self->_operandstack[i] = NULL;
-        }
-        i--;
-    }
-    self->_nextoperand = from;
-}
-
-static void push_operand(scannerdata * self, Token * token)
-{
-    if (self->_nextoperand + 1 > MAXOPERANDS) {
-        fprintf(stderr, "out of operand stack space");
-        exit(1);
-    }
-    self->_operandstack[self->_nextoperand++] = token;
-}
-
-static Token *new_operand(pdf_token_type c)
-{
-    Token *token = (Token *) priv_xmalloc(sizeof(Token));
-    memset(token, 0, sizeof(Token));
-    token->type = c;
-    return token;
-}
-
-static void _nextStream(scannerdata * self)
-{
-    ObjectList *rover = NULL;
-    if (self->uses_stream && self->buffer != NULL) {
-        if (self->uses_stream) {
-            ppstream_done(self->_stream);
-        } else {
-            free(self->_stream);
-        }
-    }
-    rover = self->_streams;
-    self->_stream = rover->stream;
-    if (self->uses_stream) {
-        self->buffer = (const char *) ppstream_all(self->_stream, &self->size, 1);
-    }
-    self->position = 0;
-    self->_streams = rover->next;
-    free(rover);
-}
-
-static int streamGetChar(scannerdata * self)
-{
-    int i = EOF;
-    if (self->position < self->size) {
-        const char c = self->buffer[self->position];
-        ++self->position;
-        i = (int) c;
-    }
-    if (i < 0 && self->_streams) {
-        _nextStream(self);
-        i = streamGetChar(self);
-    }
-    return i;
-}
-
-static int streamLookChar(scannerdata * self)
-{
-    int i = EOF;
-    if (self->position < self->size) {
-        const char c = self->buffer[self->position];
-        /*not |++self->position;| */
-        i = (int) c;
-    }
-    if (i < 0 && self->_streams) {
-        _nextStream(self);
-        i = streamGetChar(self);
-    }
-    return i;
-}
-
-static void streamReset(scannerdata * self)
-{
-    if (self->uses_stream) {
-        self->buffer = (const char *) ppstream_all(self->_stream, &self->size, 1);
-    }
-    self->position = 0;
-}
-
-static void streamClose(scannerdata * self)
-{
-    if (self->uses_stream) {
-        ppstream_done(self->_stream);
-    } else {
-        free(self->_stream);
-    }
-    self->buffer = NULL;
-    self->_stream = NULL;
-}
-
-/*tex end of stream interface */
-
-static Token *_parseSpace(scannerdata * self)
-{
-    return _parseToken(self, streamGetChar(self));
-}
-
-static Token *_parseString(scannerdata * self, int c)
-{
-    int level;
-    Token *token = NULL;
-    define_buffer(found);
-    level = 1;
-    while (1) {
-        c = streamGetChar(self);
-        if (c == '(') {
-            level = level + 1;
-        } else if (c == ')') {
-            level = level - 1;
-            if (level < 1)
-            break;
-        } else if (c == '\\') {
-            int next = streamGetChar(self);
-            if (next == '(' || next == ')' || next == '\\') {
-                c = next;
-            } else if (next == '\n' || next == '\r') {
-                c = '\0';
-            } else if (next == 'n') {
-                c = '\n';
-            } else if (next == 'r') {
-                c = '\r';
-            } else if (next == 't') {
-                c = '\t';
-            } else if (next == 'b') {
-                c = '\b';
-            } else if (next == 'f') {
-                c = '\f';
-            } else if (next >= '0' && next <= '7') {
-                int next2;
-                next = next - '0';
-                next2 = streamLookChar(self);
-                if (next2 >= '0' && next2 <= '7') {
-                    int next3;
-                    next2 = streamGetChar(self);
-                    next2 = next2 - '0';
-                    next3 = streamLookChar(self);
-                    if (next3 >= '0' && next3 <= '7') {
-                    next3 = streamGetChar(self);
-                    next3 = next3 - '0';
-                    c = (next * 64 + next2 * 8 + next3);
-                    } else {
-                    c = (next * 8 + next2);
-                    }
-                } else {
-                    c = next;
-                }
-            } else {
-                c = next;
-            }
-        }
-        check_overflow(found, foundindex);
-        if (c >= 0) {
-            found[foundindex++] = c;
-        }
-    }
-    token = new_operand(pdf_string);
-    token->value = foundindex;
-    token->string = found;
-    return token;
-}
-
-static Token *_parseNumber(scannerdata * self, int c)
-{
-    double value = 0;
-    pdf_token_type type = pdf_integer;
-    int isfraction = 0;
-    int isnegative = 0;
-    int i = 0;
-    Token *token = NULL;
-    if (c == '-') {
-        isnegative = 1;
-        c = streamGetChar(self);
-    }
-    if (c == '.') {
-        type = pdf_real;
-        isfraction = 1;
-    } else {
-        value = c - '0';
-    }
-    c = streamLookChar(self);
-    if ((c >= '0' && c <= '9') || c == '.') {
-    c = streamGetChar(self);
-    while (1) {
-        if (c == '.') {
-            type = pdf_real;
-            isfraction = 1;
-        } else {
-            i = c - '0';
-            if (isfraction > 0) {
-                value = value + (i / (pow(10.0, isfraction)));
-                isfraction = isfraction + 1;
-            } else {
-                value = (value * 10) + i;
-            }
-        }
-        c = streamLookChar(self);
-        if (!((c >= '0' && c <= '9') || c == '.'))
-            break;
-        c = streamGetChar(self);
-    }
-    }
-    if (isnegative) {
-        value = -value;
-    }
-    token = new_operand(type);
-    token->value = value;
-    return token;
-}
-
-static Token *_parseName(scannerdata * self, int c)
-{
-    Token *token = NULL;
-    define_buffer(found);
-    c = streamGetChar(self);
-    while (1) {
-        check_overflow(found, foundindex);
-        found[foundindex++] = c;
-        c = streamLookChar(self);
-        if (c == ' ' || c == '\n' || c == '\r' || c == '\t' ||
-            c == '/' || c == '[' || c == '(' || c == '<')
-            break;
-        c = streamGetChar(self);
-    }
-    token = new_operand(pdf_name);
-    token->string = found;
-    token->value = strlen(found);
-    return token;
-}
-
-#define hexdigit(c)	\
-  (c>= '0' && c<= '9') ? (c - '0') : ((c>= 'A' && c<= 'F') ? (c - 'A' + 10) : (c - 'a' + 10))
-
-static Token *_parseHexstring(scannerdata * self, int c)
-{
-    int isodd = 1;
-    int hexval = 0;
-    Token *token = NULL;
-    define_buffer(found);
-    while (c != '>') {
-    if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
-        if (isodd == 1) {
-            int v = hexdigit(c);
-            hexval = 16 * v;
-        } else {
-            hexval += hexdigit(c);
-            check_overflow(found, foundindex);
-            found[foundindex++] = hexval;
-        }
-        isodd = (isodd == 1 ? 0 : 1);
-    }
-        c = streamGetChar(self);
-    }
-    token = new_operand(pdf_string);
-    token->value = foundindex;
-    token->string = found;
-    return token;
-}
-
-#define pdf_isspace(a) (a == '\0' || a == ' ' || a == '\n' || a == '\r' || a == '\t' || a == '\v')
-
-/*tex this is rather horrible */
-
-static Token *_parseInlineImage(scannerdata * self, int c)
-{
-    Token *token = NULL;
-    define_buffer(found);
-    if (c == ' ') {
-        /*tex first space can be ignored */
-        c = streamGetChar(self);
-    }
-    check_overflow(found, foundindex);
-    found[foundindex++] = c;
-    while (1) {
-        c = streamLookChar(self);
-        if (c == 'E'
-            && (found[foundindex - 1] == '\n'
-            || found[foundindex - 1] == '\r')) {
-            c = streamGetChar(self);
-            check_overflow(found, foundindex);
-            found[foundindex++] = c;
-            c = streamLookChar(self);
-            if (c == 'I') {
-                c = streamGetChar(self);
-                check_overflow(found, foundindex);
-                found[foundindex++] = c;
-                c = streamLookChar(self);
-                if (pdf_isspace(c)) {
-                    /*tex |I| */
-                    found[--foundindex] = '\0';
-                    /*tex |E| */
-                    found[--foundindex] = '\0';
-                    /*tex remove end-of-line before |EI| */
-                    if (found[foundindex - 1] == '\n') {
-                        found[--foundindex] = '\0';
-                    }
-                    if (found[foundindex - 1] == '\r') {
-                        found[--foundindex] = '\0';
-                    }
-                    break;
-                } else {
-                    c = streamGetChar(self);
-                    check_overflow(found, foundindex);
-                    found[foundindex++] = c;
-                }
-            } else {
-                c = streamGetChar(self);
-                check_overflow(found, foundindex);
-                found[foundindex++] = c;
-            }
-        } else {
-            c = streamGetChar(self);
-            check_overflow(found, foundindex);
-            found[foundindex++] = c;
-        }
-    }
-    token = new_operand(pdf_string);
-    token->value = foundindex;
-    token->string = found;
-    return token;
-}
-
-static Token *_parseOperator(scannerdata * self, int c)
-{
-    define_buffer(found);
-    while (1) {
-        check_overflow(found, foundindex);
-        found[foundindex++] = c;
-        c = streamLookChar(self);
-        if ((c < 0) || (c == ' ' || c == '\n' || c == '\r' || c == '\t' ||
-                c == '/' || c == '[' || c == '(' || c == '<'))
-            break;
-        c = streamGetChar(self);
-    }
-    /*tex |print| (found) */
-    if (strcmp(found, "ID") == 0) {
-        self->_ininlineimage = 1;
-    }
-    if (strcmp(found, "false") == 0) {
-        Token *token = new_operand(pdf_boolean);
-        token->value = 0;
-        free(found);
-        return token;
-    } else if (strcmp(found, "true") == 0) {
-        Token *token = new_operand(pdf_boolean);
-        token->value = 1.0;
-        free(found);
-        return token;
-    } else {
-        Token *token = new_operand(pdf_operator);
-        token->string = found;
-        return token;
-    }
-}
-
-static Token *_parseComment(scannerdata * self, int c)
-{
-    do {
-        c = streamGetChar(self);
-    } while (c != '\n' && c != '\r' && c != -1);
-    return _parseToken(self, streamGetChar(self));
-}
-
-static Token *_parseLt(scannerdata * self, int c)
-{
-    c = streamGetChar(self);
-    if (c == '<') {
-        return new_operand(pdf_startdict);
-    } else {
-        return _parseHexstring(self, c);
-    }
-}
-
-static Token *_parseGt(scannerdata * self, int c)
-{
-    c = streamGetChar(self);
-    if (c == '>') {
-        return new_operand(pdf_stopdict);
-    } else {
-        fprintf(stderr, "stray > in stream");
-        return NULL;
-    }
-}
-
-static Token *_parseError(int c)
-{
-    fprintf(stderr, "stray %c [%d] in stream", c, c);
-    return NULL;
-}
-
-static Token *_parseStartarray(void)
-{
-    return new_operand(pdf_startarray);
-}
-
-static Token *_parseStoparray(void)
-{
-    return new_operand(pdf_stoparray);
-}
-
-static Token *_parseToken(scannerdata * self, int c)
-{
-    if (self->_ininlineimage == 1) {
-        self->_ininlineimage = 2;
-        return _parseInlineImage(self, c);
-    } else if (self->_ininlineimage == 2) {
-        Token *token = NULL;
-        self->_ininlineimage = 0;
-        token = new_operand(pdf_operator);
-        token->string = strdup("EI");
-        return token;
-    }
-    if (c < 0)
-        return NULL;
-    switch (c) {
-        case '(':
-            return _parseString(self, c);
-            break;
-        case ')':
-            return _parseError(c);
-            break;
-        case '[':
-            return _parseStartarray();
-            break;
-        case ']':
-            return _parseStoparray();
-            break;
-        case '/':
-            return _parseName(self, c);
-            break;
-        case '<':
-            return _parseLt(self, c);
-            break;
-        case '>':
-            return _parseGt(self, c);
-            break;
-        case '%':
-            return _parseComment(self, c);
-            break;
-        case ' ':
-        case '\r':
-        case '\n':
-        case '\t':
-            return _parseSpace(self);
-            break;
-        case '0':
-        case '1':
-        case '2':
-        case '3':
-        case '4':
-        case '5':
-        case '6':
-        case '7':
-        case '8':
-        case '9':
-        case '-':
-        case '.':
-            return _parseNumber(self, c);
-            break;
-        default:
-            if (c <= 127) {
-                return _parseOperator(self, c);
-            } else {
-                return _parseError(c);
-            }
-    }
-}
-
-static int scanner_scan(lua_State * L)
-{
-    Token *token;
-    scannerdata *self;
-    if (lua_gettop(L) != 3) {
-        return 0;
-    }
-    luaL_checktype(L, 2, LUA_TTABLE);
-    luaL_checktype(L, 3, LUA_TTABLE);
-    self = scanner_push(L);
-    memset(self, 0, sizeof(scannerdata));
-    self->_operandstack = (Token **) priv_xmalloc(MAXOPERANDS * sizeof(Token));
-    memset(self->_operandstack, 0, (MAXOPERANDS * sizeof(Token)));
-    /*tex stack slot 4 = self */
-    self->uses_stream = 1;
-    if (lua_type(L, 1) == LUA_TSTRING) {
-      /*tex
-            We could make a temporary copy on the stack (or in the registry)
-            which saves memory.
-      */
-      char *buf = NULL;
-      const char *s = lua_tolstring(L, 1, &self->size);
-      if (s==NULL){
-	fprintf(stderr,"fatal: cannot convert the token to string.");
-	exit(1);
-      }
-      buf = priv_xmalloc(self->size+1);
-      buf[self->size]='\0';
-      self->uses_stream = 0;
-      memcpy(buf,s,self->size);
-      self->buffer = buf;
-    } else if (lua_type(L, 1) == LUA_TTABLE) {
-        udstruct *uin;
-        void *ud;
-        int i = 1;
-        while (1) {
-            lua_rawgeti(L, 1, i);
-            if (lua_type(L, -1) == LUA_TUSERDATA) {
-                ud = luaL_checkudata(L, -1, PDFE_METATABLE_STREAM);
-                if (ud != NULL) {
-                    ObjectList *rover = NULL;
-                    ObjectList *item = NULL;
-                    uin = (udstruct *) ud;
-                    rover = self->_streams;
-                    item = (ObjectList *) priv_xmalloc(sizeof(ObjectList));
-                    item->stream = ((ppstream *) uin->d);
-                    item->next = NULL;
-                    if (!rover) {
-                        rover = item;
-                        self->_streams = rover;
-                    } else {
-                        while (rover->next)
-                            rover = rover->next;
-                        rover->next = item;
-                    }
-                }
-            } else {
-                ObjectList *rover = self->_streams;
-                self->_stream = rover->stream;
-                self->_streams = rover->next;
-                free(rover);
-                lua_pop(L, 1);
-                break;
-            }
-            lua_pop(L, 1);
-            i++;
-        }
-    } else {
-        udstruct *uin;
-        void *ud;
-        luaL_checktype(L, 1, LUA_TUSERDATA);
-        ud = luaL_checkudata(L, 1, PDFE_METATABLE_STREAM);
-        if (ud != NULL) {
-            uin = (udstruct *) ud;
-            self->_stream = ((ppstream *) uin->d);
-        } else {
-            ud = luaL_checkudata(L, 1, PDFE_METATABLE_ARRAY);
-            if (ud != NULL) {
-            ObjectList *rover = NULL;
-            pparray *array = NULL;
-            int count;
-            int i;
-            uin = (udstruct *) ud;
-            array = (pparray *) uin->d;
-            count = array->size;
-            for (i = 0; i < count; i++) {
-                ppobj *obj = pparray_at(array, i);
-                if (obj->type == PPSTREAM) {
-                    ObjectList *rover = self->_streams;
-                    ObjectList *item =
-                        (ObjectList *)
-                        priv_xmalloc(sizeof(ObjectList));
-                    item->stream = obj->stream;
-                    item->next = NULL;
-                    if (!rover) {
-                        rover = item;
-                        self->_streams = rover;
-                    } else {
-                        while (rover->next)
-                        rover = rover->next;
-                        rover->next = item;
-                    }
-                }
-            }
-            rover = self->_streams;
-            self->_stream = rover->stream;
-            self->_streams = rover->next;
-            }
-        }
-    }
-    streamReset(self);
-    token = _parseToken(self, streamGetChar(self));
-    while (token) {
-        if (token->type == pdf_operator) {
-            lua_pushstring(L, token->string);
-            free_token(token);
-            /*tex fetch operator table */
-            lua_rawget(L, 2);
-            if (lua_isfunction(L, -1)) {
-                lua_pushvalue(L, 4);
-                lua_pushvalue(L, 3);
-                (void) lua_call(L, 2, 0);
-            } else {
-                /*tex nil */
-                lua_pop(L, 1);
-            }
-            clear_operand_stack(self, 0);
-        } else {
-            push_operand(self, token);
-        }
-        if (self->uses_stream) {
-            if (!self->_stream) {
-                break;
-            }
-        } else {
-            if (self->buffer == NULL) {
-                break;
-            }
-        }
-        token = _parseToken(self, streamGetChar(self));
-    }
-    /*tex wrap up */
-    if (self->_stream) {
-        streamClose(self);
-    }
-    clear_operand_stack(self, 0);
-    free(self->_operandstack);
-    return 0;
-}
-
-static int scanner_done(lua_State * L)
-{
-    int c;
-    scannerdata *self = scanner_check(L, 1);
-    while ((c = streamGetChar(self)) >= 0);
-    return 0;
-}
-
-/*tex here are the stack popping functions, and their helpers */
-
-static void operandstack_backup(scannerdata * self)
-{
-    int i = self->_nextoperand - 1;
-    int balance = 0;
-    int backupstart = 0;
-    int backupstop = self->_operandstack[i]->type;
-    if (backupstop == pdf_stopdict) {
-        backupstart = pdf_startdict;
-    } else if (backupstop == pdf_stoparray) {
-        backupstart = pdf_startarray;
-    } else {
-        return;
-    }
-    for (; i >= 0; i--) {
-        if (self->_operandstack[i]->type == backupstop) {
-            balance++;
-        } else if (self->_operandstack[i]->type == backupstart) {
-            balance--;
-        }
-        if (balance == 0) {
-            break;
-        }
-    }
-    self->_nextoperand = i + 1;
-}
-
-static void push_array(lua_State * L, scannerdata * self)
-{
-    /*tex nesting tracking */
-    int balance = 1;
-    /*tex \LUA\ array index */
-    int index = 1;
-    Token *token = self->_operandstack[self->_nextoperand++];
-    lua_newtable(L);
-    while (token) {
-    if (token->type == pdf_stoparray)
-        balance--;
-    if (token->type == pdf_startarray)
-        balance++;
-    if (!balance) {
-        break;
-    } else {
-        push_token(L, self);
-        lua_rawseti(L, -2, index++);
-    }
-    token = self->_operandstack[self->_nextoperand++];
-    }
-}
-
-
-static void push_dict(lua_State * L, scannerdata * self)
-{
-    /*tex nesting tracking */
-    int balance = 1;
-    /*tex toggle between \LUA\ value and \LUA\ key */
-    int needskey = 1;
-    Token *token = self->_operandstack[self->_nextoperand++];
-    lua_newtable(L);
-    while (token) {
-    if (token->type == pdf_stopdict)
-        balance--;
-    if (token->type == pdf_startdict)
-        balance++;
-    if (!balance) {
-        break;
-    } else if (needskey) {
-        lua_pushlstring(L, token->string, token->value);
-        needskey = 0;
-    } else {
-        push_token(L, self);
-        needskey = 1;
-        lua_rawset(L, -3);
-    }
-    token = self->_operandstack[self->_nextoperand++];
-    }
-}
-
-const char *typenames[pdf_stopdict + 1] = {
-    "unknown", "integer", "real", "boolean", "name", "operator",
-    "string", "array", "array", "dict", "dict"
-};
-
-static void push_token(lua_State * L, scannerdata * self)
-{
-    Token *token = self->_operandstack[self->_nextoperand - 1];
-    lua_createtable(L, 2, 0);
-    lua_pushstring(L, typenames[token->type]);
-    lua_rawseti(L, -2, 1);
-    if (token->type == pdf_string || token->type == pdf_name) {
-        lua_pushlstring(L, token->string, token->value);
-    } else if (token->type == pdf_real || token->type == pdf_integer) {
-        /*tex This is an integer or float. */
-        lua_pushnumber(L, token->value);
-    } else if (token->type == pdf_boolean) {
-        lua_pushboolean(L, (int) token->value);
-    } else if (token->type == pdf_startarray) {
-        push_array(L, self);
-    } else if (token->type == pdf_startdict) {
-        push_dict(L, self);
-    } else {
-        lua_pushnil(L);
-    }
-    lua_rawseti(L, -2, 2);
-}
-
-static int scanner_popsingular(lua_State * L, int token_type)
-{
-    Token *token = NULL;
-    /*tex this keeps track of how much of the operand stack needs deleting: */
-    int clear = 0;
-    scannerdata *self = scanner_check(L, 1);
-    if (self->_nextoperand == 0) {
-        return 0;
-    }
-    clear = self->_nextoperand - 1;
-    token = self->_operandstack[self->_nextoperand - 1];
-    if (token == NULL || (token->type != token_type)) {
-        return 0;
-    }
-    /*tex
-        The simple cases can be written out directly, but dicts and
-        arrays are better done via the recursive function.
-    */
-    if (token_type == pdf_stoparray || token_type == pdf_stopdict) {
-        operandstack_backup(self);
-        clear = self->_nextoperand - 1;
-        push_token(L, self);
-        lua_rawgeti(L, -1, 2);
-    } else if (token_type == pdf_real || token_type == pdf_integer) {
-        /*tex the number can be an integer or float */
-        lua_pushnumber(L, token->value);
-    } else if (token_type == pdf_boolean) {
-        lua_pushboolean(L, (int) token->value);
-    } else if (token_type == pdf_name || token_type == pdf_string) {
-        lua_pushlstring(L, token->string, token->value);
-    } else {
-        return 0;
-    }
-    clear_operand_stack(self, clear);
-    return 1;
-}
-
-static int scanner_popanything(lua_State * L)
-{
-    Token *token = NULL;
-    /*tex how much of the operand stack needs deleting: */
-    int clear = 0;
-    int token_type;
-    scannerdata *self = scanner_check(L, 1);
-    if (self->_nextoperand == 0) {
-        return 0;
-    }
-    clear = self->_nextoperand - 1;
-    token = self->_operandstack[self->_nextoperand - 1];
-    if (token == NULL) {
-        return 0;
-    }
-    token_type = token->type;
-    /*tex
-        The simple cases can be written out directly, but dicts and
-        arrays are better done via the recursive function.
-    */
-    if (token_type == pdf_stoparray || token_type == pdf_stopdict) {
-        operandstack_backup(self);
-        clear = self->_nextoperand - 1;
-        push_token(L, self);
-    } else {
-        push_token(L, self);
-    }
-    clear_operand_stack(self, clear);
-    return 1;
-}
-
-static int scanner_popnumber(lua_State * L)
-{
-    if (scanner_popsingular(L, pdf_real))
-        return 1;
-    if (scanner_popsingular(L, pdf_integer))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popboolean(lua_State * L)
-{
-    if (scanner_popsingular(L, pdf_boolean))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popstring(lua_State * L)
-{
-    if (scanner_popsingular(L, pdf_string))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popname(lua_State * L)
-{
-    if (scanner_popsingular(L, pdf_name))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_poparray(lua_State * L)
-{
-    if (scanner_popsingular(L, pdf_stoparray))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popdictionary(lua_State * L)
-{
-    if (scanner_popsingular(L, pdf_stopdict))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static int scanner_popany(lua_State * L)
-{
-    if (scanner_popanything(L))
-        return 1;
-    lua_pushnil(L);
-    return 1;
-}
-
-static const luaL_Reg scannerlib_meta[] = {
-    {0, 0}
-};
-
-static const struct luaL_Reg scannerlib_m[] = {
-    { "done",          scanner_done },
-    { "pop",           scanner_popany },
-    { "popnumber",     scanner_popnumber },
-    { "popname",       scanner_popname },
-    { "popstring",     scanner_popstring },
-    { "poparray",      scanner_poparray },
-    { "popdictionary", scanner_popdictionary },
-    { "popboolean",    scanner_popboolean },
-    /*tex For old times sake: */
-    { "popNumber",     scanner_popnumber },
-    { "popName",       scanner_popname },
-    { "popString",     scanner_popstring },
-    { "popArray",      scanner_poparray },
-    { "popDict",       scanner_popdictionary },
-    { "popBool",       scanner_popboolean },
-    /*tex Sentinel: */
-    { NULL,            NULL }
-};
-
-static const luaL_Reg scannerlib[] = {
-    { "scan", scanner_scan },
-    /*tex Sentinel: */
-    { NULL,   NULL }
-};
-
-LUALIB_API int luaopen_pdfscanner(lua_State * L)
-{
-    luaL_newmetatable(L, SCANNER);
-    luaL_openlib(L, 0, scannerlib_meta, 0);
-    lua_pushvalue(L, -1);
-    lua_setfield(L, -2, "__index");
-    luaL_openlib(L, NULL, scannerlib_m, 0);
-    luaL_openlib(L, "pdfscanner", scannerlib, 0);
-    return 1;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lua/luainit.w
@@ -0,0 +1,1166 @@
+% luainit.w
+%
+% Copyright 2006-2018 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 (see man 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 = xstrdup(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 2018 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 Hogsberg (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 = xstrdup(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 && !lua_only) {
+	  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 %s: exit now.\n",old_locale);
+	  exit(1);
+	}
+        xfree(old_locale);
+    } 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;
+        int tex_table_id = hide_lua_table(Luas, "tex");
+        int token_table_id = hide_lua_table(Luas, "token");
+        int node_table_id = hide_lua_table(Luas, "node");
+        int pdf_table_id = hide_lua_table(Luas, "pdf");
+        if (lua_only) {
+            /* hide the 'tex' and 'pdf' table */
+            if (load_luatex_core_lua(Luas)) {
+                fprintf(stderr, "Error in execution of luatex-core.lua .\n");
+            }
+            if (luaL_loadfile(Luas, startup_filename)) {
+                fprintf(stdout, "%s\n", lua_tostring(Luas, -1));
+                exit(1);
+            }
+            init_tex_table(Luas); /* needed ? */
+            if (lua_pcall(Luas, 0, 0, 0)) {
+                fprintf(stdout, "%s\n", lua_tostring(Luas, -1));
+                lua_traceback(Luas);
+             /* lua_close(Luas); */
+                exit(1);
+            } else {
+                if (given_file)
+                    free(given_file);
+             /* lua_close(Luas); */
+                exit(0);
+            }
+        }
+        /* a normal tex run */
+        init_tex_table(Luas);
+        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);
+        if (luaL_loadfile(Luas, startup_filename)) {
+            fprintf(stdout, "%s\n", lua_tostring(Luas, -1));
+            exit(1);
+        }
+        if (lua_pcall(Luas, 0, 0, 0)) {
+            fprintf(stdout, "%s\n", lua_tostring(Luas, -1));
+            lua_traceback(Luas);
+            exit(1);
+        }
+        if (!input_name) {
+            get_lua_string("texconfig", "jobname", &input_name);
+        }
+        if (!dump_name) {
+            get_lua_string("texconfig", "formatname", &dump_name);
+        }
+        /* |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();
+                }
+            }
+        }
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/lua/luainit.c
+++ /dev/null
@@ -1,1172 +0,0 @@
-/*
-
-luainit.w
-
-Copyright 2006-2018 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/>.
-
-*/
-
-#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);
-
-/*tex internalized strings: see luatex-api.h */
-
-set_make_keys;
-
-/*tex
-
-This file is getting a bit messy, but it is not simple to fix unilaterally. 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|
-
-*/
-
-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 (see man 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
-};
-
-/*tex
-
-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.
-
-*/
-
-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",
-*/
-
-/*tex
-
-The return value will be the directory of the executable, e.g.: \.{c:/TeX/bin}
-
-*/
-
-static char *ex_selfdir(char *argv0)
-{
-#if defined(WIN32)
-#if defined(__MINGW32__)
-    char path[PATH_MAX], *fp;
-    /*tex 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);
-    /*tex 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;
-    /*tex 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
-}
-
-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;
-}
-
-int kpse_init = -1;
-
-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;
-
-/*tex
-
-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|.
-
-*/
-
-#define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a)
-
-/*tex
-    Nota Bene: we still intercept some options that other engines handle
-    so that existing scripted usage will not fail.
-
-    SunOS cc can't initialize automatic structs, so make this static.
-*/
-
-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},
-    /*tex 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},
-    /*tex Synchronization: just like ``interaction'' above */
-    {"synctex", 1, 0, 0},
-    {0, 0, 0, 0}
-};
-
-int lua_numeric_field_by_index(lua_State * L, int name_index, int dflt)
-{
-    register int i = dflt;
-    /*tex fetch the stringptr */
-    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);
-    lua_rawget(L, -2);
-    if (lua_type(L, -1) == LUA_TNUMBER) {
-        i = lua_roundnumber(L, -1);
-    }
-    lua_pop(L, 1);
-    return i;
-}
-
-unsigned int lua_unsigned_numeric_field_by_index(lua_State * L, int name_index, int dflt)
-{
-    register unsigned int i = dflt;
-    /*tex fetch the stringptr */
-    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);
-    lua_rawget(L, -2);
-    if (lua_type(L, -1) == LUA_TNUMBER) {
-        i = lua_uroundnumber(L, -1);
-    }
-    lua_pop(L, 1);
-    return i;
-}
-
-static int recorderoption = 0;
-
-static void parse_options(int ac, char **av)
-{
-#ifdef WIN32
-    /*tex We save |argc| and |argv|. */
-    int sargc = argc;
-    char **sargv = argv;
-#endif
-     /*tex The `getopt' return code. */
-    int g;
-    int option_index;
-    char *firstfile = NULL;
-    /*tex Dont whine. */
-    opterr = 0;
-#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) {
-            /*tex End of arguments, exit the loop. */
-            break;
-        }
-        if (g == '?')  {
-            /*tex Unknown option. */
-            if (!luainit)
-                fprintf(stderr,"%s: unrecognized option '%s'\n", argv[0], argv[optind-1]);
-            continue;
-        }
-        /* We have no short option names. */
-        assert(g == 0);
-        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")) {
-            /*tex 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 2018 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, Luigi Scarso, Hans Hagen and friends\n"
-                 "pplib     : Paweł Jackowski\n"
-                 "fontforge : George Williams (partial)\n"
-                 "luajit    : Mike Pall (used in LuajitTeX)\n");
-            /* *INDENT-ON* */
-            puts(versions);
-            uexit(0);
-        }
-    }
-    /*tex attempt to find |input_name| and |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
-    }
-    /*tex |--safer| implies |--nosocket| */
-    if (safer_option)
-        nosocket_option = 1;
-    /*tex Finalize the input filename. */
-    if (input_name != NULL) {
-        argv[optind] = normalize_quotes(input_name, "argument");
-    }
-}
-
-/*tex
-    Test for readability.
-*/
-
-#define is_readable(a) (stat(a,&finfo)==0) \
-    && S_ISREG(finfo.st_mode) \
-    && (f=fopen(a,"r")) != NULL && !fclose(f)
-
-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;
-}
-
-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);
-    /*tex set up 'restrictedshell' */
-    init_shell_escape();
-    init_start_time();
-    program_name_set = 1 ;
-    if (recorderoption) {
-        recorder_enabled = 1;
-    }
-}
-
-static void fix_dumpname(void)
-{
-    int dist;
-    if (dump_name) {
-        /*tex 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 {
-        /*tex For |dump_name| to be NULL is a bug. */
-        if (!ini_version) {
-          fprintf(stdout, "no format given, quitting\n");
-          exit(1);
-        }
-    }
-}
-
-/*tex
-    Auxiliary function for kpse search.
-*/
-
-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;
-    /*tex Lua convention */
-    altname = luaL_gsub(L, name, ".", "/");
-    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;
-}
-
-/*tex
-
-    Here comes 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 .
-
-*/
-
-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) {
-        /*tex library not found in this path */
-        return 1;
-    }
-    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));
-    }
-    /*tex library loaded successfully */
-    return 1;
-}
-
-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) {
-        /*tex library not found in this path */
-        lua_pushliteral(L, "\n\t[C searcher disabled in safer mode]");
-        return 1;
-    }
-    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) {
-            /*tex library not found in this path */
-            return 1;
-        }
-        extensionless = strdup(filename);
-        if (!extensionless) {
-            /*tex allocation failure */
-            return 1;
-        }
-        /*tex 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) {
-            /*tex this would be exceedingly weird */
-            return 1;
-        }
-        *p = '\0';
-        prefix = strdup(extensionless);
-        if (!prefix) {
-            /*tex allocation failure */
-            return 1;
-        }
-        postfix = strdup(p+strlen(name));
-        if (!postfix) {
-            /*tex allocation failure */
-            return 1;
-        }
-        total = malloc(strlen(prefix)+strlen(postfix)+2);
-        if (!total) return 1;  /* allocation failure */
-        snprintf(total,strlen(prefix)+strlen(postfix)+2, "%s?%s", prefix, postfix);
-        /*tex save package.path */
-        lua_getglobal(L,"package");
-        lua_getfield(L,-1,"cpath");
-        path_saved = lua_tostring(L,-1);
-        lua_pop(L,1);
-        /*tex set package.path = "?" */
-        lua_pushstring(L,total);
-        lua_setfield(L,-2,"cpath");
-        /*tex pop ``package'' */
-        lua_pop(L,1);
-        /*tex run function */
-        lua_rawgeti(L, LUA_REGISTRYINDEX, clua_loader_function);
-        lua_pushstring(L, name);
-        lua_call(L, 1, 1);
-        /*tex restore package.path */
-        lua_getglobal(L,"package");
-        lua_pushstring(L,path_saved);
-        lua_setfield(L,-2,"cpath");
-        /*tex pop ``package'' */
-        lua_pop(L,1);
-        free(extensionless);
-        free(total);
-        free(temp_name);
-        return 1;
-    }
-}
-
-/*tex
-
-    Setting up the new search functions. This replaces package.searchers[2] and
-    package.searchers[3] with the functions defined above.
-
-*/
-
-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
-    /*tex package.searchers[2] */
-    lua_rawgeti(L, -1, 2);
-    lua_loader_function = luaL_ref(L, LUA_REGISTRYINDEX);
-    lua_pushcfunction(L, luatex_kpse_lua_find);
-    /*tex replace the normal lua loader */
-    lua_rawseti(L, -2, 2);
-    /*tex package.searchers[3] */
-    lua_rawgeti(L, -1, 3);
-    clua_loader_function = luaL_ref(L, LUA_REGISTRYINDEX);
-    lua_pushcfunction(L, luatex_kpse_clua_find);
-    /*tex replace the normal lua lib loader */
-    lua_rawseti(L, -2, 3);
-    /*tex pop the array and table */
-    lua_pop(L, 2);
-}
-
-/*tex
-
-    Helper variables for the safe keeping of table ids.
-
-*/
-
-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_normal [DIR_TEXT_SIZE];
-int l_dir_text_index_cancel [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 offset, int max)
-{
-    int i;
-    lua_newtable(L);
-    for (i = 0; i < max; i++) {
-        lua_pushinteger(L,i+offset);
-        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;
-
-/* Why do we add script stuff to this weird incomplete. Let's go more minimal. */
-
-/*
-    #define EXE_SUFFIXES ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.ws;.tcl;.py;.pyw"
-*/
-
-#define EXE_SUFFIXES ".com;.exe;.bat;.cmd"
-
-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");
-    /*tex strlwr() exists also in MingW */
-    if (v)
-        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
-
-void lua_initialize(int ac, char **av)
-{
-    char *given_file = NULL;
-    char *banner;
-    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;
-    /*tex 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]);
-    /*tex 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
-    /*tex Must be initialized before options are parsed.  */
-    interactionoption = 4;
-    dump_name = NULL;
-    /*tex
-        In the next option 0 means ``disable Synchronize TeXnology''. The
-        |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;
-    /*tex parse commandline */
-    parse_options(ac, av);
-    if (lua_only) {
-        /*tex Shell has no restrictions. */
-        shellenabledp = true;
-        restrictedshell = false;
-        safer_option = 0;
-    }
-    /*tex
-        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 = xstrdup(setlocale (LC_ALL, NULL));
-    lc_ctype = NULL;
-    lc_collate = NULL;
-    lc_numeric = NULL;
-    if (old_locale) {
-        /*tex
-            If |setlocale| fails here, then the state could be compromised, and
-            we exit.
-        */
-        env_locale = setlocale (LC_ALL, "");
-        if (!env_locale && !lua_only) {
-            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);
-        }
-        /*tex
-            Return 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 %s: exit now.\n",old_locale);
-          exit(1);
-        }
-        xfree(old_locale);
-    } else {
-       fprintf(stderr,"Unable to store environment locale.\n");
-    }
-    /*tex make sure that the locale is 'sane' (for lua) */
-    putenv(LC_CTYPE_C);
-    putenv(LC_COLLATE_C);
-    putenv(LC_NUMERIC_C);
-    /*tex this is sometimes needed */
-    putenv(engine_luatex);
-    luainterpreter();
-    /*tex init internalized strings */
-    set_init_keys;
-    lua_pushstring(Luas,"lua.functions");
-    lua_newtable(Luas);
-    lua_settable(Luas,LUA_REGISTRYINDEX);
-    /*tex 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;
-    l_set_node_data();
-    l_set_whatsit_data();
-    l_set_token_data();
-    set_l_img_keys_index;
-    set_l_img_pageboxes_index;
-    /*tex collect arguments */
-    prepare_cmdline(Luas, argv, argc, lua_offset);
-    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");
-    }
-    /*tex now run the file */
-    if (startup_filename != NULL) {
-        char *v1;
-        int tex_table_id = hide_lua_table(Luas, "tex");
-        int token_table_id = hide_lua_table(Luas, "token");
-        int node_table_id = hide_lua_table(Luas, "node");
-        int pdf_table_id = hide_lua_table(Luas, "pdf");
-        if (lua_only) {
-            /*tex hide the 'tex' and 'pdf' table */
-            if (load_luatex_core_lua(Luas)) {
-                fprintf(stderr, "Error in execution of luatex-core.lua .\n");
-            }
-            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);
-             /*tex lua_close(Luas); */
-                exit(1);
-            } else {
-                if (given_file)
-                    free(given_file);
-                /*tex lua_close(Luas); */
-                exit(0);
-            }
-        }
-        /*tex a normal tex run */
-        init_tex_table(Luas);
-        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);
-        if (luaL_loadfile(Luas, startup_filename)) {
-            fprintf(stdout, "%s\n", lua_tostring(Luas, -1));
-            exit(1);
-        }
-        if (lua_pcall(Luas, 0, 0, 0)) {
-            fprintf(stdout, "%s\n", lua_tostring(Luas, -1));
-            lua_traceback(Luas);
-            exit(1);
-        }
-        if (!input_name) {
-            get_lua_string("texconfig", "jobname", &input_name);
-        }
-        if (!dump_name) {
-            get_lua_string("texconfig", "formatname", &dump_name);
-        }
-        kpse_init = -1;
-        get_lua_boolean("texconfig", "kpse_init", &kpse_init);
-
-        if (kpse_init != 0) {
-            /*tex re-enable loading of texmf.cnf values, see luatex.ch */
-            luainit = 0;
-            init_kpse();
-            kpse_init = 1;
-        }
-        /*tex |prohibit_file_trace| (boolean) */
-        tracefilenames = 1;
-        get_lua_boolean("texconfig", "trace_file_names", &tracefilenames);
-        /*tex |file_line_error| */
-        filelineerrorstylep = false;
-        get_lua_boolean("texconfig", "file_line_error", &filelineerrorstylep);
-        /*tex |halt_on_error| */
-        haltonerrorp = false;
-        get_lua_boolean("texconfig", "halt_on_error", &haltonerrorp);
-        /*tex |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);
-        }
-        /*tex 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) {
-            /*tex
-                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();
-    }
-    /*tex 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");
-    }
-}
-
-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) {
-                    /*tex
-                        We can't be more precise hereas it's called before \TEX\
-                        initialization happens.
-                    */
-                    fprintf(stderr, "This went wrong: %s\n", lua_tostring(Luas, -1));
-                    error();
-                }
-            }
-        }
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lua/luanode.w
@@ -0,0 +1,451 @@
+% luanode.w
+%
+% Copyright 2006-2008 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/>.
+
+/* hh-ls: we make sure that lua never sees prev of head but also that when
+nodes are removed or inserted, temp nodes don't interfere */
+
+@ @c
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+@ @c
+void lua_node_filter_s(int filterid, int extrainfo)
+{
+    int callback_id = callback_defined(filterid);
+    int s_top = lua_gettop(Luas);
+    if (callback_id <= 0) {
+        lua_settop(Luas, s_top);
+        return;
+    }
+    if (!get_callback(Luas, callback_id)) {
+        lua_settop(Luas, s_top);
+        return;
+    }
+    lua_push_string_by_index(Luas,extrainfo); /* arg 1 */
+    if (lua_pcall(Luas, 1, 0, 0) != 0) {
+        fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1));
+        lua_settop(Luas, s_top);
+        error();
+        return;
+    }
+    lua_settop(Luas, s_top);
+    return;
+}
+
+@ @c
+void lua_node_filter(int filterid, int extrainfo, halfword head_node, halfword * tail_node)
+{
+    halfword start_node, start_done, last_node;
+    int s_top = lua_gettop(Luas);
+    int callback_id = callback_defined(filterid);
+    if (head_node == null || callback_id <= 0) {
+        lua_settop(Luas, s_top);
+        return;
+    }
+    /* we start after head */
+    start_node = vlink(head_node);
+    if (start_node == null || !get_callback(Luas, callback_id)) {
+        lua_settop(Luas, s_top);
+        return;
+    }
+    /* we make sure we have no prev */
+    alink(start_node) = null ;
+    /* the action */
+    nodelist_to_lua(Luas, start_node);
+    lua_push_group_code(Luas,extrainfo);
+    if (lua_pcall(Luas, 2, 1, 0) != 0) {
+        fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1));
+        lua_settop(Luas, s_top);
+        error();
+        return;
+    }
+    /* the result */
+    if (lua_isboolean(Luas, -1)) {
+        if (lua_toboolean(Luas, -1) != 1) {
+            /* discard */
+            flush_node_list(start_node);
+            vlink(head_node) = null;
+        } else {
+            /* keep */
+        }
+    } else {
+        /* append to old head */
+        start_done = nodelist_from_lua(Luas);
+        try_couple_nodes(head_node,start_done);
+    }
+    /* redundant as we set top anyway */
+    lua_pop(Luas, 2);
+    /* find tail in order to update tail */
+    start_node = vlink(head_node);
+    if (start_node != null) {
+        /* maybe just always slide (harmless and fast) */
+        if (fix_node_lists) {
+            /* slides and returns last node */
+            *tail_node = fix_node_list(start_node);
+        } else {
+            last_node = vlink(start_node);
+            while (last_node != null) {
+                start_node = last_node;
+                last_node = vlink(start_node);
+            }
+            /* we're at the end now */
+            *tail_node = start_node;
+        }
+    } else {
+        /* we're already at the end */
+        *tail_node = head_node;
+    }
+    /* clean up */
+    lua_settop(Luas, s_top);
+    return;
+}
+
+@ @c
+int lua_linebreak_callback(int is_broken, halfword head_node, halfword * new_head)
+{
+    int a;
+    register halfword *p;
+    int ret = 0;                /* failure */
+    int s_top = lua_gettop(Luas);
+    int callback_id = callback_defined(linebreak_filter_callback);
+    if (head_node == null || vlink(head_node) == null || callback_id <= 0) {
+        lua_settop(Luas, s_top);
+        return ret;
+    }
+    if (!get_callback(Luas, callback_id)) {
+       lua_settop(Luas, s_top);
+        return ret;
+    }
+    alink(vlink(head_node)) = null ; /* hh-ls */
+    nodelist_to_lua(Luas, vlink(head_node));       /* arg 1 */
+    lua_pushboolean(Luas, is_broken);      /* arg 2 */
+    if (lua_pcall(Luas, 2, 1, 0) != 0) {   /* no arg, 1 result */
+        fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1));
+        lua_settop(Luas, s_top);
+        error();
+        return ret;
+    }
+    p = lua_touserdata(Luas, -1);
+    if (p != NULL) {
+        a = nodelist_from_lua(Luas);
+        try_couple_nodes(*new_head,a);
+        ret = 1;
+    }
+    lua_settop(Luas, s_top);
+    return ret;
+}
+
+@ @c
+int lua_appendtovlist_callback(halfword box, int location, halfword prev_depth, boolean is_mirrored, halfword * result, int * next_depth, boolean * prev_set)
+{
+    register halfword *p;
+    int s_top = lua_gettop(Luas);
+    int callback_id = callback_defined(append_to_vlist_filter_callback);
+    if (box == null || callback_id <= 0) {
+        lua_settop(Luas, s_top);
+        return 0;
+    }
+    if (!get_callback(Luas, callback_id)) {
+        lua_settop(Luas, s_top);
+        return 0;
+    }
+    nodelist_to_lua(Luas, box);
+    lua_push_string_by_index(Luas,location);
+    lua_pushinteger(Luas, (int) prev_depth);
+    lua_pushboolean(Luas, is_mirrored);
+    if (lua_pcall(Luas, 4, 2, 0) != 0) {
+        fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1));
+        lua_settop(Luas, s_top);
+        error();
+        return 0;
+    }
+    if (lua_type(Luas,-1) == LUA_TNUMBER) {
+        *next_depth = lua_roundnumber(Luas,-1);
+        *prev_set = true;
+        if (lua_type(Luas, -2) != LUA_TNIL) {
+            p = check_isnode(Luas, -2);
+            *result = *p;
+        }
+    } else if (lua_type(Luas, -1) != LUA_TNIL) {
+        p = check_isnode(Luas, -1);
+        *result = *p;
+    }
+    lua_settop(Luas, s_top);
+    return 1;
+}
+
+@ @c
+halfword lua_hpack_filter(halfword head_node, scaled size, int pack_type, int extrainfo, int pack_direction, halfword attr)
+{
+    halfword ret;
+    int s_top = lua_gettop(Luas);
+    int callback_id = callback_defined(hpack_filter_callback);
+    if (head_node == null || callback_id <= 0) {
+        lua_settop(Luas, s_top);
+        return head_node;
+    }
+    if (!get_callback(Luas, callback_id)) {
+        lua_settop(Luas, s_top);
+        return head_node;
+    }
+    alink(head_node) = null ; /* hh-ls */
+    nodelist_to_lua(Luas, head_node);
+    lua_push_group_code(Luas,extrainfo);
+    lua_pushinteger(Luas, size);
+    lua_push_pack_type(Luas, pack_type);
+    if (pack_direction >= 0) {
+        lua_push_dir_par(Luas, pack_direction);
+    } else {
+        lua_pushnil(Luas);
+    }
+    if (attr != null) {
+        nodelist_to_lua(Luas, attr);
+    } else {
+        lua_pushnil(Luas);
+    }
+    if (lua_pcall(Luas, 6, 1, 0) != 0) {
+        fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1));
+        lua_settop(Luas, s_top);
+        error();
+        return head_node;
+    }
+    ret = head_node;
+    if (lua_isboolean(Luas, -1)) {
+        if (lua_toboolean(Luas, -1) != 1) {
+            flush_node_list(head_node);
+            ret = null;
+        }
+    } else {
+        ret = nodelist_from_lua(Luas);
+    }
+    lua_settop(Luas, s_top);
+#if 0
+    lua_gc(Luas,LUA_GCSTEP, LUA_GC_STEP_SIZE);
+#endif
+    if (fix_node_lists)
+        fix_node_list(ret);
+    return ret;
+}
+
+@ @c
+halfword lua_vpack_filter(halfword head_node, scaled size, int pack_type, scaled maxd,
+                 int extrainfo, int pack_direction, halfword attr)
+{
+    halfword ret;
+    int callback_id;
+    int s_top = lua_gettop(Luas);
+    if (head_node == null) {
+        lua_settop(Luas, s_top);
+        return head_node;
+    }
+    if  (extrainfo == 8)  { /* output */
+        callback_id = callback_defined(pre_output_filter_callback);
+    } else {
+        callback_id = callback_defined(vpack_filter_callback);
+    }
+    if (callback_id <= 0) {
+        lua_settop(Luas, s_top);
+        return head_node;
+    }
+    if (!get_callback(Luas, callback_id)) {
+        lua_settop(Luas, s_top);
+        return head_node;
+    }
+    alink(head_node) = null ; /* hh-ls */
+    nodelist_to_lua(Luas, head_node);
+    lua_push_group_code(Luas, extrainfo);
+    lua_pushinteger(Luas, size);
+    lua_push_pack_type(Luas, pack_type);
+    lua_pushinteger(Luas, maxd);
+    if (pack_direction >= 0) {
+         lua_push_dir_par(Luas, pack_direction);
+    } else {
+        lua_pushnil(Luas);
+    }
+    if (attr != null) {
+        nodelist_to_lua(Luas, attr);
+    } else {
+        lua_pushnil(Luas);
+    }
+    if (lua_pcall(Luas, 7, 1, 0) != 0) {
+        fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1));
+        lua_settop(Luas, s_top);
+        error();
+        return head_node;
+    }
+    ret = head_node;
+    if (lua_isboolean(Luas, -1)) {
+        if (lua_toboolean(Luas, -1) != 1) {
+            flush_node_list(head_node);
+            ret = null;
+        }
+    } else {
+        ret = nodelist_from_lua(Luas);
+    }
+    lua_settop(Luas, s_top);
+#if 0
+    lua_gc(Luas,LUA_GCSTEP, LUA_GC_STEP_SIZE);
+#endif
+    if (fix_node_lists)
+        fix_node_list(ret);
+    return ret;
+}
+
+@ This is a quick hack to fix etex's \.{\\lastnodetype} now that
+  there are many more visible node types. TODO: check the
+  eTeX manual for the expected return values.
+
+@c
+int visible_last_node_type(int n)
+{
+    int i = type(n);
+    if (i != glyph_node) {
+        return get_etex_code(i);
+    } else if (is_ligature(n)) {
+        return 7; /* old ligature value */
+    } else {
+        return 0; /* old character value */
+    }
+}
+
+@ @c
+void lua_pdf_literal(PDF pdf, int i)
+{
+    const char *s = NULL;
+    size_t l = 0;
+    lua_rawgeti(Luas, LUA_REGISTRYINDEX, i);
+    s = lua_tolstring(Luas, -1, &l);
+    pdf_out_block(pdf, s, l);
+    pdf_out(pdf, 10);           /* |pdf_print_nl| */
+    lua_pop(Luas, 1);
+}
+
+@ @c
+void copy_pdf_literal(pointer r, pointer p)
+{
+    pdf_literal_type(r) = pdf_literal_type(p);
+    pdf_literal_mode(r) = pdf_literal_mode(p);
+    if (pdf_literal_type(p) == normal) {
+        pdf_literal_data(r) = pdf_literal_data(p);
+        add_token_ref(pdf_literal_data(p));
+    } else {
+        lua_rawgeti(Luas, LUA_REGISTRYINDEX, pdf_literal_data(p));
+        pdf_literal_data(r) = luaL_ref(Luas, LUA_REGISTRYINDEX);
+    }
+}
+
+@ @c
+void copy_late_lua(pointer r, pointer p)
+{
+    late_lua_type(r) = late_lua_type(p);
+    if (late_lua_name(p) > 0)
+        add_token_ref(late_lua_name(p));
+    if (late_lua_type(p) == normal) {
+        late_lua_data(r) = late_lua_data(p);
+        add_token_ref(late_lua_data(p));
+    } else {
+        lua_rawgeti(Luas, LUA_REGISTRYINDEX, late_lua_data(p));
+        late_lua_data(r) = luaL_ref(Luas, LUA_REGISTRYINDEX);
+    }
+}
+
+@ @c
+void copy_user_lua(pointer r, pointer p)
+{
+    if (user_node_value(p) != 0) {
+        lua_rawgeti(Luas, LUA_REGISTRYINDEX, user_node_value(p));
+        user_node_value(r) = luaL_ref(Luas, LUA_REGISTRYINDEX);
+    }
+}
+
+@ @c
+void free_pdf_literal(pointer p)
+{
+    if (pdf_literal_type(p) == normal) {
+        delete_token_ref(pdf_literal_data(p));
+    } else {
+        luaL_unref(Luas, LUA_REGISTRYINDEX, pdf_literal_data(p));
+    }
+}
+
+void free_late_lua(pointer p)
+{
+    if (late_lua_name(p) > 0)
+        delete_token_ref(late_lua_name(p));
+    if (late_lua_type(p) == normal) {
+        delete_token_ref(late_lua_data(p));
+    } else {
+        luaL_unref(Luas, LUA_REGISTRYINDEX, late_lua_data(p));
+    }
+}
+
+@ @c
+void free_user_lua(pointer p)
+{
+    if (user_node_value(p) != 0) {
+        luaL_unref(Luas, LUA_REGISTRYINDEX, user_node_value(p));
+    }
+}
+
+@ @c
+void show_pdf_literal(pointer p)
+{
+    tprint_esc("pdfliteral");
+    switch (pdf_literal_mode(p)) {
+        case set_origin:
+            tprint(" origin");
+            break;
+        case direct_page:
+            tprint(" page");
+            break;
+        case direct_always:
+            tprint(" direct");
+            break;
+        case direct_raw:
+            tprint(" raw");
+            break;
+        default:
+            confusion("literal2");
+            break;
+    }
+    if (pdf_literal_type(p) == normal) {
+        print_mark(pdf_literal_data(p));
+    } else {
+        lua_rawgeti(Luas, LUA_REGISTRYINDEX, pdf_literal_data(p));
+        tprint("\"");
+        tprint(lua_tostring(Luas, -1));
+        tprint("\"");
+        lua_pop(Luas, 1);
+    }
+}
+
+@ @c
+void show_late_lua(pointer p)
+{
+    tprint_esc("latelua");
+    print_int(late_lua_reg(p));
+    if (late_lua_type(p) == normal) {
+        print_mark(late_lua_data(p));
+    } else {
+        tprint(" <function ");
+        print_int(late_lua_data(p));
+        tprint(">");
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/lua/luanode.c
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
-
-luanode.w
-
-Copyright 2006-2008 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-void lua_node_filter_s(int filterid, int extrainfo)
-{
-    int i;
-    int callback_id = callback_defined(filterid);
-    int s_top = lua_gettop(Luas);
-    if (callback_id <= 0) {
-        lua_settop(Luas, s_top);
-        return;
-    }
-    if (!get_callback(Luas, callback_id)) {
-        lua_settop(Luas, s_top);
-        return;
-    }
-    lua_push_string_by_index(Luas,extrainfo);
-    if ((i=lua_pcall(Luas, 1, 0, 0)) != 0) {
-        formatted_warning("node filter","error: %s", lua_tostring(Luas, -1));
-        lua_settop(Luas, s_top);
-        luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        return;
-    }
-    lua_settop(Luas, s_top);
-    return;
-}
-
-void lua_node_filter(int filterid, int extrainfo, halfword head_node, halfword * tail_node)
-{
-    int i;
-    halfword start_node, start_done, last_node;
-    int s_top = lua_gettop(Luas);
-    int callback_id = callback_defined(filterid);
-    if (head_node == null || callback_id <= 0) {
-        lua_settop(Luas, s_top);
-        return;
-    }
-    /*tex We start after head. */
-    start_node = vlink(head_node);
-    if (start_node == null || !get_callback(Luas, callback_id)) {
-        lua_settop(Luas, s_top);
-        return;
-    }
-    /*tex We make sure we have no prev */
-    alink(start_node) = null ;
-    /*tex the action */
-    nodelist_to_lua(Luas, start_node);
-    lua_push_group_code(Luas,extrainfo);
-    if ((i=lua_pcall(Luas, 2, 1, 0)) != 0) {
-        formatted_warning("node filter", "error: %s\n", lua_tostring(Luas, -1));
-        lua_settop(Luas, s_top);
-        luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        return;
-    }
-    /*tex the result */
-    if (lua_isboolean(Luas, -1)) {
-        if (lua_toboolean(Luas, -1) != 1) {
-            /*tex discard */
-            flush_node_list(start_node);
-            vlink(head_node) = null;
-        } else {
-            /*tex keep */
-        }
-    } else {
-        /*tex append to old head */
-        start_done = nodelist_from_lua(Luas,-1);
-        try_couple_nodes(head_node,start_done);
-    }
-    /*tex redundant as we set top anyway */
-    lua_pop(Luas, 2);
-    /*tex find tail in order to update tail */
-    start_node = vlink(head_node);
-    if (start_node != null) {
-        /*tex maybe just always slide (harmless and fast) */
-        if (fix_node_lists) {
-            /*tex slides and returns last node */
-            *tail_node = fix_node_list(start_node);
-        } else {
-            last_node = vlink(start_node);
-            while (last_node != null) {
-                start_node = last_node;
-                last_node = vlink(start_node);
-            }
-            /*tex we're at the end now */
-            *tail_node = start_node;
-        }
-    } else {
-        /*tex we're already at the end */
-        *tail_node = head_node;
-    }
-    /*tex clean up */
-    lua_settop(Luas, s_top);
-    return;
-}
-
-int lua_linebreak_callback(int is_broken, halfword head_node, halfword * new_head)
-{
-    int a, i;
-    register halfword *p;
-    int ret = 0;
-    int s_top = lua_gettop(Luas);
-    int callback_id = callback_defined(linebreak_filter_callback);
-    if (head_node == null || vlink(head_node) == null || callback_id <= 0) {
-        lua_settop(Luas, s_top);
-        return ret;
-    }
-    if (!get_callback(Luas, callback_id)) {
-        lua_settop(Luas, s_top);
-        return ret;
-    }
-    alink(vlink(head_node)) = null ;
-    nodelist_to_lua(Luas, vlink(head_node));
-    lua_pushboolean(Luas, is_broken);
-    if ((i=lua_pcall(Luas, 2, 1, 0)) != 0) {
-        formatted_warning("linebreak", "error: %s", lua_tostring(Luas, -1));
-        lua_settop(Luas, s_top);
-        luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        return ret;
-    }
-    lua_settop(Luas, s_top);
-    p = lua_touserdata(Luas, -1);
-    if (p != NULL) {
-        a = nodelist_from_lua(Luas,-1);
-        try_couple_nodes(*new_head,a);
-        ret = 1;
-    }
-    return ret;
-}
-
-int lua_appendtovlist_callback(halfword box, int location, halfword prev_depth,
-    boolean is_mirrored, halfword * result, int * next_depth, boolean * prev_set)
-{
-    register halfword *p;
-    int i;
-    int s_top = lua_gettop(Luas);
-    int callback_id = callback_defined(append_to_vlist_filter_callback);
-    if (box == null || callback_id <= 0) {
-        lua_settop(Luas, s_top);
-        return 0;
-    }
-    if (!get_callback(Luas, callback_id)) {
-        lua_settop(Luas, s_top);
-        return 0;
-    }
-    nodelist_to_lua(Luas, box);
-    lua_push_string_by_index(Luas,location);
-    lua_pushinteger(Luas, (int) prev_depth);
-    lua_pushboolean(Luas, is_mirrored);
-    if ((i=lua_pcall(Luas, 4, 2, 0)) != 0) {
-        formatted_warning("append to vlist","error: %s", lua_tostring(Luas, -1));
-        lua_settop(Luas, s_top);
-        luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        return 0;
-    }
-    if (lua_type(Luas,-1) == LUA_TNUMBER) {
-        *next_depth = lua_roundnumber(Luas,-1);
-        *prev_set = true;
-        if (lua_type(Luas, -2) != LUA_TNIL) {
-            p = check_isnode(Luas, -2);
-            *result = *p;
-        }
-    } else if (lua_type(Luas, -1) != LUA_TNIL) {
-        p = check_isnode(Luas, -1);
-        *result = *p;
-    }
-    return 1;
-}
-
-halfword lua_hpack_filter(halfword head_node, scaled size, int pack_type, int extrainfo,
-    int pack_direction, halfword attr)
-{
-    int i;
-    halfword ret;
-    int s_top = lua_gettop(Luas);
-    int callback_id = callback_defined(hpack_filter_callback);
-    if (head_node == null || callback_id <= 0) {
-        lua_settop(Luas, s_top);
-        return head_node;
-    }
-    if (!get_callback(Luas, callback_id)) {
-        lua_settop(Luas, s_top);
-        return head_node;
-    }
-    alink(head_node) = null ;
-    nodelist_to_lua(Luas, head_node);
-    lua_push_group_code(Luas,extrainfo);
-    lua_pushinteger(Luas, size);
-    lua_push_pack_type(Luas, pack_type);
-    if (pack_direction >= 0) {
-        lua_push_dir_par(Luas, pack_direction);
-    } else {
-        lua_pushnil(Luas);
-    }
-    if (attr != null) {
-        nodelist_to_lua(Luas, attr);
-    } else {
-        lua_pushnil(Luas);
-    }
-    if ((i=lua_pcall(Luas, 6, 1, 0)) != 0) {
-        formatted_warning("hpack filter", "error: %s\n", lua_tostring(Luas, -1));
-        lua_settop(Luas, s_top);
-        luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        return head_node;
-    }
-    ret = head_node;
-    if (lua_isboolean(Luas, -1)) {
-        if (lua_toboolean(Luas, -1) != 1) {
-            flush_node_list(head_node);
-            ret = null;
-        }
-    } else {
-        ret = nodelist_from_lua(Luas,-1);
-    }
-    lua_settop(Luas, s_top);
-    if (fix_node_lists)
-        fix_node_list(ret);
-    return ret;
-}
-
-halfword lua_vpack_filter(halfword head_node, scaled size, int pack_type, scaled maxd,
-    int extrainfo, int pack_direction, halfword attr)
-{
-    halfword ret;
-    int i;
-    int callback_id;
-    int s_top = lua_gettop(Luas);
-    if (head_node == null) {
-        lua_settop(Luas, s_top);
-        return head_node;
-    }
-    if  (extrainfo == 8)  {
-        callback_id = callback_defined(pre_output_filter_callback);
-    } else {
-        callback_id = callback_defined(vpack_filter_callback);
-    }
-    if (callback_id <= 0) {
-        lua_settop(Luas, s_top);
-        return head_node;
-    }
-    if (!get_callback(Luas, callback_id)) {
-        lua_settop(Luas, s_top);
-        return head_node;
-    }
-    alink(head_node) = null ;
-    nodelist_to_lua(Luas, head_node);
-    lua_push_group_code(Luas, extrainfo);
-    lua_pushinteger(Luas, size);
-    lua_push_pack_type(Luas, pack_type);
-    lua_pushinteger(Luas, maxd);
-    if (pack_direction >= 0) {
-         lua_push_dir_par(Luas, pack_direction);
-    } else {
-        lua_pushnil(Luas);
-    }
-    if (attr != null) {
-        nodelist_to_lua(Luas, attr);
-    } else {
-        lua_pushnil(Luas);
-    }
-    if ((i=lua_pcall(Luas, 7, 1, 0)) != 0) {
-        formatted_warning("vpack filter", "error: %s", lua_tostring(Luas, -1));
-        lua_settop(Luas, s_top);
-        luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        return head_node;
-    }
-    ret = head_node;
-    if (lua_isboolean(Luas, -1)) {
-        if (lua_toboolean(Luas, -1) != 1) {
-            flush_node_list(head_node);
-            ret = null;
-        }
-    } else {
-        ret = nodelist_from_lua(Luas,-1);
-    }
-    lua_settop(Luas, s_top);
-    if (fix_node_lists)
-        fix_node_list(ret);
-    return ret;
-}
-
-/*tex
-
-    This is a quick hack to fix \ETEX's \.{\\lastnodetype} now that there are many
-    more visible node types.
-
-*/
-
-int visible_last_node_type(int n)
-{
-    int i = type(n);
-    if (i != glyph_node) {
-        return get_etex_code(i);
-    } else if (is_ligature(n)) {
-        /*tex old ligature value */
-        return 7;
-    } else {
-        /*tex old character value */
-        return 0;
-    }
-}
-
-void lua_pdf_literal(PDF pdf, int i, int noline)
-{
-    const char *s = NULL;
-    size_t l = 0;
-    lua_rawgeti(Luas, LUA_REGISTRYINDEX, i);
-    s = lua_tolstring(Luas, -1, &l);
-    if (noline) {
-        pdf_check_space(pdf);
-        pdf_out_block(pdf, s, l);
-        pdf_set_space(pdf);
-    } else {
-        pdf_out_block(pdf, s, l);
-        pdf_out(pdf, 10);
-    }
-    lua_pop(Luas, 1);
-}
-
-void copy_pdf_literal(pointer r, pointer p)
-{
-    int t = pdf_literal_type(p);
-    pdf_literal_type(r) = t;
-    pdf_literal_mode(r) = pdf_literal_mode(p);
-    if (t == normal) {
-        pdf_literal_data(r) = pdf_literal_data(p);
-        add_token_ref(pdf_literal_data(p));
-    } else if (t == lua_refid_literal) {
-        lua_rawgeti(Luas, LUA_REGISTRYINDEX, pdf_literal_data(p));
-        pdf_literal_data(r) = luaL_ref(Luas, LUA_REGISTRYINDEX);
-    } else {
-        /* maybe something user, we don't support a call here but best keep it sane anyway. */
-        pdf_literal_data(r) = pdf_literal_data(p);
-    }
-}
-
-void copy_late_lua(pointer r, pointer p)
-{
-    int t = late_lua_type(p);
-    late_lua_type(r) = t;
-    if (late_lua_name(p) > 0)
-        add_token_ref(late_lua_name(p));
-    if (t == normal) {
-        late_lua_data(r) = late_lua_data(p);
-        add_token_ref(late_lua_data(p));
-    } else if (t == lua_refid_literal) {
-        lua_rawgeti(Luas, LUA_REGISTRYINDEX, late_lua_data(p));
-        late_lua_data(r) = luaL_ref(Luas, LUA_REGISTRYINDEX);
-    }
-}
-
-void copy_user_lua(pointer r, pointer p)
-{
-    if (user_node_value(p) != 0) {
-        lua_rawgeti(Luas, LUA_REGISTRYINDEX, user_node_value(p));
-        user_node_value(r) = luaL_ref(Luas, LUA_REGISTRYINDEX);
-    }
-}
-
-void free_pdf_literal(pointer p)
-{
-    int t = pdf_literal_type(p);
-    if (t == normal) {
-        delete_token_ref(pdf_literal_data(p));
-    } else if (t == lua_refid_literal) {
-        luaL_unref(Luas, LUA_REGISTRYINDEX, pdf_literal_data(p));
-    }
-}
-
-void free_late_lua(pointer p)
-{
-    int t = late_lua_type(p);
-    if (late_lua_name(p) > 0)
-        delete_token_ref(late_lua_name(p));
-    if (t == normal) {
-        delete_token_ref(late_lua_data(p));
-    } else if (t == lua_refid_literal) {
-        luaL_unref(Luas, LUA_REGISTRYINDEX, late_lua_data(p));
-    }
-}
-
-void free_user_lua(pointer p)
-{
-    if (user_node_value(p) != 0) {
-        luaL_unref(Luas, LUA_REGISTRYINDEX, user_node_value(p));
-    }
-}
-
-void show_pdf_literal(pointer p)
-{
-    int t = pdf_literal_type(p);
-    tprint_esc("pdfliteral");
-    switch (pdf_literal_mode(p)) {
-        case set_origin:
-            tprint(" origin");
-            break;
-        case direct_page:
-            tprint(" page");
-            break;
-        case direct_always:
-            tprint(" direct");
-            break;
-        case direct_raw:
-            tprint(" raw");
-            break;
-        default:
-            tprint(" <invalid mode>");
-            break;
-    }
-    if (t == normal) {
-        print_mark(pdf_literal_data(p));
-    } else if (t == lua_refid_literal) {
-        tprint(" <lua data reference ");
-        print_int(pdf_literal_data(p));
-        tprint(">");
-    } else {
-        tprint(" <invalid data>");
-    }
-}
-
-void show_late_lua(pointer p)
-{
-    int t = late_lua_type(p);
-    tprint_esc("latelua");
-    print_int(late_lua_reg(p));
-    if (t == normal) {
-        print_mark(late_lua_data(p));
-    } else if (t == lua_refid_literal) {
-        tprint(" <function reference ");
-        print_int(late_lua_data(p));
-        tprint(">");
-    } else if (t == lua_refid_call) {
-        tprint(" <functioncall reference ");
-        print_int(late_lua_data(p));
-        tprint(">");
-    } else {
-        tprint(" <invalid data>");
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lua/luastuff.w
@@ -0,0 +1,654 @@
+% 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
+#if LUA_VERSION_NUM == 503
+    { "utf8",      luaopen_utf8 },
+#endif
+    { "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
+
+@ @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
--- texlive-bin.orig/texk/web2c/luatexdir/lua/luastuff.c
+++ /dev/null
@@ -1,759 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-#ifdef LuajitTeX
-#include "lua/lauxlib_bridge.h"
-#endif
-
-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
-
-void make_table(lua_State * L, const char *tab, const char *mttab, const char *getfunc, const char *setfunc)
-{
-    /*tex make the table *//* |[{<tex>}]| */
-    /*tex |[{<tex>},"dimen"]| */
-    lua_pushstring(L, tab);
-    /*tex |[{<tex>},"dimen",{}]| */
-    lua_newtable(L);
-    /*tex |[{<tex>}]| */
-    lua_settable(L, -3);
-    /*tex fetch it back */
-    /*tex |[{<tex>},"dimen"]| */
-    lua_pushstring(L, tab);
-    /*tex |[{<tex>},{<dimen>}]| */
-    lua_gettable(L, -2);
-    /*tex make the meta entries */
-    /*tex |[{<tex>},{<dimen>},{<dimen_m>}]| */
-    luaL_newmetatable(L, mttab);
-    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__index"]| */
-    lua_pushstring(L, "__index");
-    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__index","getdimen"]| */
-    lua_pushstring(L, getfunc);
-    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__index",<tex.getdimen>]| */
-    lua_gettable(L, -5);
-    /*tex |[{<tex>},{<dimen>},{<dimen_m>}]|  */
-    lua_settable(L, -3);
-    lua_pushstring(L, "__newindex");  /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__newindex"]| */
-    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__newindex","setdimen"]| */
-    lua_pushstring(L, setfunc);
-    /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__newindex",<tex.setdimen>]| */
-    lua_gettable(L, -5);
-    /*tex |[{<tex>},{<dimen>},{<dimen_m>}]| */
-    lua_settable(L, -3);
-    /*tex |[{<tex>},{<dimen>}]| : assign the metatable */
-    lua_setmetatable(L, -2);
-    /*tex |[{<tex>}]| : clean the stack */
-    lua_pop(L, 1);
-}
-
-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;
-}
-
-#ifdef LuajitTeX
-    /*
-        \LUATEX\ has its own memory allocator, \LUAJIITEX\ 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;
-    /*tex define |ud| for -Wunused */
-    (void) ud;
-    if (nsize == 0)
-        free(ptr);
-    else
-        ret = realloc(ptr, nsize);
-    luastate_bytes += (int) (nsize - osize);
-    return ret;
-}
-#endif
-
-static int my_luapanic(lua_State * L)
-{
-    /*tex define |L| to avoid warnings */
-    (void) L;
-    fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1));
-    return 0;
-}
-
-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)) {
-        /*tex function index */
-        int base = lua_gettop(Luas);
-        lua_pushinteger(Luas, slot);
-        /* push traceback function */
-        lua_pushcfunction(Luas, lua_traceback);
-        /*tex put it under chunk  */
-        lua_insert(Luas, base);
-        ++function_callback_count;
-        i = lua_pcall(Luas, 1, 0, base);
-        /*tex remove traceback function */
-        lua_remove(Luas, base);
-        if (i != 0) {
-            lua_gc(Luas, LUA_GCCOLLECT, 0);
-            Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        }
-    }
-    lua_settop(Luas,stacktop);
-    lua_active--;
-}
-
-static const luaL_Reg lualibs[] = {
-    /*tex 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
-    /*tex |bit| is only in \LUAJIT */
-    /*tex |coroutine| is loaded in a special way */
-    { "bit",       luaopen_bit },
-#else
-#if LUA_VERSION_NUM == 503
-    { "utf8",      luaopen_utf8 },
-#endif
-    { "coroutine", luaopen_coroutine },
-#endif
-    /*tex additional (public) libraries */
-    { "unicode",   luaopen_unicode },
-    { "zip",       luaopen_zip },
-    { "md5",       luaopen_md5 },
-    { "sha2",      luaopen_sha2 },
-    { "lfs",       luaopen_lfs },
-    /*tex extra standard lua libraries */
-#ifdef LuajitTeX
-    { "jit",       luaopen_jit },
-#endif
-    { "ffi",       luaopen_ffi },
-    /*tex more libraries will be loaded later */
-    { NULL,        NULL }
-};
-
-static void do_openlibs(lua_State * L)
-{
-    const luaL_Reg *lib = lualibs;
-    for (; lib->func; lib++) {
-        Luas_open(lib->name,lib->func);
-    }
-}
-
-#ifdef LuajitTeX
-    /*tex in \LUAJIT\ |load_aux| is not used.*/
-#else
-static int load_aux (lua_State *L, int status) {
-    if (status == 0)
-        /*tex okay */
-        return 1;
-    else {
-        /*tex return nil plus error message */
-        lua_pushnil(L);
-        /*tex put before error message */
-        lua_insert(L, -2);
-        return 2;
-    }
-}
-#endif
-
-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
-    /*tex the |env| parameter */
-    int env = !lua_isnone(L, 3);
-#endif
-    if (!lua_only && !fname && interaction == batch_mode) {
-        /*tex return |nil| plus error message */
-        lua_pushnil(L);
-        lua_pushstring(L, "reading from stdin is disabled in batch mode");
-        return 2;
-    }
-    status = luaL_loadfilex(L, fname, mode);
-    if (status == LUA_OK) {
-        recorder_record_input(fname);
-#ifdef LuajitTeX
-    /* 5.1 */
-#else
-        if (env) {
-            /*tex the |env| parameter */
-            lua_pushvalue(L, 3);
-            /*tex set it as first upvalue of loaded chunk */
-            lua_setupvalue(L, -2, 1);
-        }
-#endif
-    }
-#ifdef LuajitTeX
-    return RESERVED_load_aux_JIT(L, status,3);
-#else
-    return load_aux(L, status);
-#endif
-}
-
-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) {
-            /*tex return |nil| plus error message */
-            lua_pushnil(L);
-            lua_pushstring(L, "reading from stdin is disabled in batch mode");
-            return 2;
-        } 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;
-}
-
-void luainterpreter(void)
-{
-    lua_State *L;
-#ifdef LuajitTeX
-    if (jithash_hashname == NULL) {
-        /*tex 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 {
-        /*tex 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);
-    /*tex This initializes all the `simple' libraries: */
-    do_openlibs(L);
-#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);
-    /*tex
-        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");
-        }
-        /*tex |package.loaded.socket = nil| */
-        luaopen_socket_core(L);
-        lua_setfield(L, -2, "socket.core");
-        lua_pushnil(L);
-        lua_setfield(L, -2, "socket");
-        /*tex |package.loaded.mime = nil| */
-        luaopen_mime_core(L);
-        lua_setfield(L, -2, "mime.core");
-        lua_pushnil(L);
-        lua_setfield(L, -2, "mime");
-        /*tex pop the tables */
-        lua_pop(L, 2);
-        /*tex preload the pure \LUA\ modules */
-        luatex_socketlua_open(L);
-    }
-    /*tex |zlib|'s slightly odd calling convention */
-    luaopen_zlib(L);
-    lua_setglobal(L, "zlib");
-    luaopen_gzip(L);
-    /*tex 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);
-    /*tex now we plug in extra \LUA\ startup code */
-    luaopen_lua(L, startup_filename);
-    /*tex and open some \TEX\ ones */
-    luaopen_stats(L);
-    luaopen_font(L);
-    luaopen_lang(L);
-    luaopen_mplib(L);
-    luaopen_vf(L);
-    luaopen_pdf(L);
-    luaopen_pdfe(L);
-    luaopen_pdfscanner(L);
-    if (!lua_only) {
-        luaopen_img(L);
-    }
-    lua_createtable(L, 0, 0);
-    lua_setglobal(L, "texconfig");
-    Luas = L;
-}
-
-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;
-}
-
-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);
-}
-
-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;
-}
-
-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);
-    }
-}
-
-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;
-    }
-    /*tex pass error message */
-    lua_pushvalue(L, 1);
-    /*tex skip this function and traceback */
-    lua_pushinteger(L, 2);
-    /*tex call |debug.traceback| */
-    lua_call(L, 2, 1);
-    return 1;
-}
-
-static void luacall(int p, int nameptr, boolean is_string)
-{
-    LoadS ls;
-    int i;
-    size_t ll = 0;
-    char *lua_id;
-    char *s = NULL;
-    int stacktop = lua_gettop(Luas);
-    if (Luas == NULL) {
-        luainterpreter();
-    }
-    lua_active++;
-    if (is_string) {
-        const char *ss = NULL;
-        lua_rawgeti(Luas, LUA_REGISTRYINDEX, p);
-        if (lua_isfunction(Luas,-1)) {
-            /*tex function index */
-            int base = lua_gettop(Luas);
-            lua_checkstack(Luas, 1);
-            /*tex push traceback function */
-            lua_pushcfunction(Luas, lua_traceback);
-            /*tex put it under chunk  */
-            lua_insert(Luas, base);
-            ++late_callback_count;
-            i = lua_pcall(Luas, 0, 0, base);
-            /*tex remove traceback function */
-            lua_remove(Luas, base);
-            if (i != 0) {
-                lua_gc(Luas, LUA_GCCOLLECT, 0);
-                Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-            }
-            lua_settop(Luas,stacktop);
-            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) {
-            /*tex |l| is not used */
-            int l = 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, "=[\\latelua]");
-            }
-        } else {
-            i = Luas_load(Luas, getS, &ls, "=[\\latelua]");
-        }
-        if (i != 0) {
-            Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1));
-        } else {
-            /*tex function index */
-            int base = lua_gettop(Luas);
-            lua_checkstack(Luas, 1);
-            /*tex push traceback function */
-            lua_pushcfunction(Luas, lua_traceback);
-            /*tex put it under chunk  */
-            lua_insert(Luas, base);
-            ++late_callback_count;
-            i = lua_pcall(Luas, 0, 0, base);
-            /*tex remove traceback function */
-            lua_remove(Luas, base);
-            if (i != 0) {
-                lua_gc(Luas, LUA_GCCOLLECT, 0);
-                Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-            }
-        }
-        xfree(ls.s);
-    }
-    lua_settop(Luas,stacktop);
-    lua_active--;
-}
-
-void luacall_vf(int p, int f, int c)
-{
-    int i;
-    int stacktop = lua_gettop(Luas);
-    if (Luas == NULL) {
-        luainterpreter();
-    }
-    lua_active++;
-    lua_rawgeti(Luas, LUA_REGISTRYINDEX, p);
-    if (lua_isfunction(Luas,-1)) {
-        /*tex function index */
-        int base = lua_gettop(Luas);
-        lua_checkstack(Luas, 1);
-        /*tex push traceback function */
-        lua_pushcfunction(Luas, lua_traceback);
-        /*tex put it under chunk  */
-        lua_insert(Luas, base);
-        lua_pushinteger(Luas, f);
-        lua_pushinteger(Luas, c);
-        ++late_callback_count;
-        i = lua_pcall(Luas, 2, 0, base);
-        /*tex remove traceback function */
-        lua_remove(Luas, base);
-        if (i != 0) {
-            lua_gc(Luas, LUA_GCCOLLECT, 0);
-            Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-        }
-    } else {
-        LoadS ls;
-        size_t ll = 0;
-        char *s = NULL;
-        const char *ss = NULL;
-        ss = lua_tolstring(Luas, -1, &ll);
-        s = xmalloc(ll+1);
-        memcpy(s,ss,ll+1);
-        lua_pop(Luas,1);
-        ls.s = s;
-        ls.size = ll;
-        if (ls.size > 0) {
-            i = Luas_load(Luas, getS, &ls, "=[vf command]");
-            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  */
-                ++late_callback_count;
-                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_settop(Luas,stacktop);
-    lua_active--;
-}
-
-void late_lua(PDF pdf, halfword p)
-{
-    halfword t;
-    (void) pdf;
-    t = late_lua_type(p);
-    if (t == normal) {
-        /*tex sets |def_ref| */
-        expand_macros_in_tokenlist(p);
-        luacall(def_ref, late_lua_name(p), false);
-        flush_list(def_ref);
-    } else if (t == lua_refid_call) {
-        luafunctioncall(late_lua_data(p));
-    } else if (t == lua_refid_literal) {
-        luacall(late_lua_data(p), late_lua_name(p), true);
-    } else {
-        /*tex Let's just ignore it, could be some user specific thing. */
-    }
-}
-
-void luatokencall(int p, int nameptr)
-{
-    LoadS ls;
-    int i;
-    int l = 0;
-    char *s = NULL;
-    char *lua_id;
-    int stacktop = lua_gettop(Luas);
-    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 {
-            /*tex function index */
-            int base = lua_gettop(Luas);
-            lua_checkstack(Luas, 1);
-            /*tex push traceback function */
-            lua_pushcfunction(Luas, lua_traceback);
-            /*tex put it under chunk  */
-            lua_insert(Luas, base);
-            ++direct_callback_count;
-            i = lua_pcall(Luas, 0, 0, base);
-            /*tex remove traceback function */
-            lua_remove(Luas, base);
-            if (i != 0) {
-                lua_gc(Luas, LUA_GCCOLLECT, 0);
-                Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-            }
-        }
-    }
-    lua_settop(Luas,stacktop);
-    lua_active--;
-}
-
-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);
-        /*tex
-            Free the last one.
-        */
-        err = (char *) xmalloc((unsigned) (luaerr.l + 1));
-        snprintf(err, (luaerr.l + 1), "%s", luaerr.s);
-        /*tex
-            What if we have several .. not freed?
-        */
-        last_lua_error = err;
-    }
-    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);
-        /*tex
-            This is never reached.
-        */
-        lua_close(L);
-        return (lua_State *) NULL;
-    } else {
-        normal_warning("lua",err);
-        return L;
-    }
-}
-
-void preset_environment(lua_State * L, const parm_struct * p, const char *s)
-{
-    int i;
-    assert(L != NULL);
-    /*tex double call with same s gives assert(0) */
-    lua_pushstring(L, s);
-    /*tex state: s */
-    lua_gettable(L, LUA_REGISTRYINDEX);
-    /*tex state: t */
-    assert(lua_isnil(L, -1));
-    lua_pop(L, 1);
-    /*tex state: - */
-    lua_pushstring(L, s);
-    /*tex state: s */
-    lua_newtable(L);
-    /*tex state: t s */
-    for (i = 1, ++p; p->name != NULL; i++, p++) {
-        assert(i == p->idx);
-        lua_pushstring(L, p->name);
-        /*tex state: k t s */
-        lua_pushinteger(L, p->idx);
-        /*tex state: v k t s */
-        lua_settable(L, -3);
-        /*tex state: t s */
-    }
-    lua_settable(L, LUA_REGISTRYINDEX);
-    /* tex state: - */
-}
-
-/*tex
-    Here comes a \LUAJIT\ compatibility layer for \LUATEX\ \LUA5.2:
-*/
-
-#ifdef LuajitTeX
-
-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) ;
-}
-
-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;
-}
-
-#endif
--- texlive-bin.orig/texk/web2c/luatexdir/lua/luatoken.c
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
-
-luatoken.w
-
-Copyright 2006-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-command_item command_names[] = {
-    { relax_cmd,                NULL, 0},
-    { left_brace_cmd,           NULL, 0},
-    { right_brace_cmd,          NULL, 0},
-    { math_shift_cmd,           NULL, 0},
-    { tab_mark_cmd,             NULL, 0},
-    { car_ret_cmd,              NULL, 0},
-    { mac_param_cmd,            NULL, 0},
-    { sup_mark_cmd,             NULL, 0},
-    { sub_mark_cmd,             NULL, 0},
-    { endv_cmd,                 NULL, 0},
-    { spacer_cmd,               NULL, 0},
-    { letter_cmd,               NULL, 0},
-    { other_char_cmd,           NULL, 0},
-    { par_end_cmd,              NULL, 0},
-    { stop_cmd,                 NULL, 0},
-    { delim_num_cmd,            NULL, 0},
-    { char_num_cmd,             NULL, 0},
-    { math_char_num_cmd,        NULL, 0},
-    { mark_cmd,                 NULL, 0},
-    { node_cmd,                 NULL, 0},
-    { xray_cmd,                 NULL, 0},
-    { make_box_cmd,             NULL, 0},
-    { hmove_cmd,                NULL, 0},
-    { vmove_cmd,                NULL, 0},
-    { un_hbox_cmd,              NULL, 0},
-    { un_vbox_cmd,              NULL, 0},
-    { remove_item_cmd,          NULL, 0},
-    { hskip_cmd,                NULL, 0},
-    { vskip_cmd,                NULL, 0},
-    { mskip_cmd,                NULL, 0},
-    { kern_cmd,                 NULL, 0},
-    { mkern_cmd,                NULL, 0},
-    { leader_ship_cmd,          NULL, 0},
-    { halign_cmd,               NULL, 0},
-    { valign_cmd,               NULL, 0},
-    { no_align_cmd,             NULL, 0},
-    { no_vrule_cmd,             NULL, 0},
-    { no_hrule_cmd,             NULL, 0},
-    { vrule_cmd,                NULL, 0},
-    { hrule_cmd,                NULL, 0},
-    { insert_cmd,               NULL, 0},
-    { vadjust_cmd,              NULL, 0},
-    { ignore_spaces_cmd,        NULL, 0},
-    { after_assignment_cmd,     NULL, 0},
-    { after_group_cmd,          NULL, 0},
-    { break_penalty_cmd,        NULL, 0},
-    { start_par_cmd,            NULL, 0},
-    { ital_corr_cmd,            NULL, 0},
-    { accent_cmd,               NULL, 0},
-    { math_accent_cmd,          NULL, 0},
-    { discretionary_cmd,        NULL, 0},
-    { eq_no_cmd,                NULL, 0},
-    { left_right_cmd,           NULL, 0},
-    { math_comp_cmd,            NULL, 0},
-    { limit_switch_cmd,         NULL, 0},
-    { above_cmd,                NULL, 0},
-    { math_style_cmd,           NULL, 0},
-    { math_choice_cmd,          NULL, 0},
-    { non_script_cmd,           NULL, 0},
-    { vcenter_cmd,              NULL, 0},
-    { case_shift_cmd,           NULL, 0},
-    { message_cmd,              NULL, 0},
-    { normal_cmd,               NULL, 0},
-    { extension_cmd,            NULL, 0},
-    { option_cmd,               NULL, 0},
-    { lua_function_call_cmd,    NULL, 0},
-    { lua_bytecode_call_cmd,    NULL, 0},
-    { lua_call_cmd,             NULL, 0},
-    { in_stream_cmd,            NULL, 0},
-    { begin_group_cmd,          NULL, 0},
-    { end_group_cmd,            NULL, 0},
-    { omit_cmd,                 NULL, 0},
-    { ex_space_cmd,             NULL, 0},
-    { boundary_cmd,             NULL, 0},
-    { radical_cmd,              NULL, 0},
-    { super_sub_script_cmd,     NULL, 0},
-    { no_super_sub_script_cmd,  NULL, 0},
-    { math_shift_cs_cmd,        NULL, 0},
-    { end_cs_name_cmd,          NULL, 0},
-    { char_ghost_cmd,           NULL, 0},
-    { assign_local_box_cmd,     NULL, 0},
-    { char_given_cmd,           NULL, 0},
-    { math_given_cmd,           NULL, 0},
-    { xmath_given_cmd,          NULL, 0},
-    { last_item_cmd,            NULL, 0},
-    { toks_register_cmd,        NULL, 0},
-    { assign_toks_cmd,          NULL, 0},
-    { assign_int_cmd,           NULL, 0},
-    { assign_attr_cmd,          NULL, 0},
-    { assign_dimen_cmd,         NULL, 0},
-    { assign_glue_cmd,          NULL, 0},
-    { assign_mu_glue_cmd,       NULL, 0},
-    { assign_font_dimen_cmd,    NULL, 0},
-    { assign_font_int_cmd,      NULL, 0},
-    { assign_hang_indent_cmd,   NULL, 0},
-    { set_aux_cmd,              NULL, 0},
-    { set_prev_graf_cmd,        NULL, 0},
-    { set_page_dimen_cmd,       NULL, 0},
-    { set_page_int_cmd,         NULL, 0},
-    { set_box_dimen_cmd,        NULL, 0},
-    { set_tex_shape_cmd,        NULL, 0},
-    { set_etex_shape_cmd,       NULL, 0},
-    { def_char_code_cmd,        NULL, 0},
-    { def_del_code_cmd,         NULL, 0},
-    { extdef_math_code_cmd,     NULL, 0},
-    { extdef_del_code_cmd,      NULL, 0},
-    { def_family_cmd,           NULL, 0},
-    { set_math_param_cmd,       NULL, 0},
-    { set_font_cmd,             NULL, 0},
-    { def_font_cmd,             NULL, 0},
-    { register_cmd,             NULL, 0},
-    { assign_box_direction_cmd, NULL, 0},
-    { assign_box_dir_cmd,       NULL, 0},
-    { assign_direction_cmd,     NULL, 0},
-    { assign_dir_cmd,           NULL, 0},
-    { advance_cmd,              NULL, 0},
-    { multiply_cmd,             NULL, 0},
-    { divide_cmd,               NULL, 0},
-    { prefix_cmd,               NULL, 0},
-    { let_cmd,                  NULL, 0},
-    { shorthand_def_cmd,        NULL, 0},
-    { def_lua_call_cmd,         NULL, 0},
-    { read_to_cs_cmd,           NULL, 0},
-    { def_cmd,                  NULL, 0},
-    { set_box_cmd,              NULL, 0},
-    { hyph_data_cmd,            NULL, 0},
-    { set_interaction_cmd,      NULL, 0},
-    { letterspace_font_cmd,     NULL, 0},
-    { expand_font_cmd,          NULL, 0},
-    { copy_font_cmd,            NULL, 0},
-    { set_font_id_cmd,          NULL, 0},
-    { undefined_cs_cmd,         NULL, 0},
-    { expand_after_cmd,         NULL, 0},
-    { no_expand_cmd,            NULL, 0},
-    { input_cmd,                NULL, 0},
-    { lua_expandable_call_cmd,  NULL, 0},
-    { lua_local_call_cmd,       NULL, 0},
-    { if_test_cmd,              NULL, 0},
-    { fi_or_else_cmd,           NULL, 0},
-    { cs_name_cmd,              NULL, 0},
-    { convert_cmd,              NULL, 0},
-    { variable_cmd,             NULL, 0},
-    { feedback_cmd,             NULL, 0},
-    { the_cmd,                  NULL, 0},
-    { combine_toks_cmd,         NULL, 0},
-    { top_bot_mark_cmd,         NULL, 0},
-    { call_cmd,                 NULL, 0},
-    { long_call_cmd,            NULL, 0},
-    { outer_call_cmd,           NULL, 0},
-    { long_outer_call_cmd,      NULL, 0},
-    { end_template_cmd,         NULL, 0},
-    { dont_expand_cmd,          NULL, 0},
-    { glue_ref_cmd,             NULL, 0},
-    { shape_ref_cmd,            NULL, 0},
-    { box_ref_cmd,              NULL, 0},
-    { data_cmd,                 NULL, 0},
-    { -1,                       NULL, 0}
-};
-
-# define init_token_key(target,n,key) \
-    target[n].lua  = luaS_##key##_index; \
-    target[n].name = luaS_##key##_ptr;
-
-void l_set_token_data(void)
-{
-    init_token_key(command_names, relax_cmd,                relax);
-    init_token_key(command_names, left_brace_cmd,           left_brace);
-    init_token_key(command_names, right_brace_cmd,          right_brace);
-    init_token_key(command_names, math_shift_cmd,           math_shift);
-    init_token_key(command_names, tab_mark_cmd,             tab_mark);
-    init_token_key(command_names, car_ret_cmd,              car_ret);
-    init_token_key(command_names, mac_param_cmd,            mac_param);
-    init_token_key(command_names, sup_mark_cmd,             sup_mark);
-    init_token_key(command_names, sub_mark_cmd,             sub_mark);
-    init_token_key(command_names, endv_cmd,                 endv);
-    init_token_key(command_names, spacer_cmd,               spacer);
-    init_token_key(command_names, letter_cmd,               letter);
-    init_token_key(command_names, other_char_cmd,           other_char);
-    init_token_key(command_names, par_end_cmd,              par_end);
-    init_token_key(command_names, stop_cmd,                 stop);
-    init_token_key(command_names, delim_num_cmd,            delim_num);
-    init_token_key(command_names, char_num_cmd,             char_num);
-    init_token_key(command_names, math_char_num_cmd,        math_char_num);
-    init_token_key(command_names, mark_cmd,                 mark);
-    init_token_key(command_names, node_cmd,                 node);
-    init_token_key(command_names, xray_cmd,                 xray);
-    init_token_key(command_names, make_box_cmd,             make_box);
-    init_token_key(command_names, hmove_cmd,                hmove);
-    init_token_key(command_names, vmove_cmd,                vmove);
-    init_token_key(command_names, un_hbox_cmd,              un_hbox);
-    init_token_key(command_names, un_vbox_cmd,              un_vbox);
-    init_token_key(command_names, remove_item_cmd,          remove_item);
-    init_token_key(command_names, hskip_cmd,                hskip);
-    init_token_key(command_names, vskip_cmd,                vskip);
-    init_token_key(command_names, mskip_cmd,                mskip);
-    init_token_key(command_names, kern_cmd,                 kern);
-    init_token_key(command_names, mkern_cmd,                mkern);
-    init_token_key(command_names, leader_ship_cmd,          leader_ship);
-    init_token_key(command_names, halign_cmd,               halign);
-    init_token_key(command_names, valign_cmd,               valign);
-    init_token_key(command_names, no_align_cmd,             no_align);
-    init_token_key(command_names, vrule_cmd,                vrule);
-    init_token_key(command_names, hrule_cmd,                hrule);
-    init_token_key(command_names, no_vrule_cmd,             novrule);
-    init_token_key(command_names, no_hrule_cmd,             nohrule);
-    init_token_key(command_names, insert_cmd,               insert);
-    init_token_key(command_names, vadjust_cmd,              vadjust);
-    init_token_key(command_names, ignore_spaces_cmd,        ignore_spaces);
-    init_token_key(command_names, after_assignment_cmd,     after_assignment);
-    init_token_key(command_names, after_group_cmd,          after_group);
-    init_token_key(command_names, break_penalty_cmd,        break_penalty);
-    init_token_key(command_names, start_par_cmd,            start_par);
-    init_token_key(command_names, ital_corr_cmd,            ital_corr);
-    init_token_key(command_names, accent_cmd,               accent);
-    init_token_key(command_names, math_accent_cmd,          math_accent);
-    init_token_key(command_names, discretionary_cmd,        discretionary);
-    init_token_key(command_names, eq_no_cmd,                eq_no);
-    init_token_key(command_names, left_right_cmd,           left_right);
-    init_token_key(command_names, math_comp_cmd,            math_comp);
-    init_token_key(command_names, limit_switch_cmd,         limit_switch);
-    init_token_key(command_names, above_cmd,                above);
-    init_token_key(command_names, math_style_cmd,           math_style);
-    init_token_key(command_names, math_choice_cmd,          math_choice);
-    init_token_key(command_names, non_script_cmd,           non_script);
-    init_token_key(command_names, vcenter_cmd,              vcenter);
-    init_token_key(command_names, case_shift_cmd,           case_shift);
-    init_token_key(command_names, message_cmd,              message);
-    init_token_key(command_names, normal_cmd,               normal);
-    init_token_key(command_names, extension_cmd,            extension);
-    init_token_key(command_names, option_cmd,               option);
-    init_token_key(command_names, lua_function_call_cmd,    lua_function_call);
-    init_token_key(command_names, lua_bytecode_call_cmd,    lua_bytecode_call);
-    init_token_key(command_names, lua_call_cmd,             lua_call);
-    init_token_key(command_names, in_stream_cmd,            in_stream);
-    init_token_key(command_names, begin_group_cmd,          begin_group);
-    init_token_key(command_names, end_group_cmd,            end_group);
-    init_token_key(command_names, omit_cmd,                 omit);
-    init_token_key(command_names, ex_space_cmd,             ex_space);
-    init_token_key(command_names, boundary_cmd,             boundary);
-    init_token_key(command_names, radical_cmd,              radical);
-    init_token_key(command_names, super_sub_script_cmd,     super_sub_script);
-    init_token_key(command_names, no_super_sub_script_cmd,  no_super_sub_script);
-    init_token_key(command_names, math_shift_cs_cmd,        math_shift_cs);
-    init_token_key(command_names, end_cs_name_cmd,          end_cs_name);
-    init_token_key(command_names, char_ghost_cmd,           char_ghost);
-    init_token_key(command_names, assign_local_box_cmd,     assign_local_box);
-    init_token_key(command_names, char_given_cmd,           char_given);
-    init_token_key(command_names, math_given_cmd,           math_given);
-    init_token_key(command_names, xmath_given_cmd,          xmath_given);
-    init_token_key(command_names, last_item_cmd,            last_item);
-    init_token_key(command_names, toks_register_cmd,        toks_register);
-    init_token_key(command_names, assign_toks_cmd,          assign_toks);
-    init_token_key(command_names, assign_int_cmd,           assign_int);
-    init_token_key(command_names, assign_attr_cmd,          assign_attr);
-    init_token_key(command_names, assign_dimen_cmd,         assign_dimen);
-    init_token_key(command_names, assign_glue_cmd,          assign_glue);
-    init_token_key(command_names, assign_mu_glue_cmd,       assign_mu_glue);
-    init_token_key(command_names, assign_font_dimen_cmd,    assign_font_dimen);
-    init_token_key(command_names, assign_font_int_cmd,      assign_font_int);
-    init_token_key(command_names, assign_hang_indent_cmd,   assign_hang_indent);
-    init_token_key(command_names, set_aux_cmd,              set_aux);
-    init_token_key(command_names, set_prev_graf_cmd,        set_prev_graf);
-    init_token_key(command_names, set_page_dimen_cmd,       set_page_dimen);
-    init_token_key(command_names, set_page_int_cmd,         set_page_int);
-    init_token_key(command_names, set_box_dimen_cmd,        set_box_dimen);
-    init_token_key(command_names, set_tex_shape_cmd,        set_tex_shape);
-    init_token_key(command_names, set_etex_shape_cmd,       set_etex_shape);
-    init_token_key(command_names, def_char_code_cmd,        def_char_code);
-    init_token_key(command_names, def_del_code_cmd,         def_del_code);
-    init_token_key(command_names, extdef_math_code_cmd,     extdef_math_code);
-    init_token_key(command_names, extdef_del_code_cmd,      extdef_del_code);
-    init_token_key(command_names, def_family_cmd,           def_family);
-    init_token_key(command_names, set_math_param_cmd,       set_math_param);
-    init_token_key(command_names, set_font_cmd,             set_font);
-    init_token_key(command_names, def_font_cmd,             def_font);
-    init_token_key(command_names, def_lua_call_cmd,         def_lua_call);
-    init_token_key(command_names, register_cmd,             register);
-    init_token_key(command_names, assign_box_direction_cmd, assign_box_direction);
-    init_token_key(command_names, assign_box_dir_cmd,       assign_box_dir);
-    init_token_key(command_names, assign_direction_cmd,     assign_direction);
-    init_token_key(command_names, assign_dir_cmd,           assign_dir);
-    init_token_key(command_names, advance_cmd,              advance);
-    init_token_key(command_names, multiply_cmd,             multiply);
-    init_token_key(command_names, divide_cmd,               divide);
-    init_token_key(command_names, prefix_cmd,               prefix);
-    init_token_key(command_names, let_cmd,                  let);
-    init_token_key(command_names, shorthand_def_cmd,        shorthand_def);
-    init_token_key(command_names, read_to_cs_cmd,           read_to_cs);
-    init_token_key(command_names, def_cmd,                  def);
-    init_token_key(command_names, set_box_cmd,              set_box);
-    init_token_key(command_names, hyph_data_cmd,            hyph_data);
-    init_token_key(command_names, set_interaction_cmd,      set_interaction);
-    init_token_key(command_names, letterspace_font_cmd,     letterspace_font);
-    init_token_key(command_names, expand_font_cmd,          expand_font);
-    init_token_key(command_names, copy_font_cmd,            copy_font);
-    init_token_key(command_names, set_font_id_cmd,          set_font_id);
-    init_token_key(command_names, undefined_cs_cmd,         undefined_cs);
-    init_token_key(command_names, expand_after_cmd,         expand_after);
-    init_token_key(command_names, no_expand_cmd,            no_expand);
-    init_token_key(command_names, input_cmd,                input);
-    init_token_key(command_names, lua_expandable_call_cmd,  lua_expandable_call);
-    init_token_key(command_names, lua_local_call_cmd,       lua_local_call);
-    init_token_key(command_names, if_test_cmd,              if_test);
-    init_token_key(command_names, fi_or_else_cmd,           fi_or_else);
-    init_token_key(command_names, cs_name_cmd,              cs_name);
-    init_token_key(command_names, convert_cmd,              convert);
-    init_token_key(command_names, variable_cmd,             variable);
-    init_token_key(command_names, feedback_cmd,             feedback);
-    init_token_key(command_names, the_cmd,                  the);
-    init_token_key(command_names, combine_toks_cmd,         combinetoks);
-    init_token_key(command_names, top_bot_mark_cmd,         top_bot_mark);
-    init_token_key(command_names, call_cmd,                 call);
-    init_token_key(command_names, long_call_cmd,            long_call);
-    init_token_key(command_names, outer_call_cmd,           outer_call);
-    init_token_key(command_names, long_outer_call_cmd,      long_outer_call);
-    init_token_key(command_names, end_template_cmd,         end_template);
-    init_token_key(command_names, dont_expand_cmd,          dont_expand);
-    init_token_key(command_names, glue_ref_cmd,             glue_ref);
-    init_token_key(command_names, shape_ref_cmd,            shape_ref);
-    init_token_key(command_names, box_ref_cmd,              box_ref);
-    init_token_key(command_names, data_cmd,                 data);
-}
-
-int get_command_id(const char *s)
-{
-    int i;
-    for (i = 0; command_names[i].id != -1; i++) {
-        if (s == command_names[i].name)
-            return i;
-    }
-    return -1;
-}
-
-/*
-static int get_cur_cmd(lua_State * L)
-{
-    int r = 0;
-    size_t len = lua_rawlen(L, -1);
-    cur_cs = 0;
-    if (len == 3 || len == 2) {
-        r = 1;
-        lua_rawgeti(L, -1, 1);
-        cur_cmd = (int) lua_tointeger(L, -1);
-        lua_rawgeti(L, -2, 2);
-        cur_chr = (halfword) lua_tointeger(L, -1);
-        if (len == 3) {
-            lua_rawgeti(L, -3, 3);
-            cur_cs = (halfword) lua_tointeger(L, -1);
-        }
-        lua_pop(L, (int) len);
-        if (cur_cs == 0)
-            cur_tok = token_val(cur_cmd, cur_chr);
-        else
-            cur_tok = cs_token_flag + cur_cs;
-    }
-    return r;
-}
-*/
-
-static int token_from_lua(lua_State * L)
-{
-    int cmd, chr;
-    int cs = 0;
-    size_t len = lua_rawlen(L, -1);
-    if (len == 3 || len == 2) {
-        lua_rawgeti(L, -1, 1);
-        cmd = (int) lua_tointeger(L, -1);
-        lua_rawgeti(L, -2, 2);
-        chr = (int) lua_tointeger(L, -1);
-        if (len == 3) {
-            lua_rawgeti(L, -3, 3);
-            cs = (int) lua_tointeger(L, -1);
-        }
-        lua_pop(L, (int) len);
-        if (cs == 0) {
-            return token_val(cmd, chr);
-        } else {
-            return cs_token_flag + cs;
-        }
-    }
-    return -1;
-}
-
-/*
-static int get_cur_cs(lua_State * L)
-{
-    const char *s;
-    unsigned j;
-    size_t l;
-    int cs;
-    int save_nncs;
-    int ret;
-    ret = 0;
-    cur_cs = 0;
-    lua_getfield(L, -1, "name");
-    if (lua_type(L, -1) == LUA_TSTRING) {
-        s = lua_tolstring(L, -1, &l);
-        if (l > 0) {
-            if ((last + (int) l) > buf_size)
-                check_buffer_overflow((last + (int) l));
-            for (j = 0; j < l; j++) {
-                buffer[(unsigned) last + 1 + j] = (packed_ASCII_code) * s++;
-            }
-            save_nncs = no_new_control_sequence;
-            no_new_control_sequence = false;
-            cs = id_lookup((last + 1), (int) l);
-            cur_tok = cs_token_flag + cs;
-            cur_cmd = eq_type(cs);
-            cur_chr = equiv(cs);
-            no_new_control_sequence = save_nncs;
-            ret = 1;
-        }
-    }
-    lua_pop(L, 1);
-    return ret;
-}
-*/
-
-void tokenlist_to_lua(lua_State * L, int p)
-{
-    int cmd, chr, cs;
-    int v;
-    int i = 1;
-    v = p;
-    while (v != null && v < (int) fix_mem_end) {
-        i++;
-        v = token_link(v);
-    }
-    i = 1;
-    lua_createtable(L, i, 0);
-    while (p != null && p < (int) fix_mem_end) {
-        if (token_info(p) >= cs_token_flag) {
-            cs = token_info(p) - cs_token_flag;
-            cmd = eq_type(cs);
-            chr = equiv(cs);
-            make_token_table(L, cmd, chr, cs);
-        } else {
-            cmd = token_cmd(token_info(p));
-            chr = token_chr(token_info(p));
-            make_token_table(L, cmd, chr, 0);
-        }
-        lua_rawseti(L, -2, i++);
-        p = token_link(p);
-    }
-}
-
-void tokenlist_to_luastring(lua_State * L, int p)
-{
-    int l;
-    char *s;
-    s = tokenlist_to_cstring(p, 1, &l);
-    lua_pushlstring(L, s, (size_t) l);
-    free(s);
-}
-
-int tokenlist_from_lua(lua_State * L)
-{
-    const char *s;
-    int tok, t;
-    size_t i, j;
-    halfword p, q, r;
-    r = get_avail();
-    token_info(r) = 0;
-    token_link(r) = null;
-    p = r;
-    t = lua_type(L, -1);
-    if (t == LUA_TTABLE) {
-        j = lua_rawlen(L, -1);
-        if (j > 0) {
-            for (i = 1; i <= j; i++) {
-                lua_rawgeti(L, -1, (int) i);
-                tok = token_from_lua(L);
-                if (tok >= 0) {
-                    store_new_token(tok);
-                }
-                lua_pop(L, 1);
-            };
-        }
-        return r;
-    } else if (t == LUA_TSTRING) {
-        s = lua_tolstring(L, -1, &j);
-        for (i = 0; i < j; i++) {
-            if (s[i] == 32) {
-                tok = token_val(10, s[i]);
-            } else {
-                int j1 = (int) str2uni((const unsigned char *) (s + i));
-                i = i + (size_t) (utf8_size(j1) - 1);
-                tok = token_val(12, j1);
-            }
-            store_new_token(tok);
-        }
-        return r;
-    } else {
-        free_avail(r);
-        return null;
-    }
-}
-
-/*
-static void do_get_token_lua(int callback_id)
-{
-    while (1) {
-        if (!get_callback(Luas, callback_id)) {
-            get_next();
-            lua_pop(Luas, 2);
-            break;
-        }
-        if (lua_pcall(Luas, 0, 1, 0) != 0) {
-            tex_error(lua_tostring(Luas, -1), NULL);
-            lua_pop(Luas, 2);
-            break;
-        }
-        if (lua_istable(Luas, -1)) {
-            lua_rawgeti(Luas, -1, 1);
-            if (lua_istable(Luas, -1)) {
-                int p, q, r;
-                size_t i, j;
-                lua_pop(Luas, 1);
-                r = get_avail();
-                p = r;
-                j = lua_rawlen(Luas, -1);
-                if (j > 0) {
-                    for (i = 1; i <= j; i++) {
-                        lua_rawgeti(Luas, -1, (int) i);
-                        if (get_cur_cmd(Luas) || get_cur_cs(Luas)) {
-                            store_new_token(cur_tok);
-                        }
-                        lua_pop(Luas, 1);
-                    }
-                }
-                if (p != r) {
-                    p = token_link(r);
-                    free_avail(r);
-                    begin_token_list(p, inserted);
-                    cur_input.nofilter_field = true;
-                    get_next();
-                } else {
-                    tex_error("error: illegal or empty token list returned", NULL);
-                }
-                lua_pop(Luas, 2);
-                break;
-            } else {
-                lua_pop(Luas, 1);
-                if (get_cur_cmd(Luas) || get_cur_cs(Luas)) {
-                    lua_pop(Luas, 2);
-                    break;
-                } else {
-                    lua_pop(Luas, 2);
-                    continue;
-                }
-            }
-        } else {
-            lua_pop(Luas, 2);
-        }
-    }
-    return;
-}
-*/
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lua/luatoken.w
@@ -0,0 +1,424 @@
+% luatoken.w
+%
+% Copyright 2006-2012 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
+command_item command_names[] = {
+    {"relax", relax_cmd, NULL},
+    {"left_brace", left_brace_cmd, NULL},
+    {"right_brace", right_brace_cmd, NULL},
+    {"math_shift", math_shift_cmd, NULL},
+    {"tab_mark", tab_mark_cmd, NULL},
+    {"car_ret", car_ret_cmd, NULL},
+    {"mac_param", mac_param_cmd, NULL},
+    {"sup_mark", sup_mark_cmd, NULL},
+    {"sub_mark", sub_mark_cmd, NULL},
+    {"endv", endv_cmd, NULL},
+    {"spacer", spacer_cmd, NULL},
+    {"letter", letter_cmd, NULL},
+    {"other_char", other_char_cmd, NULL},
+    {"par_end", par_end_cmd, NULL},
+    {"stop", stop_cmd, NULL},
+    {"delim_num", delim_num_cmd, NULL},
+    {"char_num", char_num_cmd, NULL},
+    {"math_char_num", math_char_num_cmd, NULL},
+    {"mark", mark_cmd, NULL},
+    {"xray", xray_cmd, NULL},
+    {"make_box", make_box_cmd, NULL},
+    {"hmove", hmove_cmd, NULL},
+    {"vmove", vmove_cmd, NULL},
+    {"un_hbox", un_hbox_cmd, NULL},
+    {"un_vbox", un_vbox_cmd, NULL},
+    {"remove_item", remove_item_cmd, NULL},
+    {"hskip", hskip_cmd, NULL},
+    {"vskip", vskip_cmd, NULL},
+    {"mskip", mskip_cmd, NULL},
+    {"kern", kern_cmd, NULL},
+    {"mkern", mkern_cmd, NULL},
+    {"leader_ship", leader_ship_cmd, NULL},
+    {"halign", halign_cmd, NULL},
+    {"valign", valign_cmd, NULL},
+    {"no_align", no_align_cmd, NULL},
+    {"novrule", no_vrule_cmd, NULL},
+    {"nohrule", no_hrule_cmd, NULL},
+    {"vrule", vrule_cmd, NULL},
+    {"hrule", hrule_cmd, NULL},
+    {"insert", insert_cmd, NULL},
+    {"vadjust", vadjust_cmd, NULL},
+    {"ignore_spaces", ignore_spaces_cmd, NULL},
+    {"after_assignment", after_assignment_cmd, NULL},
+    {"after_group", after_group_cmd, NULL},
+    {"break_penalty", break_penalty_cmd, NULL},
+    {"start_par", start_par_cmd, NULL},
+    {"ital_corr", ital_corr_cmd, NULL},
+    {"accent", accent_cmd, NULL},
+    {"math_accent", math_accent_cmd, NULL},
+    {"discretionary", discretionary_cmd, NULL},
+    {"eq_no", eq_no_cmd, NULL},
+    {"left_right", left_right_cmd, NULL},
+    {"math_comp", math_comp_cmd, NULL},
+    {"limit_switch", limit_switch_cmd, NULL},
+    {"above", above_cmd, NULL},
+    {"math_style", math_style_cmd, NULL},
+    {"math_choice", math_choice_cmd, NULL},
+    {"non_script", non_script_cmd, NULL},
+    {"vcenter", vcenter_cmd, NULL},
+    {"case_shift", case_shift_cmd, NULL},
+    {"message", message_cmd, NULL},
+    {"normal", normal_cmd, NULL},
+    {"extension", extension_cmd, NULL},
+    {"option", option_cmd, NULL},
+    {"in_stream", in_stream_cmd, NULL},
+    {"begin_group", begin_group_cmd, NULL},
+    {"end_group", end_group_cmd, NULL},
+    {"omit", omit_cmd, NULL},
+    {"ex_space", ex_space_cmd, NULL},
+    {"boundary", boundary_cmd, NULL},
+    {"radical", radical_cmd, NULL},
+    {"super_sub_script", super_sub_script_cmd, NULL},
+    {"no_super_sub_script", no_super_sub_script_cmd, NULL},
+    {"math_shift_cs", math_shift_cs_cmd, NULL},
+    {"end_cs_name", end_cs_name_cmd, NULL},
+    {"char_ghost", char_ghost_cmd, NULL},
+    {"assign_local_box", assign_local_box_cmd, NULL},
+    {"char_given", char_given_cmd, NULL},
+    {"math_given", math_given_cmd, NULL},
+    {"xmath_given", xmath_given_cmd, NULL},
+    {"last_item", last_item_cmd, NULL},
+    {"toks_register", toks_register_cmd, NULL},
+    {"assign_toks", assign_toks_cmd, NULL},
+    {"assign_int", assign_int_cmd, NULL},
+    {"assign_attr", assign_attr_cmd, NULL},
+    {"assign_dimen", assign_dimen_cmd, NULL},
+    {"assign_glue", assign_glue_cmd, NULL},
+    {"assign_mu_glue", assign_mu_glue_cmd, NULL},
+    {"assign_font_dimen", assign_font_dimen_cmd, NULL},
+    {"assign_font_int", assign_font_int_cmd, NULL},
+    {"assign_hang_indent", assign_hang_indent_cmd, NULL},
+    {"set_aux", set_aux_cmd, NULL},
+    {"set_prev_graf", set_prev_graf_cmd, NULL},
+    {"set_page_dimen", set_page_dimen_cmd, NULL},
+    {"set_page_int", set_page_int_cmd, NULL},
+    {"set_box_dimen", set_box_dimen_cmd, NULL},
+    {"set_tex_shape", set_tex_shape_cmd, NULL},
+    {"set_etex_shape", set_etex_shape_cmd, NULL},
+    {"def_char_code", def_char_code_cmd, NULL},
+    {"def_del_code", def_del_code_cmd, NULL},
+    {"extdef_math_code", extdef_math_code_cmd, NULL},
+    {"extdef_del_code", extdef_del_code_cmd, NULL},
+    {"def_family", def_family_cmd, NULL},
+    {"set_math_param", set_math_param_cmd, NULL},
+    {"set_font", set_font_cmd, NULL},
+    {"def_font", def_font_cmd, NULL},
+    {"register", register_cmd, NULL},
+    {"assign_box_dir", assign_box_dir_cmd, NULL},
+    {"assign_dir", assign_dir_cmd, NULL},
+    {"advance", advance_cmd, NULL},
+    {"multiply", multiply_cmd, NULL},
+    {"divide", divide_cmd, NULL},
+    {"prefix", prefix_cmd, NULL},
+    {"let", let_cmd, NULL},
+    {"shorthand_def", shorthand_def_cmd, NULL},
+    {"read_to_cs", read_to_cs_cmd, NULL},
+    {"def", def_cmd, NULL},
+    {"set_box", set_box_cmd, NULL},
+    {"hyph_data", hyph_data_cmd, NULL},
+    {"set_interaction", set_interaction_cmd, NULL},
+    {"letterspace_font", letterspace_font_cmd, NULL},
+    {"expand_font",expand_font_cmd, NULL},
+    {"copy_font", copy_font_cmd, NULL},
+    {"set_font_id", set_font_id_cmd, NULL},
+    {"undefined_cs", undefined_cs_cmd, NULL},
+    {"expand_after", expand_after_cmd, NULL},
+    {"no_expand", no_expand_cmd, NULL},
+    {"input", input_cmd, NULL},
+    {"if_test", if_test_cmd, NULL},
+    {"fi_or_else", fi_or_else_cmd, NULL},
+    {"cs_name", cs_name_cmd, NULL},
+    {"convert", convert_cmd, NULL},
+    {"variable", variable_cmd, NULL},
+    {"feedback", feedback_cmd, NULL},
+    {"the", the_cmd, NULL},
+    {"combinetoks", combine_toks_cmd, NULL},
+    {"top_bot_mark", top_bot_mark_cmd, NULL},
+    {"call", call_cmd, NULL},
+    {"long_call", long_call_cmd, NULL},
+    {"outer_call", outer_call_cmd, NULL},
+    {"long_outer_call", long_outer_call_cmd, NULL},
+    {"end_template", end_template_cmd, NULL},
+    {"dont_expand", dont_expand_cmd, NULL},
+    {"glue_ref", glue_ref_cmd, NULL},
+    {"shape_ref", shape_ref_cmd, NULL},
+    {"box_ref", box_ref_cmd, NULL},
+    {"data", data_cmd, NULL},
+    {NULL, 0, NULL}
+};
+
+
+@ @c
+int get_command_id(const char *s)
+{
+    int i;
+    int cmd = -1;
+    for (i = 0; command_names[i].cmd_name != NULL; i++) {
+        if (strcmp(s, command_names[i].cmd_name) == 0)
+            break;
+    }
+    if (command_names[i].cmd_name != NULL) {
+        cmd = i;
+    }
+    return cmd;
+}
+
+/*
+static int get_cur_cmd(lua_State * L)
+{
+    int r = 0;
+    size_t len = lua_rawlen(L, -1);
+    cur_cs = 0;
+    if (len == 3 || len == 2) {
+        r = 1;
+        lua_rawgeti(L, -1, 1);
+        cur_cmd = (int) lua_tointeger(L, -1);
+        lua_rawgeti(L, -2, 2);
+        cur_chr = (halfword) lua_tointeger(L, -1);
+        if (len == 3) {
+            lua_rawgeti(L, -3, 3);
+            cur_cs = (halfword) lua_tointeger(L, -1);
+        }
+        lua_pop(L, (int) len);
+        if (cur_cs == 0)
+            cur_tok = token_val(cur_cmd, cur_chr);
+        else
+            cur_tok = cs_token_flag + cur_cs;
+    }
+    return r;
+}
+*/
+
+@ @c
+static int token_from_lua(lua_State * L)
+{
+    int cmd, chr;
+    int cs = 0;
+    size_t len = lua_rawlen(L, -1);
+    if (len == 3 || len == 2) {
+        lua_rawgeti(L, -1, 1);
+        cmd = (int) lua_tointeger(L, -1);
+        lua_rawgeti(L, -2, 2);
+        chr = (int) lua_tointeger(L, -1);
+        if (len == 3) {
+            lua_rawgeti(L, -3, 3);
+            cs = (int) lua_tointeger(L, -1);
+        }
+        lua_pop(L, (int) len);
+        if (cs == 0) {
+            return token_val(cmd, chr);
+        } else {
+            return cs_token_flag + cs;
+        }
+    }
+    return -1;
+}
+
+/*
+static int get_cur_cs(lua_State * L)
+{
+    const char *s;
+    unsigned j;
+    size_t l;
+    int cs;
+    int save_nncs;
+    int ret;
+    ret = 0;
+    cur_cs = 0;
+    lua_getfield(L, -1, "name");
+    if (lua_type(L, -1) == LUA_TSTRING) {
+        s = lua_tolstring(L, -1, &l);
+        if (l > 0) {
+            if ((last + (int) l) > buf_size)
+                check_buffer_overflow((last + (int) l));
+            for (j = 0; j < l; j++) {
+                buffer[(unsigned) last + 1 + j] = (packed_ASCII_code) * s++;
+            }
+            save_nncs = no_new_control_sequence;
+            no_new_control_sequence = false;
+            cs = id_lookup((last + 1), (int) l);
+            cur_tok = cs_token_flag + cs;
+            cur_cmd = eq_type(cs);
+            cur_chr = equiv(cs);
+            no_new_control_sequence = save_nncs;
+            ret = 1;
+        }
+    }
+    lua_pop(L, 1);
+    return ret;
+}
+*/
+
+@ @c
+void tokenlist_to_lua(lua_State * L, int p)
+{
+    int cmd, chr, cs;
+    int v;
+    int i = 1;
+    v = p;
+    while (v != null && v < (int) fix_mem_end) {
+        i++;
+        v = token_link(v);
+    }
+    i = 1;
+    lua_createtable(L, i, 0);
+    while (p != null && p < (int) fix_mem_end) {
+        if (token_info(p) >= cs_token_flag) {
+            cs = token_info(p) - cs_token_flag;
+            cmd = eq_type(cs);
+            chr = equiv(cs);
+            make_token_table(L, cmd, chr, cs);
+        } else {
+            cmd = token_cmd(token_info(p));
+            chr = token_chr(token_info(p));
+            make_token_table(L, cmd, chr, 0);
+        }
+        lua_rawseti(L, -2, i++);
+        p = token_link(p);
+    }
+}
+
+@ @c
+void tokenlist_to_luastring(lua_State * L, int p)
+{
+    int l;
+    char *s;
+    s = tokenlist_to_cstring(p, 1, &l);
+    lua_pushlstring(L, s, (size_t) l);
+    free(s);
+}
+
+
+@ @c
+int tokenlist_from_lua(lua_State * L)
+{
+    const char *s;
+    int tok, t;
+    size_t i, j;
+    halfword p, q, r;
+    r = get_avail();
+    token_info(r) = 0;          /* ref count */
+    token_link(r) = null;
+    p = r;
+    t = lua_type(L, -1);
+    if (t == LUA_TTABLE) {
+        j = lua_rawlen(L, -1);
+        if (j > 0) {
+            for (i = 1; i <= j; i++) {
+                lua_rawgeti(L, -1, (int) i);
+                tok = token_from_lua(L);
+                if (tok >= 0) {
+                    store_new_token(tok);
+                }
+                lua_pop(L, 1);
+            };
+        }
+        return r;
+    } else if (t == LUA_TSTRING) {
+        s = lua_tolstring(L, -1, &j);
+        for (i = 0; i < j; i++) {
+            if (s[i] == 32) {
+                tok = token_val(10, s[i]);
+            } else {
+                int j1 = (int) str2uni((const unsigned char *) (s + i));
+                i = i + (size_t) (utf8_size(j1) - 1);
+                tok = token_val(12, j1);
+            }
+            store_new_token(tok);
+        }
+        return r;
+    } else {
+        free_avail(r);
+        return null;
+    }
+}
+
+/*
+
+static void do_get_token_lua(int callback_id)
+{
+    while (1) {
+        if (!get_callback(Luas, callback_id)) {
+            get_next();
+            lua_pop(Luas, 2);
+            break;
+        }
+        if (lua_pcall(Luas, 0, 1, 0) != 0) {
+            tex_error(lua_tostring(Luas, -1), NULL);
+            lua_pop(Luas, 2);
+            break;
+        }
+        if (lua_istable(Luas, -1)) {
+            lua_rawgeti(Luas, -1, 1);
+            if (lua_istable(Luas, -1)) {
+                int p, q, r;
+                size_t i, j;
+                lua_pop(Luas, 1);
+                r = get_avail();
+                p = r;
+                j = lua_rawlen(Luas, -1);
+                if (j > 0) {
+                    for (i = 1; i <= j; i++) {
+                        lua_rawgeti(Luas, -1, (int) i);
+                        if (get_cur_cmd(Luas) || get_cur_cs(Luas)) {
+                            store_new_token(cur_tok);
+                        }
+                        lua_pop(Luas, 1);
+                    }
+                }
+                if (p != r) {
+                    p = token_link(r);
+                    free_avail(r);
+                    begin_token_list(p, inserted);
+                    cur_input.nofilter_field = true;
+                    get_next();
+                } else {
+                    tex_error("error: illegal or empty token list returned", NULL);
+                }
+                lua_pop(Luas, 2);
+                break;
+            } else {
+                lua_pop(Luas, 1);
+                if (get_cur_cmd(Luas) || get_cur_cs(Luas)) {
+                    lua_pop(Luas, 2);
+                    break;
+                } else {
+                    lua_pop(Luas, 2);
+                    continue;
+                }
+            }
+        } else {
+            lua_pop(Luas, 2);
+        }
+    }
+    return;
+}
+
+*/
--- texlive-bin.orig/texk/web2c/luatexdir/lua/mplibstuff.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-/*tex
-
-The \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.
-
-*/
-
-#include <stdio.h>
-
-extern void normal_warning(const char *t, const char *p);
-
-extern int lua_only;
-
-#define mplibstuff_message(MSG) do { \
-    if (lua_only) { \
-        fprintf(stdout,"mplib: " #MSG " not available.\n"); \
-    } else { \
-        normal_warning("mplib",  #MSG " not available."); \
-    } \
-} while (0)
-
-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);
-
-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);
-
-void *mp_initialize_binary_math(void *mp);
-
-
-void mp_png_backend_initialize (void *mp)                         { return; }
-void mp_png_backend_free (void *mp)                               { return; }
-int mp_png_gr_ship_out (void *hh, void *options, int standalone)  { mplibstuff_message(png backend); return 1; }
-int mp_png_ship_out (void *hh, const char *options)               { mplibstuff_message(png backend); return 1; }
-
-void mp_svg_backend_initialize (void *mp)                         { return; }
-void mp_svg_backend_free (void *mp)                               { return; }
-int mp_svg_ship_out (void *hh, int prologues)                     { mplibstuff_message(svg bakend); return 1; }
-int mp_svg_gr_ship_out (void *hh, int qprologues, int standalone) { mplibstuff_message(svg backend); return 1; }
-
-
-void *mp_initialize_binary_math(void *mp)                         {mplibstuff_message(math binary);return NULL; }
-
-const char* cairo_version_string (void);
-const char* mpfr_get_version(void);
-const char* pixman_version_string (void);
-
-
-#define CAIRO_VERSION_STRING "CAIRO NOT AVAILABLE"
-const char *COMPILED_CAIRO_VERSION_STRING = CAIRO_VERSION_STRING;
-
-#define MPFR_VERSION_STRING "MPFR NOT AVAILABLE"
-const char *COMPILED_MPFR_VERSION_STRING = MPFR_VERSION_STRING;
-
-
-#define __GNU_MP_VERSION -1
-#define __GNU_MP_VERSION_MINOR -1
-#define __GNU_MP_VERSION_PATCHLEVEL -1 
-int COMPILED__GNU_MP_VERSION = __GNU_MP_VERSION ;
-int COMPILED__GNU_MP_VERSION_MINOR = __GNU_MP_VERSION_MINOR ;
-int COMPILED__GNU_MP_VERSION_PATCHLEVEL = __GNU_MP_VERSION_PATCHLEVEL ;
-const char * const COMPILED_gmp_version="GMP NOT AVAILABLE";
-
-#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* mpfr_get_version(void)
-{
-    return MPFR_VERSION_STRING;
-}
-
-const char* pixman_version_string (void)
-{
-    return PIXMAN_VERSION_STRING;
-}
-
-char png_libpng_ver[] = "PNG NOT AVAILABLE";
-
-
-
--- /dev/null
+++ texlive-bin/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";
+
--- texlive-bin.orig/texk/web2c/luatexdir/lua/texluac.c
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
-
-texluac.w
-
-Copyright (C) 1994-2007 Lua.org, PUC-Rio.  All rights reserved.
-Copyright 2006-2013 Taco Hoekwater <taco@@luatex.org>
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-This file is part of LuaTeX.
-
-*/
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define luac_c
-#define LUA_CORE
-
-#include "lua.h"
-#include "lauxlib.h"
-
-#include "ldebug.h"
-#include "ldo.h"
-#include "lfunc.h"
-#include "lmem.h"
-#include "lobject.h"
-#include "lopcodes.h"
-#include "lstring.h"
-#include "lundump.h"
-
-#include "lua/luatex-api.h"
-
-static void PrintFunction(const Proto* f, int full);
-
-#define luaU_print PrintFunction
-
-/*tex A fix for non-gcc compilation: */
-
-#if !defined(__GNUC__) || (__GNUC__ < 2)
-# define __attribute__(x)
-#endif
-
-/*tex default program name */
-
-#define PROGNAME "texluac"
-
-/*tex default output file */
-
-#define OUTPUT PROGNAME ".out"
-
-/*tex list bytecodes? */
-
-static int listing = 0;
-
-/*tex dump bytecodes? */
-
-static int dumping = 1;
-
-/*tex strip debug information? */
-
-static int stripping = 0;
-
-/*tex default output file name */
-
-static char Output[] = { OUTPUT };
-
-/*tex actual output file name */
-
-static const char *output = Output;
-
-/*tex actual program name */
-
-static const char *progname = PROGNAME;
-
-__attribute__ ((noreturn))
-static void fatal(const char *message) {
-    fprintf(stderr,"%s: %s\n",progname,message);
-    exit(EXIT_FAILURE);
-}
-
-__attribute__ ((noreturn))
-static void cannot(const char *what) {
-    fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
-    exit(EXIT_FAILURE);
-}
-
-__attribute__ ((noreturn))
-static void usage(const char* message)
-{
-    if (*message=='-')
-        fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message);
-    else
-        fprintf(stderr,"%s: %s\n",progname,message);
-    fprintf(stderr,
-        "usage: %s [options] [filenames]\n"
-        "Available options are:\n"
-        "  -l       list (use -l -l for full listing)\n"
-        "  -o name  output to file " LUA_QL("name") " (default is \"%s\")\n"
-        "  -p       parse only\n"
-        "  -s       strip debug information\n"
-        "  -v       show version information\n"
-        "  --       stop handling options\n"
-        "  -        stop handling options and process stdin\n"
-        ,progname,Output);
-    exit(EXIT_FAILURE);
-}
-
-#define IS(s) (strcmp(argv[i],s)==0)
-
-static int doargs(int argc, char* argv[])
-{
-    int i;
-    int version=0;
-    if (argv[0]!=NULL && *argv[0]!=0)
-        progname=argv[0];
-    for (i=1; i<argc; i++) {
-        if (*argv[i]!='-') {
-            /* end of options; keep it */
-            break;
-        } else if (IS("--")) {
-            /* end of options; skip it */
-            ++i;
-            if (version) ++version;
-                break;
-        } else if (IS("-")) {
-            /* end of options; use stdin */
-            break;
-        } else if (IS("-l")) {
-            /* list */
-            ++listing;
-        } else if (IS("-o")) {
-            /* output file */
-            output=argv[++i];
-            if (output==NULL || *output==0 || (*output=='-' && output[1]!=0))
-                usage(LUA_QL("-o") " needs argument");
-            if (IS("-"))
-                output=NULL;
-        } else if (IS("-p")) {
-            /* parse only */
-            dumping=0;
-        } else if (IS("-s")) {
-            /* strip debug information */
-            stripping=1;
-        } else if (IS("-v")) {
-            /* show version */
-            ++version;
-        } else {
-            /* unknown option */
-            usage(argv[i]);
-        }
-    }
-    if (i==argc && (listing || !dumping)) {
-        dumping=0;
-        argv[--i]=Output;
-    }
-    if (version) {
-        printf("%s\n",LUA_COPYRIGHT);
-        if (version==argc-1)
-            exit(EXIT_SUCCESS);
-    }
-    return i;
-}
-
-#define FUNCTION "(function()end)();"
-
-static const char* reader(lua_State *L, void *ud, size_t *size)
-{
-    UNUSED(L);
-    if ((*(int*)ud)--) {
-        *size=sizeof(FUNCTION)-1;
-        return FUNCTION;
-    } else {
-        *size=0;
-        return NULL;
-    }
-}
-
-#define toproto(L,i) getproto(L->top+(i))
-
-static const Proto* combine(lua_State* L, int n)
-{
-    if (n==1) {
-        return toproto(L,-1);
-    } else {
-        Proto* f;
-        int i=n;
-        if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK)
-            fatal(lua_tostring(L,-1));
-        f=toproto(L,-1);
-        for (i=0; i<n; i++) {
-            f->p[i]=toproto(L,i-n-1);
-            if (f->p[i]->sizeupvalues>0)
-                f->p[i]->upvalues[0].instack=0;
-        }
-        f->sizelineinfo=0;
-        return f;
-    }
-}
-
-static int writer(lua_State* L, const void* p, size_t size, void* u)
-{
-    UNUSED(L);
-    return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
-}
-
-static int pmain(lua_State* L)
-{
-    int argc=(int)lua_tointeger(L,1);
-    char** argv=(char**)lua_touserdata(L,2);
-    const Proto* f;
-    int i;
-    if (!lua_checkstack(L,argc))
-        fatal("too many input files");
-    /*tex
-        Open standard libraries: we need to to this to keep the symbol
-        |luaL_openlibs|.
-    */
-    luaL_checkversion(L);
-    /*tex stop collector during initialization */
-    lua_gc(L, LUA_GCSTOP, 0);
-    /*tex open libraries */
-    luaL_openlibs(L);
-    lua_gc(L, LUA_GCRESTART, 0);
-    for (i=0; i<argc; i++) {
-        const char* filename=IS("-") ? NULL : argv[i];
-    if (luaL_loadfile(L,filename)!=LUA_OK)
-        fatal(lua_tostring(L,-1));
-    }
-    f=combine(L,argc);
-    if (listing)
-        luaU_print(f,listing>1);
-    if (dumping) {
-        FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
-        if (D==NULL)
-                cannot("open");
-        lua_lock(L);
-        luaU_dump(L,f,writer,D,stripping);
-        lua_unlock(L);
-        if (ferror(D))
-            cannot("write");
-        if (fclose(D))
-            cannot("close");
-    }
-    return 0;
-}
-
-int luac_main(int ac, char *av[])
-{
-    lua_State *L;
-    int i = doargs(ac, av);
-    ac -= i;
-    av += i;
-    if (ac <= 0)
-        usage("no input files given");
-    L=luaL_newstate();
-    if (L == NULL)
-        fatal("not enough memory for state");
-    lua_pushcfunction(L,&pmain);
-    lua_pushinteger(L,ac);
-    lua_pushlightuserdata(L,av);
-    if (lua_pcall(L,2,0,0)!=LUA_OK)
-      fatal(lua_tostring(L,-1));
-    lua_close(L);
-    return EXIT_SUCCESS;
-}
-
-/*
-    See Copyright Notice in |lua.h|.
-*/
-
-#define VOID(p)		((const void*)(p))
-
-#if (defined(LuajitTeX)) || (LUA_VERSION_NUM == 502)
-#define TSVALUE(o) rawtsvalue(o)
-#endif
-#if (LUA_VERSION_NUM == 503)
-#define TSVALUE(o) tsvalue(o)
-#endif
-
-static void PrintString(const TString* ts)
-{
-    const char* s=getstr(ts);
-    size_t i,n;
-#if (defined(LuajitTeX)) || (LUA_VERSION_NUM == 502)
-    n=ts->tsv.len;
-#endif
-#if (LUA_VERSION_NUM == 503)
-    n=(ts->tt == LUA_TSHRSTR ? ts->shrlen : ts->u.lnglen);
-#endif
-    printf("%c",'"');
-    for (i=0; i<n; i++) {
-        int c=(int)(unsigned char)s[i];
-        switch (c) {
-            case '"':
-                printf("\\\"");
-                break;
-            case '\\':
-                printf("\\\\");
-                break;
-            case '\a':
-                printf("\\a");
-            break;
-            case '\b':
-                printf("\\b");
-                break;
-            case '\f':
-                printf("\\f");
-                break;
-            case '\n':
-                printf("\\n");
-                break;
-            case '\r':
-                printf("\\r");
-                break;
-            case '\t':
-                printf("\\t");
-                break;
-            case '\v':
-                printf("\\v");
-                break;
-            default  :
-                if (isprint(c))
-                    printf("%c",c);
-                else
-                    printf("\\%03d",c);
-                break;
-        }
-    }
-    printf("%c",'"');
-}
-
-static void PrintConstant(const Proto* f, int i)
-{
-    const TValue* o=&f->k[i];
-    switch (ttype(o)) {
-        case LUA_TNIL:
-            printf("nil");
-            break;
-        case LUA_TBOOLEAN:
-            printf(bvalue(o) ? "true" : "false");
-            break;
-        case LUA_TNUMBER:
-            printf(LUA_NUMBER_FMT,nvalue(o));
-            break;
-        case LUA_TSTRING:
-            PrintString(TSVALUE(o));
-            break;
-        default:
-            /*tex This cannot happen. */
-            printf("? type=%d",ttype(o));
-            break;
-    }
-}
-
-#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
-#define MYK(x) (-1-(x))
-
-static void PrintCode(const Proto* f)
-{
-    const Instruction* code=f->code;
-    int pc,n=f->sizecode;
-    for (pc=0; pc<n; pc++) {
-        Instruction i=code[pc];
-        OpCode o=GET_OPCODE(i);
-        int a=GETARG_A(i);
-        int b=GETARG_B(i);
-        int c=GETARG_C(i);
-        int ax=GETARG_Ax(i);
-        int bx=GETARG_Bx(i);
-        int sbx=GETARG_sBx(i);
-        int line=getfuncline(f,pc);
-        printf("\t%d\t",pc+1);
-        if (line>0)
-            printf("[%d]\t",line);
-        else
-            printf("[-]\t");
-        printf("%-9s\t",luaP_opnames[o]);
-        switch (getOpMode(o)) {
-            case iABC:
-                printf("%d",a);
-                if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (MYK(INDEXK(b))) : b);
-                if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (MYK(INDEXK(c))) : c);
-                break;
-            case iABx:
-                printf("%d",a);
-                if (getBMode(o)==OpArgK) printf(" %d",MYK(bx));
-                if (getBMode(o)==OpArgU) printf(" %d",bx);
-                break;
-            case iAsBx:
-                printf("%d %d",a,sbx);
-                break;
-            case iAx:
-                printf("%d",MYK(ax));
-                break;
-        }
-        switch (o) {
-            case OP_LOADK:
-                printf("\t; "); PrintConstant(f,bx);
-                break;
-            case OP_GETUPVAL:
-            case OP_SETUPVAL:
-                printf("\t; %s",UPVALNAME(b));
-                break;
-            case OP_GETTABUP:
-                printf("\t; %s",UPVALNAME(b));
-                if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); }
-                break;
-            case OP_SETTABUP:
-                printf("\t; %s",UPVALNAME(a));
-                if (ISK(b)) { printf(" "); PrintConstant(f,INDEXK(b)); }
-                if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); }
-                break;
-            case OP_GETTABLE:
-            case OP_SELF:
-                if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); }
-                break;
-            case OP_SETTABLE:
-            case OP_ADD:
-            case OP_SUB:
-            case OP_MUL:
-            case OP_DIV:
-            case OP_POW:
-            case OP_EQ:
-            case OP_LT:
-            case OP_LE:
-                if (ISK(b) || ISK(c)) {
-                    printf("\t; ");
-                    if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-");
-                    printf(" ");
-                    if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-");
-                }
-                break;
-            case OP_JMP:
-            case OP_FORLOOP:
-            case OP_FORPREP:
-            case OP_TFORLOOP:
-                printf("\t; to %d",sbx+pc+2);
-                break;
-            case OP_CLOSURE:
-                printf("\t; %p",VOID(f->p[bx]));
-                break;
-            case OP_SETLIST:
-                if (c==0) printf("\t; %d",(int)code[++pc]); else printf("\t; %d",c);
-                break;
-            case OP_EXTRAARG:
-                printf("\t; "); PrintConstant(f,ax);
-                break;
-            default:
-                break;
-        }
-        printf("\n");
-    }
-}
-
-#define SS(x) ((x==1)?"":"s")
-#define S(x)  (int)(x),SS(x)
-
-static void PrintHeader(const Proto* f)
-{
-    const char* s=f->source ? getstr(f->source) : "=?";
-    if (*s=='@' || *s=='=')
-        s++;
-    else if (*s==LUA_SIGNATURE[0])
-        s="(bstring)";
-    else
-        s="(string)";
-    printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
-        (f->linedefined==0)?"main":"function",s,
-        f->linedefined,f->lastlinedefined,
-        S(f->sizecode),VOID(f));
-    printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
-        (int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams),
-        S(f->maxstacksize),S(f->sizeupvalues));
-    printf("%d local%s, %d constant%s, %d function%s\n",
-        S(f->sizelocvars),S(f->sizek),S(f->sizep));
-}
-
-static void PrintDebug(const Proto* f)
-{
-    int i,n;
-    n=f->sizek;
-    printf("constants (%d) for %p:\n",n,VOID(f));
-    for (i=0; i<n; i++) {
-        printf("\t%d\t",i+1);
-        PrintConstant(f,i);
-        printf("\n");
-    }
-    n=f->sizelocvars;
-    printf("locals (%d) for %p:\n",n,VOID(f));
-    for (i=0; i<n; i++) {
-        printf("\t%d\t%s\t%d\t%d\n",
-        i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
-    }
-    n=f->sizeupvalues;
-    printf("upvalues (%d) for %p:\n",n,VOID(f));
-    for (i=0; i<n; i++) {
-        printf("\t%d\t%s\t%d\t%d\n",
-        i,UPVALNAME(i),f->upvalues[i].instack,f->upvalues[i].idx);
-    }
-}
-
-static void PrintFunction(const Proto* f, int full)
-{
-    int i,n=f->sizep;
-    PrintHeader(f);
-    PrintCode(f);
-    if (full)
-        PrintDebug(f);
-    for (i=0; i<n; i++)
-        PrintFunction(f->p[i],full);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lua/texluac.w
@@ -0,0 +1,495 @@
+% texluac.w
+%
+% Copyright (C) 1994-2007 Lua.org, PUC-Rio.  All rights reserved.
+% Copyright 2006-2013 Taco Hoekwater <taco@@luatex.org>
+%
+% Permission is hereby granted, free of charge, to any person obtaining
+% a copy of this software and associated documentation files (the
+% "Software"), to deal in the Software without restriction, including
+% without limitation the rights to use, copy, modify, merge, publish,
+% distribute, sublicense, and/or sell copies of the Software, and to
+% permit persons to whom the Software is furnished to do so, subject to
+% the following conditions:
+%
+% The above copyright notice and this permission notice shall be
+% included in all copies or substantial portions of the Software.
+%
+% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+% IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+% CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+% TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+% SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+%
+% This file is part of LuaTeX.
+
+@ @c
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define luac_c
+#define LUA_CORE
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstring.h"
+#include "lundump.h"
+
+#include "lua/luatex-api.h"
+
+static void PrintFunction(const Proto* f, int full);
+#define luaU_print	PrintFunction
+
+@ @c
+/*  fix for non-gcc compilation: */
+#if !defined(__GNUC__) || (__GNUC__ < 2) 
+# define __attribute__(x)
+#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
+
+@ @c
+#define PROGNAME        "texluac"       /* default program name */
+#define OUTPUT          PROGNAME ".out" /* default output file */
+
+static int listing=0;			/* list bytecodes? */
+static int dumping = 1;         /* dump bytecodes? */
+static int stripping = 0;       /* strip debug information? */
+static char Output[] = { OUTPUT };      /* default output file name */
+
+static const char *output = Output;     /* actual output file name */
+static const char *progname = PROGNAME; /* actual program name */
+
+@ @c
+__attribute__ ((noreturn))
+static void fatal(const char *message)
+{
+ fprintf(stderr,"%s: %s\n",progname,message);
+ exit(EXIT_FAILURE);
+}
+
+@ @c
+__attribute__ ((noreturn))
+static void cannot(const char *what)
+{
+ fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+@ @c
+__attribute__ ((noreturn))
+static void usage(const char* message)
+{
+ if (*message=='-')
+  fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message);
+ else
+  fprintf(stderr,"%s: %s\n",progname,message);
+ fprintf(stderr,
+  "usage: %s [options] [filenames]\n"
+  "Available options are:\n"
+  "  -l       list (use -l -l for full listing)\n"
+  "  -o name  output to file " LUA_QL("name") " (default is \"%s\")\n"
+  "  -p       parse only\n"
+  "  -s       strip debug information\n"
+  "  -v       show version information\n"
+  "  --       stop handling options\n"
+  "  -        stop handling options and process stdin\n"
+  ,progname,Output);
+ exit(EXIT_FAILURE);
+}
+
+@ @c
+#define IS(s)   (strcmp(argv[i],s)==0)
+
+static int doargs(int argc, char* argv[])
+{
+ int i;
+ int version=0;
+ if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
+ for (i=1; i<argc; i++)
+ {
+  if (*argv[i]!='-')			/* end of options; keep it */
+   break;
+  else if (IS("--"))			/* end of options; skip it */
+  {
+   ++i;
+   if (version) ++version;
+   break;
+  }
+  else if (IS("-"))			/* end of options; use stdin */
+   break;
+  else if (IS("-l"))			/* list */
+   ++listing;
+  else if (IS("-o"))			/* output file */
+  {
+   output=argv[++i];
+   if (output==NULL || *output==0 || (*output=='-' && output[1]!=0))
+    usage(LUA_QL("-o") " needs argument");
+   if (IS("-")) output=NULL;
+  }
+  else if (IS("-p"))			/* parse only */
+   dumping=0;
+  else if (IS("-s"))			/* strip debug information */
+   stripping=1;
+  else if (IS("-v"))			/* show version */
+   ++version;
+  else					/* unknown option */
+   usage(argv[i]);
+ }
+ if (i==argc && (listing || !dumping))
+ {
+  dumping=0;
+  argv[--i]=Output;
+ }
+ if (version)
+ {
+  printf("%s\n",LUA_COPYRIGHT);
+  if (version==argc-1) exit(EXIT_SUCCESS);
+ }
+ return i;
+}
+
+@ @c
+#define FUNCTION "(function()end)();"
+
+static const char* reader(lua_State *L, void *ud, size_t *size)
+{
+ UNUSED(L);
+ if ((*(int*)ud)--)
+ {
+  *size=sizeof(FUNCTION)-1;
+  return FUNCTION;
+ }
+ else
+ {
+  *size=0;
+  return NULL;
+ }
+}
+
+#define toproto(L,i) getproto(L->top+(i))
+
+static const Proto* combine(lua_State* L, int n)
+{
+ if (n==1)
+  return toproto(L,-1);
+ else
+ {
+  Proto* f;
+  int i=n;
+  if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1));
+  f=toproto(L,-1);
+  for (i=0; i<n; i++)
+  {
+   f->p[i]=toproto(L,i-n-1);
+   if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0;
+  }
+  f->sizelineinfo=0;
+  return f;
+ }
+}
+
+@ @c
+static int writer(lua_State* L, const void* p, size_t size, void* u)
+{
+ UNUSED(L);
+ return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
+}
+
+static int pmain(lua_State* L)
+{
+ int argc=(int)lua_tointeger(L,1);
+ char** argv=(char**)lua_touserdata(L,2);
+ const Proto* f;
+ int i;
+ if (!lua_checkstack(L,argc)) fatal("too many input files");
+ /* open standard libraries:    */
+ /* we need to to this to keep */
+ /* the symbol luaL_openlibs   */ 
+ luaL_checkversion(L);
+ lua_gc(L, LUA_GCSTOP, 0);  /* stop collector during initialization */
+ luaL_openlibs(L);  /* open libraries */
+ lua_gc(L, LUA_GCRESTART, 0);
+ for (i=0; i<argc; i++)
+ {
+  const char* filename=IS("-") ? NULL : argv[i];
+  if (luaL_loadfile(L,filename)!=LUA_OK) fatal(lua_tostring(L,-1));
+ }
+ f=combine(L,argc);
+ if (listing) luaU_print(f,listing>1);
+ if (dumping)
+ {
+  FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
+  if (D==NULL) cannot("open");
+  lua_lock(L);
+  luaU_dump(L,f,writer,D,stripping);
+  lua_unlock(L);
+  if (ferror(D)) cannot("write");
+  if (fclose(D)) cannot("close");
+ }
+ return 0;
+}
+
+@ @c
+int luac_main(int ac, char *av[])
+{
+    lua_State *L;
+    int i = doargs(ac, av);
+    ac -= i;
+    av += i;
+    if (ac <= 0)
+        usage("no input files given");
+    L=luaL_newstate();
+    if (L == NULL)
+        fatal("not enough memory for state");
+    lua_pushcfunction(L,&pmain);
+    lua_pushinteger(L,ac);
+    lua_pushlightuserdata(L,av);
+    if (lua_pcall(L,2,0,0)!=LUA_OK) 
+      fatal(lua_tostring(L,-1));
+    lua_close(L);
+    return EXIT_SUCCESS;
+}
+
+/*
+** print bytecodes
+** See Copyright Notice in lua.h
+*/
+
+#define VOID(p)		((const void*)(p))
+
+#if (defined(LuajitTeX)) || (LUA_VERSION_NUM == 502) 
+#define TSVALUE(o) rawtsvalue(o)
+#endif
+#if (LUA_VERSION_NUM == 503) 
+#define TSVALUE(o) tsvalue(o)
+#endif
+
+
+static void PrintString(const TString* ts)
+{
+ const char* s=getstr(ts);
+ size_t i,n;
+#if (defined(LuajitTeX)) || (LUA_VERSION_NUM == 502) 
+  n=ts->tsv.len;
+#endif
+#if (LUA_VERSION_NUM == 503) 
+  n=(ts->tt == LUA_TSHRSTR ? ts->shrlen : ts->u.lnglen);
+#endif
+ printf("%c",'"');
+ for (i=0; i<n; i++)
+ {
+  int c=(int)(unsigned char)s[i];
+  switch (c)
+  {
+   case '"':  printf("\\\""); break;
+   case '\\': printf("\\\\"); break;
+   case '\a': printf("\\a"); break;
+   case '\b': printf("\\b"); break;
+   case '\f': printf("\\f"); break;
+   case '\n': printf("\\n"); break;
+   case '\r': printf("\\r"); break;
+   case '\t': printf("\\t"); break;
+   case '\v': printf("\\v"); break;
+   default:	if (isprint(c))
+   			printf("%c",c);
+		else
+			printf("\\%03d",c);
+  }
+ }
+ printf("%c",'"');
+}
+
+static void PrintConstant(const Proto* f, int i)
+{
+ const TValue* o=&f->k[i];
+ switch (ttype(o))
+ {
+  case LUA_TNIL:
+	printf("nil");
+	break;
+  case LUA_TBOOLEAN:
+	printf(bvalue(o) ? "true" : "false");
+	break;
+  case LUA_TNUMBER:
+	printf(LUA_NUMBER_FMT,nvalue(o));
+	break;
+  case LUA_TSTRING:
+        PrintString(TSVALUE(o));
+	break;
+  default:				/* cannot happen */
+	printf("? type=%d",ttype(o));
+	break;
+ }
+}
+
+#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
+#define MYK(x)		(-1-(x))
+
+static void PrintCode(const Proto* f)
+{
+ const Instruction* code=f->code;
+ int pc,n=f->sizecode;
+ for (pc=0; pc<n; pc++)
+ {
+  Instruction i=code[pc];
+  OpCode o=GET_OPCODE(i);
+  int a=GETARG_A(i);
+  int b=GETARG_B(i);
+  int c=GETARG_C(i);
+  int ax=GETARG_Ax(i);
+  int bx=GETARG_Bx(i);
+  int sbx=GETARG_sBx(i);
+  int line=getfuncline(f,pc);
+  printf("\t%d\t",pc+1);
+  if (line>0) printf("[%d]\t",line); else printf("[-]\t");
+  printf("%-9s\t",luaP_opnames[o]);
+  switch (getOpMode(o))
+  {
+   case iABC:
+    printf("%d",a);
+    if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (MYK(INDEXK(b))) : b);
+    if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (MYK(INDEXK(c))) : c);
+    break;
+   case iABx:
+    printf("%d",a);
+    if (getBMode(o)==OpArgK) printf(" %d",MYK(bx));
+    if (getBMode(o)==OpArgU) printf(" %d",bx);
+    break;
+   case iAsBx:
+    printf("%d %d",a,sbx);
+    break;
+   case iAx:
+    printf("%d",MYK(ax));
+    break;
+  }
+  switch (o)
+  {
+   case OP_LOADK:
+    printf("\t; "); PrintConstant(f,bx);
+    break;
+   case OP_GETUPVAL:
+   case OP_SETUPVAL:
+    printf("\t; %s",UPVALNAME(b));
+    break;
+   case OP_GETTABUP:
+    printf("\t; %s",UPVALNAME(b));
+    if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); }
+    break;
+   case OP_SETTABUP:
+    printf("\t; %s",UPVALNAME(a));
+    if (ISK(b)) { printf(" "); PrintConstant(f,INDEXK(b)); }
+    if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); }
+    break;
+   case OP_GETTABLE:
+   case OP_SELF:
+    if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); }
+    break;
+   case OP_SETTABLE:
+   case OP_ADD:
+   case OP_SUB:
+   case OP_MUL:
+   case OP_DIV:
+   case OP_POW:
+   case OP_EQ:
+   case OP_LT:
+   case OP_LE:
+    if (ISK(b) || ISK(c))
+    {
+     printf("\t; ");
+     if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-");
+     printf(" ");
+     if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-");
+    }
+    break;
+   case OP_JMP:
+   case OP_FORLOOP:
+   case OP_FORPREP:
+   case OP_TFORLOOP:
+    printf("\t; to %d",sbx+pc+2);
+    break;
+   case OP_CLOSURE:
+    printf("\t; %p",VOID(f->p[bx]));
+    break;
+   case OP_SETLIST:
+    if (c==0) printf("\t; %d",(int)code[++pc]); else printf("\t; %d",c);
+    break;
+   case OP_EXTRAARG:
+    printf("\t; "); PrintConstant(f,ax);
+    break;
+   default:
+    break;
+  }
+  printf("\n");
+ }
+}
+
+#define SS(x)	((x==1)?"":"s")
+#define S(x)	(int)(x),SS(x)
+
+static void PrintHeader(const Proto* f)
+{
+ const char* s=f->source ? getstr(f->source) : "=?";
+ if (*s=='@@' || *s=='=')
+  s++;
+ else if (*s==LUA_SIGNATURE[0])
+  s="(bstring)";
+ else
+  s="(string)";
+ printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
+ 	(f->linedefined==0)?"main":"function",s,
+	f->linedefined,f->lastlinedefined,
+	S(f->sizecode),VOID(f));
+ printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
+	(int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams),
+	S(f->maxstacksize),S(f->sizeupvalues));
+ printf("%d local%s, %d constant%s, %d function%s\n",
+	S(f->sizelocvars),S(f->sizek),S(f->sizep));
+}
+
+static void PrintDebug(const Proto* f)
+{
+ int i,n;
+ n=f->sizek;
+ printf("constants (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+  printf("\t%d\t",i+1);
+  PrintConstant(f,i);
+  printf("\n");
+ }
+ n=f->sizelocvars;
+ printf("locals (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+  printf("\t%d\t%s\t%d\t%d\n",
+  i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
+ }
+ n=f->sizeupvalues;
+ printf("upvalues (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+  printf("\t%d\t%s\t%d\t%d\n",
+  i,UPVALNAME(i),f->upvalues[i].instack,f->upvalues[i].idx);
+ }
+}
+
+static void PrintFunction(const Proto* f, int full)
+{
+ int i,n=f->sizep;
+ PrintHeader(f);
+ PrintCode(f);
+ if (full) PrintDebug(f);
+ for (i=0; i<n; i++) PrintFunction(f->p[i],full);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/lua/texluajitc.c
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
-
-    LuaJIT frontend. Runs commands, scripts, read-eval-print (REPL) etc.
-    Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h
-
-    Major portions taken verbatim or adapted from the Lua interpreter.
-    Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
-
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define luajit_c
-
-#include "lua.h"
-#include "lauxlib.h"
-#include "lualib.h"
-#include "luajit.h"
-
-#include "lj_arch.h"
-
-#if LJ_TARGET_POSIX
-#include <unistd.h>
-#define lua_stdin_is_tty()	isatty(0)
-#elif LJ_TARGET_WINDOWS
-#include <io.h>
-#ifdef __BORLANDC__
-#define lua_stdin_is_tty()	isatty(_fileno(stdin))
-#else
-#define lua_stdin_is_tty()	_isatty(_fileno(stdin))
-#endif
-#else
-#define lua_stdin_is_tty()	1
-#endif
-
-#if !LJ_TARGET_CONSOLE
-#include <signal.h>
-#endif
-
-#include "lua/luatex-api.h"
-
-static lua_State *globalL = NULL;
-static const char *progname = LUA_PROGNAME;
-
-#if !LJ_TARGET_CONSOLE
-
-static void lstop(lua_State *L, lua_Debug *ar)
-{
-  /*tex Unused arg. */
-  (void)ar;
-  lua_sethook(L, NULL, 0, 0);
-  /*tex Avoid luaL_error -- a C hook doesn't add an extra frame. */
-  luaL_where(L, 0);
-  lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1));
-  lua_error(L);
-}
-
-/*tex if another SIGINT happens before lstop, terminate process (default action) */
-
-static void laction(int i)
-{
-  signal(i, SIG_DFL);
-  lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
-}
-
-#endif
-
-static void print_usage(void)
-{
-    fprintf(stderr,
-    "usage: %s [options]... [script [args]...].\n"
-    "Available options are:\n"
-    "  -e chunk  Execute string " LUA_QL("chunk") ".\n"
-    "  -l name   Require library " LUA_QL("name") ".\n"
-    "  -b ...    Save or list bytecode.\n"
-    "  -j cmd    Perform LuaJIT control command.\n"
-    "  -O[opt]   Control LuaJIT optimizations.\n"
-    "  -i        Enter interactive mode after executing " LUA_QL("script") ".\n"
-    "  -v        Show version information.\n"
-    "  -E        Ignore environment variables.\n"
-    "  --        Stop handling options.\n"
-    "  -         Execute stdin and stop handling options.\n"
-    ,
-    progname);
-    fflush(stderr);
-}
-
-static void l_message(const char *pname, const char *msg)
-{
-    if (pname) fprintf(stderr, "%s: ", pname);
-    fprintf(stderr, "%s\n", msg);
-    fflush(stderr);
-}
-
-static int report(lua_State *L, int status)
-{
-    if (status && !lua_isnil(L, -1)) {
-        const char *msg = lua_tostring(L, -1);
-        if (msg == NULL)
-            msg = "(error object is not a string)";
-        l_message(progname, msg);
-        lua_pop(L, 1);
-    }
-    return status;
-}
-
-static int traceback(lua_State *L)
-{
-    if (!lua_isstring(L, 1)) {
-        /*tex Non-string error object? Try metamethod. */
-        if (lua_isnoneornil(L, 1) || !luaL_callmeta(L, 1, "__tostring") || !lua_isstring(L, -1)) {
-            /*tex Return non-string error object. */
-            return 1;
-        }
-        /*tex Replace object by result of __tostring metamethod. */
-        lua_remove(L, 1);
-    }
-    luaL_traceback(L, L, lua_tostring(L, 1), 1);
-    return 1;
-}
-
-static int docall(lua_State *L, int narg, int clear)
-{
-    int status;
-    /*tex function index */
-    int base = lua_gettop(L) - narg;
-    /*tex push traceback function */
-    lua_pushcfunction(L, traceback);
-    /*tex put it under chunk and args */
-    lua_insert(L, base);
-#if !LJ_TARGET_CONSOLE
-    signal(SIGINT, laction);
-#endif
-    status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
-#if !LJ_TARGET_CONSOLE
-    signal(SIGINT, SIG_DFL);
-#endif
-    /*tex remove traceback function */
-    lua_remove(L, base);
-    /*tex force a complete garbage collection in case of errors */
-    if (status != 0)
-        lua_gc(L, LUA_GCCOLLECT, 0);
-    return status;
-}
-
-static void print_version(void)
-{
-    fputs(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n", stdout);
-}
-
-static void print_jit_status(lua_State *L)
-{
-    int n;
-    const char *s;
-    lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
-    /*tex Get jit.* module table. */
-    lua_getfield(L, -1, "jit");
-    lua_remove(L, -2);
-    lua_getfield(L, -1, "status");
-    lua_remove(L, -2);
-    n = lua_gettop(L);
-    lua_call(L, 0, LUA_MULTRET);
-    fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stdout);
-    for (n++; (s = lua_tostring(L, n)); n++) {
-        putc(' ', stdout);
-        fputs(s, stdout);
-    }
-    putc('\n', stdout);
-}
-
-static int getargs(lua_State *L, char **argv, int n)
-{
-    int narg = 0;
-    int i;
-    /*tex count total number of arguments */
-    while (argv[argc]) argc++;
-    /*tex number of arguments to the script */
-    narg = argc - (n + 1);
-    luaL_checkstack(L, narg + 3, "too many arguments to script");
-    for (i = n+1; i < argc; i++) {
-        lua_pushstring(L, argv[i]);
-    }
-    lua_createtable(L, narg, n + 1);
-    for (i = 0; i < argc; i++) {
-        lua_pushstring(L, argv[i]);
-        lua_rawseti(L, -2, i - n);
-    }
-    return narg;
-}
-
-static int dofile(lua_State *L, const char *name)
-{
-    int status = luaL_loadfile(L, name) || docall(L, 0, 1);
-    return report(L, status);
-}
-
-static int dostring(lua_State *L, const char *s, const char *name)
-{
-    int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
-    return report(L, status);
-}
-
-static int dolibrary(lua_State *L, const char *name)
-{
-    lua_getglobal(L, "require");
-    lua_pushstring(L, name);
-    return report(L, docall(L, 1, 1));
-}
-
-static void write_prompt(lua_State *L, int firstline)
-{
-    const char *p;
-    lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
-    p = lua_tostring(L, -1);
-    if (p == NULL) p = firstline ? LUA_PROMPT : LUA_PROMPT2;
-    fputs(p, stdout);
-    fflush(stdout);
-    /*tex remove global */
-    lua_pop(L, 1);
-}
-
-static int incomplete(lua_State *L, int status)
-{
-    if (status == LUA_ERRSYNTAX) {
-        size_t lmsg;
-        const char *msg = lua_tolstring(L, -1, &lmsg);
-        const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
-        if (strstr(msg, LUA_QL("<eof>")) == tp) {
-            lua_pop(L, 1);
-            return 1;
-        }
-    }
-    return 0;
-}
-
-static int pushline(lua_State *L, int firstline)
-{
-    char buf[LUA_MAXINPUT];
-    write_prompt(L, firstline);
-    if (fgets(buf, LUA_MAXINPUT, stdin)) {
-        size_t len = strlen(buf);
-        if (len > 0 && buf[len-1] == '\n')
-            buf[len-1] = '\0';
-        if (firstline && buf[0] == '=')
-            lua_pushfstring(L, "return %s", buf+1);
-        else
-            lua_pushstring(L, buf);
-        return 1;
-    }
-    return 0;
-}
-
-static int loadline(lua_State *L)
-{
-    int status;
-    lua_settop(L, 0);
-    if (!pushline(L, 1)) {
-        /*tex no input */
-        return -1;
-    }
-    for (;;) {
-        /*tex repeat until gets a complete line */
-        status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
-        /*tex cannot try to add lines? */
-        if (!incomplete(L, status)) 
-            break;
-        /*tex no more input? */
-        if (!pushline(L, 0))
-            return -1;
-        /*tex add a new line... */
-        lua_pushliteral(L, "\n");
-        /*tex ...between the two lines */
-        lua_insert(L, -2);
-        /*tex join them */
-        lua_concat(L, 3);
-    }
-    /*tex remove line */
-    lua_remove(L, 1);
-    return status;
-}
-
-static void dotty(lua_State *L)
-{
-    int status;
-    const char *oldprogname = progname;
-    progname = NULL;
-    while ((status = loadline(L)) != -1) {
-        if (status == 0)
-            status = docall(L, 0, 0);
-        report(L, status);
-        if (status == 0 && lua_gettop(L) > 0) {
-            /*tex any result to print? */
-            lua_getglobal(L, "print");
-            lua_insert(L, 1);
-            if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
-                l_message(progname,
-            lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)",
-            lua_tostring(L, -1)));
-        }
-    }
-    /*tex clear stack */
-    lua_settop(L, 0);
-    fputs("\n", stdout);
-    fflush(stdout);
-    progname = oldprogname;
-}
-
-static int handle_script(lua_State *L, char **argv, int n)
-{
-    int status;
-    const char *fname;
-    /*tex collect arguments */
-    int narg = getargs(L, argv, n);
-    lua_setglobal(L, "arg");
-    fname = argv[n];
-    if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0) {
-        /*tex use stdin */
-        fname = NULL;
-    }
-    status = luaL_loadfile(L, fname);
-    lua_insert(L, -(narg+1));
-    if (status == 0)
-        status = docall(L, narg, 0);
-    else
-        lua_pop(L, narg);
-    return report(L, status);
-}
-
-/*tex Load add-on module. */
-
-static int loadjitmodule(lua_State *L)
-{
-    lua_getglobal(L, "require");
-    lua_pushliteral(L, "jit.");
-    lua_pushvalue(L, -3);
-    lua_concat(L, 2);
-    if (lua_pcall(L, 1, 1, 0)) {
-        const char *msg = lua_tostring(L, -1);
-        if (msg && !strncmp(msg, "module ", 7)) {
-          err:
-            l_message(progname,
-            "unknown luaJIT command or jit.* modules not installed");
-            return 1;
-        } else {
-            return report(L, 1);
-        }
-    }
-    lua_getfield(L, -1, "start");
-    if (lua_isnil(L, -1))
-        goto err;
-    /*tex Drop module table. */
-    lua_remove(L, -2);
-    return 0;
-}
-
-/*tex Run command with options. */
-
-static int runcmdopt(lua_State *L, const char *opt)
-{
-    int narg = 0;
-    if (opt && *opt) {
-        /*tex Split arguments. */
-        for (;;) {
-            const char *p = strchr(opt, ',');
-            narg++;
-            if (!p)
-                break;
-            if (p == opt)
-                lua_pushnil(L);
-            else
-                lua_pushlstring(L, opt, (size_t)(p - opt));
-            opt = p + 1;
-        }
-        if (*opt)
-            lua_pushstring(L, opt);
-        else
-            lua_pushnil(L);
-    }
-    return report(L, lua_pcall(L, narg, 0, 0));
-}
-
-/*tex JIT engine control command: try jit library first or load add-on module. */
-
-static int dojitcmd(lua_State *L, const char *cmd)
-{
-    const char *opt = strchr(cmd, '=');
-    lua_pushlstring(L, cmd, opt ? (size_t)(opt - cmd) : strlen(cmd));
-    lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
-    /*tex Get jit.* module table. */
-    lua_getfield(L, -1, "jit");
-    lua_remove(L, -2);
-    /*tex Lookup library function. */
-    lua_pushvalue(L, -2);
-    lua_gettable(L, -2);
-    if (!lua_isfunction(L, -1)) {
-        /*tex Drop non-function and jit.* table, keep module name. */
-        lua_pop(L, 2);
-        if (loadjitmodule(L))
-            return 1;
-    } else {
-        /*tex Drop jit.* table. */
-        lua_remove(L, -2);
-    }
-    /*tex Drop module name. */
-    lua_remove(L, -2);
-    return runcmdopt(L, opt ? opt+1 : opt);
-}
-
-/*tex Optimization flags. */
-
-static int dojitopt(lua_State *L, const char *opt)
-{
-    lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
-    lua_getfield(L, -1, "jit.opt");
-    lua_remove(L, -2);
-    lua_getfield(L, -1, "start");
-    lua_remove(L, -2);
-    return runcmdopt(L, opt);
-}
-
-/*tex Save or list bytecode. */
-
-static int dobytecode(lua_State *L, char **argv)
-{
-    int narg = 0;
-    lua_pushliteral(L, "bcsave");
-    if (loadjitmodule(L))
-        return 1;
-    if (argv[0][2]) {
-        narg++;
-        argv[0][1] = '-';
-        lua_pushstring(L, argv[0]+1);
-    }
-    for (argv++; *argv != NULL; narg++, argv++)
-        lua_pushstring(L, *argv);
-    return report(L, lua_pcall(L, narg, 0, 0));
-}
-
-/*tex Check that argument has no extra characters at the end */
-
-#define notail(x)	{if ((x)[2] != '\0') return -1;}
-
-#define FLAGS_INTERACTIVE  1
-#define FLAGS_VERSION      2
-#define FLAGS_EXEC         4
-#define FLAGS_OPTION       8
-#define FLAGS_NOENV       16
-
-static int collectargs(char **argv, int *flags)
-{
-    int i;
-    for (i = 1; argv[i] != NULL; i++) {
-        /*tex Not an option? */
-        if (argv[i][0] != '-')
-            return i;
-        /*tex Check option. */
-        switch (argv[i][1]) {
-            case '-':
-                notail(argv[i]);
-                return (argv[i+1] != NULL ? i+1 : 0);
-            case '\0':
-                return i;
-            case 'i':
-                notail(argv[i]);
-                *flags |= FLAGS_INTERACTIVE;
-                /*tex fallthrough */
-            case 'v':
-                notail(argv[i]);
-                *flags |= FLAGS_VERSION;
-                break;
-            case 'e':
-                *flags |= FLAGS_EXEC;
-            case 'j':
-                /*tex LuaJIT extension */
-            case 'l':
-                *flags |= FLAGS_OPTION;
-                if (argv[i][2] == '\0') {
-                    i++;
-                    if (argv[i] == NULL)
-                        return -1;
-                }
-                break;
-            case 'O':
-                /*tex LuaJIT extension */
-                break;
-            case 'b':
-                /*tex LuaJIT extension */
-                if (*flags) return -1;
-                    *flags |= FLAGS_EXEC;
-                return 0;
-            case 'E':
-                *flags |= FLAGS_NOENV;
-                break;
-            default:
-                /*tex invalid option */
-                return -1;
-        }
-    }
-    return 0;
-}
-
-static int runargs(lua_State *L, char **argv, int n)
-{
-    int i;
-    for (i = 1; i < n; i++) {
-        if (argv[i] == NULL)
-            continue;
-        lua_assert(argv[i][0] == '-');
-        switch (argv[i][1]) {
-            /*tex Options */
-            case 'e': {
-                const char *chunk = argv[i] + 2;
-                if (*chunk == '\0')
-                    chunk = argv[++i];
-                lua_assert(chunk != NULL);
-                if (dostring(L, chunk, "=(command line)") != 0)
-                    return 1;
-                break;
-            }
-            case 'l': {
-                const char *filename = argv[i] + 2;
-                if (*filename == '\0')
-                    filename = argv[++i];
-                lua_assert(filename != NULL);
-                if (dolibrary(L, filename)) {
-                    /*tex stop if file fails */
-                    return 1;
-                }
-                break;
-            }
-            case 'j': {
-                /*tex LuaJIT extension */
-                const char *cmd = argv[i] + 2;
-                if (*cmd == '\0')
-                    cmd = argv[++i];
-                lua_assert(cmd != NULL);
-                if (dojitcmd(L, cmd))
-                    return 1;
-                break;
-            }
-            case 'O':
-                /*tex LuaJIT extension */
-                if (dojitopt(L, argv[i] + 2))
-                    return 1;
-                break;
-            case 'b':
-                /*tex LuaJIT extension */
-                return dobytecode(L, argv+i);
-                default: break;
-            }
-    }
-    return 0;
-}
-
-static int handle_luainit(lua_State *L)
-{
-#if LJ_TARGET_CONSOLE
-    const char *init = NULL;
-#else
-    const char *init = getenv(LUA_INIT);
-#endif
-    if (init == NULL) {
-        /*tex status OK */
-        return 0;
-    } else if (init[0] == 64) {
-        return dofile(L, init+1);
-    } else {
-        return dostring(L, init, "=" LUA_INIT);
-    }
-}
-
-struct Smain {
-    char **argv;
-    int argc;
-    int status;
-};
-
-static int pmain(lua_State *L)
-{
-    struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
-    char **argv = s->argv;
-    int script;
-    int flags = 0;
-    globalL = L;
-    if (argv[0] && argv[0][0])
-        progname = argv[0];
-    /*tex linker-enforced version check */
-    LUAJIT_VERSION_SYM();
-    script = collectargs(argv, &flags);
-    if (script < 0) {
-        /*tex invalid args? */
-        print_usage();
-        s->status = 1;
-        return 0;
-    }
-    if ((flags & FLAGS_NOENV)) {
-        lua_pushboolean(L, 1);
-        lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
-    }
-    /*tex stop collector during initialization */
-    lua_gc(L, LUA_GCSTOP, 0);
-    /*tex open libraries */
-    luaL_openlibs(L);
-    lua_gc(L, LUA_GCRESTART, -1);
-    if (!(flags & FLAGS_NOENV)) {
-        s->status = handle_luainit(L);
-        if (s->status != 0) return 0;
-    }
-    if ((flags & FLAGS_VERSION)) print_version();
-    s->status = runargs(L, argv, (script > 0) ? script : s->argc);
-    if (s->status != 0) return 0;
-    if (script) {
-        s->status = handle_script(L, argv, script);
-        if (s->status != 0) return 0;
-    }
-    if ((flags & FLAGS_INTERACTIVE)) {
-        print_jit_status(L);
-        dotty(L);
-    } else if (script == 0 && !(flags & (FLAGS_EXEC|FLAGS_VERSION))) {
-        if (lua_stdin_is_tty()) {
-            print_version();
-            print_jit_status(L);
-            dotty(L);
-        } else {
-            /*tex executes stdin as a file */
-            dofile(L, NULL);
-        }
-    }
-    return 0;
-}
-
-int luac_main(int argc, char **argv)
-{
-    int status;
-    struct Smain s;
-    lua_State *L = lua_open();  /* create state */
-    if (L == NULL) {
-        l_message(argv[0], "cannot create state: not enough memory");
-        return EXIT_FAILURE;
-    }
-    s.argc = argc;
-    s.argv = argv;
-    status = lua_cpcall(L, pmain, &s);
-    report(L, status);
-    lua_close(L);
-    return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
-}
-
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/lua/texluajitc.w
@@ -0,0 +1,575 @@
+/*
+** LuaJIT frontend. Runs commands, scripts, read-eval-print (REPL) etc.
+** Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h
+**
+** Major portions taken verbatim or adapted from the Lua interpreter.
+** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
+*/
+
+@ @c
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define luajit_c
+
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+#include "luajit.h"
+
+#include "lj_arch.h"
+
+#if LJ_TARGET_POSIX
+#include <unistd.h>
+#define lua_stdin_is_tty()	isatty(0)
+#elif LJ_TARGET_WINDOWS
+#include <io.h>
+#ifdef __BORLANDC__
+#define lua_stdin_is_tty()	isatty(_fileno(stdin))
+#else
+#define lua_stdin_is_tty()	_isatty(_fileno(stdin))
+#endif
+#else
+#define lua_stdin_is_tty()	1
+#endif
+
+#if !LJ_TARGET_CONSOLE
+#include <signal.h>
+#endif
+
+#include "lua/luatex-api.h"
+
+static lua_State *globalL = NULL;
+static const char *progname = LUA_PROGNAME;
+
+#if !LJ_TARGET_CONSOLE
+static void lstop(lua_State *L, lua_Debug *ar)
+{
+  (void)ar;  /* unused arg. */
+  lua_sethook(L, NULL, 0, 0);
+  /* Avoid luaL_error -- a C hook doesn't add an extra frame. */
+  luaL_where(L, 0);
+  lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1));
+  lua_error(L);
+}
+
+static void laction(int i)
+{
+  signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
+			 terminate process (default action) */
+  lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
+}
+#endif
+
+static void print_usage(void)
+{
+  fprintf(stderr,
+  "usage: %s [options]... [script [args]...].\n"
+  "Available options are:\n"
+  "  -e chunk  Execute string " LUA_QL("chunk") ".\n"
+  "  -l name   Require library " LUA_QL("name") ".\n"
+  "  -b ...    Save or list bytecode.\n"
+  "  -j cmd    Perform LuaJIT control command.\n"
+  "  -O[opt]   Control LuaJIT optimizations.\n"
+  "  -i        Enter interactive mode after executing " LUA_QL("script") ".\n"
+  "  -v        Show version information.\n"
+  "  -E        Ignore environment variables.\n"
+  "  --        Stop handling options.\n"
+  "  -         Execute stdin and stop handling options.\n"
+  ,
+  progname);
+  fflush(stderr);
+}
+
+static void l_message(const char *pname, const char *msg)
+{
+  if (pname) fprintf(stderr, "%s: ", pname);
+  fprintf(stderr, "%s\n", msg);
+  fflush(stderr);
+}
+
+static int report(lua_State *L, int status)
+{
+  if (status && !lua_isnil(L, -1)) {
+    const char *msg = lua_tostring(L, -1);
+    if (msg == NULL) msg = "(error object is not a string)";
+    l_message(progname, msg);
+    lua_pop(L, 1);
+  }
+  return status;
+}
+
+static int traceback(lua_State *L)
+{
+  if (!lua_isstring(L, 1)) { /* Non-string error object? Try metamethod. */
+    if (lua_isnoneornil(L, 1) ||
+	!luaL_callmeta(L, 1, "__tostring") ||
+	!lua_isstring(L, -1))
+      return 1;  /* Return non-string error object. */
+    lua_remove(L, 1);  /* Replace object by result of __tostring metamethod. */
+  }
+  luaL_traceback(L, L, lua_tostring(L, 1), 1);
+  return 1;
+}
+
+static int docall(lua_State *L, int narg, int clear)
+{
+  int status;
+  int base = lua_gettop(L) - narg;  /* function index */
+  lua_pushcfunction(L, traceback);  /* push traceback function */
+  lua_insert(L, base);  /* put it under chunk and args */
+#if !LJ_TARGET_CONSOLE
+  signal(SIGINT, laction);
+#endif
+  status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
+#if !LJ_TARGET_CONSOLE
+  signal(SIGINT, SIG_DFL);
+#endif
+  lua_remove(L, base);  /* remove traceback function */
+  /* force a complete garbage collection in case of errors */
+  if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
+  return status;
+}
+
+static void print_version(void)
+{
+  fputs(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n", stdout);
+}
+
+static void print_jit_status(lua_State *L)
+{
+  int n;
+  const char *s;
+  lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+  lua_getfield(L, -1, "jit");  /* Get jit.* module table. */
+  lua_remove(L, -2);
+  lua_getfield(L, -1, "status");
+  lua_remove(L, -2);
+  n = lua_gettop(L);
+  lua_call(L, 0, LUA_MULTRET);
+  fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stdout);
+  for (n++; (s = lua_tostring(L, n)); n++) {
+    putc(' ', stdout);
+    fputs(s, stdout);
+  }
+  putc('\n', stdout);
+}
+
+static int getargs(lua_State *L, char **argv, int n)
+{
+  int narg;
+  int i;
+  int argc = 0;
+  while (argv[argc]) argc++;  /* count total number of arguments */
+  narg = argc - (n + 1);  /* number of arguments to the script */
+  luaL_checkstack(L, narg + 3, "too many arguments to script");
+  for (i = n+1; i < argc; i++)
+    lua_pushstring(L, argv[i]);
+  lua_createtable(L, narg, n + 1);
+  for (i = 0; i < argc; i++) {
+    lua_pushstring(L, argv[i]);
+    lua_rawseti(L, -2, i - n);
+  }
+  return narg;
+}
+
+static int dofile(lua_State *L, const char *name)
+{
+  int status = luaL_loadfile(L, name) || docall(L, 0, 1);
+  return report(L, status);
+}
+
+static int dostring(lua_State *L, const char *s, const char *name)
+{
+  int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
+  return report(L, status);
+}
+
+static int dolibrary(lua_State *L, const char *name)
+{
+  lua_getglobal(L, "require");
+  lua_pushstring(L, name);
+  return report(L, docall(L, 1, 1));
+}
+
+static void write_prompt(lua_State *L, int firstline)
+{
+  const char *p;
+  lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
+  p = lua_tostring(L, -1);
+  if (p == NULL) p = firstline ? LUA_PROMPT : LUA_PROMPT2;
+  fputs(p, stdout);
+  fflush(stdout);
+  lua_pop(L, 1);  /* remove global */
+}
+
+static int incomplete(lua_State *L, int status)
+{
+  if (status == LUA_ERRSYNTAX) {
+    size_t lmsg;
+    const char *msg = lua_tolstring(L, -1, &lmsg);
+    const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
+    if (strstr(msg, LUA_QL("<eof>")) == tp) {
+      lua_pop(L, 1);
+      return 1;
+    }
+  }
+  return 0;  /* else... */
+}
+
+static int pushline(lua_State *L, int firstline)
+{
+  char buf[LUA_MAXINPUT];
+  write_prompt(L, firstline);
+  if (fgets(buf, LUA_MAXINPUT, stdin)) {
+    size_t len = strlen(buf);
+    if (len > 0 && buf[len-1] == '\n')
+      buf[len-1] = '\0';
+    if (firstline && buf[0] == '=')
+      lua_pushfstring(L, "return %s", buf+1);
+    else
+      lua_pushstring(L, buf);
+    return 1;
+  }
+  return 0;
+}
+
+static int loadline(lua_State *L)
+{
+  int status;
+  lua_settop(L, 0);
+  if (!pushline(L, 1))
+    return -1;  /* no input */
+  for (;;) {  /* repeat until gets a complete line */
+    status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
+    if (!incomplete(L, status)) break;  /* cannot try to add lines? */
+    if (!pushline(L, 0))  /* no more input? */
+      return -1;
+    lua_pushliteral(L, "\n");  /* add a new line... */
+    lua_insert(L, -2);  /* ...between the two lines */
+    lua_concat(L, 3);  /* join them */
+  }
+  lua_remove(L, 1);  /* remove line */
+  return status;
+}
+
+static void dotty(lua_State *L)
+{
+  int status;
+  const char *oldprogname = progname;
+  progname = NULL;
+  while ((status = loadline(L)) != -1) {
+    if (status == 0) status = docall(L, 0, 0);
+    report(L, status);
+    if (status == 0 && lua_gettop(L) > 0) {  /* any result to print? */
+      lua_getglobal(L, "print");
+      lua_insert(L, 1);
+      if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
+	l_message(progname,
+	  lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)",
+			      lua_tostring(L, -1)));
+    }
+  }
+  lua_settop(L, 0);  /* clear stack */
+  fputs("\n", stdout);
+  fflush(stdout);
+  progname = oldprogname;
+}
+
+static int handle_script(lua_State *L, char **argv, int n)
+{
+  int status;
+  const char *fname;
+  int narg = getargs(L, argv, n);  /* collect arguments */
+  lua_setglobal(L, "arg");
+  fname = argv[n];
+  if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
+    fname = NULL;  /* stdin */
+  status = luaL_loadfile(L, fname);
+  lua_insert(L, -(narg+1));
+  if (status == 0)
+    status = docall(L, narg, 0);
+  else
+    lua_pop(L, narg);
+  return report(L, status);
+}
+
+/* Load add-on module. */
+static int loadjitmodule(lua_State *L)
+{
+  lua_getglobal(L, "require");
+  lua_pushliteral(L, "jit.");
+  lua_pushvalue(L, -3);
+  lua_concat(L, 2);
+  if (lua_pcall(L, 1, 1, 0)) {
+    const char *msg = lua_tostring(L, -1);
+    if (msg && !strncmp(msg, "module ", 7)) {
+    err:
+      l_message(progname,
+		"unknown luaJIT command or jit.* modules not installed");
+      return 1;
+    } else {
+      return report(L, 1);
+    }
+  }
+  lua_getfield(L, -1, "start");
+  if (lua_isnil(L, -1)) goto err;
+  lua_remove(L, -2);  /* Drop module table. */
+  return 0;
+}
+
+/* Run command with options. */
+static int runcmdopt(lua_State *L, const char *opt)
+{
+  int narg = 0;
+  if (opt && *opt) {
+    for (;;) {  /* Split arguments. */
+      const char *p = strchr(opt, ',');
+      narg++;
+      if (!p) break;
+      if (p == opt)
+	lua_pushnil(L);
+      else
+	lua_pushlstring(L, opt, (size_t)(p - opt));
+      opt = p + 1;
+    }
+    if (*opt)
+      lua_pushstring(L, opt);
+    else
+      lua_pushnil(L);
+  }
+  return report(L, lua_pcall(L, narg, 0, 0));
+}
+
+/* JIT engine control command: try jit library first or load add-on module. */
+static int dojitcmd(lua_State *L, const char *cmd)
+{
+  const char *opt = strchr(cmd, '=');
+  lua_pushlstring(L, cmd, opt ? (size_t)(opt - cmd) : strlen(cmd));
+  lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+  lua_getfield(L, -1, "jit");  /* Get jit.* module table. */
+  lua_remove(L, -2);
+  lua_pushvalue(L, -2);
+  lua_gettable(L, -2);  /* Lookup library function. */
+  if (!lua_isfunction(L, -1)) {
+    lua_pop(L, 2);  /* Drop non-function and jit.* table, keep module name. */
+    if (loadjitmodule(L))
+      return 1;
+  } else {
+    lua_remove(L, -2);  /* Drop jit.* table. */
+  }
+  lua_remove(L, -2);  /* Drop module name. */
+  return runcmdopt(L, opt ? opt+1 : opt);
+}
+
+/* Optimization flags. */
+static int dojitopt(lua_State *L, const char *opt)
+{
+  lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+  lua_getfield(L, -1, "jit.opt");  /* Get jit.opt.* module table. */
+  lua_remove(L, -2);
+  lua_getfield(L, -1, "start");
+  lua_remove(L, -2);
+  return runcmdopt(L, opt);
+}
+
+/* Save or list bytecode. */
+static int dobytecode(lua_State *L, char **argv)
+{
+  int narg = 0;
+  lua_pushliteral(L, "bcsave");
+  if (loadjitmodule(L))
+    return 1;
+  if (argv[0][2]) {
+    narg++;
+    argv[0][1] = '-';
+    lua_pushstring(L, argv[0]+1);
+  }
+  for (argv++; *argv != NULL; narg++, argv++)
+    lua_pushstring(L, *argv);
+  return report(L, lua_pcall(L, narg, 0, 0));
+}
+
+/* check that argument has no extra characters at the end */
+#define notail(x)	{if ((x)[2] != '\0') return -1;}
+
+#define FLAGS_INTERACTIVE	1
+#define FLAGS_VERSION		2
+#define FLAGS_EXEC		4
+#define FLAGS_OPTION		8
+#define FLAGS_NOENV		16
+
+static int collectargs(char **argv, int *flags)
+{
+  int i;
+  for (i = 1; argv[i] != NULL; i++) {
+    if (argv[i][0] != '-')  /* Not an option? */
+      return i;
+    switch (argv[i][1]) {  /* Check option. */
+    case '-':
+      notail(argv[i]);
+      return (argv[i+1] != NULL ? i+1 : 0);
+    case '\0':
+      return i;
+    case 'i':
+      notail(argv[i]);
+      *flags |= FLAGS_INTERACTIVE;
+      /* fallthrough */
+    case 'v':
+      notail(argv[i]);
+      *flags |= FLAGS_VERSION;
+      break;
+    case 'e':
+      *flags |= FLAGS_EXEC;
+    case 'j':  /* LuaJIT extension */
+    case 'l':
+      *flags |= FLAGS_OPTION;
+      if (argv[i][2] == '\0') {
+	i++;
+	if (argv[i] == NULL) return -1;
+      }
+      break;
+    case 'O': break;  /* LuaJIT extension */
+    case 'b':  /* LuaJIT extension */
+      if (*flags) return -1;
+      *flags |= FLAGS_EXEC;
+      return 0;
+    case 'E':
+      *flags |= FLAGS_NOENV;
+      break;
+    default: return -1;  /* invalid option */
+    }
+  }
+  return 0;
+}
+
+static int runargs(lua_State *L, char **argv, int n)
+{
+  int i;
+  for (i = 1; i < n; i++) {
+    if (argv[i] == NULL) continue;
+    lua_assert(argv[i][0] == '-');
+    switch (argv[i][1]) {  /* option */
+    case 'e': {
+      const char *chunk = argv[i] + 2;
+      if (*chunk == '\0') chunk = argv[++i];
+      lua_assert(chunk != NULL);
+      if (dostring(L, chunk, "=(command line)") != 0)
+	return 1;
+      break;
+      }
+    case 'l': {
+      const char *filename = argv[i] + 2;
+      if (*filename == '\0') filename = argv[++i];
+      lua_assert(filename != NULL);
+      if (dolibrary(L, filename))
+	return 1;  /* stop if file fails */
+      break;
+      }
+    case 'j': {  /* LuaJIT extension */
+      const char *cmd = argv[i] + 2;
+      if (*cmd == '\0') cmd = argv[++i];
+      lua_assert(cmd != NULL);
+      if (dojitcmd(L, cmd))
+	return 1;
+      break;
+      }
+    case 'O':  /* LuaJIT extension */
+      if (dojitopt(L, argv[i] + 2))
+	return 1;
+      break;
+    case 'b':  /* LuaJIT extension */
+      return dobytecode(L, argv+i);
+    default: break;
+    }
+  }
+  return 0;
+}
+
+static int handle_luainit(lua_State *L)
+{
+#if LJ_TARGET_CONSOLE
+  const char *init = NULL;
+#else
+  const char *init = getenv(LUA_INIT);
+#endif
+  if (init == NULL)
+    return 0;  /* status OK */
+  else if (init[0] == 64)
+    return dofile(L, init+1);
+  else
+    return dostring(L, init, "=" LUA_INIT);
+}
+
+struct Smain {
+  char **argv;
+  int argc;
+  int status;
+};
+
+static int pmain(lua_State *L)
+{
+  struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
+  char **argv = s->argv;
+  int script;
+  int flags = 0;
+  globalL = L;
+  if (argv[0] && argv[0][0]) progname = argv[0];
+  LUAJIT_VERSION_SYM();  /* linker-enforced version check */
+  script = collectargs(argv, &flags);
+  if (script < 0) {  /* invalid args? */
+    print_usage();
+    s->status = 1;
+    return 0;
+  }
+  if ((flags & FLAGS_NOENV)) {
+    lua_pushboolean(L, 1);
+    lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
+  }
+  lua_gc(L, LUA_GCSTOP, 0);  /* stop collector during initialization */
+  luaL_openlibs(L);  /* open libraries */
+  lua_gc(L, LUA_GCRESTART, -1);
+  if (!(flags & FLAGS_NOENV)) {
+    s->status = handle_luainit(L);
+    if (s->status != 0) return 0;
+  }
+  if ((flags & FLAGS_VERSION)) print_version();
+  s->status = runargs(L, argv, (script > 0) ? script : s->argc);
+  if (s->status != 0) return 0;
+  if (script) {
+    s->status = handle_script(L, argv, script);
+    if (s->status != 0) return 0;
+  }
+  if ((flags & FLAGS_INTERACTIVE)) {
+    print_jit_status(L);
+    dotty(L);
+  } else if (script == 0 && !(flags & (FLAGS_EXEC|FLAGS_VERSION))) {
+    if (lua_stdin_is_tty()) {
+      print_version();
+      print_jit_status(L);
+      dotty(L);
+    } else {
+      dofile(L, NULL);  /* executes stdin as a file */
+    }
+  }
+  return 0;
+}
+
+int luac_main(int argc, char **argv)
+{
+  int status;
+  struct Smain s;
+  lua_State *L = lua_open();  /* create state */
+  if (L == NULL) {
+    l_message(argv[0], "cannot create state: not enough memory");
+    return EXIT_FAILURE;
+  }
+  s.argc = argc;
+  s.argv = argv;
+  status = lua_cpcall(L, pmain, &s);
+  report(L, status);
+  lua_close(L);
+  return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_ftp.lua
@@ -0,0 +1,329 @@
+-----------------------------------------------------------------------------
+-- FTP support for the Lua language
+-- LuaSocket toolkit.
+-- Author: Diego Nehab
+-----------------------------------------------------------------------------
+
+-----------------------------------------------------------------------------
+-- Declare module and import dependencies
+-----------------------------------------------------------------------------
+local base = _G
+local table = require("table")
+local string = require("string")
+local math = require("math")
+local socket = socket or require("socket")
+local url = socket.url or require("socket.url")
+local tp = socket.tp or require("socket.tp")
+local ltn12 = ltn12 or require("ltn12")
+socket.ftp = {}
+local _M = socket.ftp
+-----------------------------------------------------------------------------
+-- Program constants
+-----------------------------------------------------------------------------
+-- timeout in seconds before the program gives up on a connection
+_M.TIMEOUT = 60
+-- default port for ftp service
+local PORT = 21
+-- this is the default anonymous password. used when no password is
+-- provided in url. should be changed to your e-mail.
+_M.USER = "ftp"
+_M.PASSWORD = "anonymous@anonymous.org"
+
+-----------------------------------------------------------------------------
+-- Low level FTP API
+-----------------------------------------------------------------------------
+local metat = { __index = {} }
+
+function _M.open(server, port, create)
+    local tp = socket.try(tp.connect(server, port or PORT, _M.TIMEOUT, create))
+    local f = base.setmetatable({ tp = tp }, metat)
+    -- make sure everything gets closed in an exception
+    f.try = socket.newtry(function() f:close() end)
+    return f
+end
+
+function metat.__index:portconnect()
+    self.try(self.server:settimeout(_M.TIMEOUT))
+    self.data = self.try(self.server:accept())
+    self.try(self.data:settimeout(_M.TIMEOUT))
+end
+
+function metat.__index:pasvconnect()
+    self.data = self.try(socket.tcp())
+    self.try(self.data:settimeout(_M.TIMEOUT))
+    self.try(self.data:connect(self.pasvt.address, self.pasvt.port))
+end
+
+function metat.__index:login(user, password)
+    self.try(self.tp:command("user", user or _M.USER))
+    local code, reply = self.try(self.tp:check{"2..", 331})
+    if code == 331 then
+        self.try(self.tp:command("pass", password or _M.PASSWORD))
+        self.try(self.tp:check("2.."))
+    end
+    return 1
+end
+
+function metat.__index:pasv()
+    self.try(self.tp:command("pasv"))
+    local code, reply = self.try(self.tp:check("2.."))
+    local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)"
+    local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))
+    self.try(a and b and c and d and p1 and p2, reply)
+    self.pasvt = {
+        address = string.format("%d.%d.%d.%d", a, b, c, d),
+        port = p1*256 + p2
+    }
+    if self.server then
+        self.server:close()
+        self.server = nil
+    end
+    return self.pasvt.address, self.pasvt.port
+end
+
+function metat.__index:epsv()
+    self.try(self.tp:command("epsv"))
+    local code, reply = self.try(self.tp:check("229"))
+    local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)"
+    local d, prt, address, port = string.match(reply, pattern)
+    self.try(port, "invalid epsv response")
+    self.pasvt = {
+        address = self.tp:getpeername(),
+        port = port
+    }
+    if self.server then
+        self.server:close()
+        self.server = nil
+    end
+    return self.pasvt.address, self.pasvt.port
+end
+
+
+function metat.__index:port(address, port)
+    self.pasvt = nil
+    if not address then
+        address, port = self.try(self.tp:getsockname())
+        self.server = self.try(socket.bind(address, 0))
+        address, port = self.try(self.server:getsockname())
+        self.try(self.server:settimeout(_M.TIMEOUT))
+    end
+    local pl = math.mod(port, 256)
+    local ph = (port - pl)/256
+    local arg = string.gsub(string.format("%s,%d,%d", address, ph, pl), "%.", ",")
+    self.try(self.tp:command("port", arg))
+    self.try(self.tp:check("2.."))
+    return 1
+end
+
+function metat.__index:eprt(family, address, port)
+    self.pasvt = nil
+    if not address then
+        address, port = self.try(self.tp:getsockname())
+        self.server = self.try(socket.bind(address, 0))
+        address, port = self.try(self.server:getsockname())
+        self.try(self.server:settimeout(_M.TIMEOUT))
+    end
+    local arg = string.format("|%s|%s|%d|", family, address, port)
+    self.try(self.tp:command("eprt", arg))
+    self.try(self.tp:check("2.."))
+    return 1
+end
+
+
+function metat.__index:send(sendt)
+    self.try(self.pasvt or self.server, "need port or pasv first")
+    -- if there is a pasvt table, we already sent a PASV command
+    -- we just get the data connection into self.data
+    if self.pasvt then self:pasvconnect() end
+    -- get the transfer argument and command
+    local argument = sendt.argument or
+        url.unescape(string.gsub(sendt.path or "", "^[/\\]", ""))
+    if argument == "" then argument = nil end
+    local command = sendt.command or "stor"
+    -- send the transfer command and check the reply
+    self.try(self.tp:command(command, argument))
+    local code, reply = self.try(self.tp:check{"2..", "1.."})
+    -- if there is not a pasvt table, then there is a server
+    -- and we already sent a PORT command
+    if not self.pasvt then self:portconnect() end
+    -- get the sink, source and step for the transfer
+    local step = sendt.step or ltn12.pump.step
+    local readt = { self.tp }
+    local checkstep = function(src, snk)
+        -- check status in control connection while downloading
+        local readyt = socket.select(readt, nil, 0)
+        if readyt[tp] then code = self.try(self.tp:check("2..")) end
+        return step(src, snk)
+    end
+    local sink = socket.sink("close-when-done", self.data)
+    -- transfer all data and check error
+    self.try(ltn12.pump.all(sendt.source, sink, checkstep))
+    if string.find(code, "1..") then self.try(self.tp:check("2..")) end
+    -- done with data connection
+    self.data:close()
+    -- find out how many bytes were sent
+    local sent = socket.skip(1, self.data:getstats())
+    self.data = nil
+    return sent
+end
+
+function metat.__index:receive(recvt)
+    self.try(self.pasvt or self.server, "need port or pasv first")
+    if self.pasvt then self:pasvconnect() end
+    local argument = recvt.argument or
+        url.unescape(string.gsub(recvt.path or "", "^[/\\]", ""))
+    if argument == "" then argument = nil end
+    local command = recvt.command or "retr"
+    self.try(self.tp:command(command, argument))
+    local code,reply = self.try(self.tp:check{"1..", "2.."})
+    if (code >= 200) and (code <= 299) then
+        recvt.sink(reply)
+        return 1
+    end
+    if not self.pasvt then self:portconnect() end
+    local source = socket.source("until-closed", self.data)
+    local step = recvt.step or ltn12.pump.step
+    self.try(ltn12.pump.all(source, recvt.sink, step))
+    if string.find(code, "1..") then self.try(self.tp:check("2..")) end
+    self.data:close()
+    self.data = nil
+    return 1
+end
+
+function metat.__index:cwd(dir)
+    self.try(self.tp:command("cwd", dir))
+    self.try(self.tp:check(250))
+    return 1
+end
+
+function metat.__index:type(type)
+    self.try(self.tp:command("type", type))
+    self.try(self.tp:check(200))
+    return 1
+end
+
+function metat.__index:greet()
+    local code = self.try(self.tp:check{"1..", "2.."})
+    if string.find(code, "1..") then self.try(self.tp:check("2..")) end
+    return 1
+end
+
+function metat.__index:quit()
+    self.try(self.tp:command("quit"))
+    self.try(self.tp:check("2.."))
+    return 1
+end
+
+function metat.__index:close()
+    if self.data then self.data:close() end
+    if self.server then self.server:close() end
+    return self.tp:close()
+end
+
+-----------------------------------------------------------------------------
+-- High level FTP API
+-----------------------------------------------------------------------------
+local function override(t)
+    if t.url then
+        local u = url.parse(t.url)
+        for i,v in base.pairs(t) do
+            u[i] = v
+        end
+        return u
+    else return t end
+end
+
+local function tput(putt)
+    putt = override(putt)
+    socket.try(putt.host, "missing hostname")
+    local f = _M.open(putt.host, putt.port, putt.create)
+    f:greet()
+    f:login(putt.user, putt.password)
+    if putt.type then f:type(putt.type) end
+    f:epsv()
+    local sent = f:send(putt)
+    f:quit()
+    f:close()
+    return sent
+end
+
+local default = {
+    path = "/",
+    scheme = "ftp"
+}
+
+local function genericform(u)
+    local t = socket.try(url.parse(u, default))
+    socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
+    socket.try(t.host, "missing hostname")
+    local pat = "^type=(.)$"
+    if t.params then
+        t.type = socket.skip(2, string.find(t.params, pat))
+        socket.try(t.type == "a" or t.type == "i",
+            "invalid type '" .. t.type .. "'")
+    end
+    return t
+end
+
+_M.genericform = genericform
+
+local function sput(u, body)
+    local putt = genericform(u)
+    putt.source = ltn12.source.string(body)
+    return tput(putt)
+end
+
+_M.put = socket.protect(function(putt, body)
+    if base.type(putt) == "string" then return sput(putt, body)
+    else return tput(putt) end
+end)
+
+local function tget(gett)
+    gett = override(gett)
+    socket.try(gett.host, "missing hostname")
+    local f = _M.open(gett.host, gett.port, gett.create)
+    f:greet()
+    f:login(gett.user, gett.password)
+    if gett.type then f:type(gett.type) end
+    f:epsv()
+    f:receive(gett)
+    f:quit()
+    return f:close()
+end
+
+local function sget(u)
+    local gett = genericform(u)
+    local t = {}
+    gett.sink = ltn12.sink.table(t)
+    tget(gett)
+    return table.concat(t)
+end
+
+_M.command = socket.protect(function(cmdt)
+    cmdt = override(cmdt)
+    socket.try(cmdt.host, "missing hostname")
+    socket.try(cmdt.command, "missing command")
+    local f = _M.open(cmdt.host, cmdt.port, cmdt.create)
+    f:greet()
+    f:login(cmdt.user, cmdt.password)
+    if type(cmdt.command) == "table" then
+        local argument = cmdt.argument or {}
+        local check = cmdt.check or {}
+        for i,cmd in ipairs(cmdt.command) do
+            f.try(f.tp:command(cmd, argument[i]))
+            if check[i] then f.try(f.tp:check(check[i])) end
+        end
+    else
+        f.try(f.tp:command(cmdt.command, cmdt.argument))
+        if cmdt.check then f.try(f.tp:check(cmdt.check)) end
+    end
+    f:quit()
+    return f:close()
+end)
+
+_M.get = socket.protect(function(gett)
+    if base.type(gett) == "string" then return sget(gett)
+    else return tget(gett) end
+end)
+
+return _M
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_ftp_lua.c
@@ -0,0 +1,749 @@
+/*
+ * This file is generated with xxd -i and bit of bash script.
+*/
+#include "lua.h"
+#include "lauxlib.h"
+ 
+int luatex_ftp_lua_open (lua_State *L) { 
+    static unsigned char B[] = {
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x3d,
+  0x20, 0x5f, 0x47, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x61,
+  0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
+  0x65, 0x28, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x29, 0x0a, 0x6c,
+  0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20,
+  0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73,
+  0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x6d, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71,
+  0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x6d, 0x61, 0x74, 0x68, 0x22, 0x29,
+  0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+  0x74, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x6f,
+  0x72, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73,
+  0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b,
+  0x65, 0x74, 0x2e, 0x75, 0x72, 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65,
+  0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+  0x74, 0x2e, 0x75, 0x72, 0x6c, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x74, 0x70, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+  0x74, 0x2e, 0x74, 0x70, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x71, 0x75,
+  0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e,
+  0x74, 0x70, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c,
+  0x74, 0x6e, 0x31, 0x32, 0x20, 0x3d, 0x20, 0x6c, 0x74, 0x6e, 0x31, 0x32,
+  0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28,
+  0x22, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x22, 0x29, 0x0a, 0x73, 0x6f, 0x63,
+  0x6b, 0x65, 0x74, 0x2e, 0x66, 0x74, 0x70, 0x20, 0x3d, 0x20, 0x7b, 0x7d,
+  0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x5f, 0x4d, 0x20, 0x3d, 0x20,
+  0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x66, 0x74, 0x70, 0x0a, 0x5f,
+  0x4d, 0x2e, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x20, 0x3d, 0x20,
+  0x36, 0x30, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x50, 0x4f, 0x52,
+  0x54, 0x20, 0x3d, 0x20, 0x32, 0x31, 0x0a, 0x5f, 0x4d, 0x2e, 0x55, 0x53,
+  0x45, 0x52, 0x20, 0x3d, 0x20, 0x22, 0x66, 0x74, 0x70, 0x22, 0x0a, 0x5f,
+  0x4d, 0x2e, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x20, 0x3d,
+  0x20, 0x22, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, 0x73, 0x40,
+  0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, 0x73, 0x2e, 0x6f, 0x72,
+  0x67, 0x22, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x65, 0x74,
+  0x61, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x69, 0x6e, 0x64,
+  0x65, 0x78, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x20, 0x7d, 0x0a, 0x66, 0x75,
+  0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e, 0x6f, 0x70,
+  0x65, 0x6e, 0x28, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x20, 0x70,
+  0x6f, 0x72, 0x74, 0x2c, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74,
+  0x70, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74,
+  0x72, 0x79, 0x28, 0x74, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+  0x74, 0x28, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x6f,
+  0x72, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x50, 0x4f, 0x52, 0x54, 0x2c, 0x20,
+  0x5f, 0x4d, 0x2e, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x2c, 0x20,
+  0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x62,
+  0x61, 0x73, 0x65, 0x2e, 0x73, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74,
+  0x61, 0x62, 0x6c, 0x65, 0x28, 0x7b, 0x20, 0x74, 0x70, 0x20, 0x3d, 0x20,
+  0x74, 0x70, 0x20, 0x7d, 0x2c, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x20, 0x3d,
+  0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6e, 0x65, 0x77, 0x74,
+  0x72, 0x79, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28,
+  0x29, 0x20, 0x66, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x20,
+  0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x66, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75,
+  0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74,
+  0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x70, 0x6f, 0x72,
+  0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x28, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a,
+  0x73, 0x65, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x5f,
+  0x4d, 0x2e, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x29, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64, 0x61, 0x74,
+  0x61, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79,
+  0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+  0x3a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x28, 0x29, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x73, 0x65,
+  0x74, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x5f, 0x4d, 0x2e,
+  0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x29, 0x29, 0x0a, 0x65, 0x6e,
+  0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d,
+  0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
+  0x3a, 0x70, 0x61, 0x73, 0x76, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
+  0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x72, 0x79, 0x28, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74,
+  0x63, 0x70, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x64, 0x61, 0x74, 0x61, 0x3a, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6d, 0x65,
+  0x6f, 0x75, 0x74, 0x28, 0x5f, 0x4d, 0x2e, 0x54, 0x49, 0x4d, 0x45, 0x4f,
+  0x55, 0x54, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64,
+  0x61, 0x74, 0x61, 0x3a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61, 0x73, 0x76, 0x74, 0x2e, 0x61,
+  0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x70, 0x61, 0x73, 0x76, 0x74, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x29,
+  0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69,
+  0x6e, 0x64, 0x65, 0x78, 0x3a, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x28, 0x75,
+  0x73, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
+  0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a,
+  0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x22, 0x75, 0x73, 0x65,
+  0x72, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x6f, 0x72, 0x20,
+  0x5f, 0x4d, 0x2e, 0x55, 0x53, 0x45, 0x52, 0x29, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x64, 0x65,
+  0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x20, 0x3d, 0x20, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x7b, 0x22, 0x32, 0x2e,
+  0x2e, 0x22, 0x2c, 0x20, 0x33, 0x33, 0x31, 0x7d, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x69, 0x66, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d,
+  0x20, 0x33, 0x33, 0x31, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63,
+  0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x22, 0x70, 0x61, 0x73, 0x73,
+  0x22, 0x2c, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x20,
+  0x6f, 0x72, 0x20, 0x5f, 0x4d, 0x2e, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f,
+  0x52, 0x44, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28,
+  0x22, 0x32, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x31, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e,
+  0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e,
+  0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x70, 0x61, 0x73, 0x76,
+  0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a,
+  0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x22, 0x70, 0x61, 0x73,
+  0x76, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x70,
+  0x6c, 0x79, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72,
+  0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68,
+  0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x61,
+  0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x3d, 0x20, 0x22, 0x28, 0x25, 0x64,
+  0x2b, 0x29, 0x25, 0x44, 0x28, 0x25, 0x64, 0x2b, 0x29, 0x25, 0x44, 0x28,
+  0x25, 0x64, 0x2b, 0x29, 0x25, 0x44, 0x28, 0x25, 0x64, 0x2b, 0x29, 0x25,
+  0x44, 0x28, 0x25, 0x64, 0x2b, 0x29, 0x25, 0x44, 0x28, 0x25, 0x64, 0x2b,
+  0x29, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x63, 0x2c, 0x20, 0x64, 0x2c,
+  0x20, 0x70, 0x31, 0x2c, 0x20, 0x70, 0x32, 0x20, 0x3d, 0x20, 0x73, 0x6f,
+  0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x28, 0x32, 0x2c,
+  0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64,
+  0x28, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x2c, 0x20, 0x70, 0x61, 0x74, 0x74,
+  0x65, 0x72, 0x6e, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x61, 0x20, 0x61, 0x6e, 0x64,
+  0x20, 0x62, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x20, 0x61, 0x6e, 0x64,
+  0x20, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x31, 0x20, 0x61, 0x6e,
+  0x64, 0x20, 0x70, 0x32, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61,
+  0x73, 0x76, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20,
+  0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x6f, 0x72,
+  0x6d, 0x61, 0x74, 0x28, 0x22, 0x25, 0x64, 0x2e, 0x25, 0x64, 0x2e, 0x25,
+  0x64, 0x2e, 0x25, 0x64, 0x22, 0x2c, 0x20, 0x61, 0x2c, 0x20, 0x62, 0x2c,
+  0x20, 0x63, 0x2c, 0x20, 0x64, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x70,
+  0x31, 0x2a, 0x32, 0x35, 0x36, 0x20, 0x2b, 0x20, 0x70, 0x32, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+  0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65,
+  0x72, 0x76, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70,
+  0x61, 0x73, 0x76, 0x74, 0x2e, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+  0x2c, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61, 0x73, 0x76, 0x74,
+  0x2e, 0x70, 0x6f, 0x72, 0x74, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75,
+  0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74,
+  0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x65, 0x70, 0x73,
+  0x76, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70,
+  0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x22, 0x65, 0x70,
+  0x73, 0x76, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
+  0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65,
+  0x70, 0x6c, 0x79, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63,
+  0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x32, 0x39, 0x22, 0x29, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70,
+  0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x3d, 0x20, 0x22, 0x25, 0x28,
+  0x28, 0x2e, 0x29, 0x28, 0x2e, 0x2d, 0x29, 0x25, 0x31, 0x28, 0x2e, 0x2d,
+  0x29, 0x25, 0x31, 0x28, 0x2e, 0x2d, 0x29, 0x25, 0x31, 0x25, 0x29, 0x22,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x64,
+  0x2c, 0x20, 0x70, 0x72, 0x74, 0x2c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65,
+  0x73, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x73,
+  0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28,
+  0x72, 0x65, 0x70, 0x6c, 0x79, 0x2c, 0x20, 0x70, 0x61, 0x74, 0x74, 0x65,
+  0x72, 0x6e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x72, 0x79, 0x28, 0x70, 0x6f, 0x72, 0x74, 0x2c, 0x20, 0x22,
+  0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x65, 0x70, 0x73, 0x76,
+  0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61, 0x73,
+  0x76, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x3d,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x67, 0x65, 0x74,
+  0x70, 0x65, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x29, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x72, 0x74,
+  0x20, 0x3d, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x63, 0x6c,
+  0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
+  0x72, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61, 0x73, 0x76,
+  0x74, 0x2e, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61, 0x73, 0x76, 0x74, 0x2e, 0x70, 0x6f,
+  0x72, 0x74, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f,
+  0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x70, 0x6f, 0x72, 0x74, 0x28, 0x61,
+  0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x72, 0x74,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70,
+  0x61, 0x73, 0x76, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x64,
+  0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65,
+  0x73, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x70, 0x3a, 0x67, 0x65, 0x74, 0x73, 0x6f, 0x63, 0x6b, 0x6e,
+  0x61, 0x6d, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76,
+  0x65, 0x72, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72,
+  0x79, 0x28, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x62, 0x69, 0x6e,
+  0x64, 0x28, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x30,
+  0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61,
+  0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x72, 0x74,
+  0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a,
+  0x67, 0x65, 0x74, 0x73, 0x6f, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x28,
+  0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x73, 0x65, 0x74, 0x74,
+  0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x5f, 0x4d, 0x2e, 0x54, 0x49,
+  0x4d, 0x45, 0x4f, 0x55, 0x54, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x70, 0x6c, 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x74, 0x68, 0x2e,
+  0x6d, 0x6f, 0x64, 0x28, 0x70, 0x6f, 0x72, 0x74, 0x2c, 0x20, 0x32, 0x35,
+  0x36, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x70, 0x68, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x6f, 0x72, 0x74, 0x20,
+  0x2d, 0x20, 0x70, 0x6c, 0x29, 0x2f, 0x32, 0x35, 0x36, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x72, 0x67, 0x20,
+  0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75,
+  0x62, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x6f, 0x72,
+  0x6d, 0x61, 0x74, 0x28, 0x22, 0x25, 0x73, 0x2c, 0x25, 0x64, 0x2c, 0x25,
+  0x64, 0x22, 0x2c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c,
+  0x20, 0x70, 0x68, 0x2c, 0x20, 0x70, 0x6c, 0x29, 0x2c, 0x20, 0x22, 0x25,
+  0x2e, 0x22, 0x2c, 0x20, 0x22, 0x2c, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
+  0x64, 0x28, 0x22, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x2c, 0x20, 0x61, 0x72,
+  0x67, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70,
+  0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e, 0x22,
+  0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72,
+  0x6e, 0x20, 0x31, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f,
+  0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x65, 0x70, 0x72, 0x74, 0x28,
+  0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x2c, 0x20, 0x61, 0x64, 0x64, 0x72,
+  0x65, 0x73, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61, 0x73, 0x76,
+  0x74, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65,
+  0x73, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c,
+  0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70,
+  0x3a, 0x67, 0x65, 0x74, 0x73, 0x6f, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65,
+  0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+  0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73,
+  0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x61,
+  0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x30, 0x29, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x64, 0x64, 0x72,
+  0x65, 0x73, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x3d, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x67, 0x65, 0x74,
+  0x73, 0x6f, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x29, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65,
+  0x72, 0x76, 0x65, 0x72, 0x3a, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6d, 0x65,
+  0x6f, 0x75, 0x74, 0x28, 0x5f, 0x4d, 0x2e, 0x54, 0x49, 0x4d, 0x45, 0x4f,
+  0x55, 0x54, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61,
+  0x72, 0x67, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+  0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x22, 0x7c, 0x25, 0x73, 0x7c,
+  0x25, 0x73, 0x7c, 0x25, 0x64, 0x7c, 0x22, 0x2c, 0x20, 0x66, 0x61, 0x6d,
+  0x69, 0x6c, 0x79, 0x2c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+  0x2c, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
+  0x28, 0x22, 0x65, 0x70, 0x72, 0x74, 0x22, 0x2c, 0x20, 0x61, 0x72, 0x67,
+  0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a,
+  0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e, 0x22, 0x29,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x31, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f,
+  0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x73,
+  0x65, 0x6e, 0x64, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x70, 0x61, 0x73, 0x76, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x20, 0x22, 0x6e,
+  0x65, 0x65, 0x64, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x72, 0x20,
+  0x70, 0x61, 0x73, 0x76, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x22, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x70, 0x61, 0x73, 0x76, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x3a, 0x70, 0x61, 0x73, 0x76, 0x63, 0x6f, 0x6e,
+  0x6e, 0x65, 0x63, 0x74, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x72, 0x67,
+  0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6e, 0x64,
+  0x74, 0x2e, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f,
+  0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72,
+  0x6c, 0x2e, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x28, 0x73,
+  0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73,
+  0x65, 0x6e, 0x64, 0x74, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x20, 0x6f, 0x72,
+  0x20, 0x22, 0x22, 0x2c, 0x20, 0x22, 0x5e, 0x5b, 0x2f, 0x5c, 0x5c, 0x5d,
+  0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+  0x3d, 0x3d, 0x20, 0x22, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x61,
+  0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x69,
+  0x6c, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
+  0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20,
+  0x3d, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
+  0x61, 0x6e, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x72,
+  0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63,
+  0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
+  0x6e, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+  0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79,
+  0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63,
+  0x6b, 0x7b, 0x22, 0x32, 0x2e, 0x2e, 0x22, 0x2c, 0x20, 0x22, 0x31, 0x2e,
+  0x2e, 0x22, 0x7d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61, 0x73,
+  0x76, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x3a, 0x70, 0x6f, 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
+  0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
+  0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x3d, 0x20,
+  0x73, 0x65, 0x6e, 0x64, 0x74, 0x2e, 0x73, 0x74, 0x65, 0x70, 0x20, 0x6f,
+  0x72, 0x20, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x2e, 0x70, 0x75, 0x6d, 0x70,
+  0x2e, 0x73, 0x74, 0x65, 0x70, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
+  0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x61, 0x64, 0x74, 0x20, 0x3d, 0x20,
+  0x7b, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x20, 0x7d, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x68,
+  0x65, 0x63, 0x6b, 0x73, 0x74, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75,
+  0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20,
+  0x73, 0x6e, 0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79,
+  0x74, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73,
+  0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x72, 0x65, 0x61, 0x64, 0x74, 0x2c,
+  0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x72, 0x65, 0x61, 0x64,
+  0x79, 0x74, 0x5b, 0x74, 0x70, 0x5d, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
+  0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a,
+  0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e, 0x22, 0x29,
+  0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x74, 0x65,
+  0x70, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x73, 0x6e, 0x6b, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x69, 0x6e, 0x6b, 0x20, 0x3d,
+  0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x69, 0x6e, 0x6b,
+  0x28, 0x22, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x2d, 0x77, 0x68, 0x65, 0x6e,
+  0x2d, 0x64, 0x6f, 0x6e, 0x65, 0x22, 0x2c, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x64, 0x61, 0x74, 0x61, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x6c, 0x74, 0x6e, 0x31,
+  0x32, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x2e, 0x61, 0x6c, 0x6c, 0x28, 0x73,
+  0x65, 0x6e, 0x64, 0x74, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c,
+  0x20, 0x73, 0x69, 0x6e, 0x6b, 0x2c, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b,
+  0x73, 0x74, 0x65, 0x70, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
+  0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69, 0x6e,
+  0x64, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x22, 0x31, 0x2e, 0x2e,
+  0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70,
+  0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e, 0x22,
+  0x29, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x63, 0x6c, 0x6f,
+  0x73, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x6f,
+  0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x28, 0x31, 0x2c,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x67,
+  0x65, 0x74, 0x73, 0x74, 0x61, 0x74, 0x73, 0x28, 0x29, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64, 0x61, 0x74, 0x61,
+  0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x0a, 0x65,
+  0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+  0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65,
+  0x78, 0x3a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x28, 0x72, 0x65,
+  0x63, 0x76, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70,
+  0x61, 0x73, 0x76, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x20, 0x22, 0x6e, 0x65,
+  0x65, 0x64, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x70,
+  0x61, 0x73, 0x76, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x22, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x70, 0x61, 0x73, 0x76, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x3a, 0x70, 0x61, 0x73, 0x76, 0x63, 0x6f, 0x6e, 0x6e,
+  0x65, 0x63, 0x74, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x72, 0x67, 0x75,
+  0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x63, 0x76, 0x74,
+  0x2e, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x72,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c,
+  0x2e, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x28, 0x73, 0x74,
+  0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x72, 0x65,
+  0x63, 0x76, 0x74, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x20, 0x6f, 0x72, 0x20,
+  0x22, 0x22, 0x2c, 0x20, 0x22, 0x5e, 0x5b, 0x2f, 0x5c, 0x5c, 0x5d, 0x22,
+  0x2c, 0x20, 0x22, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
+  0x66, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d,
+  0x3d, 0x20, 0x22, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x72,
+  0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c,
+  0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x3d,
+  0x20, 0x72, 0x65, 0x63, 0x76, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
+  0x6e, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x72, 0x65, 0x74, 0x72, 0x22,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72,
+  0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f,
+  0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
+  0x64, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x29,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x63, 0x6f, 0x64, 0x65, 0x2c, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x20, 0x3d,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x7b,
+  0x22, 0x31, 0x2e, 0x2e, 0x22, 0x2c, 0x20, 0x22, 0x32, 0x2e, 0x2e, 0x22,
+  0x7d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63,
+  0x6f, 0x64, 0x65, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x30, 0x30, 0x29, 0x20,
+  0x61, 0x6e, 0x64, 0x20, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3c, 0x3d,
+  0x20, 0x32, 0x39, 0x39, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x63, 0x76, 0x74,
+  0x2e, 0x73, 0x69, 0x6e, 0x6b, 0x28, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x31, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x61, 0x73, 0x76, 0x74, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x3a, 0x70, 0x6f,
+  0x72, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x28, 0x29, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x73,
+  0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+  0x28, 0x22, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x2d, 0x63, 0x6c, 0x6f, 0x73,
+  0x65, 0x64, 0x22, 0x2c, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64, 0x61,
+  0x74, 0x61, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x63,
+  0x76, 0x74, 0x2e, 0x73, 0x74, 0x65, 0x70, 0x20, 0x6f, 0x72, 0x20, 0x6c,
+  0x74, 0x6e, 0x31, 0x32, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x2e, 0x73, 0x74,
+  0x65, 0x70, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x72, 0x79, 0x28, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x2e, 0x70, 0x75,
+  0x6d, 0x70, 0x2e, 0x61, 0x6c, 0x6c, 0x28, 0x73, 0x6f, 0x75, 0x72, 0x63,
+  0x65, 0x2c, 0x20, 0x72, 0x65, 0x63, 0x76, 0x74, 0x2e, 0x73, 0x69, 0x6e,
+  0x6b, 0x2c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x29, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+  0x66, 0x69, 0x6e, 0x64, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x22,
+  0x31, 0x2e, 0x2e, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32,
+  0x2e, 0x2e, 0x22, 0x29, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x3a,
+  0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20,
+  0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x31, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e,
+  0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e,
+  0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x63, 0x77, 0x64, 0x28,
+  0x64, 0x69, 0x72, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x22, 0x63,
+  0x77, 0x64, 0x22, 0x2c, 0x20, 0x64, 0x69, 0x72, 0x29, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63,
+  0x6b, 0x28, 0x32, 0x35, 0x30, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x0a, 0x65, 0x6e, 0x64,
+  0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65,
+  0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a,
+  0x74, 0x79, 0x70, 0x65, 0x28, 0x74, 0x79, 0x70, 0x65, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d,
+  0x61, 0x6e, 0x64, 0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x20,
+  0x74, 0x79, 0x70, 0x65, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x32, 0x30,
+  0x30, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x31, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e,
+  0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e,
+  0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x67, 0x72, 0x65, 0x65,
+  0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x7b, 0x22, 0x31, 0x2e, 0x2e,
+  0x22, 0x2c, 0x20, 0x22, 0x32, 0x2e, 0x2e, 0x22, 0x7d, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+  0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20,
+  0x22, 0x31, 0x2e, 0x2e, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22,
+  0x32, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x0a,
+  0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+  0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64,
+  0x65, 0x78, 0x3a, 0x71, 0x75, 0x69, 0x74, 0x28, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
+  0x6e, 0x64, 0x28, 0x22, 0x71, 0x75, 0x69, 0x74, 0x22, 0x29, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79,
+  0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65,
+  0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x0a,
+  0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+  0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64,
+  0x65, 0x78, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x64,
+  0x61, 0x74, 0x61, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65,
+  0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
+  0x66, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
+  0x72, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65,
+  0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x70, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x65, 0x6e,
+  0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
+  0x65, 0x28, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x74, 0x2e, 0x75, 0x72, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x75, 0x20, 0x3d, 0x20, 0x75, 0x72, 0x6c, 0x2e, 0x70, 0x61, 0x72,
+  0x73, 0x65, 0x28, 0x74, 0x2e, 0x75, 0x72, 0x6c, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c,
+  0x76, 0x20, 0x69, 0x6e, 0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x70, 0x61,
+  0x69, 0x72, 0x73, 0x28, 0x74, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x5b,
+  0x69, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e,
+  0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x70, 0x75, 0x74, 0x28, 0x70, 0x75,
+  0x74, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x74, 0x74,
+  0x20, 0x3d, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x28,
+  0x70, 0x75, 0x74, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f,
+  0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x70, 0x75, 0x74,
+  0x74, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2c, 0x20, 0x22, 0x6d, 0x69, 0x73,
+  0x73, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
+  0x65, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x5f, 0x4d, 0x2e, 0x6f, 0x70, 0x65,
+  0x6e, 0x28, 0x70, 0x75, 0x74, 0x74, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2c,
+  0x20, 0x70, 0x75, 0x74, 0x74, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x2c, 0x20,
+  0x70, 0x75, 0x74, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x3a, 0x67, 0x72, 0x65, 0x65, 0x74,
+  0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x3a, 0x6c, 0x6f, 0x67,
+  0x69, 0x6e, 0x28, 0x70, 0x75, 0x74, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72,
+  0x2c, 0x20, 0x70, 0x75, 0x74, 0x74, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77,
+  0x6f, 0x72, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x70, 0x75, 0x74, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x20, 0x74, 0x68,
+  0x65, 0x6e, 0x20, 0x66, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x28, 0x70, 0x75,
+  0x74, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x29, 0x20, 0x65, 0x6e, 0x64,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x3a, 0x65, 0x70, 0x73, 0x76, 0x28,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x73, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x3a, 0x73, 0x65, 0x6e,
+  0x64, 0x28, 0x70, 0x75, 0x74, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x66, 0x3a, 0x71, 0x75, 0x69, 0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x66, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65,
+  0x6e, 0x74, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20,
+  0x22, 0x2f, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x63, 0x68,
+  0x65, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x66, 0x74, 0x70, 0x22, 0x0a,
+  0x7d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63,
+  0x66, 0x6f, 0x72, 0x6d, 0x28, 0x75, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x6f,
+  0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x75, 0x72, 0x6c,
+  0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x75, 0x2c, 0x20, 0x64, 0x65,
+  0x66, 0x61, 0x75, 0x6c, 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x74,
+  0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x22,
+  0x66, 0x74, 0x70, 0x22, 0x2c, 0x20, 0x22, 0x77, 0x72, 0x6f, 0x6e, 0x67,
+  0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x20, 0x27, 0x22, 0x20, 0x2e,
+  0x2e, 0x20, 0x74, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x20, 0x2e,
+  0x2e, 0x20, 0x22, 0x27, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x74, 0x2e,
+  0x68, 0x6f, 0x73, 0x74, 0x2c, 0x20, 0x22, 0x6d, 0x69, 0x73, 0x73, 0x69,
+  0x6e, 0x67, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x70, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x22, 0x5e, 0x74, 0x79, 0x70, 0x65,
+  0x3d, 0x28, 0x2e, 0x29, 0x24, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
+  0x66, 0x20, 0x74, 0x2e, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x74,
+  0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63,
+  0x6b, 0x65, 0x74, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x28, 0x32, 0x2c, 0x20,
+  0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28,
+  0x74, 0x2e, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x70, 0x61,
+  0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x74,
+  0x2e, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x61, 0x22,
+  0x20, 0x6f, 0x72, 0x20, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d,
+  0x3d, 0x20, 0x22, 0x69, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, 0x6e, 0x76, 0x61,
+  0x6c, 0x69, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x27, 0x22, 0x20,
+  0x2e, 0x2e, 0x20, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x20, 0x2e, 0x2e,
+  0x20, 0x22, 0x27, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x74, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x5f, 0x4d, 0x2e, 0x67, 0x65,
+  0x6e, 0x65, 0x72, 0x69, 0x63, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x3d, 0x20,
+  0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x66, 0x6f, 0x72, 0x6d, 0x0a,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x73, 0x70, 0x75, 0x74, 0x28, 0x75, 0x2c, 0x20, 0x62,
+  0x6f, 0x64, 0x79, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x70, 0x75, 0x74, 0x74, 0x20, 0x3d, 0x20, 0x67, 0x65,
+  0x6e, 0x65, 0x72, 0x69, 0x63, 0x66, 0x6f, 0x72, 0x6d, 0x28, 0x75, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x74, 0x74, 0x2e, 0x73, 0x6f,
+  0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x6c, 0x74, 0x6e, 0x31, 0x32,
+  0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x73, 0x74, 0x72, 0x69,
+  0x6e, 0x67, 0x28, 0x62, 0x6f, 0x64, 0x79, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x70, 0x75, 0x74,
+  0x28, 0x70, 0x75, 0x74, 0x74, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x5f,
+  0x4d, 0x2e, 0x70, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b,
+  0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x28, 0x66,
+  0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x75, 0x74, 0x74,
+  0x2c, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65,
+  0x28, 0x70, 0x75, 0x74, 0x74, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x73,
+  0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x70, 0x75, 0x74, 0x28,
+  0x70, 0x75, 0x74, 0x74, 0x2c, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x74, 0x70, 0x75, 0x74, 0x28, 0x70, 0x75, 0x74,
+  0x74, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x29, 0x0a,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x74, 0x67, 0x65, 0x74, 0x28, 0x67, 0x65, 0x74, 0x74,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67, 0x65, 0x74, 0x74, 0x20, 0x3d,
+  0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x28, 0x67, 0x65,
+  0x74, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x63, 0x6b,
+  0x65, 0x74, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x67, 0x65, 0x74, 0x74, 0x2e,
+  0x68, 0x6f, 0x73, 0x74, 0x2c, 0x20, 0x22, 0x6d, 0x69, 0x73, 0x73, 0x69,
+  0x6e, 0x67, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x66, 0x20, 0x3d, 0x20, 0x5f, 0x4d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x28,
+  0x67, 0x65, 0x74, 0x74, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2c, 0x20, 0x67,
+  0x65, 0x74, 0x74, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x2c, 0x20, 0x67, 0x65,
+  0x74, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x66, 0x3a, 0x67, 0x72, 0x65, 0x65, 0x74, 0x28, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x3a, 0x6c, 0x6f, 0x67, 0x69, 0x6e,
+  0x28, 0x67, 0x65, 0x74, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x20,
+  0x67, 0x65, 0x74, 0x74, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
+  0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x67, 0x65,
+  0x74, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e,
+  0x20, 0x66, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x28, 0x67, 0x65, 0x74, 0x74,
+  0x2e, 0x74, 0x79, 0x70, 0x65, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x66, 0x3a, 0x65, 0x70, 0x73, 0x76, 0x28, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x66, 0x3a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76,
+  0x65, 0x28, 0x67, 0x65, 0x74, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x66, 0x3a, 0x71, 0x75, 0x69, 0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x3a, 0x63, 0x6c,
+  0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c, 0x6f,
+  0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+  0x20, 0x73, 0x67, 0x65, 0x74, 0x28, 0x75, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x67, 0x65, 0x74, 0x74, 0x20,
+  0x3d, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x66, 0x6f, 0x72,
+  0x6d, 0x28, 0x75, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x67, 0x65, 0x74, 0x74, 0x2e, 0x73, 0x69, 0x6e, 0x6b, 0x20,
+  0x3d, 0x20, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x2e, 0x73, 0x69, 0x6e, 0x6b,
+  0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x74, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x74, 0x67, 0x65, 0x74, 0x28, 0x67, 0x65, 0x74, 0x74, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74,
+  0x28, 0x74, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x5f, 0x4d, 0x2e, 0x63,
+  0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63,
+  0x6b, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x28,
+  0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x6d, 0x64,
+  0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6d, 0x64, 0x74, 0x20,
+  0x3d, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x28, 0x63,
+  0x6d, 0x64, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x63,
+  0x6b, 0x65, 0x74, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x63, 0x6d, 0x64, 0x74,
+  0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2c, 0x20, 0x22, 0x6d, 0x69, 0x73, 0x73,
+  0x69, 0x6e, 0x67, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
+  0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+  0x74, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x63, 0x6d, 0x64, 0x74, 0x2e, 0x63,
+  0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2c, 0x20, 0x22, 0x6d, 0x69, 0x73,
+  0x73, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
+  0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x66, 0x20, 0x3d, 0x20, 0x5f, 0x4d, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+  0x28, 0x63, 0x6d, 0x64, 0x74, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2c, 0x20,
+  0x63, 0x6d, 0x64, 0x74, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x2c, 0x20, 0x63,
+  0x6d, 0x64, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x66, 0x3a, 0x67, 0x72, 0x65, 0x65, 0x74, 0x28,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x3a, 0x6c, 0x6f, 0x67, 0x69,
+  0x6e, 0x28, 0x63, 0x6d, 0x64, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2c,
+  0x20, 0x63, 0x6d, 0x64, 0x74, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
+  0x72, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74,
+  0x79, 0x70, 0x65, 0x28, 0x63, 0x6d, 0x64, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+  0x6d, 0x61, 0x6e, 0x64, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x74, 0x61,
+  0x62, 0x6c, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x63,
+  0x6d, 0x64, 0x74, 0x2e, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+  0x20, 0x6f, 0x72, 0x20, 0x7b, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x65,
+  0x63, 0x6b, 0x20, 0x3d, 0x20, 0x63, 0x6d, 0x64, 0x74, 0x2e, 0x63, 0x68,
+  0x65, 0x63, 0x6b, 0x20, 0x6f, 0x72, 0x20, 0x7b, 0x7d, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c,
+  0x63, 0x6d, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x70, 0x61, 0x69, 0x72,
+  0x73, 0x28, 0x63, 0x6d, 0x64, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
+  0x6e, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x2e, 0x74, 0x72, 0x79,
+  0x28, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
+  0x64, 0x28, 0x63, 0x6d, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d,
+  0x65, 0x6e, 0x74, 0x5b, 0x69, 0x5d, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x63, 0x68, 0x65, 0x63, 0x6b, 0x5b, 0x69, 0x5d, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x20, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x66, 0x2e, 0x74, 0x70,
+  0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x63, 0x68, 0x65, 0x63, 0x6b,
+  0x5b, 0x69, 0x5d, 0x29, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x66, 0x2e, 0x74,
+  0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x63, 0x6d,
+  0x64, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2c, 0x20,
+  0x63, 0x6d, 0x64, 0x74, 0x2e, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e,
+  0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x63, 0x6d, 0x64, 0x74, 0x2e, 0x63, 0x68, 0x65, 0x63,
+  0x6b, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x66, 0x2e, 0x74, 0x72, 0x79,
+  0x28, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28,
+  0x63, 0x6d, 0x64, 0x74, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x29, 0x29,
+  0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x3a, 0x71, 0x75, 0x69, 0x74, 0x28,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x66, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x65,
+  0x6e, 0x64, 0x29, 0x0a, 0x5f, 0x4d, 0x2e, 0x67, 0x65, 0x74, 0x20, 0x3d,
+  0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+  0x65, 0x63, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+  0x28, 0x67, 0x65, 0x74, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
+  0x66, 0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x28,
+  0x67, 0x65, 0x74, 0x74, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x73, 0x74,
+  0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x67, 0x65, 0x74, 0x28, 0x67,
+  0x65, 0x74, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73,
+  0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x67, 0x65,
+  0x74, 0x28, 0x67, 0x65, 0x74, 0x74, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+  0x65, 0x6e, 0x64, 0x29, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x5f, 0x4d, 0x0a,0x0};
+  return luaL_dostring(L, (const char*)B); 
+} /* end of embedded lua code */
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_headers.lua
@@ -0,0 +1,106 @@
+-----------------------------------------------------------------------------
+-- Canonic header field capitalization
+-- LuaSocket toolkit.
+-- Author: Diego Nehab
+-----------------------------------------------------------------------------
+if not(socket) then 
+ local socket = require("socket")
+end
+socket.headers = {}
+local _M = socket.headers
+
+_M.canonic = {
+    ["accept"] = "Accept",
+    ["accept-charset"] = "Accept-Charset",
+    ["accept-encoding"] = "Accept-Encoding",
+    ["accept-language"] = "Accept-Language",
+    ["accept-ranges"] = "Accept-Ranges",
+    ["action"] = "Action",
+    ["alternate-recipient"] = "Alternate-Recipient",
+    ["age"] = "Age",
+    ["allow"] = "Allow",
+    ["arrival-date"] = "Arrival-Date",
+    ["authorization"] = "Authorization",
+    ["bcc"] = "Bcc",
+    ["cache-control"] = "Cache-Control",
+    ["cc"] = "Cc",
+    ["comments"] = "Comments",
+    ["connection"] = "Connection",
+    ["content-description"] = "Content-Description",
+    ["content-disposition"] = "Content-Disposition",
+    ["content-encoding"] = "Content-Encoding",
+    ["content-id"] = "Content-ID",
+    ["content-language"] = "Content-Language",
+    ["content-length"] = "Content-Length",
+    ["content-location"] = "Content-Location",
+    ["content-md5"] = "Content-MD5",
+    ["content-range"] = "Content-Range",
+    ["content-transfer-encoding"] = "Content-Transfer-Encoding",
+    ["content-type"] = "Content-Type",
+    ["cookie"] = "Cookie",
+    ["date"] = "Date",
+    ["diagnostic-code"] = "Diagnostic-Code",
+    ["dsn-gateway"] = "DSN-Gateway",
+    ["etag"] = "ETag",
+    ["expect"] = "Expect",
+    ["expires"] = "Expires",
+    ["final-log-id"] = "Final-Log-ID",
+    ["final-recipient"] = "Final-Recipient",
+    ["from"] = "From",
+    ["host"] = "Host",
+    ["if-match"] = "If-Match",
+    ["if-modified-since"] = "If-Modified-Since",
+    ["if-none-match"] = "If-None-Match",
+    ["if-range"] = "If-Range",
+    ["if-unmodified-since"] = "If-Unmodified-Since",
+    ["in-reply-to"] = "In-Reply-To",
+    ["keywords"] = "Keywords",
+    ["last-attempt-date"] = "Last-Attempt-Date",
+    ["last-modified"] = "Last-Modified",
+    ["location"] = "Location",
+    ["max-forwards"] = "Max-Forwards",
+    ["message-id"] = "Message-ID",
+    ["mime-version"] = "MIME-Version",
+    ["original-envelope-id"] = "Original-Envelope-ID",
+    ["original-recipient"] = "Original-Recipient",
+    ["pragma"] = "Pragma",
+    ["proxy-authenticate"] = "Proxy-Authenticate",
+    ["proxy-authorization"] = "Proxy-Authorization",
+    ["range"] = "Range",
+    ["received"] = "Received",
+    ["received-from-mta"] = "Received-From-MTA",
+    ["references"] = "References",
+    ["referer"] = "Referer",
+    ["remote-mta"] = "Remote-MTA",
+    ["reply-to"] = "Reply-To",
+    ["reporting-mta"] = "Reporting-MTA",
+    ["resent-bcc"] = "Resent-Bcc",
+    ["resent-cc"] = "Resent-Cc",
+    ["resent-date"] = "Resent-Date",
+    ["resent-from"] = "Resent-From",
+    ["resent-message-id"] = "Resent-Message-ID",
+    ["resent-reply-to"] = "Resent-Reply-To",
+    ["resent-sender"] = "Resent-Sender",
+    ["resent-to"] = "Resent-To",
+    ["retry-after"] = "Retry-After",
+    ["return-path"] = "Return-Path",
+    ["sender"] = "Sender",
+    ["server"] = "Server",
+    ["smtp-remote-recipient"] = "SMTP-Remote-Recipient",
+    ["status"] = "Status",
+    ["subject"] = "Subject",
+    ["te"] = "TE",
+    ["to"] = "To",
+    ["trailer"] = "Trailer",
+    ["transfer-encoding"] = "Transfer-Encoding",
+    ["upgrade"] = "Upgrade",
+    ["user-agent"] = "User-Agent",
+    ["vary"] = "Vary",
+    ["via"] = "Via",
+    ["warning"] = "Warning",
+    ["will-retry-until"] = "Will-Retry-Until",
+    ["www-authenticate"] = "WWW-Authenticate",
+    ["x-mailer"] = "X-Mailer",
+}
+
+return _M
\ No newline at end of file
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_headers_lua.c
@@ -0,0 +1,301 @@
+/*
+ * This file is generated with xxd -i and bit of bash script.
+*/
+#include "lua.h"
+#include "lauxlib.h"
+ 
+int luatex_headers_lua_open (lua_State *L) { 
+    static unsigned char B[] = {
+  0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x28, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+  0x74, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x0a, 0x20, 0x6c, 0x6f,
+  0x63, 0x61, 0x6c, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x3d,
+  0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f,
+  0x63, 0x6b, 0x65, 0x74, 0x22, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x73,
+  0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+  0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x5f, 0x4d, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+  0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x0a, 0x5f, 0x4d, 0x2e,
+  0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x3d, 0x20, 0x7b, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74,
+  0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x61, 0x63, 0x63,
+  0x65, 0x70, 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x22,
+  0x5d, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+  0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x65,
+  0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x5d, 0x20, 0x3d, 0x20,
+  0x22, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, 0x63, 0x6f,
+  0x64, 0x69, 0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b,
+  0x22, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67,
+  0x75, 0x61, 0x67, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x63,
+  0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67,
+  0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x61, 0x63,
+  0x63, 0x65, 0x70, 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22,
+  0x5d, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+  0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x20,
+  0x3d, 0x20, 0x22, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e,
+  0x61, 0x74, 0x65, 0x2d, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e,
+  0x74, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x6c, 0x74, 0x65, 0x72,
+  0x6e, 0x61, 0x74, 0x65, 0x2d, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65,
+  0x6e, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x61,
+  0x67, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x67, 0x65, 0x22,
+  0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x61, 0x6c, 0x6c, 0x6f,
+  0x77, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x6c, 0x6c, 0x6f, 0x77,
+  0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x61, 0x72, 0x72,
+  0x69, 0x76, 0x61, 0x6c, 0x2d, 0x64, 0x61, 0x74, 0x65, 0x22, 0x5d, 0x20,
+  0x3d, 0x20, 0x22, 0x41, 0x72, 0x72, 0x69, 0x76, 0x61, 0x6c, 0x2d, 0x44,
+  0x61, 0x74, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22,
+  0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
+  0x6e, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x41, 0x75, 0x74, 0x68, 0x6f,
+  0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x62, 0x63, 0x63, 0x22, 0x5d, 0x20, 0x3d,
+  0x20, 0x22, 0x42, 0x63, 0x63, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74,
+  0x72, 0x6f, 0x6c, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x43, 0x61, 0x63,
+  0x68, 0x65, 0x2d, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x22, 0x2c,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x63, 0x22, 0x5d, 0x20,
+  0x3d, 0x20, 0x22, 0x43, 0x63, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x5d,
+  0x20, 0x3d, 0x20, 0x22, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+  0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e,
+  0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x20, 0x3d, 0x20,
+  0x22, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22,
+  0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e, 0x74,
+  0x65, 0x6e, 0x74, 0x2d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+  0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x43, 0x6f, 0x6e,
+  0x74, 0x65, 0x6e, 0x74, 0x2d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+  0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b,
+  0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x64, 0x69, 0x73,
+  0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x20, 0x3d,
+  0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x44, 0x69,
+  0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+  0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x5d,
+  0x20, 0x3d, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
+  0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+  0x2d, 0x69, 0x64, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x43, 0x6f, 0x6e,
+  0x74, 0x65, 0x6e, 0x74, 0x2d, 0x49, 0x44, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
+  0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22, 0x5d, 0x20, 0x3d,
+  0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c, 0x61,
+  0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c,
+  0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x43,
+  0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74,
+  0x68, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f,
+  0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
+  0x6f, 0x6e, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74,
+  0x65, 0x6e, 0x74, 0x2d, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+  0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e,
+  0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x22, 0x5d, 0x20, 0x3d,
+  0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4d, 0x44,
+  0x35, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f,
+  0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22,
+  0x5d, 0x20, 0x3d, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+  0x2d, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74,
+  0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x63, 0x6f,
+  0x64, 0x69, 0x6e, 0x67, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x43, 0x6f,
+  0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66,
+  0x65, 0x72, 0x2d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22,
+  0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63, 0x6f, 0x6e, 0x74,
+  0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x22, 0x5d, 0x20, 0x3d,
+  0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+  0x70, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x63,
+  0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x43,
+  0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x64, 0x61, 0x74, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22,
+  0x44, 0x61, 0x74, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b,
+  0x22, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x2d,
+  0x63, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x44, 0x69,
+  0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x2d, 0x43, 0x6f, 0x64,
+  0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x64, 0x73,
+  0x6e, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x5d, 0x20,
+  0x3d, 0x20, 0x22, 0x44, 0x53, 0x4e, 0x2d, 0x47, 0x61, 0x74, 0x65, 0x77,
+  0x61, 0x79, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x65,
+  0x74, 0x61, 0x67, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x45, 0x54, 0x61,
+  0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x65, 0x78,
+  0x70, 0x65, 0x63, 0x74, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x45, 0x78,
+  0x70, 0x65, 0x63, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b,
+  0x22, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x22, 0x5d, 0x20, 0x3d,
+  0x20, 0x22, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x22, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x2d,
+  0x6c, 0x6f, 0x67, 0x2d, 0x69, 0x64, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22,
+  0x46, 0x69, 0x6e, 0x61, 0x6c, 0x2d, 0x4c, 0x6f, 0x67, 0x2d, 0x49, 0x44,
+  0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x66, 0x69, 0x6e,
+  0x61, 0x6c, 0x2d, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x2d,
+  0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0x5d,
+  0x20, 0x3d, 0x20, 0x22, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x5d, 0x20,
+  0x3d, 0x20, 0x22, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x69, 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x49, 0x66, 0x2d, 0x4d, 0x61, 0x74,
+  0x63, 0x68, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x69,
+  0x66, 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,
+  0x69, 0x6e, 0x63, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x49, 0x66,
+  0x2d, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x53, 0x69,
+  0x6e, 0x63, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22,
+  0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, 0x6d, 0x61, 0x74, 0x63,
+  0x68, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x49, 0x66, 0x2d, 0x4e, 0x6f,
+  0x6e, 0x65, 0x2d, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,
+  0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x49, 0x66, 0x2d, 0x52, 0x61,
+  0x6e, 0x67, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22,
+  0x69, 0x66, 0x2d, 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,
+  0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20,
+  0x22, 0x49, 0x66, 0x2d, 0x55, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,
+  0x65, 0x64, 0x2d, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x69, 0x6e, 0x2d, 0x72, 0x65, 0x70, 0x6c,
+  0x79, 0x2d, 0x74, 0x6f, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x49, 0x6e,
+  0x2d, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2d, 0x54, 0x6f, 0x22, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72,
+  0x64, 0x73, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x77,
+  0x6f, 0x72, 0x64, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b,
+  0x22, 0x6c, 0x61, 0x73, 0x74, 0x2d, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70,
+  0x74, 0x2d, 0x64, 0x61, 0x74, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22,
+  0x4c, 0x61, 0x73, 0x74, 0x2d, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74,
+  0x2d, 0x44, 0x61, 0x74, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x6c, 0x61, 0x73, 0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66,
+  0x69, 0x65, 0x64, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x4c, 0x61, 0x73,
+  0x74, 0x2d, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x22, 0x2c,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x74,
+  0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x4c, 0x6f, 0x63,
+  0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72,
+  0x64, 0x73, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x4d, 0x61, 0x78, 0x2d,
+  0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x73, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+  0x2d, 0x69, 0x64, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x4d, 0x65, 0x73,
+  0x73, 0x61, 0x67, 0x65, 0x2d, 0x49, 0x44, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x6d, 0x69, 0x6d, 0x65, 0x2d, 0x76, 0x65, 0x72,
+  0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x4d, 0x49,
+  0x4d, 0x45, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x2c,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x6f, 0x72, 0x69, 0x67, 0x69,
+  0x6e, 0x61, 0x6c, 0x2d, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65,
+  0x2d, 0x69, 0x64, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x4f, 0x72, 0x69,
+  0x67, 0x69, 0x6e, 0x61, 0x6c, 0x2d, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f,
+  0x70, 0x65, 0x2d, 0x49, 0x44, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x2d, 0x72,
+  0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x5d, 0x20, 0x3d,
+  0x20, 0x22, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x2d, 0x52,
+  0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x22,
+  0x5d, 0x20, 0x3d, 0x20, 0x22, 0x50, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x22,
+  0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x70, 0x72, 0x6f, 0x78,
+  0x79, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
+  0x74, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x50, 0x72, 0x6f, 0x78,
+  0x79, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
+  0x74, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x70,
+  0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+  0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22,
+  0x50, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+  0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x5d, 0x20,
+  0x3d, 0x20, 0x22, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
+  0x64, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x63, 0x65, 0x69,
+  0x76, 0x65, 0x64, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22,
+  0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x2d, 0x66, 0x72, 0x6f,
+  0x6d, 0x2d, 0x6d, 0x74, 0x61, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52,
+  0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x2d, 0x46, 0x72, 0x6f, 0x6d,
+  0x2d, 0x4d, 0x54, 0x41, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b,
+  0x22, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x22,
+  0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
+  0x63, 0x65, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22,
+  0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, 0x22, 0x5d, 0x20, 0x3d, 0x20,
+  0x22, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d,
+  0x6d, 0x74, 0x61, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x6d,
+  0x6f, 0x74, 0x65, 0x2d, 0x4d, 0x54, 0x41, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x2d, 0x74, 0x6f,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2d,
+  0x54, 0x6f, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x72,
+  0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x6d, 0x74, 0x61,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74,
+  0x69, 0x6e, 0x67, 0x2d, 0x4d, 0x54, 0x41, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x62,
+  0x63, 0x63, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x73, 0x65,
+  0x6e, 0x74, 0x2d, 0x42, 0x63, 0x63, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x63, 0x63,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x73, 0x65, 0x6e, 0x74,
+  0x2d, 0x43, 0x63, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22,
+  0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x64, 0x61, 0x74, 0x65, 0x22,
+  0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d,
+  0x44, 0x61, 0x74, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b,
+  0x22, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x72, 0x6f, 0x6d,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x73, 0x65, 0x6e, 0x74,
+  0x2d, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x6d, 0x65, 0x73,
+  0x73, 0x61, 0x67, 0x65, 0x2d, 0x69, 0x64, 0x22, 0x5d, 0x20, 0x3d, 0x20,
+  0x22, 0x52, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x4d, 0x65, 0x73, 0x73,
+  0x61, 0x67, 0x65, 0x2d, 0x49, 0x44, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x65,
+  0x70, 0x6c, 0x79, 0x2d, 0x74, 0x6f, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22,
+  0x52, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x52, 0x65, 0x70, 0x6c, 0x79,
+  0x2d, 0x54, 0x6f, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22,
+  0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d, 0x73, 0x65, 0x6e, 0x64, 0x65,
+  0x72, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x73, 0x65, 0x6e,
+  0x74, 0x2d, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x2d,
+  0x74, 0x6f, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x73, 0x65,
+  0x6e, 0x74, 0x2d, 0x54, 0x6f, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x72, 0x65, 0x74, 0x72, 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65,
+  0x72, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x74, 0x72, 0x79,
+  0x2d, 0x41, 0x66, 0x74, 0x65, 0x72, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x70, 0x61,
+  0x74, 0x68, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x52, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x2d, 0x50, 0x61, 0x74, 0x68, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x5d,
+  0x20, 0x3d, 0x20, 0x22, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x2c,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x73, 0x65, 0x72, 0x76, 0x65,
+  0x72, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x53, 0x65, 0x72, 0x76, 0x65,
+  0x72, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x73, 0x6d,
+  0x74, 0x70, 0x2d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x72, 0x65,
+  0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x5d, 0x20, 0x3d, 0x20,
+  0x22, 0x53, 0x4d, 0x54, 0x50, 0x2d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65,
+  0x2d, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x2c,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75,
+  0x73, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x53, 0x74, 0x61, 0x74, 0x75,
+  0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x73, 0x75,
+  0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x53,
+  0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x74, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x54,
+  0x45, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x74, 0x6f,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x54, 0x6f, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x5b, 0x22, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72,
+  0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x54, 0x72, 0x61, 0x69, 0x6c, 0x65,
+  0x72, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x74, 0x72,
+  0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x63, 0x6f, 0x64,
+  0x69, 0x6e, 0x67, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x54, 0x72, 0x61,
+  0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69,
+  0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x75,
+  0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22,
+  0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65,
+  0x6e, 0x74, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x55, 0x73, 0x65, 0x72,
+  0x2d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x76, 0x61, 0x72, 0x79, 0x22, 0x5d, 0x20, 0x3d, 0x20,
+  0x22, 0x56, 0x61, 0x72, 0x79, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x76, 0x69, 0x61, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x56,
+  0x69, 0x61, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x22, 0x77,
+  0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22,
+  0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x5b, 0x22, 0x77, 0x69, 0x6c, 0x6c, 0x2d, 0x72, 0x65, 0x74,
+  0x72, 0x79, 0x2d, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x22, 0x5d, 0x20, 0x3d,
+  0x20, 0x22, 0x57, 0x69, 0x6c, 0x6c, 0x2d, 0x52, 0x65, 0x74, 0x72, 0x79,
+  0x2d, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x5b, 0x22, 0x77, 0x77, 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65,
+  0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20,
+  0x22, 0x57, 0x57, 0x57, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
+  0x69, 0x63, 0x61, 0x74, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x5b, 0x22, 0x78, 0x2d, 0x6d, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x22, 0x5d,
+  0x20, 0x3d, 0x20, 0x22, 0x58, 0x2d, 0x4d, 0x61, 0x69, 0x6c, 0x65, 0x72,
+  0x22, 0x2c, 0x0a, 0x7d, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x5f, 0x4d,0x0};
+  return luaL_dostring(L, (const char*)B); 
+} /* end of embedded lua code */
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_smtp.lua
@@ -0,0 +1,256 @@
+-----------------------------------------------------------------------------
+-- SMTP client support for the Lua language.
+-- LuaSocket toolkit.
+-- Author: Diego Nehab
+-----------------------------------------------------------------------------
+
+-----------------------------------------------------------------------------
+-- Declare module and import dependencies
+-----------------------------------------------------------------------------
+local base = _G
+local coroutine = require("coroutine")
+local string = require("string")
+local math = require("math")
+local os = require("os")
+local socket = socket or require("socket") ; 
+local tp = socket.tp or require("socket.tp"); 
+local ltn12 = ltn12 or require("ltn12");
+local headers = socket.headers or require("socket.headers"); 
+local mime = mime or require("mime"); 
+
+socket.smtp = {}
+local _M = socket.smtp
+
+-----------------------------------------------------------------------------
+-- Program constants
+-----------------------------------------------------------------------------
+-- timeout for connection
+_M.TIMEOUT = 60
+-- default server used to send e-mails
+_M.SERVER = "localhost"
+-- default port
+_M.PORT = 25
+-- domain used in HELO command and default sendmail
+-- If we are under a CGI, try to get from environment
+_M.DOMAIN = os.getenv("SERVER_NAME") or "localhost"
+-- default time zone (means we don't know)
+_M.ZONE = "-0000"
+
+---------------------------------------------------------------------------
+-- Low level SMTP API
+-----------------------------------------------------------------------------
+local metat = { __index = {} }
+
+function metat.__index:greet(domain)
+    self.try(self.tp:check("2.."))
+    self.try(self.tp:command("EHLO", domain or _M.DOMAIN))
+    return socket.skip(1, self.try(self.tp:check("2..")))
+end
+
+function metat.__index:mail(from)
+    self.try(self.tp:command("MAIL", "FROM:" .. from))
+    return self.try(self.tp:check("2.."))
+end
+
+function metat.__index:rcpt(to)
+    self.try(self.tp:command("RCPT", "TO:" .. to))
+    return self.try(self.tp:check("2.."))
+end
+
+function metat.__index:data(src, step)
+    self.try(self.tp:command("DATA"))
+    self.try(self.tp:check("3.."))
+    self.try(self.tp:source(src, step))
+    self.try(self.tp:send("\r\n.\r\n"))
+    return self.try(self.tp:check("2.."))
+end
+
+function metat.__index:quit()
+    self.try(self.tp:command("QUIT"))
+    return self.try(self.tp:check("2.."))
+end
+
+function metat.__index:close()
+    return self.tp:close()
+end
+
+function metat.__index:login(user, password)
+    self.try(self.tp:command("AUTH", "LOGIN"))
+    self.try(self.tp:check("3.."))
+    self.try(self.tp:send(mime.b64(user) .. "\r\n"))
+    self.try(self.tp:check("3.."))
+    self.try(self.tp:send(mime.b64(password) .. "\r\n"))
+    return self.try(self.tp:check("2.."))
+end
+
+function metat.__index:plain(user, password)
+    local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password)
+    self.try(self.tp:command("AUTH", auth))
+    return self.try(self.tp:check("2.."))
+end
+
+function metat.__index:auth(user, password, ext)
+    if not user or not password then return 1 end
+    if string.find(ext, "AUTH[^\n]+LOGIN") then
+        return self:login(user, password)
+    elseif string.find(ext, "AUTH[^\n]+PLAIN") then
+        return self:plain(user, password)
+    else
+        self.try(nil, "authentication not supported")
+    end
+end
+
+-- send message or throw an exception
+function metat.__index:send(mailt)
+    self:mail(mailt.from)
+    if base.type(mailt.rcpt) == "table" then
+        for i,v in base.ipairs(mailt.rcpt) do
+            self:rcpt(v)
+        end
+    else
+        self:rcpt(mailt.rcpt)
+    end
+    self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step)
+end
+
+function _M.open(server, port, create)
+    local tp = socket.try(tp.connect(server or _M.SERVER, port or _M.PORT,
+        _M.TIMEOUT, create))
+    local s = base.setmetatable({tp = tp}, metat)
+    -- make sure tp is closed if we get an exception
+    s.try = socket.newtry(function()
+        s:close()
+    end)
+    return s
+end
+
+-- convert headers to lowercase
+local function lower_headers(headers)
+    local lower = {}
+    for i,v in base.pairs(headers or lower) do
+        lower[string.lower(i)] = v
+    end
+    return lower
+end
+
+---------------------------------------------------------------------------
+-- Multipart message source
+-----------------------------------------------------------------------------
+-- returns a hopefully unique mime boundary
+local seqno = 0
+local function newboundary()
+    seqno = seqno + 1
+    return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'),
+        math.random(0, 99999), seqno)
+end
+
+-- send_message forward declaration
+local send_message
+
+-- yield the headers all at once, it's faster
+local function send_headers(tosend)
+    local canonic = headers.canonic
+    local h = "\r\n"
+    for f,v in base.pairs(tosend) do
+        h = (canonic[f] or f) .. ': ' .. v .. "\r\n" .. h
+    end
+    coroutine.yield(h)
+end
+
+-- yield multipart message body from a multipart message table
+local function send_multipart(mesgt)
+    -- make sure we have our boundary and send headers
+    local bd = newboundary()
+    local headers = lower_headers(mesgt.headers or {})
+    headers['content-type'] = headers['content-type'] or 'multipart/mixed'
+    headers['content-type'] = headers['content-type'] ..
+        '; boundary="' ..  bd .. '"'
+    send_headers(headers)
+    -- send preamble
+    if mesgt.body.preamble then
+        coroutine.yield(mesgt.body.preamble)
+        coroutine.yield("\r\n")
+    end
+    -- send each part separated by a boundary
+    for i, m in base.ipairs(mesgt.body) do
+        coroutine.yield("\r\n--" .. bd .. "\r\n")
+        send_message(m)
+    end
+    -- send last boundary
+    coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
+    -- send epilogue
+    if mesgt.body.epilogue then
+        coroutine.yield(mesgt.body.epilogue)
+        coroutine.yield("\r\n")
+    end
+end
+
+-- yield message body from a source
+local function send_source(mesgt)
+    -- make sure we have a content-type
+    local headers = lower_headers(mesgt.headers or {})
+    headers['content-type'] = headers['content-type'] or
+        'text/plain; charset="iso-8859-1"'
+    send_headers(headers)
+    -- send body from source
+    while true do
+        local chunk, err = mesgt.body()
+        if err then coroutine.yield(nil, err)
+        elseif chunk then coroutine.yield(chunk)
+        else break end
+    end
+end
+
+-- yield message body from a string
+local function send_string(mesgt)
+    -- make sure we have a content-type
+    local headers = lower_headers(mesgt.headers or {})
+    headers['content-type'] = headers['content-type'] or
+        'text/plain; charset="iso-8859-1"'
+    send_headers(headers)
+    -- send body from string
+    coroutine.yield(mesgt.body)
+end
+
+-- message source
+function send_message(mesgt)
+    if base.type(mesgt.body) == "table" then send_multipart(mesgt)
+    elseif base.type(mesgt.body) == "function" then send_source(mesgt)
+    else send_string(mesgt) end
+end
+
+-- set defaul headers
+local function adjust_headers(mesgt)
+    local lower = lower_headers(mesgt.headers)
+    lower["date"] = lower["date"] or
+        os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or _M.ZONE)
+    lower["x-mailer"] = lower["x-mailer"] or socket._VERSION
+    -- this can't be overriden
+    lower["mime-version"] = "1.0"
+    return lower
+end
+
+function _M.message(mesgt)
+    mesgt.headers = adjust_headers(mesgt)
+    -- create and return message source
+    local co = coroutine.create(function() send_message(mesgt) end)
+    return function()
+        local ret, a, b = coroutine.resume(co)
+        if ret then return a, b
+        else return nil, a end
+    end
+end
+
+---------------------------------------------------------------------------
+-- High level SMTP API
+-----------------------------------------------------------------------------
+_M.send = socket.protect(function(mailt)
+    local s = _M.open(mailt.server, mailt.port, mailt.create)
+    local ext = s:greet(mailt.domain)
+    s:auth(mailt.user, mailt.password, ext)
+    s:send(mailt)
+    s:quit()
+    return s:close()
+end)
+
+return _M
\ No newline at end of file
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_smtp_lua.c
@@ -0,0 +1,504 @@
+/*
+ * This file is generated with xxd -i and bit of bash script.
+*/
+#include "lua.h"
+#include "lauxlib.h"
+ 
+int luatex_smtp_lua_open (lua_State *L) { 
+    static unsigned char B[] = {
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x3d,
+  0x20, 0x5f, 0x47, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f,
+  0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x20, 0x3d, 0x20, 0x72, 0x65,
+  0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x63, 0x6f, 0x72, 0x6f, 0x75,
+  0x74, 0x69, 0x6e, 0x65, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x72, 0x65,
+  0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e,
+  0x67, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x61,
+  0x74, 0x68, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65,
+  0x28, 0x22, 0x6d, 0x61, 0x74, 0x68, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75,
+  0x69, 0x72, 0x65, 0x28, 0x22, 0x6f, 0x73, 0x22, 0x29, 0x0a, 0x6c, 0x6f,
+  0x63, 0x61, 0x6c, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x3d,
+  0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x72,
+  0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f, 0x63, 0x6b,
+  0x65, 0x74, 0x22, 0x29, 0x20, 0x3b, 0x20, 0x0a, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x74, 0x70, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+  0x74, 0x2e, 0x74, 0x70, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x71, 0x75,
+  0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e,
+  0x74, 0x70, 0x22, 0x29, 0x3b, 0x20, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x20, 0x3d, 0x20, 0x6c, 0x74, 0x6e,
+  0x31, 0x32, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
+  0x65, 0x28, 0x22, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x22, 0x29, 0x3b, 0x0a,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+  0x73, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x68,
+  0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65,
+  0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+  0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0x29, 0x3b,
+  0x20, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x69, 0x6d, 0x65,
+  0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x72,
+  0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x6d, 0x69, 0x6d, 0x65,
+  0x22, 0x29, 0x3b, 0x20, 0x0a, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e,
+  0x73, 0x6d, 0x74, 0x70, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x6c, 0x6f,
+  0x63, 0x61, 0x6c, 0x20, 0x5f, 0x4d, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63,
+  0x6b, 0x65, 0x74, 0x2e, 0x73, 0x6d, 0x74, 0x70, 0x0a, 0x5f, 0x4d, 0x2e,
+  0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x20, 0x3d, 0x20, 0x36, 0x30,
+  0x0a, 0x5f, 0x4d, 0x2e, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x20, 0x3d,
+  0x20, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x22,
+  0x0a, 0x5f, 0x4d, 0x2e, 0x50, 0x4f, 0x52, 0x54, 0x20, 0x3d, 0x20, 0x32,
+  0x35, 0x0a, 0x5f, 0x4d, 0x2e, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x20,
+  0x3d, 0x20, 0x6f, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x65, 0x6e, 0x76, 0x28,
+  0x22, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4e, 0x41, 0x4d, 0x45,
+  0x22, 0x29, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x68, 0x6f, 0x73, 0x74, 0x22, 0x0a, 0x5f, 0x4d, 0x2e, 0x5a, 0x4f, 0x4e,
+  0x45, 0x20, 0x3d, 0x20, 0x22, 0x2d, 0x30, 0x30, 0x30, 0x30, 0x22, 0x0a,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x20,
+  0x3d, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20,
+  0x3d, 0x20, 0x7b, 0x7d, 0x20, 0x7d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f,
+  0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x67, 0x72, 0x65, 0x65, 0x74, 0x28,
+  0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22,
+  0x32, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28,
+  0x22, 0x45, 0x48, 0x4c, 0x4f, 0x22, 0x2c, 0x20, 0x64, 0x6f, 0x6d, 0x61,
+  0x69, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x5f, 0x4d, 0x2e, 0x44, 0x4f, 0x4d,
+  0x41, 0x49, 0x4e, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e,
+  0x73, 0x6b, 0x69, 0x70, 0x28, 0x31, 0x2c, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70,
+  0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e, 0x22,
+  0x29, 0x29, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f,
+  0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x6d, 0x61, 0x69, 0x6c, 0x28,
+  0x66, 0x72, 0x6f, 0x6d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x22,
+  0x4d, 0x41, 0x49, 0x4c, 0x22, 0x2c, 0x20, 0x22, 0x46, 0x52, 0x4f, 0x4d,
+  0x3a, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x29, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22,
+  0x32, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66,
+  0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61,
+  0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x72, 0x63,
+  0x70, 0x74, 0x28, 0x74, 0x6f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28,
+  0x22, 0x52, 0x43, 0x50, 0x54, 0x22, 0x2c, 0x20, 0x22, 0x54, 0x4f, 0x3a,
+  0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x29, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e,
+  0x22, 0x29, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f,
+  0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x64, 0x61, 0x74, 0x61, 0x28,
+  0x73, 0x72, 0x63, 0x2c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d,
+  0x61, 0x6e, 0x64, 0x28, 0x22, 0x44, 0x41, 0x54, 0x41, 0x22, 0x29, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72,
+  0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68,
+  0x65, 0x63, 0x6b, 0x28, 0x22, 0x33, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79,
+  0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x73, 0x6f, 0x75,
+  0x72, 0x63, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x73, 0x74, 0x65,
+  0x70, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70,
+  0x3a, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x2e,
+  0x5c, 0x72, 0x5c, 0x6e, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a,
+  0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e, 0x22, 0x29,
+  0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69,
+  0x6e, 0x64, 0x65, 0x78, 0x3a, 0x71, 0x75, 0x69, 0x74, 0x28, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79,
+  0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d,
+  0x6d, 0x61, 0x6e, 0x64, 0x28, 0x22, 0x51, 0x55, 0x49, 0x54, 0x22, 0x29,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28,
+  0x22, 0x32, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a,
+  0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74,
+  0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x63,
+  0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x70, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x65, 0x6e,
+  0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d,
+  0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
+  0x3a, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x28, 0x75, 0x73, 0x65, 0x72, 0x2c,
+  0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d,
+  0x61, 0x6e, 0x64, 0x28, 0x22, 0x41, 0x55, 0x54, 0x48, 0x22, 0x2c, 0x20,
+  0x22, 0x4c, 0x4f, 0x47, 0x49, 0x4e, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b,
+  0x28, 0x22, 0x33, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65,
+  0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x6d,
+  0x69, 0x6d, 0x65, 0x2e, 0x62, 0x36, 0x34, 0x28, 0x75, 0x73, 0x65, 0x72,
+  0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x22, 0x29,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63,
+  0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x33, 0x2e, 0x2e, 0x22, 0x29, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72,
+  0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x70, 0x3a, 0x73, 0x65,
+  0x6e, 0x64, 0x28, 0x6d, 0x69, 0x6d, 0x65, 0x2e, 0x62, 0x36, 0x34, 0x28,
+  0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x29, 0x20, 0x2e, 0x2e,
+  0x20, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22, 0x32, 0x2e, 0x2e,
+  0x22, 0x29, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f,
+  0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x70, 0x6c, 0x61, 0x69, 0x6e,
+  0x28, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77,
+  0x6f, 0x72, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x61, 0x75, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x22, 0x50,
+  0x4c, 0x41, 0x49, 0x4e, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x6d, 0x69,
+  0x6d, 0x65, 0x2e, 0x62, 0x36, 0x34, 0x28, 0x22, 0x5c, 0x30, 0x22, 0x20,
+  0x2e, 0x2e, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x2e, 0x2e, 0x20, 0x22,
+  0x5c, 0x30, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77,
+  0x6f, 0x72, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74,
+  0x70, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x28, 0x22, 0x41,
+  0x55, 0x54, 0x48, 0x22, 0x2c, 0x20, 0x61, 0x75, 0x74, 0x68, 0x29, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x73, 0x65, 0x6c,
+  0x66, 0x2e, 0x74, 0x70, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x22,
+  0x32, 0x2e, 0x2e, 0x22, 0x29, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66,
+  0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61,
+  0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x61, 0x75,
+  0x74, 0x68, 0x28, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x61, 0x73,
+  0x73, 0x77, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x65, 0x78, 0x74, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x75,
+  0x73, 0x65, 0x72, 0x20, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x70,
+  0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72,
+  0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x65, 0x78, 0x74,
+  0x2c, 0x20, 0x22, 0x41, 0x55, 0x54, 0x48, 0x5b, 0x5e, 0x5c, 0x6e, 0x5d,
+  0x2b, 0x4c, 0x4f, 0x47, 0x49, 0x4e, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x3a, 0x6c, 0x6f,
+  0x67, 0x69, 0x6e, 0x28, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x61,
+  0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
+  0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x65, 0x78, 0x74, 0x2c, 0x20,
+  0x22, 0x41, 0x55, 0x54, 0x48, 0x5b, 0x5e, 0x5c, 0x6e, 0x5d, 0x2b, 0x50,
+  0x4c, 0x41, 0x49, 0x4e, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x3a, 0x70, 0x6c, 0x61, 0x69,
+  0x6e, 0x28, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x61, 0x73, 0x73,
+  0x77, 0x6f, 0x72, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c,
+  0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x74, 0x72, 0x79, 0x28, 0x6e, 0x69, 0x6c, 0x2c,
+  0x20, 0x22, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x70,
+  0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e,
+  0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e,
+  0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x73, 0x65, 0x6e, 0x64,
+  0x28, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x3a, 0x6d, 0x61, 0x69, 0x6c, 0x28, 0x6d, 0x61,
+  0x69, 0x6c, 0x74, 0x2e, 0x66, 0x72, 0x6f, 0x6d, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x69, 0x66, 0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x74, 0x79,
+  0x70, 0x65, 0x28, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x2e, 0x72, 0x63, 0x70,
+  0x74, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65,
+  0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c, 0x76, 0x20, 0x69,
+  0x6e, 0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x69, 0x70, 0x61, 0x69, 0x72,
+  0x73, 0x28, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x2e, 0x72, 0x63, 0x70, 0x74,
+  0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x3a, 0x72, 0x63,
+  0x70, 0x74, 0x28, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c,
+  0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x3a, 0x72, 0x63, 0x70, 0x74, 0x28, 0x6d, 0x61, 0x69,
+  0x6c, 0x74, 0x2e, 0x72, 0x63, 0x70, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c,
+  0x66, 0x3a, 0x64, 0x61, 0x74, 0x61, 0x28, 0x6c, 0x74, 0x6e, 0x31, 0x32,
+  0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x63, 0x68, 0x61, 0x69,
+  0x6e, 0x28, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x2e, 0x73, 0x6f, 0x75, 0x72,
+  0x63, 0x65, 0x2c, 0x20, 0x6d, 0x69, 0x6d, 0x65, 0x2e, 0x73, 0x74, 0x75,
+  0x66, 0x66, 0x28, 0x29, 0x29, 0x2c, 0x20, 0x6d, 0x61, 0x69, 0x6c, 0x74,
+  0x2e, 0x73, 0x74, 0x65, 0x70, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66,
+  0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e, 0x6f,
+  0x70, 0x65, 0x6e, 0x28, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x20,
+  0x70, 0x6f, 0x72, 0x74, 0x2c, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x74, 0x70, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e,
+  0x74, 0x72, 0x79, 0x28, 0x74, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
+  0x63, 0x74, 0x28, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x6f, 0x72,
+  0x20, 0x5f, 0x4d, 0x2e, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2c, 0x20,
+  0x70, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x5f, 0x4d, 0x2e, 0x50,
+  0x4f, 0x52, 0x54, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x5f, 0x4d, 0x2e, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x2c,
+  0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x29, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x20, 0x3d, 0x20,
+  0x62, 0x61, 0x73, 0x65, 0x2e, 0x73, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61,
+  0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x7b, 0x74, 0x70, 0x20, 0x3d, 0x20,
+  0x74, 0x70, 0x7d, 0x2c, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x73, 0x2e, 0x74, 0x72, 0x79, 0x20, 0x3d, 0x20,
+  0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6e, 0x65, 0x77, 0x74, 0x72,
+  0x79, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x3a, 0x63,
+  0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
+  0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x73, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+  0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+  0x73, 0x28, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x6f, 0x77,
+  0x65, 0x72, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x62,
+  0x61, 0x73, 0x65, 0x2e, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28, 0x68, 0x65,
+  0x61, 0x64, 0x65, 0x72, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x6f, 0x77,
+  0x65, 0x72, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5b, 0x73, 0x74, 0x72,
+  0x69, 0x6e, 0x67, 0x2e, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x28, 0x69, 0x29,
+  0x5d, 0x20, 0x3d, 0x20, 0x76, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c,
+  0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x65, 0x71, 0x6e, 0x6f, 0x20, 0x3d,
+  0x20, 0x30, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e,
+  0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x77, 0x62, 0x6f, 0x75,
+  0x6e, 0x64, 0x61, 0x72, 0x79, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x65, 0x71, 0x6e, 0x6f, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x71, 0x6e,
+  0x6f, 0x20, 0x2b, 0x20, 0x31, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+  0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x27, 0x25, 0x73, 0x25, 0x30,
+  0x35, 0x64, 0x3d, 0x3d, 0x25, 0x30, 0x35, 0x75, 0x27, 0x2c, 0x20, 0x6f,
+  0x73, 0x2e, 0x64, 0x61, 0x74, 0x65, 0x28, 0x27, 0x25, 0x64, 0x25, 0x6d,
+  0x25, 0x59, 0x25, 0x48, 0x25, 0x4d, 0x25, 0x53, 0x27, 0x29, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x74, 0x68,
+  0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x30, 0x2c, 0x20, 0x39,
+  0x39, 0x39, 0x39, 0x39, 0x29, 0x2c, 0x20, 0x73, 0x65, 0x71, 0x6e, 0x6f,
+  0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x73, 0x65, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+  0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x68, 0x65, 0x61,
+  0x64, 0x65, 0x72, 0x73, 0x28, 0x74, 0x6f, 0x73, 0x65, 0x6e, 0x64, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63,
+  0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x3d, 0x20, 0x68, 0x65, 0x61,
+  0x64, 0x65, 0x72, 0x73, 0x2e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x68,
+  0x20, 0x3d, 0x20, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x22, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x66, 0x2c, 0x76, 0x20, 0x69, 0x6e,
+  0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28,
+  0x74, 0x6f, 0x73, 0x65, 0x6e, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x20, 0x3d, 0x20, 0x28,
+  0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x5b, 0x66, 0x5d, 0x20, 0x6f,
+  0x72, 0x20, 0x66, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x27, 0x3a, 0x20, 0x27,
+  0x20, 0x2e, 0x2e, 0x20, 0x76, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5c, 0x72,
+  0x5c, 0x6e, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x68, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x72,
+  0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x79, 0x69, 0x65, 0x6c, 0x64,
+  0x28, 0x68, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73,
+  0x65, 0x6e, 0x64, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72,
+  0x74, 0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x64, 0x20, 0x3d, 0x20,
+  0x6e, 0x65, 0x77, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x28,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x6f,
+  0x77, 0x65, 0x72, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x28,
+  0x6d, 0x65, 0x73, 0x67, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+  0x73, 0x20, 0x6f, 0x72, 0x20, 0x7b, 0x7d, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x5b, 0x27, 0x63, 0x6f,
+  0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x27, 0x5d,
+  0x20, 0x3d, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x5b, 0x27,
+  0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65,
+  0x27, 0x5d, 0x20, 0x6f, 0x72, 0x20, 0x27, 0x6d, 0x75, 0x6c, 0x74, 0x69,
+  0x70, 0x61, 0x72, 0x74, 0x2f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x27, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x5b,
+  0x27, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70,
+  0x65, 0x27, 0x5d, 0x20, 0x3d, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+  0x73, 0x5b, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74,
+  0x79, 0x70, 0x65, 0x27, 0x5d, 0x20, 0x2e, 0x2e, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x3b, 0x20, 0x62, 0x6f, 0x75, 0x6e,
+  0x64, 0x61, 0x72, 0x79, 0x3d, 0x22, 0x27, 0x20, 0x2e, 0x2e, 0x20, 0x20,
+  0x62, 0x64, 0x20, 0x2e, 0x2e, 0x20, 0x27, 0x22, 0x27, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65,
+  0x72, 0x73, 0x28, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6d, 0x65, 0x73, 0x67, 0x74,
+  0x2e, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x70, 0x72, 0x65, 0x61, 0x6d, 0x62,
+  0x6c, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e,
+  0x65, 0x2e, 0x79, 0x69, 0x65, 0x6c, 0x64, 0x28, 0x6d, 0x65, 0x73, 0x67,
+  0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x70, 0x72, 0x65, 0x61, 0x6d,
+  0x62, 0x6c, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x79,
+  0x69, 0x65, 0x6c, 0x64, 0x28, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x22, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c, 0x20, 0x6d, 0x20, 0x69, 0x6e,
+  0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x69, 0x70, 0x61, 0x69, 0x72, 0x73,
+  0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x29,
+  0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x79, 0x69,
+  0x65, 0x6c, 0x64, 0x28, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x2d, 0x2d, 0x22,
+  0x20, 0x2e, 0x2e, 0x20, 0x62, 0x64, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5c,
+  0x72, 0x5c, 0x6e, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
+  0x67, 0x65, 0x28, 0x6d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74,
+  0x69, 0x6e, 0x65, 0x2e, 0x79, 0x69, 0x65, 0x6c, 0x64, 0x28, 0x22, 0x5c,
+  0x72, 0x5c, 0x6e, 0x2d, 0x2d, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x62, 0x64,
+  0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2d, 0x2d, 0x5c, 0x72, 0x5c, 0x6e, 0x5c,
+  0x72, 0x5c, 0x6e, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
+  0x20, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x2e,
+  0x65, 0x70, 0x69, 0x6c, 0x6f, 0x67, 0x75, 0x65, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f,
+  0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x79, 0x69, 0x65, 0x6c,
+  0x64, 0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79,
+  0x2e, 0x65, 0x70, 0x69, 0x6c, 0x6f, 0x67, 0x75, 0x65, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x72, 0x6f, 0x75,
+  0x74, 0x69, 0x6e, 0x65, 0x2e, 0x79, 0x69, 0x65, 0x6c, 0x64, 0x28, 0x22,
+  0x5c, 0x72, 0x5c, 0x6e, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
+  0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65,
+  0x6e, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x28, 0x6d, 0x65,
+  0x73, 0x67, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x20, 0x3d,
+  0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65,
+  0x72, 0x73, 0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x2e, 0x68, 0x65, 0x61,
+  0x64, 0x65, 0x72, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x7b, 0x7d, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x5b,
+  0x27, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70,
+  0x65, 0x27, 0x5d, 0x20, 0x3d, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+  0x73, 0x5b, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74,
+  0x79, 0x70, 0x65, 0x27, 0x5d, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70,
+  0x6c, 0x61, 0x69, 0x6e, 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,
+  0x74, 0x3d, 0x22, 0x69, 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,
+  0x31, 0x22, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6e, 0x64,
+  0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x28, 0x68, 0x65, 0x61,
+  0x64, 0x65, 0x72, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68,
+  0x69, 0x6c, 0x65, 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x64, 0x6f, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x2c, 0x20, 0x65, 0x72, 0x72,
+  0x20, 0x3d, 0x20, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x2e, 0x62, 0x6f, 0x64,
+  0x79, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x65, 0x72, 0x72, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
+  0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x79, 0x69,
+  0x65, 0x6c, 0x64, 0x28, 0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x65, 0x72, 0x72,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c,
+  0x73, 0x65, 0x69, 0x66, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x20, 0x74,
+  0x68, 0x65, 0x6e, 0x20, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e,
+  0x65, 0x2e, 0x79, 0x69, 0x65, 0x6c, 0x64, 0x28, 0x63, 0x68, 0x75, 0x6e,
+  0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65,
+  0x6c, 0x73, 0x65, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e,
+  0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x74,
+  0x72, 0x69, 0x6e, 0x67, 0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x68, 0x65,
+  0x61, 0x64, 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x77, 0x65,
+  0x72, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x28, 0x6d, 0x65,
+  0x73, 0x67, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x20,
+  0x6f, 0x72, 0x20, 0x7b, 0x7d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68,
+  0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x5b, 0x27, 0x63, 0x6f, 0x6e, 0x74,
+  0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x27, 0x5d, 0x20, 0x3d,
+  0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x5b, 0x27, 0x63, 0x6f,
+  0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x27, 0x5d,
+  0x20, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x27, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x3b,
+  0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x22, 0x69, 0x73,
+  0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, 0x31, 0x22, 0x27, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64,
+  0x65, 0x72, 0x73, 0x28, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69,
+  0x6e, 0x65, 0x2e, 0x79, 0x69, 0x65, 0x6c, 0x64, 0x28, 0x6d, 0x65, 0x73,
+  0x67, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x29, 0x0a, 0x65, 0x6e, 0x64,
+  0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65,
+  0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x6d,
+  0x65, 0x73, 0x67, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
+  0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6d,
+  0x65, 0x73, 0x67, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x29, 0x20, 0x3d,
+  0x3d, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x74, 0x68,
+  0x65, 0x6e, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x6d, 0x75, 0x6c, 0x74,
+  0x69, 0x70, 0x61, 0x72, 0x74, 0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20,
+  0x62, 0x61, 0x73, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6d, 0x65,
+  0x73, 0x67, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x29, 0x20, 0x3d, 0x3d,
+  0x20, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x6f,
+  0x75, 0x72, 0x63, 0x65, 0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x73, 0x65, 0x6e,
+  0x64, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6d, 0x65, 0x73,
+  0x67, 0x74, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x5f, 0x68, 0x65,
+  0x61, 0x64, 0x65, 0x72, 0x73, 0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c,
+  0x6f, 0x77, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72,
+  0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x28, 0x6d, 0x65, 0x73,
+  0x67, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5b, 0x22, 0x64,
+  0x61, 0x74, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x77, 0x65,
+  0x72, 0x5b, 0x22, 0x64, 0x61, 0x74, 0x65, 0x22, 0x5d, 0x20, 0x6f, 0x72,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e,
+  0x64, 0x61, 0x74, 0x65, 0x28, 0x22, 0x21, 0x25, 0x61, 0x2c, 0x20, 0x25,
+  0x64, 0x20, 0x25, 0x62, 0x20, 0x25, 0x59, 0x20, 0x25, 0x48, 0x3a, 0x25,
+  0x4d, 0x3a, 0x25, 0x53, 0x20, 0x22, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x28,
+  0x6d, 0x65, 0x73, 0x67, 0x74, 0x2e, 0x7a, 0x6f, 0x6e, 0x65, 0x20, 0x6f,
+  0x72, 0x20, 0x5f, 0x4d, 0x2e, 0x5a, 0x4f, 0x4e, 0x45, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5b, 0x22, 0x78, 0x2d,
+  0x6d, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6c,
+  0x6f, 0x77, 0x65, 0x72, 0x5b, 0x22, 0x78, 0x2d, 0x6d, 0x61, 0x69, 0x6c,
+  0x65, 0x72, 0x22, 0x5d, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x6f, 0x63, 0x6b,
+  0x65, 0x74, 0x2e, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5b, 0x22, 0x6d,
+  0x69, 0x6d, 0x65, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22,
+  0x5d, 0x20, 0x3d, 0x20, 0x22, 0x31, 0x2e, 0x30, 0x22, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x77,
+  0x65, 0x72, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61,
+  0x67, 0x65, 0x28, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x6d, 0x65, 0x73, 0x67, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64,
+  0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74,
+  0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x28, 0x6d, 0x65, 0x73,
+  0x67, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x63, 0x6f, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x72, 0x6f, 0x75,
+  0x74, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28,
+  0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x73,
+  0x65, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28,
+  0x6d, 0x65, 0x73, 0x67, 0x74, 0x29, 0x20, 0x65, 0x6e, 0x64, 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, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x72, 0x65, 0x74, 0x2c, 0x20, 0x61, 0x2c, 0x20, 0x62, 0x20, 0x3d, 0x20,
+  0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x72, 0x65,
+  0x73, 0x75, 0x6d, 0x65, 0x28, 0x63, 0x6f, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x72, 0x65, 0x74, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x61, 0x2c, 0x20, 0x62, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x61, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a,
+  0x5f, 0x4d, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x73, 0x6f,
+  0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74,
+  0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x61,
+  0x69, 0x6c, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x4d, 0x2e, 0x6f, 0x70,
+  0x65, 0x6e, 0x28, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72,
+  0x76, 0x65, 0x72, 0x2c, 0x20, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x2e, 0x70,
+  0x6f, 0x72, 0x74, 0x2c, 0x20, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x2e, 0x63,
+  0x72, 0x65, 0x61, 0x74, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
+  0x6f, 0x63, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x73,
+  0x3a, 0x67, 0x72, 0x65, 0x65, 0x74, 0x28, 0x6d, 0x61, 0x69, 0x6c, 0x74,
+  0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x28, 0x6d, 0x61, 0x69, 0x6c,
+  0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x6d, 0x61, 0x69, 0x6c,
+  0x74, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2c, 0x20,
+  0x65, 0x78, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x3a, 0x73,
+  0x65, 0x6e, 0x64, 0x28, 0x6d, 0x61, 0x69, 0x6c, 0x74, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x3a, 0x71, 0x75, 0x69, 0x74, 0x28, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73,
+  0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x65, 0x6e, 0x64,
+  0x29, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x4d,0x0};
+  return luaL_dostring(L, (const char*)B); 
+} /* end of embedded lua code */
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_tp.lua
@@ -0,0 +1,134 @@
+-----------------------------------------------------------------------------
+-- Unified SMTP/FTP subsystem
+-- LuaSocket toolkit.
+-- Author: Diego Nehab
+-----------------------------------------------------------------------------
+
+-----------------------------------------------------------------------------
+-- Declare module and import dependencies
+-----------------------------------------------------------------------------
+local base = _G
+local string = require("string")
+local socket = socket or require("socket")
+local ltn12 = ltn12 or require("ltn12")
+
+socket.tp = {}
+local _M = socket.tp
+
+-----------------------------------------------------------------------------
+-- Program constants
+-----------------------------------------------------------------------------
+_M.TIMEOUT = 60
+
+-----------------------------------------------------------------------------
+-- Implementation
+-----------------------------------------------------------------------------
+-- gets server reply (works for SMTP and FTP)
+local function get_reply(c)
+    local code, current, sep
+    local line, err = c:receive()
+    local reply = line
+    if err then return nil, err end
+    code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
+    if not code then return nil, "invalid server reply" end
+    if sep == "-" then -- reply is multiline
+        repeat
+            line, err = c:receive()
+            if err then return nil, err end
+            current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
+            reply = reply .. "\n" .. line
+        -- reply ends with same code
+        until code == current and sep == " "
+    end
+    return code, reply
+end
+
+-- metatable for sock object
+local metat = { __index = {} }
+
+function metat.__index:getpeername()
+    return self.c:getpeername()
+end
+
+function metat.__index:getsockname()
+    return self.c:getpeername()
+end
+
+function metat.__index:check(ok)
+    local code, reply = get_reply(self.c)
+    if not code then return nil, reply end
+    if base.type(ok) ~= "function" then
+        if base.type(ok) == "table" then
+            for i, v in base.ipairs(ok) do
+                if string.find(code, v) then
+                    return base.tonumber(code), reply
+                end
+            end
+            return nil, reply
+        else
+            if string.find(code, ok) then return base.tonumber(code), reply
+            else return nil, reply end
+        end
+    else return ok(base.tonumber(code), reply) end
+end
+
+function metat.__index:command(cmd, arg)
+    cmd = string.upper(cmd)
+    if arg then
+        return self.c:send(cmd .. " " .. arg.. "\r\n")
+    else
+        return self.c:send(cmd .. "\r\n")
+    end
+end
+
+function metat.__index:sink(snk, pat)
+    local chunk, err = self.c:receive(pat)
+    return snk(chunk, err)
+end
+
+function metat.__index:send(data)
+    return self.c:send(data)
+end
+
+function metat.__index:receive(pat)
+    return self.c:receive(pat)
+end
+
+function metat.__index:getfd()
+    return self.c:getfd()
+end
+
+function metat.__index:dirty()
+    return self.c:dirty()
+end
+
+function metat.__index:getcontrol()
+    return self.c
+end
+
+function metat.__index:source(source, step)
+    local sink = socket.sink("keep-open", self.c)
+    local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step)
+    return ret, err
+end
+
+-- closes the underlying c
+function metat.__index:close()
+    self.c:close()
+    return 1
+end
+
+-- connect with server and return c object
+function _M.connect(host, port, timeout, create)
+    local c, e = (create or socket.tcp)()
+    if not c then return nil, e end
+    c:settimeout(timeout or _M.TIMEOUT)
+    local r, e = c:connect(host, port)
+    if not r then
+        c:close()
+        return nil, e
+    end
+    return base.setmetatable({c = c}, metat)
+end
+
+return _M
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_tp_lua.c
@@ -0,0 +1,244 @@
+/*
+ * This file is generated with xxd -i and bit of bash script.
+*/
+#include "lua.h"
+#include "lauxlib.h"
+ 
+int luatex_tp_lua_open (lua_State *L) { 
+    static unsigned char B[] = {
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x3d,
+  0x20, 0x5f, 0x47, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x74,
+  0x72, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69,
+  0x72, 0x65, 0x28, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x29,
+  0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+  0x74, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x6f,
+  0x72, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73,
+  0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x20, 0x3d, 0x20, 0x6c, 0x74,
+  0x6e, 0x31, 0x32, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69,
+  0x72, 0x65, 0x28, 0x22, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x22, 0x29, 0x0a,
+  0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x70, 0x20, 0x3d, 0x20,
+  0x7b, 0x7d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x5f, 0x4d, 0x20,
+  0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x70, 0x0a,
+  0x5f, 0x4d, 0x2e, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x20, 0x3d,
+  0x20, 0x36, 0x30, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75,
+  0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x72,
+  0x65, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20,
+  0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x70,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c,
+  0x69, 0x6e, 0x65, 0x2c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x63,
+  0x3a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x28, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x70,
+  0x6c, 0x79, 0x20, 0x3d, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x69, 0x66, 0x20, 0x65, 0x72, 0x72, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x69, 0x6c,
+  0x2c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x73, 0x65, 0x70, 0x20,
+  0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x6b, 0x69,
+  0x70, 0x28, 0x32, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+  0x66, 0x69, 0x6e, 0x64, 0x28, 0x6c, 0x69, 0x6e, 0x65, 0x2c, 0x20, 0x22,
+  0x5e, 0x28, 0x25, 0x64, 0x25, 0x64, 0x25, 0x64, 0x29, 0x28, 0x2e, 0x3f,
+  0x29, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x6e, 0x6f, 0x74, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x69, 0x6c,
+  0x2c, 0x20, 0x22, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x73,
+  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x22,
+  0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x73, 0x65, 0x70, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x2d, 0x22, 0x20, 0x74,
+  0x68, 0x65, 0x6e, 0x20, 0x2d, 0x2d, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79,
+  0x20, 0x69, 0x73, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x6c, 0x69, 0x6e,
+  0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x70, 0x65, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x2c, 0x20, 0x65,
+  0x72, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x3a, 0x72, 0x65, 0x63, 0x65, 0x69,
+  0x76, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x65, 0x72, 0x72, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x65, 0x6e, 0x64,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x73, 0x65,
+  0x70, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73,
+  0x6b, 0x69, 0x70, 0x28, 0x32, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
+  0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x6c, 0x69, 0x6e, 0x65, 0x2c,
+  0x20, 0x22, 0x5e, 0x28, 0x25, 0x64, 0x25, 0x64, 0x25, 0x64, 0x29, 0x28,
+  0x2e, 0x3f, 0x29, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79,
+  0x20, 0x3d, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x20, 0x2e, 0x2e, 0x20,
+  0x22, 0x5c, 0x6e, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x6c, 0x69, 0x6e, 0x65,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6e, 0x74,
+  0x69, 0x6c, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x63,
+  0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73,
+  0x65, 0x70, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x20, 0x22, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72,
+  0x65, 0x70, 0x6c, 0x79, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x7b,
+  0x20, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x7b,
+  0x7d, 0x20, 0x7d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+  0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64,
+  0x65, 0x78, 0x3a, 0x67, 0x65, 0x74, 0x70, 0x65, 0x65, 0x72, 0x6e, 0x61,
+  0x6d, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x3a, 0x67,
+  0x65, 0x74, 0x70, 0x65, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x29,
+  0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
+  0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e,
+  0x64, 0x65, 0x78, 0x3a, 0x67, 0x65, 0x74, 0x73, 0x6f, 0x63, 0x6b, 0x6e,
+  0x61, 0x6d, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x3a,
+  0x67, 0x65, 0x74, 0x70, 0x65, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x28,
+  0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69,
+  0x6e, 0x64, 0x65, 0x78, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x6f,
+  0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79,
+  0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x79,
+  0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x6f, 0x64, 0x65,
+  0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x62,
+  0x61, 0x73, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x6b, 0x29,
+  0x20, 0x7e, 0x3d, 0x20, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
+  0x6e, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x62, 0x61, 0x73, 0x65, 0x2e,
+  0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x6b, 0x29, 0x20, 0x3d, 0x3d, 0x20,
+  0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x20, 0x69, 0x6e,
+  0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x69, 0x70, 0x61, 0x69, 0x72, 0x73,
+  0x28, 0x6f, 0x6b, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69,
+  0x6e, 0x64, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x76, 0x29, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x61, 0x73, 0x65,
+  0x2e, 0x74, 0x6f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x28, 0x63, 0x6f,
+  0x64, 0x65, 0x29, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x72,
+  0x65, 0x70, 0x6c, 0x79, 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, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72,
+  0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x63, 0x6f, 0x64,
+  0x65, 0x2c, 0x20, 0x6f, 0x6b, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x61, 0x73, 0x65, 0x2e,
+  0x74, 0x6f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x28, 0x63, 0x6f, 0x64,
+  0x65, 0x29, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c,
+  0x73, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x69,
+  0x6c, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x20, 0x65, 0x6e, 0x64,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x6b, 0x28, 0x62, 0x61, 0x73, 0x65,
+  0x2e, 0x74, 0x6f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x28, 0x63, 0x6f,
+  0x64, 0x65, 0x29, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x29, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f,
+  0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
+  0x6e, 0x64, 0x28, 0x63, 0x6d, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6d, 0x64, 0x20, 0x3d, 0x20, 0x73,
+  0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28,
+  0x63, 0x6d, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x61, 0x72, 0x67, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x3a, 0x73, 0x65, 0x6e, 0x64, 0x28,
+  0x63, 0x6d, 0x64, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x22, 0x20, 0x2e,
+  0x2e, 0x20, 0x61, 0x72, 0x67, 0x2e, 0x2e, 0x20, 0x22, 0x5c, 0x72, 0x5c,
+  0x6e, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x3a, 0x73,
+  0x65, 0x6e, 0x64, 0x28, 0x63, 0x6d, 0x64, 0x20, 0x2e, 0x2e, 0x20, 0x22,
+  0x5c, 0x72, 0x5c, 0x6e, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
+  0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f,
+  0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x73, 0x69, 0x6e, 0x6b, 0x28, 0x73,
+  0x6e, 0x6b, 0x2c, 0x20, 0x70, 0x61, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b,
+  0x2c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x63, 0x3a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x28, 0x70,
+  0x61, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x73, 0x6e, 0x6b, 0x28, 0x63, 0x68, 0x75, 0x6e, 0x6b,
+  0x2c, 0x20, 0x65, 0x72, 0x72, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66,
+  0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61,
+  0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x73, 0x65,
+  0x6e, 0x64, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66,
+  0x2e, 0x63, 0x3a, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x64, 0x61, 0x74, 0x61,
+  0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69,
+  0x6e, 0x64, 0x65, 0x78, 0x3a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
+  0x28, 0x70, 0x61, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x3a,
+  0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x28, 0x70, 0x61, 0x74, 0x29,
+  0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
+  0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e,
+  0x64, 0x65, 0x78, 0x3a, 0x67, 0x65, 0x74, 0x66, 0x64, 0x28, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73,
+  0x65, 0x6c, 0x66, 0x2e, 0x63, 0x3a, 0x67, 0x65, 0x74, 0x66, 0x64, 0x28,
+  0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69,
+  0x6e, 0x64, 0x65, 0x78, 0x3a, 0x64, 0x69, 0x72, 0x74, 0x79, 0x28, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x3a, 0x64, 0x69, 0x72, 0x74, 0x79,
+  0x28, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f,
+  0x69, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e,
+  0x74, 0x72, 0x6f, 0x6c, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63,
+  0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
+  0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69, 0x6e,
+  0x64, 0x65, 0x78, 0x3a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x28, 0x73,
+  0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73,
+  0x69, 0x6e, 0x6b, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+  0x2e, 0x73, 0x69, 0x6e, 0x6b, 0x28, 0x22, 0x6b, 0x65, 0x65, 0x70, 0x2d,
+  0x6f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e,
+  0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x72, 0x65, 0x74, 0x2c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20,
+  0x6c, 0x74, 0x6e, 0x31, 0x32, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x2e, 0x61,
+  0x6c, 0x6c, 0x28, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x20, 0x73,
+  0x69, 0x6e, 0x6b, 0x2c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x6f, 0x72,
+  0x20, 0x6c, 0x74, 0x6e, 0x31, 0x32, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x2e,
+  0x73, 0x74, 0x65, 0x70, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x2c, 0x20, 0x65, 0x72,
+  0x72, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x2e, 0x5f, 0x5f, 0x69,
+  0x6e, 0x64, 0x65, 0x78, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x3a,
+  0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x0a, 0x65, 0x6e, 0x64,
+  0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d,
+  0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x28, 0x68, 0x6f, 0x73,
+  0x74, 0x2c, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x2c, 0x20, 0x74, 0x69, 0x6d,
+  0x65, 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x63, 0x2c, 0x20, 0x65, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x72, 0x65, 0x61,
+  0x74, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+  0x2e, 0x74, 0x63, 0x70, 0x29, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x69, 0x6c,
+  0x2c, 0x20, 0x65, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x63, 0x3a, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
+  0x28, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x72, 0x20,
+  0x5f, 0x4d, 0x2e, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x2c,
+  0x20, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x3a, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
+  0x63, 0x74, 0x28, 0x68, 0x6f, 0x73, 0x74, 0x2c, 0x20, 0x70, 0x6f, 0x72,
+  0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f,
+  0x74, 0x20, 0x72, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65,
+  0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x65,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x61, 0x73, 0x65,
+  0x2e, 0x73, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c,
+  0x65, 0x28, 0x7b, 0x63, 0x20, 0x3d, 0x20, 0x63, 0x7d, 0x2c, 0x20, 0x6d,
+  0x65, 0x74, 0x61, 0x74, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x4d, 0x0a,0x0};
+  return luaL_dostring(L, (const char*)B); 
+} /* end of embedded lua code */
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_url.lua
@@ -0,0 +1,309 @@
+-----------------------------------------------------------------------------
+-- URI parsing, composition and relative URL resolution
+-- LuaSocket toolkit.
+-- Author: Diego Nehab
+-----------------------------------------------------------------------------
+
+-----------------------------------------------------------------------------
+-- Declare module
+-----------------------------------------------------------------------------
+local string = require("string")
+local base = _G
+local table = require("table")
+local socket = socket or require("socket")
+
+socket.url = {}
+local _M = socket.url
+
+-----------------------------------------------------------------------------
+-- Module version
+-----------------------------------------------------------------------------
+_M._VERSION = "URL 1.0.3"
+
+-----------------------------------------------------------------------------
+-- Encodes a string into its escaped hexadecimal representation
+-- Input
+--   s: binary string to be encoded
+-- Returns
+--   escaped representation of string binary
+-----------------------------------------------------------------------------
+function _M.escape(s)
+    return (string.gsub(s, "([^A-Za-z0-9_])", function(c)
+        return string.format("%%%02x", string.byte(c))
+    end))
+end
+
+-----------------------------------------------------------------------------
+-- Protects a path segment, to prevent it from interfering with the
+-- url parsing.
+-- Input
+--   s: binary string to be encoded
+-- Returns
+--   escaped representation of string binary
+-----------------------------------------------------------------------------
+local function make_set(t)
+    local s = {}
+    for i,v in base.ipairs(t) do
+        s[t[i]] = 1
+    end
+    return s
+end
+
+-- these are allowed within a path segment, along with alphanum
+-- other characters must be escaped
+local segment_set = make_set {
+    "-", "_", ".", "!", "~", "*", "'", "(",
+    ")", ":", "@", "&", "=", "+", "$", ",",
+}
+
+local function protect_segment(s)
+    return string.gsub(s, "([^A-Za-z0-9_])", function (c)
+        if segment_set[c] then return c
+        else return string.format("%%%02X", string.byte(c)) end
+    end)
+end
+
+-----------------------------------------------------------------------------
+-- Unencodes a escaped hexadecimal string into its binary representation
+-- Input
+--   s: escaped hexadecimal string to be unencoded
+-- Returns
+--   unescaped binary representation of escaped hexadecimal  binary
+-----------------------------------------------------------------------------
+function _M.unescape(s)
+    return (string.gsub(s, "%%(%x%x)", function(hex)
+        return string.char(base.tonumber(hex, 16))
+    end))
+end
+
+-----------------------------------------------------------------------------
+-- Builds a path from a base path and a relative path
+-- Input
+--   base_path
+--   relative_path
+-- Returns
+--   corresponding absolute path
+-----------------------------------------------------------------------------
+local function absolute_path(base_path, relative_path)
+    if string.sub(relative_path, 1, 1) == "/" then return relative_path end
+    local path = string.gsub(base_path, "[^/]*$", "")
+    path = path .. relative_path
+    path = string.gsub(path, "([^/]*%./)", function (s)
+        if s ~= "./" then return s else return "" end
+    end)
+    path = string.gsub(path, "/%.$", "/")
+    local reduced
+    while reduced ~= path do
+        reduced = path
+        path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
+            if s ~= "../../" then return "" else return s end
+        end)
+    end
+    path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
+        if s ~= "../.." then return "" else return s end
+    end)
+    return path
+end
+
+-----------------------------------------------------------------------------
+-- Parses a url and returns a table with all its parts according to RFC 2396
+-- The following grammar describes the names given to the URL parts
+-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
+-- <authority> ::= <userinfo>@<host>:<port>
+-- <userinfo> ::= <user>[:<password>]
+-- <path> :: = {<segment>/}<segment>
+-- Input
+--   url: uniform resource locator of request
+--   default: table with default values for each field
+-- Returns
+--   table with the following fields, where RFC naming conventions have
+--   been preserved:
+--     scheme, authority, userinfo, user, password, host, port,
+--     path, params, query, fragment
+-- Obs:
+--   the leading '/' in {/<path>} is considered part of <path>
+-----------------------------------------------------------------------------
+function _M.parse(url, default)
+    -- initialize default parameters
+    local parsed = {}
+    for i,v in base.pairs(default or parsed) do parsed[i] = v end
+    -- empty url is parsed to nil
+    if not url or url == "" then return nil, "invalid url" end
+    -- remove whitespace
+    -- url = string.gsub(url, "%s", "")
+    -- get fragment
+    url = string.gsub(url, "#(.*)$", function(f)
+        parsed.fragment = f
+        return ""
+    end)
+    -- get scheme
+    url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
+        function(s) parsed.scheme = s; return "" end)
+    -- get authority
+    url = string.gsub(url, "^//([^/]*)", function(n)
+        parsed.authority = n
+        return ""
+    end)
+    -- get query string
+    url = string.gsub(url, "%?(.*)", function(q)
+        parsed.query = q
+        return ""
+    end)
+    -- get params
+    url = string.gsub(url, "%;(.*)", function(p)
+        parsed.params = p
+        return ""
+    end)
+    -- path is whatever was left
+    if url ~= "" then parsed.path = url end
+    local authority = parsed.authority
+    if not authority then return parsed end
+    authority = string.gsub(authority,"^([^@]*)@",
+        function(u) parsed.userinfo = u; return "" end)
+    authority = string.gsub(authority, ":([^:%]]*)$",
+        function(p) parsed.port = p; return "" end)
+    if authority ~= "" then 
+        -- IPv6?
+        parsed.host = string.match(authority, "^%[(.+)%]$") or authority 
+    end
+    local userinfo = parsed.userinfo
+    if not userinfo then return parsed end
+    userinfo = string.gsub(userinfo, ":([^:]*)$",
+        function(p) parsed.password = p; return "" end)
+    parsed.user = userinfo
+    return parsed
+end
+
+-----------------------------------------------------------------------------
+-- Rebuilds a parsed URL from its components.
+-- Components are protected if any reserved or unallowed characters are found
+-- Input
+--   parsed: parsed URL, as returned by parse
+-- Returns
+--   a stringing with the corresponding URL
+-----------------------------------------------------------------------------
+function _M.build(parsed)
+    --local ppath = _M.parse_path(parsed.path or "")
+    --local url = _M.build_path(ppath)
+    local url = parsed.path or ""
+    if parsed.params then url = url .. ";" .. parsed.params end
+    if parsed.query then url = url .. "?" .. parsed.query end
+    local authority = parsed.authority
+    if parsed.host then
+        authority = parsed.host
+        if string.find(authority, ":") then -- IPv6?
+            authority = "[" .. authority .. "]"
+        end
+        if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end
+        local userinfo = parsed.userinfo
+        if parsed.user then
+            userinfo = parsed.user
+            if parsed.password then
+                userinfo = userinfo .. ":" .. parsed.password
+            end
+        end
+        if userinfo then authority = userinfo .. "@" .. authority end
+    end
+    if authority then url = "//" .. authority .. url end
+    if parsed.scheme then url = parsed.scheme .. ":" .. url end
+    if parsed.fragment then url = url .. "#" .. parsed.fragment end
+    -- url = string.gsub(url, "%s", "")
+    return url
+end
+
+-----------------------------------------------------------------------------
+-- Builds a absolute URL from a base and a relative URL according to RFC 2396
+-- Input
+--   base_url
+--   relative_url
+-- Returns
+--   corresponding absolute url
+-----------------------------------------------------------------------------
+function _M.absolute(base_url, relative_url)
+    local base_parsed
+    if base.type(base_url) == "table" then
+        base_parsed = base_url
+        base_url = _M.build(base_parsed)
+    else
+        base_parsed = _M.parse(base_url)
+    end
+    local relative_parsed = _M.parse(relative_url)
+    if not base_parsed then return relative_url
+    elseif not relative_parsed then return base_url
+    elseif relative_parsed.scheme then return relative_url
+    else
+        relative_parsed.scheme = base_parsed.scheme
+        if not relative_parsed.authority then
+            relative_parsed.authority = base_parsed.authority
+            if not relative_parsed.path then
+                relative_parsed.path = base_parsed.path
+                if not relative_parsed.params then
+                    relative_parsed.params = base_parsed.params
+                    if not relative_parsed.query then
+                        relative_parsed.query = base_parsed.query
+                    end
+                end
+            else    
+                relative_parsed.path = absolute_path(base_parsed.path or "",
+                    relative_parsed.path)
+            end
+        end
+        return _M.build(relative_parsed)
+    end
+end
+
+-----------------------------------------------------------------------------
+-- Breaks a path into its segments, unescaping the segments
+-- Input
+--   path
+-- Returns
+--   segment: a table with one entry per segment
+-----------------------------------------------------------------------------
+function _M.parse_path(path)
+    local parsed = {}
+    path = path or ""
+    --path = string.gsub(path, "%s", "")
+    string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
+    for i = 1, #parsed do
+        parsed[i] = _M.unescape(parsed[i])
+    end
+    if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
+    if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
+    return parsed
+end
+
+-----------------------------------------------------------------------------
+-- Builds a path component from its segments, escaping protected characters.
+-- Input
+--   parsed: path segments
+--   unsafe: if true, segments are not protected before path is built
+-- Returns
+--   path: corresponding path stringing
+-----------------------------------------------------------------------------
+function _M.build_path(parsed, unsafe)
+    local path = ""
+    local n = #parsed
+    if unsafe then
+        for i = 1, n-1 do
+            path = path .. parsed[i]
+            path = path .. "/"
+        end
+        if n > 0 then
+            path = path .. parsed[n]
+            if parsed.is_directory then path = path .. "/" end
+        end
+    else
+        for i = 1, n-1 do
+            path = path .. protect_segment(parsed[i])
+            path = path .. "/"
+        end
+        if n > 0 then
+            path = path .. protect_segment(parsed[n])
+            if parsed.is_directory then path = path .. "/" end
+        end
+    end
+    if parsed.is_absolute then path = "/" .. path end
+    return path
+end
+
+return _M
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/luasocket/src/_url_lua.c
@@ -0,0 +1,554 @@
+/*
+ * This file is generated with xxd -i and bit of bash script.
+*/
+#include "lua.h"
+#include "lauxlib.h"
+ 
+int luatex_url_lua_open (lua_State *L) { 
+    static unsigned char B[] = {
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+  0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22,
+  0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x47,
+  0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65,
+  0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22,
+  0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x73,
+  0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x71,
+  0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+  0x22, 0x29, 0x0a, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x75, 0x72,
+  0x6c, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x5f, 0x4d, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+  0x2e, 0x75, 0x72, 0x6c, 0x0a, 0x5f, 0x4d, 0x2e, 0x5f, 0x56, 0x45, 0x52,
+  0x53, 0x49, 0x4f, 0x4e, 0x20, 0x3d, 0x20, 0x22, 0x55, 0x52, 0x4c, 0x20,
+  0x31, 0x2e, 0x30, 0x2e, 0x33, 0x22, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70,
+  0x65, 0x28, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+  0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x2c, 0x20, 0x22, 0x28, 0x5b, 0x5e,
+  0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x29,
+  0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28,
+  0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+  0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x22, 0x25, 0x25, 0x25,
+  0x30, 0x32, 0x78, 0x22, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+  0x2e, 0x62, 0x79, 0x74, 0x65, 0x28, 0x63, 0x29, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x28,
+  0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x62,
+  0x61, 0x73, 0x65, 0x2e, 0x69, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28, 0x74,
+  0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x5b, 0x74, 0x5b, 0x69, 0x5d, 0x5d, 0x20, 0x3d, 0x20, 0x31,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x0a, 0x65, 0x6e,
+  0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x65, 0x67, 0x6d,
+  0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x6d, 0x61,
+  0x6b, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x22, 0x2d, 0x22, 0x2c, 0x20, 0x22, 0x5f, 0x22, 0x2c, 0x20, 0x22,
+  0x2e, 0x22, 0x2c, 0x20, 0x22, 0x21, 0x22, 0x2c, 0x20, 0x22, 0x7e, 0x22,
+  0x2c, 0x20, 0x22, 0x2a, 0x22, 0x2c, 0x20, 0x22, 0x27, 0x22, 0x2c, 0x20,
+  0x22, 0x28, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x29, 0x22,
+  0x2c, 0x20, 0x22, 0x3a, 0x22, 0x2c, 0x20, 0x22, 0x40, 0x22, 0x2c, 0x20,
+  0x22, 0x26, 0x22, 0x2c, 0x20, 0x22, 0x3d, 0x22, 0x2c, 0x20, 0x22, 0x2b,
+  0x22, 0x2c, 0x20, 0x22, 0x24, 0x22, 0x2c, 0x20, 0x22, 0x2c, 0x22, 0x2c,
+  0x0a, 0x7d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e,
+  0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63,
+  0x74, 0x5f, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x73, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28,
+  0x73, 0x2c, 0x20, 0x22, 0x28, 0x5b, 0x5e, 0x41, 0x2d, 0x5a, 0x61, 0x2d,
+  0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x5d, 0x29, 0x22, 0x2c, 0x20, 0x66, 0x75,
+  0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x63, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x65,
+  0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5b, 0x63, 0x5d,
+  0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x63, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65,
+  0x6c, 0x73, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73,
+  0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
+  0x28, 0x22, 0x25, 0x25, 0x25, 0x30, 0x32, 0x58, 0x22, 0x2c, 0x20, 0x73,
+  0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x28, 0x63,
+  0x29, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
+  0x6e, 0x64, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e, 0x75, 0x6e, 0x65, 0x73,
+  0x63, 0x61, 0x70, 0x65, 0x28, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x73, 0x74, 0x72, 0x69,
+  0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x2c, 0x20, 0x22,
+  0x25, 0x25, 0x28, 0x25, 0x78, 0x25, 0x78, 0x29, 0x22, 0x2c, 0x20, 0x66,
+  0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x68, 0x65, 0x78, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63,
+  0x68, 0x61, 0x72, 0x28, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x74, 0x6f, 0x6e,
+  0x75, 0x6d, 0x62, 0x65, 0x72, 0x28, 0x68, 0x65, 0x78, 0x2c, 0x20, 0x31,
+  0x36, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x29,
+  0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
+  0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x73,
+  0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x28, 0x62,
+  0x61, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x72, 0x65,
+  0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69,
+  0x6e, 0x67, 0x2e, 0x73, 0x75, 0x62, 0x28, 0x72, 0x65, 0x6c, 0x61, 0x74,
+  0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x31, 0x2c,
+  0x20, 0x31, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x2f, 0x22, 0x20, 0x74,
+  0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72,
+  0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68,
+  0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
+  0x61, 0x6c, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x74,
+  0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x62, 0x61,
+  0x73, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x22, 0x5b, 0x5e,
+  0x2f, 0x5d, 0x2a, 0x24, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x70, 0x61,
+  0x74, 0x68, 0x20, 0x2e, 0x2e, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69,
+  0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
+  0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x70, 0x61, 0x74, 0x68, 0x2c,
+  0x20, 0x22, 0x28, 0x5b, 0x5e, 0x2f, 0x5d, 0x2a, 0x25, 0x2e, 0x2f, 0x29,
+  0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+  0x28, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x73, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x2e, 0x2f, 0x22,
+  0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x73, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x22, 0x22, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70,
+  0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+  0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20,
+  0x22, 0x2f, 0x25, 0x2e, 0x24, 0x22, 0x2c, 0x20, 0x22, 0x2f, 0x22, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72,
+  0x65, 0x64, 0x75, 0x63, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77,
+  0x68, 0x69, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x64,
+  0x20, 0x7e, 0x3d, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x6f, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x64, 0x75,
+  0x63, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x74, 0x68, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20,
+  0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75,
+  0x62, 0x28, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x64, 0x2c, 0x20, 0x22,
+  0x28, 0x5b, 0x5e, 0x2f, 0x5d, 0x2a, 0x2f, 0x25, 0x2e, 0x25, 0x2e, 0x2f,
+  0x29, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+  0x20, 0x28, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x20, 0x7e, 0x3d,
+  0x20, 0x22, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x22, 0x20, 0x74, 0x68,
+  0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22,
+  0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x73, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68,
+  0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73,
+  0x75, 0x62, 0x28, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x64, 0x2c, 0x20,
+  0x22, 0x28, 0x5b, 0x5e, 0x2f, 0x5d, 0x2a, 0x2f, 0x25, 0x2e, 0x25, 0x2e,
+  0x29, 0x24, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
+  0x6e, 0x20, 0x28, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x2e,
+  0x2e, 0x2f, 0x2e, 0x2e, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22, 0x20, 0x65, 0x6c, 0x73,
+  0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x20, 0x65,
+  0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70,
+  0x61, 0x74, 0x68, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63,
+  0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x28, 0x75, 0x72, 0x6c, 0x2c, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75,
+  0x6c, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x7b,
+  0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c,
+  0x76, 0x20, 0x69, 0x6e, 0x20, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x70, 0x61,
+  0x69, 0x72, 0x73, 0x28, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20,
+  0x6f, 0x72, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x29, 0x20, 0x64,
+  0x6f, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x5b, 0x69, 0x5d, 0x20,
+  0x3d, 0x20, 0x76, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x6f,
+  0x72, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x22, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x22, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69,
+  0x64, 0x20, 0x75, 0x72, 0x6c, 0x22, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72,
+  0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x75, 0x72, 0x6c,
+  0x2c, 0x20, 0x22, 0x23, 0x28, 0x2e, 0x2a, 0x29, 0x24, 0x22, 0x2c, 0x20,
+  0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x64, 0x2e, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20,
+  0x3d, 0x20, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75,
+  0x72, 0x6c, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+  0x67, 0x73, 0x75, 0x62, 0x28, 0x75, 0x72, 0x6c, 0x2c, 0x20, 0x22, 0x5e,
+  0x28, 0x5b, 0x25, 0x77, 0x5d, 0x5b, 0x25, 0x77, 0x25, 0x2b, 0x25, 0x2d,
+  0x25, 0x2e, 0x5d, 0x2a, 0x29, 0x25, 0x3a, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x28, 0x73, 0x29, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64,
+  0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x3b,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22, 0x20, 0x65,
+  0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, 0x20,
+  0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75,
+  0x62, 0x28, 0x75, 0x72, 0x6c, 0x2c, 0x20, 0x22, 0x5e, 0x2f, 0x2f, 0x28,
+  0x5b, 0x5e, 0x2f, 0x5d, 0x2a, 0x29, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e,
+  0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e,
+  0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20,
+  0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c,
+  0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73,
+  0x75, 0x62, 0x28, 0x75, 0x72, 0x6c, 0x2c, 0x20, 0x22, 0x25, 0x3f, 0x28,
+  0x2e, 0x2a, 0x29, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x28, 0x71, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x71, 0x75, 0x65,
+  0x72, 0x79, 0x20, 0x3d, 0x20, 0x71, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69,
+  0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x75, 0x72, 0x6c, 0x2c,
+  0x20, 0x22, 0x25, 0x3b, 0x28, 0x2e, 0x2a, 0x29, 0x22, 0x2c, 0x20, 0x66,
+  0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x64, 0x2e, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x3d, 0x20, 0x70,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
+  0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x75,
+  0x72, 0x6c, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x22, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x61, 0x74,
+  0x68, 0x20, 0x3d, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x75,
+  0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+  0x74, 0x79, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f,
+  0x74, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+  0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73,
+  0x75, 0x62, 0x28, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+  0x2c, 0x22, 0x5e, 0x28, 0x5b, 0x5e, 0x40, 0x5d, 0x2a, 0x29, 0x40, 0x22,
+  0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75,
+  0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x75, 0x29, 0x20, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66,
+  0x6f, 0x20, 0x3d, 0x20, 0x75, 0x3b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72,
+  0x6e, 0x20, 0x22, 0x22, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20,
+  0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75,
+  0x62, 0x28, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x2c,
+  0x20, 0x22, 0x3a, 0x28, 0x5b, 0x5e, 0x3a, 0x25, 0x5d, 0x5d, 0x2a, 0x29,
+  0x24, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x29, 0x20,
+  0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x20,
+  0x3d, 0x20, 0x70, 0x3b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x22, 0x22, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+  0x20, 0x7e, 0x3d, 0x20, 0x22, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72,
+  0x73, 0x65, 0x64, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x73,
+  0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28,
+  0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x2c, 0x20, 0x22,
+  0x5e, 0x25, 0x5b, 0x28, 0x2e, 0x2b, 0x29, 0x25, 0x5d, 0x24, 0x22, 0x29,
+  0x20, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+  0x79, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x75, 0x73, 0x65,
+  0x72, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x64, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x75,
+  0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x6e,
+  0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x64, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75,
+  0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x3d, 0x20, 0x73, 0x74,
+  0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x75, 0x73,
+  0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x2c, 0x20, 0x22, 0x3a, 0x28, 0x5b,
+  0x5e, 0x3a, 0x5d, 0x2a, 0x29, 0x24, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
+  0x6e, 0x28, 0x70, 0x29, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e,
+  0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x3d, 0x20, 0x70,
+  0x3b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22, 0x20,
+  0x65, 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72,
+  0x73, 0x65, 0x64, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x75,
+  0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x28,
+  0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x2d, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x70, 0x61, 0x74,
+  0x68, 0x20, 0x3d, 0x20, 0x5f, 0x4d, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x5f, 0x70, 0x61, 0x74, 0x68, 0x28, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64,
+  0x2e, 0x70, 0x61, 0x74, 0x68, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x22, 0x29,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x5f, 0x4d, 0x2e, 0x62, 0x75,
+  0x69, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x28, 0x70, 0x70, 0x61,
+  0x74, 0x68, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x64, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x20, 0x6f, 0x72, 0x20, 0x22,
+  0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70, 0x61, 0x72,
+  0x73, 0x65, 0x64, 0x2e, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x74,
+  0x68, 0x65, 0x6e, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x75, 0x72,
+  0x6c, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x3b, 0x22, 0x20, 0x2e, 0x2e, 0x20,
+  0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x61, 0x72, 0x61, 0x6d,
+  0x73, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
+  0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x71, 0x75, 0x65, 0x72,
+  0x79, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d,
+  0x20, 0x75, 0x72, 0x6c, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x3f, 0x22, 0x20,
+  0x2e, 0x2e, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x71, 0x75,
+  0x65, 0x72, 0x79, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+  0x69, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64,
+  0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64,
+  0x2e, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f,
+  0x72, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x64, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+  0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+  0x69, 0x74, 0x79, 0x2c, 0x20, 0x22, 0x3a, 0x22, 0x29, 0x20, 0x74, 0x68,
+  0x65, 0x6e, 0x20, 0x2d, 0x2d, 0x20, 0x49, 0x50, 0x76, 0x36, 0x3f, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20,
+  0x22, 0x5b, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f,
+  0x72, 0x69, 0x74, 0x79, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5d, 0x22, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70,
+  0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x74,
+  0x68, 0x65, 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+  0x79, 0x20, 0x3d, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+  0x79, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x3a, 0x22, 0x20, 0x2e, 0x2e, 0x20,
+  0x62, 0x61, 0x73, 0x65, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e,
+  0x67, 0x28, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x6f, 0x72,
+  0x74, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x75, 0x73, 0x65,
+  0x72, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x64, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70,
+  0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x20, 0x74,
+  0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f,
+  0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x75, 0x73,
+  0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64,
+  0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x74, 0x68,
+  0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x72, 0x69,
+  0x6e, 0x66, 0x6f, 0x20, 0x3d, 0x20, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e,
+  0x66, 0x6f, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x3a, 0x22, 0x20, 0x2e, 0x2e,
+  0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x61, 0x73, 0x73,
+  0x77, 0x6f, 0x72, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x75, 0x73, 0x65, 0x72,
+  0x69, 0x6e, 0x66, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x75,
+  0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x75, 0x73,
+  0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x40,
+  0x22, 0x20, 0x2e, 0x2e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+  0x74, 0x79, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
+  0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x61, 0x75,
+  0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x74, 0x68, 0x65, 0x6e,
+  0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x22, 0x2f, 0x2f, 0x22, 0x20,
+  0x2e, 0x2e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+  0x20, 0x2e, 0x2e, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x64, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x64, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x20, 0x2e, 0x2e,
+  0x20, 0x22, 0x3a, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x75, 0x72, 0x6c, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70,
+  0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65,
+  0x6e, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x75, 0x72, 0x6c, 0x20,
+  0x3d, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x23, 0x22,
+  0x20, 0x2e, 0x2e, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x66,
+  0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75,
+  0x72, 0x6c, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+  0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e, 0x61, 0x62, 0x73, 0x6f, 0x6c,
+  0x75, 0x74, 0x65, 0x28, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c,
+  0x2c, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x75,
+  0x72, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
+  0x6c, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x62, 0x61, 0x73,
+  0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x28, 0x62, 0x61, 0x73, 0x65, 0x5f,
+  0x75, 0x72, 0x6c, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x74, 0x61, 0x62,
+  0x6c, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f,
+  0x75, 0x72, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, 0x5f,
+  0x4d, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x28, 0x62, 0x61, 0x73, 0x65,
+  0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x64, 0x20, 0x3d, 0x20, 0x5f, 0x4d, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x28, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x29, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
+  0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76,
+  0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x5f,
+  0x4d, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x72, 0x65, 0x6c, 0x61,
+  0x74, 0x69, 0x76, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x61, 0x73,
+  0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x6c,
+  0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74,
+  0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72,
+  0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66,
+  0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x75, 0x72, 0x6c,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69,
+  0x76, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x73, 0x63,
+  0x68, 0x65, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f,
+  0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d,
+  0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
+  0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76,
+  0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x61, 0x75, 0x74,
+  0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x72,
+  0x73, 0x65, 0x64, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+  0x79, 0x20, 0x3d, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x72,
+  0x73, 0x65, 0x64, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+  0x79, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x6c,
+  0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64,
+  0x2e, 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f,
+  0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x20,
+  0x3d, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x64, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69,
+  0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69,
+  0x76, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x61,
+  0x72, 0x61, 0x6d, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69,
+  0x76, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x61,
+  0x72, 0x61, 0x6d, 0x73, 0x20, 0x3d, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f,
+  0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x70, 0x61, 0x72, 0x61, 0x6d,
+  0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
+  0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76,
+  0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x71, 0x75, 0x65,
+  0x72, 0x79, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6c, 0x61,
+  0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e,
+  0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x3d, 0x20, 0x62, 0x61, 0x73, 0x65,
+  0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x71, 0x75, 0x65, 0x72,
+  0x79, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c,
+  0x73, 0x65, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72,
+  0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x64, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x61, 0x62,
+  0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x28,
+  0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e,
+  0x70, 0x61, 0x74, 0x68, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x22, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6c, 0x61,
+  0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e,
+  0x70, 0x61, 0x74, 0x68, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x5f, 0x4d, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x28, 0x72, 0x65,
+  0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x65,
+  0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65,
+  0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+  0x5f, 0x4d, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x74,
+  0x68, 0x28, 0x70, 0x61, 0x74, 0x68, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64,
+  0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61,
+  0x74, 0x68, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x6f, 0x72,
+  0x20, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x70, 0x61,
+  0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+  0x67, 0x73, 0x75, 0x62, 0x28, 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x22,
+  0x25, 0x73, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62,
+  0x28, 0x70, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x22, 0x28, 0x5b, 0x5e, 0x2f,
+  0x5d, 0x2b, 0x29, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+  0x6f, 0x6e, 0x20, 0x28, 0x73, 0x29, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65,
+  0x2e, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x28, 0x70, 0x61, 0x72, 0x73,
+  0x65, 0x64, 0x2c, 0x20, 0x73, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20,
+  0x31, 0x2c, 0x20, 0x23, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x20, 0x64,
+  0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x5f, 0x4d,
+  0x2e, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x28, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x5b, 0x69, 0x5d, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
+  0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x73, 0x75, 0x62, 0x28, 0x70,
+  0x61, 0x74, 0x68, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x29, 0x20, 0x3d,
+  0x3d, 0x20, 0x22, 0x2f, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x70,
+  0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x69, 0x73, 0x5f, 0x61, 0x62, 0x73,
+  0x6f, 0x6c, 0x75, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x31, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72,
+  0x69, 0x6e, 0x67, 0x2e, 0x73, 0x75, 0x62, 0x28, 0x70, 0x61, 0x74, 0x68,
+  0x2c, 0x20, 0x2d, 0x31, 0x2c, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x3d, 0x3d,
+  0x20, 0x22, 0x2f, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x2e, 0x69, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x65,
+  0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x3d, 0x20, 0x31, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a,
+  0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x4d, 0x2e,
+  0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x28, 0x70,
+  0x61, 0x72, 0x73, 0x65, 0x64, 0x2c, 0x20, 0x75, 0x6e, 0x73, 0x61, 0x66,
+  0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+  0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x22, 0x22, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6e, 0x20, 0x3d,
+  0x20, 0x23, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x69, 0x66, 0x20, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x20, 0x74,
+  0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x66, 0x6f, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x2c, 0x20, 0x6e,
+  0x2d, 0x31, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d,
+  0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x2e, 0x2e, 0x20, 0x70, 0x61, 0x72,
+  0x73, 0x65, 0x64, 0x5b, 0x69, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20,
+  0x3d, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2f,
+  0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e,
+  0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
+  0x20, 0x6e, 0x20, 0x3e, 0x20, 0x30, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20,
+  0x2e, 0x2e, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x5b, 0x6e, 0x5d,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x69, 0x66, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x69,
+  0x73, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x20,
+  0x74, 0x68, 0x65, 0x6e, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20,
+  0x70, 0x61, 0x74, 0x68, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2f, 0x22, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72,
+  0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x2c, 0x20, 0x6e, 0x2d, 0x31, 0x20,
+  0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x70, 0x61,
+  0x74, 0x68, 0x20, 0x2e, 0x2e, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63,
+  0x74, 0x5f, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x70, 0x61,
+  0x72, 0x73, 0x65, 0x64, 0x5b, 0x69, 0x5d, 0x29, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x74,
+  0x68, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x2e, 0x2e, 0x20,
+  0x22, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x6e, 0x20, 0x3e, 0x20, 0x30, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x74,
+  0x68, 0x20, 0x2e, 0x2e, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74,
+  0x5f, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x70, 0x61, 0x72,
+  0x73, 0x65, 0x64, 0x5b, 0x6e, 0x5d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70,
+  0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x69, 0x73, 0x5f, 0x64, 0x69, 0x72,
+  0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
+  0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20,
+  0x2e, 0x2e, 0x20, 0x22, 0x2f, 0x22, 0x20, 0x65, 0x6e, 0x64, 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, 0x69,
+  0x66, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x64, 0x2e, 0x69, 0x73, 0x5f,
+  0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65,
+  0x6e, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x22, 0x2f, 0x22,
+  0x20, 0x2e, 0x2e, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x65, 0x6e, 0x64,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+  0x70, 0x61, 0x74, 0x68, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x5f, 0x4d, 0x0a,0x0};
+  return luaL_dostring(L, (const char*)B); 
+} /* end of embedded lua code */
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfaction.w
@@ -0,0 +1,176 @@
+% pdfaction.w
+%
+% Copyright 2009-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"
+
+@ read an action specification
+
+@c halfword scan_action(PDF pdf)
+{
+    int p = new_node(whatsit_node, pdf_action_node);
+    (void) pdf;
+    if (scan_keyword("user"))
+        set_pdf_action_type(p, pdf_action_user);
+    else if (scan_keyword("goto"))
+        set_pdf_action_type(p, pdf_action_goto);
+    else if (scan_keyword("thread"))
+        set_pdf_action_type(p, pdf_action_thread);
+    else
+        normal_error("pdf backend", "action type missing");
+    if (pdf_action_type(p) == pdf_action_user) {
+        scan_toks(false, true);
+        set_pdf_action_tokens(p, def_ref);
+        return p;
+    }
+    if (scan_keyword("file")) {
+        scan_toks(false, true);
+        set_pdf_action_file(p, def_ref);
+    }
+    if (scan_keyword("page")) {
+        if (pdf_action_type(p) != pdf_action_goto)
+            normal_error("pdf backend", "only GoTo action can be used with 'page'");
+        set_pdf_action_type(p, pdf_action_page);
+        scan_int();
+        if (cur_val <= 0)
+            normal_error("pdf backend", "page number must be positive");
+        set_pdf_action_id(p, cur_val);
+        set_pdf_action_named_id(p, 0);
+        scan_toks(false, true);
+        set_pdf_action_tokens(p, def_ref);
+    } else if (scan_keyword("name")) {
+        scan_toks(false, true);
+        set_pdf_action_named_id(p, 1);
+        set_pdf_action_id(p, def_ref);
+    } else if (scan_keyword("num")) {
+        if ((pdf_action_type(p) == pdf_action_goto) &&
+            (pdf_action_file(p) != null))
+            normal_error("pdf backend", "'goto' option cannot be used with both 'file' and 'num'");
+        scan_int();
+        if (cur_val <= 0)
+            normal_error("pdf backend", "num identifier must be positive");
+        set_pdf_action_named_id(p, 0);
+        set_pdf_action_id(p, cur_val);
+    } else {
+        normal_error("pdf backend", "identifier type missing");
+    }
+    if (scan_keyword("newwindow")) {
+        set_pdf_action_new_window(p, pdf_window_new);
+        /* Scan an optional space */
+        get_x_token();
+        if (cur_cmd != spacer_cmd)
+            back_input();
+    } else if (scan_keyword("nonewwindow")) {
+        set_pdf_action_new_window(p, pdf_window_nonew);
+        /* Scan an optional space */
+        get_x_token();
+        if (cur_cmd != spacer_cmd)
+            back_input();
+    } else {
+        set_pdf_action_new_window(p, pdf_window_notset);
+    }
+    if ((pdf_action_new_window(p) > pdf_window_notset) &&
+        (((pdf_action_type(p) != pdf_action_goto) &&
+          (pdf_action_type(p) != pdf_action_page)) ||
+         (pdf_action_file(p) == null)))
+        normal_error("pdf backend","'newwindow' or 'nonewwindow' must be used with 'goto' and 'file' option");
+    return p;
+}
+
+@ write an action specification
+
+@c void write_action(PDF pdf, halfword p)
+{
+    char *s;
+    int d = 0;
+    if (pdf_action_type(p) == pdf_action_user) {
+        pdf_out(pdf, '\n');
+        pdf_print_toks(pdf, pdf_action_tokens(p));
+        pdf_out(pdf, '\n');
+        return;
+    }
+    pdf_begin_dict(pdf);
+    if (pdf_action_file(p) != null) {
+        pdf_add_name(pdf, "F");
+        pdf_out(pdf, ' ');
+        s = tokenlist_to_cstring(pdf_action_file(p), true, NULL);
+        pdf_print_str(pdf, s);
+        xfree(s);
+        pdf_out(pdf, ' ');
+        if (pdf_action_new_window(p) > pdf_window_notset) {
+            if (pdf_action_new_window(p) == pdf_window_new)
+                pdf_dict_add_bool(pdf, "NewWindow", 1);
+            else
+                pdf_dict_add_bool(pdf, "NewWindow", 0);
+        }
+    }
+    switch (pdf_action_type(p)) {
+        case pdf_action_page:
+            pdf_dict_add_name(pdf, "S", "GoTo");
+            if (pdf_action_file(p) == null) {
+                pdf_add_name(pdf, "D");
+                pdf_begin_array(pdf);
+                pdf_add_ref(pdf, pdf_get_obj(pdf, obj_type_page, pdf_action_id(p), false));
+            } else {
+                pdf_add_name(pdf, "D");
+                pdf_begin_array(pdf);
+                pdf_print_int(pdf, pdf_action_id(p) - 1);
+            }
+            {
+                char *tokstr = tokenlist_to_cstring(pdf_action_tokens(p), true, NULL);
+                pdf_printf(pdf, " %s", tokstr);
+                pdf_end_array(pdf);
+                xfree(tokstr);
+            }
+            break;
+        case pdf_action_goto:
+            if (pdf_action_file(p) == null) {
+                pdf_dict_add_name(pdf, "S", "GoTo");
+                d = pdf_get_obj(pdf, obj_type_dest, pdf_action_id(p), pdf_action_named_id(p));
+            } else
+                pdf_dict_add_name(pdf, "S", "GoToR");
+            if (pdf_action_named_id(p) > 0) {
+                char *tokstr = tokenlist_to_cstring(pdf_action_id(p), true, NULL);
+                pdf_dict_add_string(pdf, "D", tokstr);
+                xfree(tokstr);
+            } else if (pdf_action_file(p) == null) {
+                pdf_dict_add_ref(pdf, "D", d);
+            } else {
+                normal_error("pdf backend","'goto' option cannot be used with both 'file' and 'num'");
+            }
+            break;
+        case pdf_action_thread:
+            pdf_dict_add_name(pdf, "S", "Thread");
+            if (pdf_action_file(p) == null) {
+                d = pdf_get_obj(pdf, obj_type_thread, pdf_action_id(p), pdf_action_named_id(p));
+                if (pdf_action_named_id(p) > 0) {
+                    char *tokstr = tokenlist_to_cstring(pdf_action_id(p), true, NULL);
+                    pdf_dict_add_string(pdf, "D", tokstr);
+                    xfree(tokstr);
+                } else if (pdf_action_file(p) == null) {
+                    pdf_dict_add_ref(pdf, "D", d);
+                } else {
+                    pdf_dict_add_int(pdf, "D", pdf_action_id(p));
+                }
+            }
+            break;
+    }
+    pdf_end_dict(pdf);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfaction.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex Read an action specification. */
-
-halfword scan_action(PDF pdf)
-{
-    int p = new_node(whatsit_node, pdf_action_node);
-    (void) pdf;
-    if (scan_keyword("user"))
-        set_pdf_action_type(p, pdf_action_user);
-    else if (scan_keyword("goto"))
-        set_pdf_action_type(p, pdf_action_goto);
-    else if (scan_keyword("thread"))
-        set_pdf_action_type(p, pdf_action_thread);
-    else
-        normal_error("pdf backend", "action type missing");
-    if (pdf_action_type(p) == pdf_action_user) {
-        scan_toks(false, true);
-        set_pdf_action_tokens(p, def_ref);
-        return p;
-    }
-    if (scan_keyword("file")) {
-        scan_toks(false, true);
-        set_pdf_action_file(p, def_ref);
-    }
-    if (scan_keyword("page")) {
-        if (pdf_action_type(p) != pdf_action_goto)
-            normal_error("pdf backend", "only GoTo action can be used with 'page'");
-        set_pdf_action_type(p, pdf_action_page);
-        scan_int();
-        if (cur_val <= 0)
-            normal_error("pdf backend", "page number must be positive");
-        set_pdf_action_id(p, cur_val);
-        set_pdf_action_named_id(p, 0);
-        scan_toks(false, true);
-        set_pdf_action_tokens(p, def_ref);
-    } else if (scan_keyword("name")) {
-        scan_toks(false, true);
-        set_pdf_action_named_id(p, 1);
-        set_pdf_action_id(p, def_ref);
-    } else if (scan_keyword("num")) {
-        if ((pdf_action_type(p) == pdf_action_goto) &&
-            (pdf_action_file(p) != null))
-            normal_error("pdf backend", "'goto' option cannot be used with both 'file' and 'num'");
-        scan_int();
-        if (cur_val <= 0)
-            normal_error("pdf backend", "num identifier must be positive");
-        set_pdf_action_named_id(p, 0);
-        set_pdf_action_id(p, cur_val);
-    } else {
-        normal_error("pdf backend", "identifier type missing");
-    }
-    if (scan_keyword("newwindow")) {
-        set_pdf_action_new_window(p, pdf_window_new);
-        /*tex Scan an optional space. */
-        get_x_token();
-        if (cur_cmd != spacer_cmd)
-            back_input();
-    } else if (scan_keyword("nonewwindow")) {
-        set_pdf_action_new_window(p, pdf_window_nonew);
-        /*tex Scan an optional space. */
-        get_x_token();
-        if (cur_cmd != spacer_cmd)
-            back_input();
-    } else {
-        set_pdf_action_new_window(p, pdf_window_notset);
-    }
-    if ((pdf_action_new_window(p) > pdf_window_notset) &&
-        (((pdf_action_type(p) != pdf_action_goto) &&
-          (pdf_action_type(p) != pdf_action_page)) ||
-         (pdf_action_file(p) == null)))
-        normal_error("pdf backend","'newwindow' or 'nonewwindow' must be used with 'goto' and 'file' option");
-    return p;
-}
-
-/*tex Write an action specification. */
-
-void write_action(PDF pdf, halfword p)
-{
-    char *s;
-    int d = 0;
-    if (pdf_action_type(p) == pdf_action_user) {
-        pdf_out(pdf, '\n');
-        pdf_print_toks(pdf, pdf_action_tokens(p));
-        pdf_out(pdf, '\n');
-        return;
-    }
-    pdf_begin_dict(pdf);
-    if (pdf_action_file(p) != null) {
-        pdf_add_name(pdf, "F");
-        pdf_out(pdf, ' ');
-        s = tokenlist_to_cstring(pdf_action_file(p), true, NULL);
-        pdf_print_str(pdf, s);
-        xfree(s);
-        pdf_out(pdf, ' ');
-        if (pdf_action_new_window(p) > pdf_window_notset) {
-            if (pdf_action_new_window(p) == pdf_window_new)
-                pdf_dict_add_bool(pdf, "NewWindow", 1);
-            else
-                pdf_dict_add_bool(pdf, "NewWindow", 0);
-        }
-    }
-    switch (pdf_action_type(p)) {
-        case pdf_action_page:
-            pdf_dict_add_name(pdf, "S", "GoTo");
-            if (pdf_action_file(p) == null) {
-                pdf_add_name(pdf, "D");
-                pdf_begin_array(pdf);
-                pdf_add_ref(pdf, pdf_get_obj(pdf, obj_type_page, pdf_action_id(p), false));
-            } else {
-                pdf_add_name(pdf, "D");
-                pdf_begin_array(pdf);
-                pdf_print_int(pdf, pdf_action_id(p) - 1);
-            }
-            {
-                char *tokstr = tokenlist_to_cstring(pdf_action_tokens(p), true, NULL);
-                pdf_printf(pdf, " %s", tokstr);
-                pdf_end_array(pdf);
-                xfree(tokstr);
-            }
-            break;
-        case pdf_action_goto:
-            if (pdf_action_file(p) == null) {
-                pdf_dict_add_name(pdf, "S", "GoTo");
-                d = pdf_get_obj(pdf, obj_type_dest, pdf_action_id(p), pdf_action_named_id(p));
-            } else
-                pdf_dict_add_name(pdf, "S", "GoToR");
-            if (pdf_action_named_id(p) > 0) {
-                char *tokstr = tokenlist_to_cstring(pdf_action_id(p), true, NULL);
-                pdf_dict_add_string(pdf, "D", tokstr);
-                xfree(tokstr);
-            } else if (pdf_action_file(p) == null) {
-                pdf_dict_add_ref(pdf, "D", d);
-            } else {
-                normal_error("pdf backend","'goto' option cannot be used with both 'file' and 'num'");
-            }
-            break;
-        case pdf_action_thread:
-            pdf_dict_add_name(pdf, "S", "Thread");
-            if (pdf_action_file(p) == null) {
-                d = pdf_get_obj(pdf, obj_type_thread, pdf_action_id(p), pdf_action_named_id(p));
-                if (pdf_action_named_id(p) > 0) {
-                    char *tokstr = tokenlist_to_cstring(pdf_action_id(p), true, NULL);
-                    pdf_dict_add_string(pdf, "D", tokstr);
-                    xfree(tokstr);
-                } else if (pdf_action_file(p) == null) {
-                    pdf_dict_add_ref(pdf, "D", d);
-                } else {
-                    pdf_dict_add_int(pdf, "D", pdf_action_id(p));
-                }
-            }
-            break;
-    }
-    pdf_end_dict(pdf);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfannot.w
@@ -0,0 +1,94 @@
+% pdfannot.w
+%
+% Copyright 2009-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"
+
+@ @c
+void do_annot(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
+{
+    scaled_whd alt_rule;
+    int k;
+    if (global_shipping_mode == SHIPPING_FORM)
+        normal_error("pdf backend", "annotations cannot be inside an xform");
+    if (doing_leaders)
+        return;
+    if (is_obj_scheduled(pdf, pdf_annot_objnum(p))) {
+        k = pdf_create_obj(pdf, obj_type_annot, 0);
+        obj_annot_ptr(pdf, pdf_annot_objnum(p)) = p;
+        pdf_annot_objnum(p) = k;
+    }
+    alt_rule.wd = width(p);
+    alt_rule.ht = height(p);
+    alt_rule.dp = depth(p);
+    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, 0);
+    obj_annot_ptr(pdf, pdf_annot_objnum(p)) = p;
+    addto_page_resources(pdf, obj_type_annot, pdf_annot_objnum(p));
+}
+
+@ create a new whatsit node for annotation
+
+@c void new_annot_whatsit(small_number w)
+{
+    scaled_whd alt_rule;
+    new_whatsit(w);
+    alt_rule = scan_alt_rule(); /* scans |<rule spec>| to |alt_rule| */
+    set_width(tail_par, alt_rule.wd);
+    set_height(tail_par, alt_rule.ht);
+    set_depth(tail_par, alt_rule.dp);
+    if ((w == pdf_thread_node) || (w == pdf_start_thread_node)) {
+        if (scan_keyword("attr")) {
+            scan_toks(false, true);
+            set_pdf_thread_attr(tail_par, def_ref);
+        } else {
+            set_pdf_thread_attr(tail_par, null);
+        }
+    }
+}
+
+@ scanning at the \TeX\ end
+
+@c void scan_annot(PDF pdf)
+{
+    int k;
+    if (scan_keyword("reserveobjnum")) {
+        k = pdf_create_obj(pdf, obj_type_annot, 0);
+        /* Scan an optional space */
+        get_x_token();
+        if (cur_cmd != spacer_cmd)
+            back_input();
+    } else {
+        if (scan_keyword("useobjnum")) {
+            scan_int();
+            k = cur_val;
+            check_obj_type(pdf, obj_type_annot, k);
+            if (obj_annot_ptr(pdf, k) != 0)
+                normal_error("pdf backend", "annot object in use");
+        } else {
+            k = pdf_create_obj(pdf, obj_type_annot, 0);
+        }
+        new_annot_whatsit(pdf_annot_node);
+        obj_annot_ptr(pdf, k) = tail_par;
+        set_pdf_annot_objnum(tail_par, k);
+        scan_toks(false, true);
+        set_pdf_annot_data(tail_par, def_ref);
+    }
+    pdf_last_annot = k;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfannot.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-void do_annot(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
-{
-    scaled_whd alt_rule;
-    int k;
-    if (global_shipping_mode == SHIPPING_FORM)
-        normal_error("pdf backend", "annotations cannot be inside an xform");
-    if (doing_leaders)
-        return;
-    if (is_obj_scheduled(pdf, pdf_annot_objnum(p))) {
-        k = pdf_create_obj(pdf, obj_type_annot, 0);
-        obj_annot_ptr(pdf, pdf_annot_objnum(p)) = p;
-        pdf_annot_objnum(p) = k;
-    }
-    alt_rule.wd = width(p);
-    alt_rule.ht = height(p);
-    alt_rule.dp = depth(p);
-    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, 0);
-    obj_annot_ptr(pdf, pdf_annot_objnum(p)) = p;
-    addto_page_resources(pdf, obj_type_annot, pdf_annot_objnum(p));
-}
-
-/*tex Create a new whatsit node for annotation. */
-
-void new_annot_whatsit(small_number w)
-{
-    scaled_whd alt_rule;
-    new_whatsit(w);
-    alt_rule = scan_alt_rule();
-    set_width(tail_par, alt_rule.wd);
-    set_height(tail_par, alt_rule.ht);
-    set_depth(tail_par, alt_rule.dp);
-    if ((w == pdf_thread_node) || (w == pdf_start_thread_node)) {
-        if (scan_keyword("attr")) {
-            scan_toks(false, true);
-            set_pdf_thread_attr(tail_par, def_ref);
-        } else {
-            set_pdf_thread_attr(tail_par, null);
-        }
-    }
-}
-
-/*tex Scanning at the \TEX\ end: */
-
-void scan_annot(PDF pdf)
-{
-    int k;
-    if (scan_keyword("reserveobjnum")) {
-        k = pdf_create_obj(pdf, obj_type_annot, 0);
-        /*tex Scan an optional space. */
-        get_x_token();
-        if (cur_cmd != spacer_cmd)
-            back_input();
-    } else {
-        if (scan_keyword("useobjnum")) {
-            scan_int();
-            k = cur_val;
-            check_obj_type(pdf, obj_type_annot, k);
-            if (obj_annot_ptr(pdf, k) != 0)
-                normal_error("pdf backend", "annot object in use");
-        } else {
-            k = pdf_create_obj(pdf, obj_type_annot, 0);
-        }
-        new_annot_whatsit(pdf_annot_node);
-        obj_annot_ptr(pdf, k) = tail_par;
-        set_pdf_annot_objnum(tail_par, k);
-        scan_toks(false, true);
-        set_pdf_annot_data(tail_par, def_ref);
-    }
-    pdf_last_annot = k;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfcolorstack.w
@@ -0,0 +1,358 @@
+% pdfcolorstack.w
+%
+% Copyright 2009-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"
+
+@* Color Stack and Matrix Transformation Support.
+
+@ In the following array and especially stack data structures are used.
+
+They have the following properties:
+
+\item{-} They automatically grow dynamically.
+\item{-} The size never decreases.
+\item{-} The variable with name ending in "size" contains the number how many
+    entries the data structure can hold.
+\item{-} The variable with name ending in "used" contains the number of
+    actually used entries.
+\item{-} Memory of strings in stack entries must be allocated and
+    freed if the stack is cleared.
+
+@ Color Stack
+@c
+
+#define MAX_COLORSTACKS 32768
+
+/*
+    The colorstack number is stored in two bytes (info field of the node);
+    condition (newcolorstack): |MAX_COLORSTACKS mod STACK_INCREMENT = 0|
+*/
+
+#define COLOR_DEFAULT "0 g 0 G"
+
+typedef struct {
+    char **page_stack;
+    char **form_stack;
+    char *page_current;
+    char *form_current;
+    char *form_init;
+    int page_size;
+    int form_size;
+    int page_used;
+    int form_used;
+    int literal_mode;
+    boolean page_start;
+} colstack_type;
+
+static colstack_type *colstacks = NULL;
+static int colstacks_size = 0;
+static int colstacks_used = 0;
+
+@ Initialization is done, if the color stacks are used, |init_colorstacks()| is defined
+as macro to avoid unnecessary procedure calls.
+
+@c
+#define init_colorstacks() if (colstacks_size == 0) colstacks_first_init();
+
+static void colstacks_first_init(void)
+{
+    colstacks_size = STACK_INCREMENT;
+    colstacks = xtalloc((unsigned) colstacks_size, colstack_type);
+    colstacks_used = 1;
+    colstacks[0].page_stack = NULL;
+    colstacks[0].form_stack = NULL;
+    colstacks[0].page_size = 0;
+    colstacks[0].form_size = 0;
+    colstacks[0].page_used = 0;
+    colstacks[0].form_used = 0;
+    colstacks[0].page_current = xstrdup(COLOR_DEFAULT);
+    colstacks[0].form_current = xstrdup(COLOR_DEFAULT);
+    colstacks[0].form_init = xstrdup(COLOR_DEFAULT);
+    colstacks[0].literal_mode = direct_always;
+    colstacks[0].page_start = true;
+}
+
+@ @c
+int colorstackused(void)
+{
+    init_colorstacks();
+    return colstacks_used;
+}
+
+@   A new color stack is setup with the given parameters. The stack number is returned
+or -1 in case of error (no room).
+
+@c
+int newcolorstack(const char *str, int literal_mode, boolean page_start)
+{
+    colstack_type *colstack;
+    int colstack_num;
+    init_colorstacks();
+    /* make room */
+    if (colstacks_used == MAX_COLORSTACKS) {
+        return -1;
+    }
+    if (colstacks_used == colstacks_size) {
+        colstacks_size += STACK_INCREMENT;
+        /*
+            If |(MAX_COLORSTACKS mod STACK_INCREMENT = 0)| then we don't
+            need to check the case that size overruns |MAX_COLORSTACKS|.
+        */
+        colstacks = xreallocarray(colstacks, colstack_type, (unsigned) colstacks_size);
+    }
+    /* claim new color stack */
+    colstack_num = colstacks_used++;
+    colstack = &colstacks[colstack_num];
+    /* configure the new color stack */
+    colstack->page_stack = NULL;
+    colstack->form_stack = NULL;
+    colstack->page_size = 0;
+    colstack->page_used = 0;
+    colstack->form_size = 0;
+    colstack->form_used = 0;
+    colstack->literal_mode = literal_mode;
+    colstack->page_start = page_start;
+    colstack->page_current = NULL;
+    colstack->form_current = NULL;
+    colstack->form_init = NULL;
+    if (str) {
+        colstack->page_current = xstrdup(str);
+        colstack->form_current = xstrdup(str);
+        colstack->form_init = xstrdup(str);
+    }
+    return colstack_num;
+}
+
+@ @c
+#define get_colstack(n) (&colstacks[n])
+
+@ Puts a string on top of the string pool and updates |pool_ptr|.
+
+@c static void put_cstring_on_str_pool(char *str)
+{
+    int save_selector = selector;
+    selector = new_string;
+    if (str == NULL || *str == 0) {
+        return;
+    }
+    tprint(str);
+    selector = save_selector;
+}
+
+@ @c
+static int colorstackset(int colstack_no, str_number s)
+{
+    colstack_type *colstack = get_colstack(colstack_no);
+
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        xfree(colstack->page_current);
+        colstack->page_current = makecstring(s);
+    } else {
+        xfree(colstack->form_current);
+        colstack->form_current = makecstring(s);
+    }
+    return colstack->literal_mode;
+}
+
+@ @c
+int colorstackcurrent(int colstack_no)
+{
+    colstack_type *colstack = get_colstack(colstack_no);
+
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        put_cstring_on_str_pool(colstack->page_current);
+    } else {
+        put_cstring_on_str_pool(colstack->form_current);
+    }
+    return colstack->literal_mode;
+}
+
+@ @c
+static int colorstackpush(int colstack_no, str_number s)
+{
+    colstack_type *colstack = get_colstack(colstack_no);
+    char *str;
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        if (colstack->page_used == colstack->page_size) {
+            colstack->page_size += STACK_INCREMENT;
+            xretalloc(colstack->page_stack, (unsigned) colstack->page_size, char *);
+        }
+        colstack->page_stack[colstack->page_used++] = colstack->page_current;
+        str = makecstring(s);
+        if (*str == 0) {
+            colstack->page_current = NULL;
+        } else {
+            colstack->page_current = xstrdup(str);
+        }
+        free(str);
+    } else {
+        if (colstack->form_used == colstack->form_size) {
+            colstack->form_size += STACK_INCREMENT;
+            xretalloc(colstack->form_stack, (unsigned) colstack->form_size, char *);
+        }
+        colstack->form_stack[colstack->form_used++] = colstack->form_current;
+        str = makecstring(s);
+        if (*str == 0) {
+            colstack->form_current = NULL;
+        } else {
+            colstack->form_current = xstrdup(str);
+        }
+        free(str);
+    }
+    return colstack->literal_mode;
+}
+
+@ @c
+int colorstackpop(int colstack_no)
+{
+    colstack_type *colstack = get_colstack(colstack_no);
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        if (colstack->page_used == 0) {
+            formatted_warning("pdf backend","pop empty color page stack %u",(unsigned int) colstack_no);
+            return colstack->literal_mode;
+        }
+        xfree(colstack->page_current);
+        colstack->page_current = colstack->page_stack[--colstack->page_used];
+        put_cstring_on_str_pool(colstack->page_current);
+    } else {
+        if (colstack->form_used == 0) {
+            formatted_warning("pdf backend","pop empty color form stack %u",(unsigned int) colstack_no);
+            return colstack->literal_mode;
+        }
+        xfree(colstack->form_current);
+        colstack->form_current = colstack->form_stack[--colstack->form_used];
+        put_cstring_on_str_pool(colstack->form_current);
+    }
+    return colstack->literal_mode;
+}
+
+@ @c
+void colorstackpagestart(void)
+{
+    int i, j;
+    colstack_type *colstack;
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        /* see procedure |pdf_out_colorstack_startpage| */
+        return;
+    }
+
+    for (i = 0; i < colstacks_used; i++) {
+        colstack = &colstacks[i];
+        for (j = 0; j < colstack->form_used; j++) {
+            xfree(colstack->form_stack[j]);
+        }
+        colstack->form_used = 0;
+        xfree(colstack->form_current);
+        if (colstack->form_init == NULL) {
+            colstack->form_current = NULL;
+        } else {
+            colstack->form_current = xstrdup(colstack->form_init);
+        }
+    }
+}
+
+@ @c
+int colorstackskippagestart(int colstack_no)
+{
+    colstack_type *colstack = get_colstack(colstack_no);
+    if (!colstack->page_start) {
+        return 1;
+    }
+    if (colstack->page_current == NULL) {
+        return 0;
+    }
+    if (strcmp(COLOR_DEFAULT, colstack->page_current) == 0) {
+        return 2;
+    }
+    return 0;
+}
+
+
+@ @c
+void pdf_out_colorstack(PDF pdf, halfword p)
+{
+    int old_setting;
+    str_number s;
+    int cmd = pdf_colorstack_cmd(p);
+    int stack_no = pdf_colorstack_stack(p);
+    int literal_mode = 0;
+    if (stack_no >= colorstackused()) {
+        tprint_nl("");
+        tprint("Color stack ");
+        print_int(stack_no);
+        tprint(" is not initialized for use!");
+        tprint_nl("");
+        return;
+    }
+    switch (cmd) {
+        case colorstack_set:
+        case colorstack_push:
+            old_setting = selector;
+            selector = new_string;
+            show_token_list(token_link(pdf_colorstack_data(p)), null, -1);
+            selector = old_setting;
+            s = make_string();
+            if (cmd == colorstack_set)
+                literal_mode = colorstackset(stack_no, s);
+            else
+                literal_mode = colorstackpush(stack_no, s);
+            if (str_length(s) > 0)
+                pdf_literal(pdf, s, literal_mode, false);
+            flush_str(s);
+            return;
+            break;
+        case colorstack_pop:
+            literal_mode = colorstackpop(stack_no);
+            break;
+        case colorstack_current:
+            literal_mode = colorstackcurrent(stack_no);
+            break;
+        default:
+            break;
+    }
+    if (cur_length > 0) {
+        s = make_string();
+        pdf_literal(pdf, s, literal_mode, false);
+        flush_str(s);
+    }
+}
+
+@ @c
+void pdf_out_colorstack_startpage(PDF pdf)
+{
+    int start_status;
+    int literal_mode;
+    str_number s;
+    int i = 0;
+    int max = colorstackused();
+    while (i < max) {
+        start_status = colorstackskippagestart(i);
+        if (start_status == 0) {
+            literal_mode = colorstackcurrent(i);
+            if (cur_length > 0) {
+                s = make_string();
+                pdf_literal(pdf, s, literal_mode, false);
+                flush_str(s);
+            }
+        }
+        i++;
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfcolorstack.c
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    This module implements color stack support. The following array and
-    especially stack data structures are used.
-
-    \startitemize
-        \startitem
-            They automatically grow dynamically.
-        \stopitem
-        \startitem
-            The size never decreases.
-        \stopitem
-        \startitem
-            The variable with name ending in "size" contains the number how many
-            entries the data structure can hold.
-        \stopitem
-        \startitem
-            The variable with name ending in "used" contains the number of
-            actually used entries.
-        \stopitem
-        \startitem
-            Memory of strings in stack entries must be allocated and freed if the
-            stack is cleared.
-        \stopitem
-    \stopitemize
-
-*/
-
-#define MAX_COLORSTACKS 32768
-
-/*
-    The colorstack number is stored in two bytes (info field of the node);
-    condition (newcolorstack): |MAX_COLORSTACKS mod STACK_INCREMENT = 0|
-*/
-
-#define COLOR_DEFAULT "0 g 0 G"
-
-typedef struct {
-    char **page_stack;
-    char **form_stack;
-    char *page_current;
-    char *form_current;
-    char *form_init;
-    int page_size;
-    int form_size;
-    int page_used;
-    int form_used;
-    int literal_mode;
-    boolean page_start;
-} colstack_type;
-
-static colstack_type *colstacks = NULL;
-static int colstacks_size = 0;
-static int colstacks_used = 0;
-
-/*tex
-
-    Initialization is done, if the color stacks are used, |init_colorstacks()| is
-    defined as macro to avoid unnecessary procedure calls.
-
-*/
-
-#define init_colorstacks() if (colstacks_size == 0) colstacks_first_init();
-
-static void colstacks_first_init(void)
-{
-    colstacks_size = STACK_INCREMENT;
-    colstacks = xtalloc((unsigned) colstacks_size, colstack_type);
-    colstacks_used = 1;
-    colstacks[0].page_stack = NULL;
-    colstacks[0].form_stack = NULL;
-    colstacks[0].page_size = 0;
-    colstacks[0].form_size = 0;
-    colstacks[0].page_used = 0;
-    colstacks[0].form_used = 0;
-    colstacks[0].page_current = xstrdup(COLOR_DEFAULT);
-    colstacks[0].form_current = xstrdup(COLOR_DEFAULT);
-    colstacks[0].form_init = xstrdup(COLOR_DEFAULT);
-    colstacks[0].literal_mode = direct_always;
-    colstacks[0].page_start = true;
-}
-
-int colorstackused(void)
-{
-    init_colorstacks();
-    return colstacks_used;
-}
-
-/*tex
-
-    A new color stack is setup with the given parameters. The stack number is
-    returned or -1 in case of error (no room).
-
-*/
-
-int newcolorstack(const char *str, int literal_mode, boolean page_start)
-{
-    colstack_type *colstack;
-    int colstack_num;
-    init_colorstacks();
-    /*tex Make room: */
-    if (colstacks_used == MAX_COLORSTACKS) {
-        return -1;
-    }
-    if (colstacks_used == colstacks_size) {
-        colstacks_size += STACK_INCREMENT;
-        /*tex
-            If |(MAX_COLORSTACKS mod STACK_INCREMENT = 0)| then we don't
-            need to check the case that size overruns |MAX_COLORSTACKS|.
-        */
-        colstacks = xreallocarray(colstacks, colstack_type, (unsigned) colstacks_size);
-    }
-    /*tex Claim new color stack. */
-    colstack_num = colstacks_used++;
-    colstack = &colstacks[colstack_num];
-    /*tex Configure the new color stack. */
-    colstack->page_stack = NULL;
-    colstack->form_stack = NULL;
-    colstack->page_size = 0;
-    colstack->page_used = 0;
-    colstack->form_size = 0;
-    colstack->form_used = 0;
-    colstack->literal_mode = literal_mode;
-    colstack->page_start = page_start;
-    colstack->page_current = NULL;
-    colstack->form_current = NULL;
-    colstack->form_init = NULL;
-    if (str) {
-        colstack->page_current = xstrdup(str);
-        colstack->form_current = xstrdup(str);
-        colstack->form_init = xstrdup(str);
-    }
-    return colstack_num;
-}
-
-#define get_colstack(n) (&colstacks[n])
-
-/*tex Put a string on top of the string pool and updates |pool_ptr|. */
-
-static void put_cstring_on_str_pool(char *str)
-{
-    int save_selector = selector;
-    selector = new_string;
-    if (str == NULL || *str == 0) {
-        return;
-    }
-    tprint(str);
-    selector = save_selector;
-}
-
-static int colorstackset(int colstack_no, str_number s)
-{
-    colstack_type *colstack = get_colstack(colstack_no);
-
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        xfree(colstack->page_current);
-        colstack->page_current = makecstring(s);
-    } else {
-        xfree(colstack->form_current);
-        colstack->form_current = makecstring(s);
-    }
-    return colstack->literal_mode;
-}
-
-int colorstackcurrent(int colstack_no)
-{
-    colstack_type *colstack = get_colstack(colstack_no);
-
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        put_cstring_on_str_pool(colstack->page_current);
-    } else {
-        put_cstring_on_str_pool(colstack->form_current);
-    }
-    return colstack->literal_mode;
-}
-
-static int colorstackpush(int colstack_no, str_number s)
-{
-    colstack_type *colstack = get_colstack(colstack_no);
-    char *str;
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        if (colstack->page_used == colstack->page_size) {
-            colstack->page_size += STACK_INCREMENT;
-            xretalloc(colstack->page_stack, (unsigned) colstack->page_size, char *);
-        }
-        colstack->page_stack[colstack->page_used++] = colstack->page_current;
-        str = makecstring(s);
-        if (*str == 0) {
-            colstack->page_current = NULL;
-        } else {
-            colstack->page_current = xstrdup(str);
-        }
-        free(str);
-    } else {
-        if (colstack->form_used == colstack->form_size) {
-            colstack->form_size += STACK_INCREMENT;
-            xretalloc(colstack->form_stack, (unsigned) colstack->form_size, char *);
-        }
-        colstack->form_stack[colstack->form_used++] = colstack->form_current;
-        str = makecstring(s);
-        if (*str == 0) {
-            colstack->form_current = NULL;
-        } else {
-            colstack->form_current = xstrdup(str);
-        }
-        free(str);
-    }
-    return colstack->literal_mode;
-}
-
-int colorstackpop(int colstack_no)
-{
-    colstack_type *colstack = get_colstack(colstack_no);
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        if (colstack->page_used == 0) {
-            formatted_warning("pdf backend","pop empty color page stack %u",(unsigned int) colstack_no);
-            return colstack->literal_mode;
-        }
-        xfree(colstack->page_current);
-        colstack->page_current = colstack->page_stack[--colstack->page_used];
-        put_cstring_on_str_pool(colstack->page_current);
-    } else {
-        if (colstack->form_used == 0) {
-            formatted_warning("pdf backend","pop empty color form stack %u",(unsigned int) colstack_no);
-            return colstack->literal_mode;
-        }
-        xfree(colstack->form_current);
-        colstack->form_current = colstack->form_stack[--colstack->form_used];
-        put_cstring_on_str_pool(colstack->form_current);
-    }
-    return colstack->literal_mode;
-}
-
-void colorstackpagestart(void)
-{
-    int i, j;
-    colstack_type *colstack;
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        return;
-    }
-    for (i = 0; i < colstacks_used; i++) {
-        colstack = &colstacks[i];
-        for (j = 0; j < colstack->form_used; j++) {
-            xfree(colstack->form_stack[j]);
-        }
-        colstack->form_used = 0;
-        xfree(colstack->form_current);
-        if (colstack->form_init == NULL) {
-            colstack->form_current = NULL;
-        } else {
-            colstack->form_current = xstrdup(colstack->form_init);
-        }
-    }
-}
-
-int colorstackskippagestart(int colstack_no)
-{
-    colstack_type *colstack = get_colstack(colstack_no);
-    if (!colstack->page_start) {
-        return 1;
-    }
-    if (colstack->page_current == NULL) {
-        return 0;
-    }
-    if (strcmp(COLOR_DEFAULT, colstack->page_current) == 0) {
-        return 2;
-    }
-    return 0;
-}
-
-void pdf_out_colorstack(PDF pdf, halfword p)
-{
-    int old_setting;
-    str_number s;
-    int cmd = pdf_colorstack_cmd(p);
-    int stack_no = pdf_colorstack_stack(p);
-    int literal_mode = 0;
-    if (stack_no >= colorstackused()) {
-        formatted_warning("pdf backend","color stack %u is not initialized",(unsigned int) stack_no);
-        return;
-    }
-    switch (cmd) {
-        case colorstack_set:
-        case colorstack_push:
-            old_setting = selector;
-            selector = new_string;
-            show_token_list(token_link(pdf_colorstack_data(p)), null, -1);
-            selector = old_setting;
-            s = make_string();
-            if (cmd == colorstack_set)
-                literal_mode = colorstackset(stack_no, s);
-            else
-                literal_mode = colorstackpush(stack_no, s);
-            if (str_length(s) > 0)
-                pdf_literal(pdf, s, literal_mode, false);
-            flush_str(s);
-            return;
-            break;
-        case colorstack_pop:
-            literal_mode = colorstackpop(stack_no);
-            break;
-        case colorstack_current:
-            literal_mode = colorstackcurrent(stack_no);
-            break;
-        default:
-            break;
-    }
-    if (cur_length > 0) {
-        s = make_string();
-        pdf_literal(pdf, s, literal_mode, false);
-        flush_str(s);
-    }
-}
-
-void pdf_out_colorstack_startpage(PDF pdf)
-{
-    int start_status;
-    int literal_mode;
-    str_number s;
-    int i = 0;
-    int max = colorstackused();
-    while (i < max) {
-        start_status = colorstackskippagestart(i);
-        if (start_status == 0) {
-            literal_mode = colorstackcurrent(i);
-            if (cur_length > 0) {
-                s = make_string();
-                pdf_literal(pdf, s, literal_mode, false);
-                flush_str(s);
-            }
-        }
-        i++;
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfdest.w
@@ -0,0 +1,390 @@
+% pdfdest.w
+%
+% Copyright 2009-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"
+
+@ Here we implement subroutines for work with objects and related things. Some of
+them are used in former parts too, so we need to declare them forward.
+
+@c
+void init_dest_names(PDF pdf)
+{
+    pdf->dest_names_size = inf_dest_names_size;
+    pdf->dest_names = xmallocarray(dest_name_entry, inf_dest_names_size); /* will grow dynamically */
+}
+
+@ @c
+void append_dest_name(PDF pdf, char *s, int n)
+{
+    int a;
+    if (pdf->dest_names_ptr == sup_dest_names_size)
+        overflow("number of destination names (dest_names_size)",(unsigned) pdf->dest_names_size);
+    if (pdf->dest_names_ptr == pdf->dest_names_size) {
+        a = pdf->dest_names_size / 5;
+        if (pdf->dest_names_size < sup_dest_names_size - a)
+            pdf->dest_names_size = pdf->dest_names_size + a;
+        else
+            pdf->dest_names_size = sup_dest_names_size;
+        pdf->dest_names = xreallocarray(pdf->dest_names, dest_name_entry, (unsigned) pdf->dest_names_size);
+    }
+    pdf->dest_names[pdf->dest_names_ptr].objname = xstrdup(s);
+    pdf->dest_names[pdf->dest_names_ptr].objnum = n;
+    pdf->dest_names_ptr++;
+}
+
+@ When a destination is created we need to check whether another destination
+with the same identifier already exists and give a warning if needed.
+
+@c
+static void warn_dest_dup(int id, small_number byname)
+{
+    if (byname > 0) {
+        char *ss = tokenlist_to_cstring(id, true, NULL);
+        formatted_warning("pdf backend", "ignoring duplicate destination with the name '%s'",ss);
+    } else {
+        formatted_warning("pdf backend", "ignoring duplicate destination with the num '%d'",id);
+    }
+    /* no longer the annoying context */
+}
+
+@ @c
+void do_dest(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
+{
+    scaledpos pos = pdf->posstruct->pos;
+    scaled_whd alt_rule;
+    int k;
+    if (global_shipping_mode == SHIPPING_FORM)
+        normal_error("pdf backend", "destinations cannot be inside an xform");
+    if (doing_leaders)
+        return;
+    k = pdf_get_obj(pdf, obj_type_dest, pdf_dest_id(p), pdf_dest_named_id(p));
+    if (obj_dest_ptr(pdf, k) != null) {
+        warn_dest_dup(pdf_dest_id(p), (small_number) pdf_dest_named_id(p));
+        return;
+    }
+    obj_dest_ptr(pdf, k) = p;
+    addto_page_resources(pdf, obj_type_dest, k);
+    alt_rule.wd = width(p);
+    alt_rule.ht = height(p);
+    alt_rule.dp = depth(p);
+    /* the different branches for matrixused is somewhat strange and should always be used  */
+    switch (pdf_dest_type(p)) {
+    case pdf_dest_xyz:
+        if (matrixused())
+            set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
+        else {
+            pdf_ann_left(p) = pos.h;
+            pdf_ann_top(p) = pos.v;
+        }
+        break;
+    case pdf_dest_fith:
+    case pdf_dest_fitbh:
+        if (matrixused())
+            set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
+        else
+            pdf_ann_top(p) = pos.v;
+        break;
+    case pdf_dest_fitv:
+    case pdf_dest_fitbv:
+        if (matrixused())
+            set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
+        else
+            pdf_ann_left(p) = pos.h;
+        break;
+    case pdf_dest_fit:
+    case pdf_dest_fitb:
+        break;
+    case pdf_dest_fitr:
+        set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
+    }
+}
+
+@ @c
+void write_out_pdf_mark_destinations(PDF pdf)
+{
+    pdf_object_list *k;
+    if ((k = get_page_resources_list(pdf, obj_type_dest)) != NULL) {
+        while (k != NULL) {
+            if (is_obj_written(pdf, k->info)) {
+                normal_error("pdf backend","destination has been already written (this shouldn't happen)");
+            } else {
+                int i;
+                i = obj_dest_ptr(pdf, k->info);
+                pdf_begin_obj(pdf, k->info, OBJSTM_ALWAYS);
+                if (pdf_dest_named_id(i) > 0) {
+                    pdf_begin_dict(pdf);
+                    pdf_add_name(pdf, "D");
+                }
+                pdf_begin_array(pdf);
+                pdf_add_ref(pdf, pdf->last_page);
+                switch (pdf_dest_type(i)) {
+                    case pdf_dest_xyz:
+                        pdf_add_name(pdf, "XYZ");
+                        pdf_add_bp(pdf, pdf_ann_left(i));
+                        pdf_add_bp(pdf, pdf_ann_top(i));
+                        if (pdf_dest_xyz_zoom(i) == null) {
+                            pdf_add_null(pdf);
+                        } else {
+                            if (pdf->cave == 1)
+                                pdf_out(pdf, ' ');
+                            pdf_print_int(pdf, pdf_dest_xyz_zoom(i) / 1000);
+                            pdf_out(pdf, '.');
+                            pdf_print_int(pdf, (pdf_dest_xyz_zoom(i) % 1000));
+                            pdf->cave = 1;
+                        }
+                        break;
+                    case pdf_dest_fit:
+                        pdf_add_name(pdf, "Fit");
+                        break;
+                    case pdf_dest_fith:
+                        pdf_add_name(pdf, "FitH");
+                        pdf_add_bp(pdf, pdf_ann_top(i));
+                        break;
+                    case pdf_dest_fitv:
+                        pdf_add_name(pdf, "FitV");
+                        pdf_add_bp(pdf, pdf_ann_left(i));
+                        break;
+                    case pdf_dest_fitb:
+                        pdf_add_name(pdf, "FitB");
+                        break;
+                    case pdf_dest_fitbh:
+                        pdf_add_name(pdf, "FitBH");
+                        pdf_add_bp(pdf, pdf_ann_top(i));
+                        break;
+                    case pdf_dest_fitbv:
+                        pdf_add_name(pdf, "FitBV");
+                        pdf_add_bp(pdf, pdf_ann_left(i));
+                        break;
+                    case pdf_dest_fitr:
+                        pdf_add_name(pdf, "FitR");
+                        pdf_add_rect_spec(pdf, i);
+                        break;
+                    default:
+                        normal_error("pdf backend", "unknown dest type");
+                        break;
+                }
+                pdf_end_array(pdf);
+                if (pdf_dest_named_id(i) > 0)
+                    pdf_end_dict(pdf);
+                pdf_end_obj(pdf);
+            }
+            k = k->link;
+        }
+    }
+}
+
+@ @c
+void scan_pdfdest(PDF pdf)
+{
+    halfword q;
+    int k;
+    str_number i;
+    scaled_whd alt_rule;
+    q = cur_list.tail_field;
+    new_whatsit(pdf_dest_node);
+    if (scan_keyword("num")) {
+        scan_int();
+        if (cur_val <= 0)
+            normal_error("pdf backend", "num identifier must be positive");
+        if (cur_val > max_halfword)
+            normal_error("pdf backend", "number too big");
+        set_pdf_dest_id(cur_list.tail_field, cur_val);
+        set_pdf_dest_named_id(cur_list.tail_field, 0);
+    } else if (scan_keyword("name")) {
+        scan_toks(false, true);
+        set_pdf_dest_id(cur_list.tail_field, def_ref);
+        set_pdf_dest_named_id(cur_list.tail_field, 1);
+    } else {
+        normal_error("pdf backend", "identifier type missing");
+    }
+    if (scan_keyword("xyz")) {
+        set_pdf_dest_type(cur_list.tail_field, pdf_dest_xyz);
+        if (scan_keyword("zoom")) {
+            scan_int();
+            if (cur_val > max_halfword)
+                normal_error("pdf backend", "number too big");
+            set_pdf_dest_xyz_zoom(cur_list.tail_field, cur_val);
+        } else {
+            set_pdf_dest_xyz_zoom(cur_list.tail_field, null);
+        }
+    } else if (scan_keyword("fitbh")) {
+        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitbh);
+    } else if (scan_keyword("fitbv")) {
+        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitbv);
+    } else if (scan_keyword("fitb")) {
+        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitb);
+    } else if (scan_keyword("fith")) {
+        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fith);
+    } else if (scan_keyword("fitv")) {
+        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitv);
+    } else if (scan_keyword("fitr")) {
+        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitr);
+    } else if (scan_keyword("fit")) {
+        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fit);
+    } else {
+        normal_error("pdf backend", "destination type missing");
+    }
+    /* Scan an optional space */
+    get_x_token();
+    if (cur_cmd != spacer_cmd)
+        back_input();
+
+    if (pdf_dest_type(cur_list.tail_field) == pdf_dest_fitr) {
+        alt_rule = scan_alt_rule();     /* scans |<rule spec>| to |alt_rule| */
+        set_width(cur_list.tail_field, alt_rule.wd);
+        set_height(cur_list.tail_field, alt_rule.ht);
+        set_depth(cur_list.tail_field, alt_rule.dp);
+    }
+    if (pdf_dest_named_id(cur_list.tail_field) != 0) {
+        i = tokens_to_string(pdf_dest_id(cur_list.tail_field));
+        k = find_obj(pdf, obj_type_dest, i, true);
+        flush_str(i);
+    } else {
+        k = find_obj(pdf, obj_type_dest, pdf_dest_id(cur_list.tail_field), false);
+    }
+    if ((k != 0) && (obj_dest_ptr(pdf, k) != null)) {
+        warn_dest_dup(pdf_dest_id(cur_list.tail_field),(small_number) pdf_dest_named_id(cur_list.tail_field));
+        flush_node_list(cur_list.tail_field);
+        cur_list.tail_field = q;
+        vlink(q) = null;
+    }
+}
+
+@ sorts |dest_names| by names
+@c
+static int dest_cmp(const void *a, const void *b)
+{
+    dest_name_entry aa = *(const dest_name_entry *) a;
+    dest_name_entry bb = *(const dest_name_entry *) b;
+    return strcmp(aa.objname, bb.objname);
+}
+
+@ @c
+void sort_dest_names(PDF pdf)
+{
+    qsort(pdf->dest_names, (size_t) pdf->dest_names_ptr, sizeof(dest_name_entry), dest_cmp);
+}
+
+@ Output the name tree. The tree nature of the destination list forces the
+storing of intermediate data in |obj_info| and |obj_aux| fields, which
+is further uglified by the fact that |obj_tab| entries do not accept char
+pointers.
+
+@c
+int output_name_tree(PDF pdf)
+{
+    boolean is_names = true;    /* flag for name tree output: is it Names or Kids? */
+    int k = 0;                  /* index of current child of |l|; if |k < pdf_dest_names_ptr|
+                                   then this is pointer to |dest_names| array;
+                                   otherwise it is the pointer to |obj_tab| (object number) */
+    int b = 0;
+    int m, j, l;
+    int dests = 0;
+    int names_head = 0;
+    int names_tail = 0;
+    if (pdf->dest_names_ptr == 0) {
+        goto DONE;
+    }
+    sort_dest_names(pdf);
+    while (true) {
+        do {
+            l = pdf_create_obj(pdf, obj_type_others, 0);        /* create a new node */
+            if (b == 0)
+                b = l;          /* first in this level */
+            if (names_head == 0) {
+                names_head = l;
+                names_tail = l;
+            } else {
+                set_obj_link(pdf, names_tail, l);
+                names_tail = l;
+            }
+            set_obj_link(pdf, names_tail, 0);
+            /* Output the current node in this level */
+            pdf_begin_obj(pdf, l, OBJSTM_ALWAYS);
+            pdf_begin_dict(pdf);
+            j = 0;
+            if (is_names) {
+                set_obj_start(pdf, l, pdf->dest_names[k].objname);
+                pdf_add_name(pdf, "Names");
+                pdf_begin_array(pdf);
+                do {
+                    pdf_add_string(pdf, pdf->dest_names[k].objname);
+                    pdf_add_ref(pdf, pdf->dest_names[k].objnum);
+                    j++;
+                    k++;
+                } while (j != name_tree_kids_max && k != pdf->dest_names_ptr);
+                pdf_end_array(pdf);
+                set_obj_stop(pdf, l, pdf->dest_names[k - 1].objname);   /* for later */
+                if (k == pdf->dest_names_ptr) {
+                    is_names = false;
+                    k = names_head;
+                    b = 0;
+                }
+
+            } else {
+                set_obj_start(pdf, l, obj_start(pdf, k));
+                pdf_add_name(pdf, "Kids");
+                pdf_begin_array(pdf);
+                do {
+                    pdf_add_ref(pdf, k);
+                    set_obj_stop(pdf, l, obj_stop(pdf, k));
+                    k = obj_link(pdf, k);
+                    j++;
+                } while (j != name_tree_kids_max && k != b
+                         && obj_link(pdf, k) != 0);
+                pdf_end_array(pdf);
+                if (k == b)
+                    b = 0;
+            }
+            pdf_add_name(pdf, "Limits");
+            pdf_begin_array(pdf);
+            pdf_add_string(pdf, obj_start(pdf, l));
+            pdf_add_string(pdf, obj_stop(pdf, l));
+            pdf_end_array(pdf);
+            pdf_end_dict(pdf);
+            pdf_end_obj(pdf);
+        } while (b != 0);
+
+        if (k == l) {
+            dests = l;
+            goto DONE;
+        }
+    }
+  DONE:
+    if ((dests != 0) || (pdf_names_toks != null)) {
+        m = pdf_create_obj(pdf, obj_type_others, 0);
+        pdf_begin_obj(pdf, m, OBJSTM_ALWAYS);
+        pdf_begin_dict(pdf);
+        if (dests != 0)
+            pdf_dict_add_ref(pdf, "Dests", dests);
+        if (pdf_names_toks != null) {
+            pdf_print_toks(pdf, pdf_names_toks);
+            delete_token_ref(pdf_names_toks);
+            pdf_names_toks = null;
+        }
+        print_pdf_table_string(pdf, "names");
+        pdf_end_dict(pdf);
+        pdf_end_obj(pdf);
+        return m;
+    } else {
+        return 0;
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfdest.c
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    Here we implement subroutines for work with objects and related things. Some
-    of them are used in former parts too, so we need to declare them forward.
-    Memory will grow dynamically.
-
-*/
-
-void init_dest_names(PDF pdf)
-{
-    pdf->dest_names_size = inf_dest_names_size;
-    pdf->dest_names = xmallocarray(dest_name_entry, inf_dest_names_size);
-}
-
-void append_dest_name(PDF pdf, char *s, int n)
-{
-    int a;
-    if (pdf->dest_names_ptr == sup_dest_names_size)
-        overflow("number of destination names (dest_names_size)",(unsigned) pdf->dest_names_size);
-    if (pdf->dest_names_ptr == pdf->dest_names_size) {
-        a = pdf->dest_names_size / 5;
-        if (pdf->dest_names_size < sup_dest_names_size - a)
-            pdf->dest_names_size = pdf->dest_names_size + a;
-        else
-            pdf->dest_names_size = sup_dest_names_size;
-        pdf->dest_names = xreallocarray(pdf->dest_names, dest_name_entry, (unsigned) pdf->dest_names_size);
-    }
-    pdf->dest_names[pdf->dest_names_ptr].objname = xstrdup(s);
-    pdf->dest_names[pdf->dest_names_ptr].objnum = n;
-    pdf->dest_names_ptr++;
-}
-
-/*tex
-
-    When a destination is created we need to check whether another destination
-    with the same identifier already exists and give a warning if needed.
-
-*/
-
-static void warn_dest_dup(int id, small_number byname)
-{
-    if (byname > 0) {
-        char *ss = tokenlist_to_cstring(id, true, NULL);
-        formatted_warning("pdf backend", "ignoring duplicate destination with the name '%s'",ss);
-    } else {
-        formatted_warning("pdf backend", "ignoring duplicate destination with the num '%d'",id);
-    }
-}
-
-void do_dest(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
-{
-    scaledpos pos = pdf->posstruct->pos;
-    scaled_whd alt_rule;
-    int k;
-    if (global_shipping_mode == SHIPPING_FORM)
-        normal_error("pdf backend", "destinations cannot be inside an xform");
-    if (doing_leaders)
-        return;
-    k = pdf_get_obj(pdf, obj_type_dest, pdf_dest_id(p), pdf_dest_named_id(p));
-    if (obj_dest_ptr(pdf, k) != null) {
-        warn_dest_dup(pdf_dest_id(p), (small_number) pdf_dest_named_id(p));
-        return;
-    }
-    obj_dest_ptr(pdf, k) = p;
-    addto_page_resources(pdf, obj_type_dest, k);
-    alt_rule.wd = width(p);
-    alt_rule.ht = height(p);
-    alt_rule.dp = depth(p);
-    /*tex
-        The different branches for matrixused is somewhat strange and should
-        always be used
-    */
-    switch (pdf_dest_type(p)) {
-    case pdf_dest_xyz:
-        if (matrixused())
-            set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
-        else {
-            pdf_ann_left(p) = pos.h;
-            pdf_ann_top(p) = pos.v;
-        }
-        break;
-    case pdf_dest_fith:
-    case pdf_dest_fitbh:
-        if (matrixused())
-            set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
-        else
-            pdf_ann_top(p) = pos.v;
-        break;
-    case pdf_dest_fitv:
-    case pdf_dest_fitbv:
-        if (matrixused())
-            set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
-        else
-            pdf_ann_left(p) = pos.h;
-        break;
-    case pdf_dest_fit:
-    case pdf_dest_fitb:
-        break;
-    case pdf_dest_fitr:
-        set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
-    }
-}
-
-void write_out_pdf_mark_destinations(PDF pdf)
-{
-    pdf_object_list *k;
-    if ((k = get_page_resources_list(pdf, obj_type_dest)) != NULL) {
-        while (k != NULL) {
-            if (is_obj_written(pdf, k->info)) {
-                normal_error("pdf backend","destination has been already written (this shouldn't happen)");
-            } else {
-                int i;
-                i = obj_dest_ptr(pdf, k->info);
-                pdf_begin_obj(pdf, k->info, OBJSTM_ALWAYS);
-                if (pdf_dest_named_id(i) > 0) {
-                    pdf_begin_dict(pdf);
-                    pdf_add_name(pdf, "D");
-                }
-                pdf_begin_array(pdf);
-                pdf_add_ref(pdf, pdf->last_page);
-                switch (pdf_dest_type(i)) {
-                    case pdf_dest_xyz:
-                        pdf_add_name(pdf, "XYZ");
-                        pdf_add_bp(pdf, pdf_ann_left(i));
-                        pdf_add_bp(pdf, pdf_ann_top(i));
-                        if (pdf_dest_xyz_zoom(i) == null) {
-                            pdf_add_null(pdf);
-                        } else {
-                            pdf_check_space(pdf);
-                            pdf_print_int(pdf, pdf_dest_xyz_zoom(i) / 1000);
-                            pdf_out(pdf, '.');
-                            pdf_print_int(pdf, (pdf_dest_xyz_zoom(i) % 1000));
-                            pdf_set_space(pdf);
-                        }
-                        break;
-                    case pdf_dest_fit:
-                        pdf_add_name(pdf, "Fit");
-                        break;
-                    case pdf_dest_fith:
-                        pdf_add_name(pdf, "FitH");
-                        pdf_add_bp(pdf, pdf_ann_top(i));
-                        break;
-                    case pdf_dest_fitv:
-                        pdf_add_name(pdf, "FitV");
-                        pdf_add_bp(pdf, pdf_ann_left(i));
-                        break;
-                    case pdf_dest_fitb:
-                        pdf_add_name(pdf, "FitB");
-                        break;
-                    case pdf_dest_fitbh:
-                        pdf_add_name(pdf, "FitBH");
-                        pdf_add_bp(pdf, pdf_ann_top(i));
-                        break;
-                    case pdf_dest_fitbv:
-                        pdf_add_name(pdf, "FitBV");
-                        pdf_add_bp(pdf, pdf_ann_left(i));
-                        break;
-                    case pdf_dest_fitr:
-                        pdf_add_name(pdf, "FitR");
-                        pdf_add_rect_spec(pdf, i);
-                        break;
-                    default:
-                        normal_error("pdf backend", "unknown dest type");
-                        break;
-                }
-                pdf_end_array(pdf);
-                if (pdf_dest_named_id(i) > 0)
-                    pdf_end_dict(pdf);
-                pdf_end_obj(pdf);
-            }
-            k = k->link;
-        }
-    }
-}
-
-void scan_pdfdest(PDF pdf)
-{
-    halfword q;
-    int k;
-    str_number i;
-    scaled_whd alt_rule;
-    q = cur_list.tail_field;
-    new_whatsit(pdf_dest_node);
-    if (scan_keyword("num")) {
-        scan_int();
-        if (cur_val <= 0)
-            normal_error("pdf backend", "num identifier must be positive");
-        if (cur_val > max_halfword)
-            normal_error("pdf backend", "number too big");
-        set_pdf_dest_id(cur_list.tail_field, cur_val);
-        set_pdf_dest_named_id(cur_list.tail_field, 0);
-    } else if (scan_keyword("name")) {
-        scan_toks(false, true);
-        set_pdf_dest_id(cur_list.tail_field, def_ref);
-        set_pdf_dest_named_id(cur_list.tail_field, 1);
-    } else {
-        normal_error("pdf backend", "identifier type missing");
-    }
-    if (scan_keyword("xyz")) {
-        set_pdf_dest_type(cur_list.tail_field, pdf_dest_xyz);
-        if (scan_keyword("zoom")) {
-            scan_int();
-            if (cur_val > max_halfword)
-                normal_error("pdf backend", "number too big");
-            set_pdf_dest_xyz_zoom(cur_list.tail_field, cur_val);
-        } else {
-            set_pdf_dest_xyz_zoom(cur_list.tail_field, null);
-        }
-    } else if (scan_keyword("fitbh")) {
-        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitbh);
-    } else if (scan_keyword("fitbv")) {
-        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitbv);
-    } else if (scan_keyword("fitb")) {
-        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitb);
-    } else if (scan_keyword("fith")) {
-        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fith);
-    } else if (scan_keyword("fitv")) {
-        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitv);
-    } else if (scan_keyword("fitr")) {
-        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitr);
-    } else if (scan_keyword("fit")) {
-        set_pdf_dest_type(cur_list.tail_field, pdf_dest_fit);
-    } else {
-        normal_error("pdf backend", "destination type missing");
-    }
-    /*tex Scan an optional space. */
-    get_x_token();
-    if (cur_cmd != spacer_cmd)
-        back_input();
-    if (pdf_dest_type(cur_list.tail_field) == pdf_dest_fitr) {
-        alt_rule = scan_alt_rule();
-        set_width(cur_list.tail_field, alt_rule.wd);
-        set_height(cur_list.tail_field, alt_rule.ht);
-        set_depth(cur_list.tail_field, alt_rule.dp);
-    }
-    if (pdf_dest_named_id(cur_list.tail_field) != 0) {
-        i = tokens_to_string(pdf_dest_id(cur_list.tail_field));
-        k = find_obj(pdf, obj_type_dest, i, true);
-        flush_str(i);
-    } else {
-        k = find_obj(pdf, obj_type_dest, pdf_dest_id(cur_list.tail_field), false);
-    }
-    if ((k != 0) && (obj_dest_ptr(pdf, k) != null)) {
-        warn_dest_dup(pdf_dest_id(cur_list.tail_field),(small_number) pdf_dest_named_id(cur_list.tail_field));
-        flush_node_list(cur_list.tail_field);
-        cur_list.tail_field = q;
-        vlink(q) = null;
-    }
-}
-
-/*tex Sort |dest_names| by names: */
-
-static int dest_cmp(const void *a, const void *b)
-{
-    dest_name_entry aa = *(const dest_name_entry *) a;
-    dest_name_entry bb = *(const dest_name_entry *) b;
-    return strcmp(aa.objname, bb.objname);
-}
-
-void sort_dest_names(PDF pdf)
-{
-    qsort(pdf->dest_names, (size_t) pdf->dest_names_ptr, sizeof(dest_name_entry), dest_cmp);
-}
-
-/*tex
-
-    Output the name tree. The tree nature of the destination list forces the
-    storing of intermediate data in |obj_info| and |obj_aux| fields, which is
-    further uglified by the fact that |obj_tab| entries do not accept char
-    pointers.
-
-*/
-
-int output_name_tree(PDF pdf)
-{
-    /*tex A flag for name tree output: is it |/Names| or |/Kids|: */
-    boolean is_names = true;
-    /*tex
-        The index of current child of |l|; if |k < pdf_dest_names_ptr| then this
-        is pointer to |dest_names| array; otherwise it is the pointer to
-        |obj_tab| (object number).
-    */
-    int k = 0;
-    int b = 0;
-    int m, j, l;
-    int dests = 0;
-    int names_head = 0;
-    int names_tail = 0;
-    if (pdf->dest_names_ptr == 0) {
-        goto DONE;
-    }
-    sort_dest_names(pdf);
-    while (true) {
-        do {
-            /*tex Create a new node: */
-            l = pdf_create_obj(pdf, obj_type_others, 0);
-            if (b == 0) {
-                /*tex First in this level: */
-                b = l;
-            }
-            if (names_head == 0) {
-                names_head = l;
-                names_tail = l;
-            } else {
-                set_obj_link(pdf, names_tail, l);
-                names_tail = l;
-            }
-            set_obj_link(pdf, names_tail, 0);
-            /*tex Output the current node in this level. */
-            pdf_begin_obj(pdf, l, OBJSTM_ALWAYS);
-            pdf_begin_dict(pdf);
-            j = 0;
-            if (is_names) {
-                set_obj_start(pdf, l, pdf->dest_names[k].objname);
-                pdf_add_name(pdf, "Names");
-                pdf_begin_array(pdf);
-                do {
-                    pdf_add_string(pdf, pdf->dest_names[k].objname);
-                    pdf_add_ref(pdf, pdf->dest_names[k].objnum);
-                    j++;
-                    k++;
-                } while (j != name_tree_kids_max && k != pdf->dest_names_ptr);
-                pdf_end_array(pdf);
-                /*tex For later use: */
-                set_obj_stop(pdf, l, pdf->dest_names[k - 1].objname);
-                if (k == pdf->dest_names_ptr) {
-                    is_names = false;
-                    k = names_head;
-                    b = 0;
-                }
-            } else {
-                set_obj_start(pdf, l, obj_start(pdf, k));
-                pdf_add_name(pdf, "Kids");
-                pdf_begin_array(pdf);
-                do {
-                    pdf_add_ref(pdf, k);
-                    set_obj_stop(pdf, l, obj_stop(pdf, k));
-                    k = obj_link(pdf, k);
-                    j++;
-                } while (j != name_tree_kids_max && k != b
-                         && obj_link(pdf, k) != 0);
-                pdf_end_array(pdf);
-                if (k == b)
-                    b = 0;
-            }
-            pdf_add_name(pdf, "Limits");
-            pdf_begin_array(pdf);
-            pdf_add_string(pdf, obj_start(pdf, l));
-            pdf_add_string(pdf, obj_stop(pdf, l));
-            pdf_end_array(pdf);
-            pdf_end_dict(pdf);
-            pdf_end_obj(pdf);
-        } while (b != 0);
-        if (k == l) {
-            dests = l;
-            goto DONE;
-        }
-    }
-  DONE:
-    if ((dests != 0) || (pdf_names_toks != null)) {
-        m = pdf_create_obj(pdf, obj_type_others, 0);
-        pdf_begin_obj(pdf, m, OBJSTM_ALWAYS);
-        pdf_begin_dict(pdf);
-        if (dests != 0)
-            pdf_dict_add_ref(pdf, "Dests", dests);
-        if (pdf_names_toks != null) {
-            pdf_print_toks(pdf, pdf_names_toks);
-            delete_token_ref(pdf_names_toks);
-            pdf_names_toks = null;
-        }
-        print_pdf_table_string(pdf, "names");
-        pdf_end_dict(pdf);
-        pdf_end_obj(pdf);
-        return m;
-    } else {
-        return 0;
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdffont.w
@@ -0,0 +1,195 @@
+% pdffont.w
+%
+% Copyright 2009-2014 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\pdfTeX{pdf\TeX}
+
+@ @c
+
+#include "ptexlib.h"
+
+@ As \pdfTeX{} should also act as a back-end driver, it needs to support virtual
+fonts too. Information about virtual fonts can be found in the source of some
+\.{DVI}-related programs.
+
+Whenever we want to write out a character in a font to PDF output, we
+should check whether the used character is a virtual or real character.
+The |has_packet()| C macro checks for this condition.
+
+@ The following code typesets a character to PDF output
+
+@c
+scaled_whd output_one_char(PDF pdf, halfword p)
+{
+    internal_font_number f = font(p);
+    int c = character(p);
+    int ex_glyph = ex_glyph(p)/1000;
+    scaled_whd ci = get_charinfo_whd(f, c); /* the real width, height and depth of the character */
+    if (!(char_exists(f,c))) {
+        lua_glyph_not_found_callback(f,c);
+        /* char_warning(f,c); */
+        return ci;
+    }
+    ci.wd = ext_xn_over_d(ci.wd, 1000000 + ex_glyph(p), 1000000);
+    switch (pdf->posstruct->dir) {
+        case dir_TLT:
+            break;
+        case dir_TRT:
+            pos_left(ci.wd);
+            break;
+        case dir_LTL:
+            pos_down(ci.ht);
+            pos_left(ci.wd);
+            break;
+        case dir_RTT:
+            pos_down(ci.ht);
+            pos_left(ci.wd / 2);
+            break;
+        default:
+            formatted_warning("pdf backend","ignoring bad dir %i when outputting a character",pdf->posstruct->dir);
+    }
+    if (has_packet(f, c)) {
+        do_vf_packet(pdf, f, c, ex_glyph);
+    } else {
+        /* |pdf_place_glyph(pdf, f, c, ex_glyph);| */
+        backend_out[glyph_node] (pdf, f, c, ex_glyph);
+    }
+    return ci;
+}
+
+@ Mark |f| as a used font; set |font_used(f)|, |font_size(f)| and |pdf_font_num(f)|
+@c
+static void pdf_use_font(internal_font_number f, int fontnum)
+{
+    set_font_used(f, true);
+    if ((fontnum > 0) || ((fontnum < 0) && (pdf_font_num(-fontnum) > 0))) {
+        set_pdf_font_num(f, fontnum);
+    } else {
+        normal_error("pdf backend","bad font id");
+    }
+}
+
+@ To set PDF font we need to find out fonts with the same name, because \TeX\ can
+load the same font several times for various sizes. For such fonts we define only
+one font resource. The array |pdf_font_num| holds the object number of font
+resource. A negative value of an entry of |pdf_font_num| indicates that the
+corresponding font shares the font resource with the font
+
+@c
+#define same(n,f,k) (n(f) != NULL && n(k) != NULL && strcmp(n(f), n(k)) == 0)
+
+/*
+    For some lua-loaded (for instance AFM) fonts, it is normal to have
+    a zero cidregistry,  and such fonts do not have a fontmap entry yet
+    at this point, so the test should use the other branch
+*/
+
+static boolean font_shareable(internal_font_number f, internal_font_number k)
+{
+    if (font_cidregistry(f) == NULL && font_cidregistry(k) == NULL && font_encodingbytes(f) != 2 && font_encodingbytes(k) != 2) {
+        if (font_map(k) != NULL && font_map(f) != NULL && (same(font_name, k, f))) {
+            return 1;
+        }
+    } else if ((same(font_filename, k, f) && same(font_fullname, k, f))) {
+        return 1;
+    }
+    return 0;
+}
+
+@ create a font object
+@c
+void pdf_init_font(PDF pdf, internal_font_number f)
+{
+    internal_font_number k;
+    fm_entry *fm;
+    int i, l;
+    if (font_used(f)) {
+        formatted_error("pdf backend","font %i gets initialized twice",(int) f);
+    }
+    /*
+        check whether |f| can share the font object with some |k|: we have 2 cases
+        here: 1) |f| and |k| have the same tfm name (so they have been loaded at
+        different sizes, eg 'cmr10' and 'cmr10 at 11pt'); 2) |f| has been auto
+        expanded from |k|
+
+        take over slant and extend from map entry, if not already set;
+        this should also be the only place where getfontmap() may be called.
+    */
+    fm = getfontmap(font_name(f));
+    if (font_map(f) == NULL && fm != NULL) {
+        font_map(f) = fm;
+        if (is_slantset(fm))
+            font_slant(f) = fm->slant;
+        if (is_extendset(fm))
+            font_extend(f) = fm->extend;
+    }
+    i = pdf->head_tab[obj_type_font];
+    while (i != 0) {
+        k = obj_info(pdf, i);
+        if (font_shareable(f, k)) {
+            if (pdf_font_num(k) < 0)
+                pdf_use_font(f, pdf_font_num(k));
+            else
+                pdf_use_font(f, -k);
+            return;
+        }
+        i = obj_link(pdf, i);
+    }
+    /* create a new font object for |f| */
+    l = pdf_create_obj(pdf, obj_type_font, f);
+    pdf_use_font(f, l);
+}
+
+@ set the actual font on PDF page; sets |ff| to the tfm number of the base font
+sharing the font object with |f|; |ff| is either |f| itself (then it is its own
+base font), or some font with the same tfm name at different size and/or
+expansion.
+
+@c
+internal_font_number pdf_set_font(PDF pdf, internal_font_number f)
+{
+    int ff; /* for use with |set_ff| */
+    if (!font_used(f))
+        pdf_init_font(pdf, f);
+    ff = pdf_font_num(f) < 0 ? -pdf_font_num(f) : f; /* aka |set_ff(f)| */
+    addto_page_resources(pdf, obj_type_font, pdf_font_num(ff));
+    return ff;
+}
+
+@ @c
+void pdf_include_chars(PDF pdf)
+{
+    str_number s;
+    unsigned char *k, *j; /* running index */
+    internal_font_number f;
+    scan_font_ident();
+    f = cur_val;
+    if (f == null_font)
+        normal_error("pdf backend", "invalid font identifier for 'includechars'");
+    pdf_check_vf(cur_val);
+    if (!font_used(f))
+        pdf_init_font(pdf, f);
+    scan_toks(false, true);
+    s = tokens_to_string(def_ref);
+    delete_token_ref(def_ref);
+    j = str_string(s) + str_length(s);
+    for (k = str_string(s); k < j; k++) {
+        pdf_mark_char(f, *k);
+    }
+    flush_str(s);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdffont.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
-
-Copyright 2009-2014 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    As \LUATEX\ should also act as a back-end driver, it needs to support virtual
-    fonts too. Information about virtual fonts can be found in the source of some
-    \DVI-related programs.
-
-    Whenever we want to write out a character in a font to \PDF\ output, we
-    should check whether the used character is a virtual or real character. The
-    |has_packet| C macro checks for this condition.
-
-    The following code typesets a character to \PDF\ output
-
-*/
-
-scaled_whd output_one_char(PDF pdf, halfword p)
-{
-    internal_font_number f = font(p);
-    int c = character(p);
-    int ex_glyph = ex_glyph(p)/1000;
-    /*tex The real width, height and depth of the character: */
-    scaled_whd ci = get_charinfo_whd(f, c);
-    if (!(char_exists(f,c))) {
-        lua_glyph_not_found_callback(f,c);
-        /*tex Not here |char_warning(f,c);| */
-        return ci;
-    }
-    ci.wd = ext_xn_over_d(ci.wd, 1000000 + ex_glyph(p), 1000000);
-    switch (pdf->posstruct->dir) {
-        case dir_TLT:
-            break;
-        case dir_TRT:
-            pos_left(ci.wd);
-            break;
-        case dir_LTL:
-            pos_down(ci.ht);
-            pos_left(ci.wd);
-            break;
-        case dir_RTT:
-            pos_down(ci.ht);
-            pos_left(ci.wd / 2);
-            break;
-        default:
-            formatted_warning("pdf backend","ignoring bad dir %i when outputting a character",pdf->posstruct->dir);
-    }
-    if (has_packet(f, c)) {
-        do_vf_packet(pdf, f, c, ex_glyph);
-    } else {
-        backend_out[glyph_node] (pdf, f, c, ex_glyph);
-    }
-    return ci;
-}
-
-/*tex
-
-    Mark |f| as a used font; set |font_used(f)|, |font_size(f)| and
-    |pdf_font_num(f)|.
-
-*/
-
-static void pdf_use_font(internal_font_number f, int fontnum)
-{
-    set_font_used(f, true);
-    if ((fontnum > 0) || ((fontnum < 0) && (pdf_font_num(-fontnum) > 0))) {
-        set_pdf_font_num(f, fontnum);
-    } else {
-        normal_error("pdf backend","bad font id");
-    }
-}
-
-/*tex
-
-    To set PDF font we need to find out fonts with the same name, because \TeX\
-    can load the same font several times for various sizes. For such fonts we
-    define only one font resource. The array |pdf_font_num| holds the object
-    number of font resource. A negative value of an entry of |pdf_font_num|
-    indicates that the corresponding font shares the font resource with the font.
-
-*/
-
-#define same(n,f,k) (n(f) != NULL && n(k) != NULL && strcmp(n(f), n(k)) == 0)
-
-/*tex
-
-    For some \LUA-loaded (for instance \AFM) fonts, it is normal to have a zero
-    cidregistry, and such fonts do not have a fontmap entry yet at this point, so
-    the test should use the other branch.
-
-*/
-
-static boolean font_shareable(internal_font_number f, internal_font_number k)
-{
-    if (font_cidregistry(f) == NULL && font_cidregistry(k) == NULL && font_encodingbytes(f) != 2 && font_encodingbytes(k) != 2) {
-        if (font_map(k) != NULL && font_map(f) != NULL && (same(font_name, k, f))) {
-            return 1;
-        }
-    } else if ((same(font_filename, k, f) && same(font_fullname, k, f))) {
-        return 1;
-    }
-    return 0;
-}
-
-/*tex
-
-    We will create a font object. We check whether |f| can share the font object
-    with some |k|: we have 2 cases here: |f| and |k| have the same tfm name, so
-    they have been loaded at different sizes, eg 'cmr10' and 'cmr10 at 11pt'.
-
-    We optionally take over slant and extend from map entry, if not already set;
-    this should also be the only place where getfontmap() may be called.
-
-    Beware, \LUATEX\ is different from \PDFTEX\ in dealing with expanded and
-    slanted fonts and expansion and protrusion as it will use the same font but a
-    different transformation which is way more efficient and also cleaner.
-
-*/
-
-void pdf_init_font(PDF pdf, internal_font_number f)
-{
-    internal_font_number k;
-    fm_entry *fm;
-    int i, l;
-    if (font_used(f)) {
-        formatted_error("pdf backend","font %i gets initialized twice",(int) f);
-    }
-    fm = getfontmap(font_name(f));
-    if (font_map(f) == NULL && fm != NULL) {
-        font_map(f) = fm;
-        if (is_slantset(fm))
-            font_slant(f) = fm->slant;
-        if (is_extendset(fm))
-            font_extend(f) = fm->extend;
-    }
-    i = pdf->head_tab[obj_type_font];
-    while (i != 0) {
-        k = obj_info(pdf, i);
-        if (font_shareable(f, k)) {
-            if (pdf_font_num(k) < 0)
-                pdf_use_font(f, pdf_font_num(k));
-            else
-                pdf_use_font(f, -k);
-            return;
-        }
-        i = obj_link(pdf, i);
-    }
-    /*tex Create a new font object for |f|: */
-    l = pdf_create_obj(pdf, obj_type_font, f);
-    pdf_use_font(f, l);
-}
-
-/*tex
-
-    Set the actual font on PDF page; sets |ff| to the tfm number of the base font
-    sharing the font object with |f|; |ff| is either |f| itself (then it is its
-    own base font), or some font with the same tfm name at different size and/or
-    expansion.
-
-*/
-
-internal_font_number pdf_set_font(PDF pdf, internal_font_number f)
-{
-    /*tex For use with |set_ff|: */
-    int ff;
-    if (!font_used(f))
-        pdf_init_font(pdf, f);
-    /*tex Also known as |set_ff(f)|: */
-    ff = pdf_font_num(f) < 0 ? -pdf_font_num(f) : f;
-    addto_page_resources(pdf, obj_type_font, pdf_font_num(ff));
-    return ff;
-}
-
-void pdf_include_chars(PDF pdf)
-{
-    str_number s;
-    unsigned char *k, *j;
-    internal_font_number f;
-    scan_font_ident();
-    f = cur_val;
-    if (f == null_font)
-        normal_error("pdf backend", "invalid font identifier for 'includechars'");
- /* pdf_check_vf(c); */
-    if (!font_used(f))
-        pdf_init_font(pdf, f);
-    scan_toks(false, true);
-    s = tokens_to_string(def_ref);
-    delete_token_ref(def_ref);
-    j = str_string(s) + str_length(s);
-    for (k = str_string(s); k < j; k++) {
-        pdf_mark_char(f, *k);
-    }
-    flush_str(s);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfgen.w
@@ -0,0 +1,2520 @@
+% pdfgen.w
+%
+% Copyright 2009-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"
+
+@ @c
+#include <kpathsea/c-dir.h>
+#include <kpathsea/c-ctype.h>
+#include "lua/luatex-api.h"
+#include "md5.h"
+
+#define check_nprintf(size_get, size_want) \
+    if ((unsigned)(size_get) >= (unsigned)(size_want)) \
+        formatted_error("pdf backend","snprintf() failed in file %s at line %d", __FILE__, __LINE__);
+
+PDF static_pdf = NULL;
+
+@ commandline interface
+
+@c
+int output_mode_used;
+int output_mode_option;
+int output_mode_value;
+int draft_mode_option;
+int draft_mode_value;
+
+halfword pdf_info_toks;                              /* additional keys of Info dictionary */
+halfword pdf_catalog_toks;                           /* additional keys of Catalog dictionary */
+halfword pdf_catalog_openaction;
+halfword pdf_names_toks;                             /* additional keys of Names dictionary */
+halfword pdf_trailer_toks;                           /* additional keys of Trailer dictionary */
+shipping_mode_e global_shipping_mode = NOT_SHIPPING; /* set to |shipping_mode| when |ship_out| starts */
+
+
+@ Create a new buffer |strbuf_s| of size |size| and maximum allowed size |limit|.
+Initialize it and set |p| to begin of data.
+
+@c
+strbuf_s *new_strbuf(size_t size, size_t limit)
+{
+    strbuf_s *b;
+    b = xtalloc(1, strbuf_s);
+    b->size = size;
+    b->limit = limit;
+    if (size > 0)
+        b->p = b->data = xtalloc(b->size, unsigned char);
+    else
+        b->p = b->data = NULL; /* for other alloc */
+    return b;
+}
+
+@ Check that |n| bytes more fit into buffer; increase it if required.
+
+@c
+static void strbuf_room(strbuf_s * b, size_t n)
+{
+    unsigned int a;
+    size_t l = (size_t) (b->p - b->data);
+    if (n > b->limit - l)
+        overflow("PDF buffer", (unsigned) b->size);
+    if (n + l > b->size) {
+        a = (unsigned int) (b->size >> 2);
+        if (n + l > b->size + a)
+            b->size = n + l;
+        else if (b->size < b->limit - a)
+            b->size = b->size + a;
+        else
+            b->size = b->limit;
+        b->data = xreallocarray(b->data, unsigned char, (unsigned) b->size);
+        b->p = b->data + l;
+    }
+}
+
+@ Seek to position |offset| within buffer. Position must be valid.
+
+@c
+void strbuf_seek(strbuf_s * b, off_t offset)
+{
+    b->p = b->data + offset;
+}
+
+@ Get the current buffer fill level, the number of characters.
+
+@c
+size_t strbuf_offset(strbuf_s * b)
+{
+    return (size_t) (b->p - b->data);
+}
+
+@ Put one character into buffer. Make room before if needed.
+
+@c
+void strbuf_putchar(strbuf_s * b, unsigned char c)
+{
+    if ((size_t) (b->p - b->data + 1) > b->size)
+        strbuf_room(b, 1);
+    *b->p++ = c;
+}
+
+@ Dump filled buffer part to PDF.
+
+@c
+void strbuf_flush(PDF pdf, strbuf_s * b)
+{
+    pdf_out_block(pdf, (const char *) b->data, strbuf_offset(b));
+    strbuf_seek(b, 0);
+}
+
+@ Free all dynamically allocated buffer structures.
+
+@c
+void strbuf_free(strbuf_s * b)
+{
+    xfree(b->data);
+    xfree(b);
+}
+
+@ |init_pdf_struct()| is called early, only once, from maincontrol.w
+
+@c
+PDF init_pdf_struct(PDF pdf)
+{
+    os_struct *os;
+    pdf = xtalloc(1, pdf_output_file);
+    memset(pdf, 0, sizeof(pdf_output_file));
+    pdf->job_name = makecstring(job_name);
+
+    output_mode_used = OMODE_NONE;  /* will be set by |fix_o_mode()| */
+    pdf->o_mode = output_mode_used; /* used by synctex, we need to use output_mode_used there */
+    pdf->o_state = ST_INITIAL;
+
+    /* init PDF and object stream writing */
+    pdf->os = os = xtalloc(1, os_struct);
+    memset(pdf->os, 0, sizeof(os_struct));
+    os->buf[PDFOUT_BUF] = new_strbuf(inf_pdfout_buf_size, sup_pdfout_buf_size);
+    os->buf[OBJSTM_BUF] = new_strbuf(inf_objstm_buf_size, sup_objstm_buf_size);
+    os->obj = xtalloc(PDF_OS_MAX_OBJS, os_obj_data);
+    os->cur_objstm = 0;
+
+    os->curbuf = PDFOUT_BUF;
+    pdf->buf = os->buf[os->curbuf];
+    /*
+        Later  ttf_seek_outbuf(TABDIR_OFF + n * 4 * TTF_ULONG_SIZE); in ttf_init_font
+        we need 236 bytes, so we start with 256 bytes as in pdftex.
+    */
+    pdf->fb = new_strbuf(256, 100000000);
+
+    pdf->stream_deflate = false;
+    pdf->stream_writing = false;
+
+    /*
+        Sometimes it is neccesary to allocate memory for PDF output that cannot be
+        deallocated then, so we use |mem| for this purpose.
+    */
+    pdf->mem_size = inf_pdf_mem_size;
+    pdf->mem = xtalloc(pdf->mem_size, int);
+    /*
+        The first word is not used so we can use zero as a value for testing
+        whether a pointer to |mem| is valid.
+    */
+    pdf->mem_ptr = 1;
+
+    pdf->pstruct = NULL;
+
+    pdf->posstruct = xtalloc(1, posstructure);
+    pdf->posstruct->pos.h = 0;
+    pdf->posstruct->pos.v = 0;
+    pdf->posstruct->dir = dir_TLT;
+
+    /* allocated size of |obj_tab| array */
+    pdf->obj_tab_size = (unsigned) inf_obj_tab_size;
+    pdf->obj_tab = xtalloc(pdf->obj_tab_size + 1, obj_entry);
+    memset(pdf->obj_tab, 0, sizeof(obj_entry));
+
+    pdf->minor_version = -1;
+    pdf->major_version = -1;
+    pdf->decimal_digits = 4;
+    pdf->gamma = 65536;
+    pdf->image_gamma = 65536;
+    pdf->image_hicolor = 1;
+    pdf->image_apply_gamma = 0;
+    pdf->objcompresslevel = 0;
+    pdf->compress_level = 0;
+    pdf->draftmode = 0;
+    pdf->inclusion_copy_font = 1;
+    pdf->pk_resolution = 0;
+    pdf->pk_fixed_dpi = 0;
+    pdf->pk_scale_factor = 0;
+
+    init_dest_names(pdf);
+    pdf->page_resources = NULL;
+
+    init_pdf_pagecalculations(pdf);
+
+    pdf->vfstruct = new_vfstruct();
+
+    return pdf;
+}
+
+@  We use |pdf_get_mem| to allocate memory in |mem|.
+
+@c
+int pdf_get_mem(PDF pdf, int s)
+{
+    int a;
+    int ret;
+    if (s > sup_pdf_mem_size - pdf->mem_ptr)
+        overflow("pdf memory size (pdf_mem_size)", (unsigned) pdf->mem_size);
+    if (pdf->mem_ptr + s > pdf->mem_size) {
+        a = pdf->mem_size >> 2;
+        if (pdf->mem_ptr + s > pdf->mem_size + a) {
+            pdf->mem_size = pdf->mem_ptr + s;
+        } else if (pdf->mem_size < sup_pdf_mem_size - a) {
+            pdf->mem_size = pdf->mem_size + a;
+        } else {
+            pdf->mem_size = sup_pdf_mem_size;
+        }
+        pdf->mem = xreallocarray(pdf->mem, int, (unsigned) pdf->mem_size);
+    }
+    ret = pdf->mem_ptr;
+    pdf->mem_ptr = pdf->mem_ptr + s;
+    return ret;
+}
+
+@ there are defined in |luatex-api.h|, so we need |luatex-api.c|:
+
+@c
+output_mode get_o_mode(void)
+{
+    output_mode o_mode;
+    if (output_mode_par > 0) {
+        o_mode = OMODE_PDF;
+    } else
+        o_mode = OMODE_DVI;
+    return o_mode;
+}
+
+void fix_o_mode(void)
+{
+    output_mode o_mode = get_o_mode();
+    if (output_mode_used == OMODE_NONE) {
+        output_mode_used = o_mode;
+        static_pdf->o_mode = output_mode_used; /* used by synctex, we need to use output_mode_used there */
+    } else if (output_mode_used != o_mode) {
+        normal_error("pdf backend", "\\outputmode can only be changed before anything is written to the output");
+    }
+}
+
+@ This ensures that |pdfmajorversion| and |pdfminorversion| are set only before any
+bytes have been written to the generated \.{PDF} file. Here also all variables for
+\.{PDF} output are initialized, the \.{PDF} file is opened by |ensure_pdf_open|, and
+the \.{PDF} header is written.
+
+@c
+void fix_pdf_version(PDF pdf)
+{
+    if (pdf->major_version < 0) { /* unset */
+        if (pdf_major_version == 0) {
+            normal_warning("pdf backend","unset major version, using 1 instead");
+            pdf->major_version = 1;
+        } else if ((pdf_major_version < 0) || (pdf_major_version > 2)) {
+            formatted_warning("pdf backend","illegal major version %d, using 1 instead",pdf_major_version);
+            pdf->major_version = 1;
+        } else {
+            pdf->major_version = pdf_major_version;
+        }
+    } else if (pdf->major_version != pdf_major_version) {
+        normal_warning("pdf backend", "the major version cannot be changed after data is written to the PDF file");
+    }
+    if (pdf->minor_version < 0) { /* unset */
+        if ((pdf_minor_version < 0) || (pdf_minor_version > 9)) {
+            formatted_warning("pdf backend","illegal minor version %d, using 4 instead",pdf_minor_version);
+            pdf->minor_version = 4;
+        } else {
+            pdf->minor_version = pdf_minor_version;
+        }
+    } else if (pdf->minor_version != pdf_minor_version) {
+        normal_warning("pdf backend", "minorversion cannot be changed after data is written to the PDF file");
+    }
+}
+
+static void fix_pdf_draftmode(PDF pdf)
+{
+    if (pdf->draftmode != draft_mode_par)
+        normal_warning("pdf backend", "draftmode cannot be changed after data is written to the PDF file");
+    if (pdf->draftmode != 0) {
+        pdf->compress_level = 0;        /* re-fix it, might have been changed inbetween */
+        pdf->objcompresslevel = 0;
+    }
+}
+
+@ @c
+#define ZIP_BUF_SIZE  32768
+
+#define check_err(f, fn) \
+    if (f != Z_OK) \
+        formatted_error("pdf backend","zlib %s() failed (error code %d)", fn, f)
+
+@ @c
+static void write_zip(PDF pdf)
+{
+    int flush, err = Z_OK;
+    uInt zip_len;
+    strbuf_s *buf = pdf->buf;
+    z_stream *s = pdf->c_stream;
+    boolean finish = pdf->zip_write_state == ZIP_FINISH;
+    if (pdf->stream_length == 0) {
+        if (s == NULL) {
+            s = pdf->c_stream = xtalloc(1, z_stream);
+            s->zalloc = (alloc_func) 0;
+            s->zfree = (free_func) 0;
+            s->opaque = (voidpf) 0;
+            check_err(deflateInit(s, pdf->compress_level), "deflateInit");
+            pdf->zipbuf = xtalloc(ZIP_BUF_SIZE, char);
+        } else
+            check_err(deflateReset(s), "deflateReset");
+        s->next_out = (Bytef *) pdf->zipbuf;
+        s->avail_out = ZIP_BUF_SIZE;
+    }
+    s->next_in = buf->data;
+    s->avail_in = (uInt) (buf->p - buf->data);
+    while (true) {
+        if (s->avail_out == 0 || (finish && s->avail_out < ZIP_BUF_SIZE)) {
+            zip_len = ZIP_BUF_SIZE - s->avail_out;
+            pdf->gone += (off_t) xfwrite(pdf->zipbuf, 1, zip_len, pdf->file);
+            pdf->last_byte = pdf->zipbuf[zip_len - 1];
+            s->next_out = (Bytef *) pdf->zipbuf;
+            s->avail_out = ZIP_BUF_SIZE;
+        }
+        if (finish) {
+            if (err == Z_STREAM_END) {
+                xfflush(pdf->file);
+                pdf->zip_write_state = NO_ZIP;
+                break;
+            }
+            flush = Z_FINISH;
+        } else {
+            if (s->avail_in == 0)
+                break;
+            flush = Z_NO_FLUSH;
+        }
+        err = deflate(s, flush);
+        if (err != Z_OK && err != Z_STREAM_END)
+            formatted_error("pdf backend","zlib deflate() failed (error code %d)", err);
+    }
+    pdf->stream_length = (off_t) s->total_out;
+}
+
+@ @c
+void zip_free(PDF pdf)
+{
+    if (pdf->zipbuf != NULL) {
+        check_err(deflateEnd(pdf->c_stream), "deflateEnd");
+        xfree(pdf->zipbuf);
+    }
+    xfree(pdf->c_stream);
+}
+
+@ @c
+static void write_nozip(PDF pdf)
+{
+    strbuf_s *buf = pdf->buf;
+    size_t l = strbuf_offset(buf);
+    if (l == 0)
+        return;
+    pdf->stream_length = pdf_offset(pdf) - pdf->save_offset;
+    pdf->gone += (off_t) xfwrite((char *) buf->data, sizeof(char), l, pdf->file);
+    pdf->last_byte = *(buf->p - 1);
+}
+
+@ The PDF buffer is flushed by calling |pdf_flush|, which checks the
+variable |zip_write_state| and will compress the buffer before flushing if
+neccesary. We call |pdf_begin_stream| to begin a stream  and |pdf_end_stream|
+to finish it. The stream contents will be compressed if compression is turn on.
+
+@c
+void pdf_flush(PDF pdf)
+{
+    os_struct *os = pdf->os;
+    off_t saved_pdf_gone = pdf->gone;
+    switch (os->curbuf) {
+        case PDFOUT_BUF:
+            if (pdf->draftmode == 0) {
+                switch (pdf->zip_write_state) {
+                    case NO_ZIP:
+                        write_nozip(pdf);
+                        break;
+                    case ZIP_WRITING:
+                    case ZIP_FINISH:
+                        write_zip(pdf);
+                        break;
+                    default:
+                        normal_error("pdf backend", "bad zip state");
+                }
+            } else
+                pdf->zip_write_state = NO_ZIP;
+            strbuf_seek(pdf->buf, 0);
+            if (saved_pdf_gone > pdf->gone)
+                normal_error("pdf backend", "file size exceeds architectural limits (pdf_gone wraps around)");
+            break;
+        case OBJSTM_BUF:
+            break;
+        default:
+            normal_error("pdf backend", "bad buffer state");
+    }
+}
+
+@ @c
+static void pdf_buffer_select(PDF pdf, buffer_e buf)
+{
+    os_struct *os = pdf->os;
+    if (pdf->os_enable && buf == OBJSTM_BUF)
+        os->curbuf = OBJSTM_BUF; /* switch to object stream */
+    else
+        os->curbuf = PDFOUT_BUF; /* switch to PDF stream */
+    pdf->buf = os->buf[pdf->os->curbuf];
+}
+
+@ create new \.{/ObjStm} object if required, and set up cross reference info
+
+@c
+static void pdf_prepare_obj(PDF pdf, int k, int pdf_os_threshold)
+{
+    os_struct *os = pdf->os;
+    strbuf_s *obuf = os->buf[OBJSTM_BUF];
+    if (pdf->objcompresslevel >= pdf_os_threshold)
+        pdf_buffer_select(pdf, OBJSTM_BUF);
+    else
+        pdf_buffer_select(pdf, PDFOUT_BUF);
+    switch (os->curbuf) {
+        case PDFOUT_BUF:
+            obj_offset(pdf, k) = pdf_offset(pdf);
+            obj_os_idx(pdf, k) = PDF_OS_MAX_OBJS;   /* mark it as not included in any ObjStm */
+            break;
+        case OBJSTM_BUF:
+            if (os->cur_objstm == 0) {
+                os->cur_objstm =
+                    (unsigned int) pdf_create_obj(pdf, obj_type_objstm, 0);
+                os->idx = 0;
+                obuf->p = obuf->data;       /* start fresh object stream */
+                os->ostm_ctr++;     /* only for statistics */
+            }
+            obj_os_idx(pdf, k) = (int) os->idx;
+            obj_os_objnum(pdf, k) = (int) os->cur_objstm;
+            os->obj[os->idx].num = k;
+            os->obj[os->idx].off = obuf->p - obuf->data;
+            break;
+        default:
+            normal_error("pdf backend", "bad object state");
+    }
+}
+
+@* Low-level buffer checkers.
+
+@ Set the active buffer pointer. Make sure that there are at least |n| bytes free
+in that buffer, flush if needed.
+
+@c
+inline void pdf_room(PDF pdf, int n)
+{
+    strbuf_s *buf = pdf->buf;
+    if ((size_t) (n + buf->p - buf->data) <= buf->size)
+        return;
+    if (pdf->os->curbuf == PDFOUT_BUF) {
+        if ((size_t) n > buf->size)
+            overflow("PDF output buffer", (unsigned) buf->size);
+        if ((size_t) (n + buf->p - buf->data) < buf->limit)
+            strbuf_room(buf, (size_t) n);
+        else
+            pdf_flush(pdf);
+    } else {
+        strbuf_room(buf, (size_t) n);
+    }
+}
+
+@ @c
+void pdf_out_block(PDF pdf, const char *s, size_t n)
+{
+    size_t l;
+    strbuf_s *buf = pdf->buf;
+    do {
+        l = n;
+        if (l > buf->size)
+            l = buf->size;
+        pdf_room(pdf, (int) l);
+        (void) memcpy(buf->p, s, l);
+        buf->p += l;
+        s += l;
+        n -= l;
+    } while (n > 0);
+}
+
+@ @c
+__attribute__ ((format(printf, 2, 3)))
+void pdf_printf(PDF pdf, const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    if (pdf->printf_buf == NULL) {
+        pdf->printf_buf = xtalloc(PRINTF_BUF_SIZE, char);
+    }
+    (void) vsnprintf(pdf->printf_buf, PRINTF_BUF_SIZE, fmt, args);
+    pdf_puts(pdf, pdf->printf_buf);
+    va_end(args);
+}
+
+@ print out a string to PDF buffer
+
+@c
+void pdf_print(PDF pdf, str_number s)
+{
+    const char *ss;
+    size_t l;
+    if (s >= STRING_OFFSET) {
+        ss = (const char *) str_string(s);
+        l = str_length(s);
+        pdf_out_block(pdf, ss, l);
+    } else {
+        pdf_out(pdf, s);
+    }
+}
+
+@ print out a integer to PDF buffer
+
+@c
+void pdf_print_int(PDF pdf, longinteger n)
+{
+    char s[24];
+    int w;
+    w = snprintf(s, 23, "%" LONGINTEGER_PRI "d", (LONGINTEGER_TYPE) n);
+    check_nprintf(w, 23);
+    pdf_out_block(pdf, (const char *) s, (size_t) w);
+}
+
+@ @c
+/*
+void print_pdffloat(PDF pdf, pdffloat f)
+{
+    char a[24];
+    int e = f.e, i, l;
+    int64_t m = f.m;
+    if (m < 0) {
+        pdf_out(pdf, '-');
+        m *= -1;
+    }
+    l = m / ten_pow[e];
+    pdf_print_int(pdf, l);
+    l = m % ten_pow[e];
+    if (l != 0) {
+        pdf_out(pdf, '.');
+        snprintf(a, 23, "%d", l + ten_pow[e]);
+        for (i = e; i > 0; i--) {
+            if (a[i] != '0')
+                break;
+            a[i] = '\0';
+        }
+        pdf_puts(pdf, (a + 1));
+    }
+}
+*/
+
+void print_pdffloat(PDF pdf, pdffloat f)
+{
+    int64_t m = f.m;
+    if (m == 0) {
+        pdf_out(pdf, '0');
+    } else {
+        int e = f.e;
+        if (e == 0) {
+            /* division by ten_pow[0] == 1 */
+            if (m == 1) {
+                pdf_out(pdf, '1');
+            } else {
+                char a[24];
+                snprintf(a, 23, "%" LONGINTEGER_PRI "i", (LONGINTEGER_TYPE) m);
+                pdf_puts(pdf, a);
+            }
+        } else {
+            int t = ten_pow[e] ;
+            if (t == m) {
+                pdf_out(pdf, '1');
+            } else {
+                int i, l, w;
+                char a[24];
+                if (m < 0) {
+                    pdf_out(pdf, '-');
+                    m *= -1;
+                }
+                l = m / t;
+                w = snprintf(a, 23, "%i", l);
+                pdf_out_block(pdf, (const char *) a, (size_t) w);
+                l = m % t;
+                if (l != 0) {
+                    pdf_out(pdf, '.');
+                    snprintf(a, 23, "%d", l + t);
+                    for (i = e; i > 0; i--) {
+                        if (a[i] != '0')
+                            break;
+                        a[i] = '\0';
+                    }
+                    pdf_puts(pdf, (a + 1));
+                }
+            }
+        }
+    }
+}
+
+@ print out |s| as string in PDF output
+
+@c
+void pdf_print_str(PDF pdf, const char *s)
+{
+    const char *orig = s;
+    int l = (int) strlen(s) - 1; /* last string index */
+    if (l < 0) {
+        pdf_puts(pdf, "()");
+        return;
+    }
+    /* the next is not really safe, the string could be "(a)xx(b)" */
+    if ((s[0] == '(') && (s[l] == ')')) {
+        pdf_puts(pdf, s);
+        return;
+    }
+    if ((s[0] != '<') || (s[l] != '>') || odd((l + 1))) {
+        pdf_out(pdf, '(');
+        pdf_puts(pdf, s);
+        pdf_out(pdf, ')');
+        return;
+    }
+    s++;
+    while (isxdigit((unsigned char)*s))
+        s++;
+    if (s != orig + l) {
+        pdf_out(pdf, '(');
+        pdf_puts(pdf, orig);
+        pdf_out(pdf, ')');
+        return;
+    }
+    pdf_puts(pdf, orig); /* it was a hex string after all  */
+}
+
+@ begin a stream (needs to have a stream dictionary also)
+
+@c
+void pdf_begin_stream(PDF pdf)
+{
+    pdf_puts(pdf, "\nstream\n");
+    pdf_save_offset(pdf);
+    pdf_flush(pdf);
+    if (pdf->stream_deflate) {
+        pdf->zip_write_state = ZIP_WRITING;
+    }
+    pdf->stream_writing = true;
+    pdf->stream_length = 0;
+    pdf->last_byte = 0;
+}
+
+@ end a stream
+
+@c
+void pdf_end_stream(PDF pdf)
+{
+    os_struct *os = pdf->os;
+    switch (os->curbuf) {
+        case PDFOUT_BUF:
+            if (pdf->zip_write_state == ZIP_WRITING)
+                pdf->zip_write_state = ZIP_FINISH;
+            pdf_flush(pdf);         /* sets pdf->last_byte */
+            break;
+        case OBJSTM_BUF:
+            normal_error("pdf backend", "bad buffer in end stream, case 1");
+            break;
+        default:
+            normal_error("pdf backend", "bad buffer in end stream, case 2");
+    }
+    pdf->stream_deflate = false;
+    pdf->stream_writing = false;
+    pdf_out(pdf, '\n');     /* doesn't really belong to the stream */
+    pdf_puts(pdf, "endstream");
+    /* write stream /Length */
+    if (pdf->seek_write_length && pdf->draftmode == 0) {
+        xfseeko(pdf->file, (off_t)pdf->stream_length_offset, SEEK_SET, pdf->job_name);
+        fprintf(pdf->file, "%" LONGINTEGER_PRI "i", (LONGINTEGER_TYPE) pdf->stream_length);
+        xfseeko(pdf->file, 0, SEEK_END, pdf->job_name);
+    }
+    pdf->seek_write_length = false;
+}
+
+@ To print |scaled| value to PDF output we need some subroutines to ensure
+accurary.
+
+@c
+#define max_integer 0x7FFFFFFF  /* $2^{31}-1$ */
+
+scaled one_hundred_inch =  7227 * 65536;             /* scaled value corresponds to 100in, exact, 473628672 */
+scaled one_inch         = (7227 * 65536 + 50) / 100; /* scaled value corresponds to 1in (rounded to 4736287) */
+scaled one_true_inch    = (7227 * 65536 + 50) / 100; /* scaled value corresponds to 1truein (rounded!) */
+scaled one_hundred_bp   = (7227 * 65536)      /  72; /* scaled value corresponds to 100bp */
+scaled one_bp           = 65781;                     /* scaled value corresponds to 1bp (rounded to 65782) */
+
+/*
+    one_bp is changed on 20110411 to be exactly 65781, as in tex itself, because this value
+    is also used for \pdfpxdimen
+*/
+
+int ten_pow[10] = {
+    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 /* $10^0..10^9$ */
+};
+
+@ The function |divide_scaled| divides |s| by |m| using |dd| decimal
+digits of precision. It is defined in C because it is a good candidate
+for optimizations that are not possible in pascal.
+
+@c
+scaled round_xn_over_d(scaled x, int n, unsigned int d)
+{
+    boolean positive = true;
+    unsigned t, u, v;
+    if (x < 0) {
+        positive = !positive;
+        x = -(x);
+    }
+    if (n < 0) {
+        positive = !positive;
+        n = -(n);
+    }
+    t = (unsigned) ((x % 0100000) * n);
+    u = (unsigned) (((unsigned) (x) / 0100000) * (unsigned) n + (t / 0100000));
+    v = (u % d) * 0100000 + (t % 0100000);
+    if (u / d >= 0100000)
+        arith_error = true;
+    else
+        u = 0100000 * (u / d) + (v / d);
+    v = v % d;
+    if (2 * v >= d)
+        u++;
+    if (positive)
+        return (scaled) u;
+    else
+        return (-(scaled) u);
+}
+
+@ @c
+void pdf_add_bp(PDF pdf, scaled s)
+{
+    pdffloat a;
+    pdfstructure *p = pdf->pstruct;
+    a.m = i64round(s * p->k1);
+    a.e = pdf->decimal_digits;
+    if (pdf->cave > 0)
+        pdf_out(pdf, ' ');
+    print_pdffloat(pdf, a);
+    pdf->cave = 1;
+}
+
+@* handling page resources.
+
+@c
+typedef struct {
+    int obj_type;
+    pdf_object_list *list;
+} pr_entry;
+
+@ @c
+static int comp_page_resources(const void *pa, const void *pb, void *p)
+{
+    int a = ((const pr_entry *) pa)->obj_type;
+    int b = ((const pr_entry *) pb)->obj_type;
+    (void) p;
+    if (a > b)
+        return 1;
+    if (a < b)
+        return -1;
+    return 0;
+}
+
+@ @c
+void addto_page_resources(PDF pdf, pdf_obj_type t, int k)
+{
+    pdf_resource_struct *re;
+    pr_entry *pr, tmp;
+    void **pp;
+    pdf_object_list *p, *item = NULL;
+    re = pdf->page_resources;
+    if (re->resources_tree == NULL) {
+        re->resources_tree = avl_create(comp_page_resources, NULL, &avl_xallocator);
+        if (re->resources_tree == NULL)
+            formatted_error("pdf backend","addto_page_resources(): avl_create() page_resource_tree failed");
+    }
+    tmp.obj_type = t;
+    pr = (pr_entry *) avl_find(re->resources_tree, &tmp);
+    if (pr == NULL) {
+        pr = xtalloc(1, pr_entry);
+        pr->obj_type = t;
+        pr->list = NULL;
+        pp = avl_probe(re->resources_tree, pr);
+        if (pp == NULL)
+            formatted_error("pdf backend","addto_page_resources(): avl_probe() out of memory in insertion");
+    }
+    if (pr->list == NULL) {
+        item = xtalloc(1, pdf_object_list);
+        item->link = NULL;
+        item->info = k;
+        pr->list = item;
+        if (obj_type(pdf, k) == (int)t)
+            set_obj_scheduled(pdf, k);  /* k is an object number */
+    } else {
+        for (p = pr->list; p->info != k && p->link != NULL; p = p->link);
+        if (p->info != k) {
+            item = xtalloc(1, pdf_object_list);
+            item->link = NULL;
+            item->info = k;
+            p->link = item;
+            if (obj_type(pdf, k) == (int)t)
+                set_obj_scheduled(pdf, k);
+        }
+    }
+}
+
+@ @c
+pdf_object_list *get_page_resources_list(PDF pdf, pdf_obj_type t)
+{
+    pdf_resource_struct *re = pdf->page_resources;
+    pr_entry *pr, tmp;
+    if (re == NULL || re->resources_tree == NULL)
+        return NULL;
+    tmp.obj_type = t;
+    pr = (pr_entry *) avl_find(re->resources_tree, &tmp);
+    if (pr == NULL)
+        return NULL;
+    return pr->list;
+}
+
+@ @c
+static void reset_page_resources(PDF pdf)
+{
+    pdf_resource_struct *re = pdf->page_resources;
+    pr_entry *p;
+    struct avl_traverser t;
+    pdf_object_list *l1, *l2;
+    if (re == NULL || re->resources_tree == NULL)
+        return;
+    avl_t_init(&t, re->resources_tree);
+    for (p = avl_t_first(&t, re->resources_tree); p != NULL; p = avl_t_next(&t)) {
+        if (p->list != NULL) {
+            for (l1 = p->list; l1 != NULL; l1 = l2) {
+                l2 = l1->link;
+                free(l1);
+            }
+            p->list = NULL;     /* but the AVL tree remains */
+        }
+    }
+}
+
+@ @c
+static void destroy_pg_res_tree(void *pa, void *param)
+{
+    (void) param;
+    xfree(pa);
+}
+
+@ @c
+static void destroy_page_resources_tree(PDF pdf)
+{
+    pdf_resource_struct *re = pdf->page_resources;
+    reset_page_resources(pdf);
+    if (re->resources_tree != NULL)
+        avl_destroy(re->resources_tree, destroy_pg_res_tree);
+    re->resources_tree = NULL;
+}
+
+@* Subroutines to print out various PDF objects.
+
+@ print out an integer |n| with fixed width |w|; used for outputting cross-reference table. The
+specification says that an offset must take 10 bytes.
+@c
+static void pdf_print_fw_int(PDF pdf, longinteger n)
+{
+    unsigned char digits[11];
+
+    int k = 10;
+    do {
+        k--;
+        digits[k] = (unsigned char) ('0' + (n % 10));
+        n /= 10;
+    } while (k != 0);
+    if (n!=0)
+      /* the absolute  value of $n$ is greater than 9999999999 */
+      normal_error("pdf backend", "offset exceeds 10 bytes, try enabling object compression.");
+    digits[10]='\0';
+    pdf_puts(pdf, (const char *) digits);
+}
+
+@ print out an integer |n| as a fixed number |w| of bytes; used for outputting \.{/XRef} cross-reference stream
+@c
+static void pdf_out_bytes(PDF pdf, longinteger n, size_t w)
+{
+    int k;
+    unsigned char bytes[8];     /* digits in a number being output */
+    k = (int) w;
+    do {
+        k--;
+        bytes[k] = (unsigned char) (n % 256);
+        n /= 256;
+    } while (k != 0);
+    pdf_out_block(pdf, (const char *) bytes, w);
+}
+
+@ print out |s| as string in PDF output
+
+@c
+void pdf_print_str_ln(PDF pdf, const char *s)
+{
+    pdf_print_str(pdf, s);
+    pdf_out(pdf, '\n');
+}
+
+@ @c
+void pdf_print_toks(PDF pdf, halfword p)
+{
+    int len = 0;
+    char *s = tokenlist_to_cstring(p, true, &len);
+    if (len > 0) {
+        if (pdf->cave > 0)
+            pdf_out(pdf, ' ');
+        pdf_puts(pdf, s);
+        pdf->cave = 1;
+    }
+    xfree(s);
+}
+
+@ prints a rect spec
+
+@c
+void pdf_add_rect_spec(PDF pdf, halfword r)
+{
+    pdf_add_bp(pdf, pdf_ann_left(r));
+    pdf_add_bp(pdf, pdf_ann_bottom(r));
+    pdf_add_bp(pdf, pdf_ann_right(r));
+    pdf_add_bp(pdf, pdf_ann_top(r));
+}
+
+@ output a rectangle specification to PDF file
+
+@c
+void pdf_rectangle(PDF pdf, halfword r)
+{
+    pdf_add_name(pdf, "Rect");
+    pdf_begin_array(pdf);
+    pdf_add_rect_spec(pdf, r);
+    pdf_end_array(pdf);
+}
+
+@ @c
+static void init_pdf_outputparameters(PDF pdf)
+{
+    int pk_mode;
+    pdf->draftmode = fix_int(draft_mode_par, 0, 1);
+    pdf->compress_level = fix_int(pdf_compress_level, 0, 9);
+    pdf->decimal_digits = fix_int(pdf_decimal_digits, 3, 5);
+    pdf->gamma = fix_int(pdf_gamma, 0, 1000000);
+    pdf->image_gamma = fix_int(pdf_image_gamma, 0, 1000000);
+    pdf->image_hicolor = fix_int(pdf_image_hicolor, 0, 1);
+    pdf->image_apply_gamma = fix_int(pdf_image_apply_gamma, 0, 1);
+    pdf->objcompresslevel = fix_int(pdf_obj_compress_level, 0, MAX_OBJ_COMPRESS_LEVEL);
+    pdf->inclusion_copy_font = fix_int(pdf_inclusion_copy_font, 0, 1);
+    pdf->pk_resolution = fix_int(pdf_pk_resolution, 72, 8000);
+    pdf->pk_fixed_dpi = fix_int(pdf_pk_fixed_dpi, 0, 1);
+    if ((pdf->minor_version >= 5) && (pdf->objcompresslevel > 0)) {
+        pdf->os_enable = true;
+    } else {
+        if (pdf->objcompresslevel > 0) {
+            normal_warning("pdf backend","objcompresslevel > 0 requires minorversion > 4");
+            pdf->objcompresslevel = 0;
+        }
+        pdf->os_enable = false;
+    }
+    if (pdf->pk_resolution == 0)        /* if not set from format file or by user */
+        pdf->pk_resolution = pk_dpi;    /* take it from \.{texmf.cnf} */
+    pdf->pk_scale_factor = divide_scaled(72, pdf->pk_resolution, pk_decimal_digits(pdf,5));
+    if (!callback_defined(read_pk_file_callback)) {
+        pk_mode = pdf_pk_mode; /* lookup once */
+        if (pk_mode != null) {
+            char *s = tokenlist_to_cstring(pk_mode, true, NULL);
+            /* This will become LUATEX in 1.0. */
+            kpse_init_prog("PDFTEX", (unsigned) pdf->pk_resolution, s, nil);
+            xfree(s);
+        } else {
+            /* This will become LUATEX in 1.0. */
+            kpse_init_prog("PDFTEX", (unsigned) pdf->pk_resolution, nil, nil);
+        }
+        if (!kpse_var_value("MKTEXPK"))
+            kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_cmdline);
+    }
+    set_job_id(pdf, year_par, month_par, day_par, time_par);
+    if ((pdf_unique_resname > 0) && (pdf->resname_prefix == NULL))
+        pdf->resname_prefix = get_resname_prefix(pdf);
+}
+
+@ Checks that we have a name for the generated PDF file and that it's open.
+
+@c
+static void ensure_output_file_open(PDF pdf, const char *ext)
+{
+    char *fn;
+    if (pdf->file_name != NULL)
+        return;
+    if (job_name == 0)
+        open_log_file();
+    fn = pack_job_name(ext);
+    if (pdf->draftmode == 0 || output_mode_used == OMODE_DVI) {
+        while (!lua_b_open_out(&pdf->file, fn))
+            fn = prompt_file_name("file name for output", ext);
+    }
+    pdf->file_name = fn;
+}
+
+@ @c
+static void ensure_pdf_header_written(PDF pdf)
+{
+    /* Initialize variables for \.{PDF} output */
+    fix_pdf_version(pdf);
+    init_pdf_outputparameters(pdf);
+    fix_pdf_draftmode(pdf);
+    /* Write \.{PDF} header */
+    pdf_printf(pdf, "%%PDF-%d.%d\n", pdf->major_version, pdf->minor_version);
+    /* The next blob will be removed 1.0. */
+    pdf_out(pdf, '%');
+    pdf_out(pdf, 'P' + 128);
+    pdf_out(pdf, 'T' + 128);
+    pdf_out(pdf, 'E' + 128);
+    pdf_out(pdf, 'X' + 128);
+    pdf_out(pdf, '\n');
+}
+
+@ @c
+void ensure_output_state(PDF pdf, output_state s)
+{
+    if (pdf->o_state < s) {
+        if (s > ST_INITIAL)
+            ensure_output_state(pdf, s - 1);
+        switch (s - 1) {
+            case ST_INITIAL:
+                fix_o_mode();
+                break;
+            case ST_OMODE_FIX:
+                switch (output_mode_used) {
+                    case OMODE_DVI:
+                        ensure_output_file_open(pdf, ".dvi");
+                        break;
+                    case OMODE_PDF:
+                        ensure_output_file_open(pdf, ".pdf");
+                        break;
+                    default:
+                        normal_error("pdf backend","weird output state");
+                }
+                break;
+            case ST_FILE_OPEN:
+                switch (output_mode_used) {
+                    case OMODE_DVI:
+                        ensure_dvi_header_written(pdf);
+                        break;
+                    case OMODE_PDF:
+                        ensure_pdf_header_written(pdf);
+                        break;
+                    default:
+                        normal_error("pdf backend","weird output state");
+                }
+                break;
+            case ST_HEADER_WRITTEN:
+                break;
+            case ST_FILE_CLOSED:
+                break;
+            default:
+                normal_error("pdf backend","weird output state");
+        }
+        pdf->o_state++;
+    }
+}
+
+@ Write out an accumulated object stream.
+
+First the object number and byte offset pairs are generated and appended to the
+ready buffered object stream. By this the value of \.{/First} can be calculated.
+Then a new \.{/ObjStm} object is generated, and everything is copied to the PDF
+output buffer, where also compression is done. When calling this procedure,
+|pdf_os_mode| must be |true|.
+
+@c
+static void pdf_os_write_objstream(PDF pdf)
+{
+    os_struct *os = pdf->os;
+    unsigned int i, j, n1, n2;  /* n1, n2: ObjStm buffer may be reallocated! */
+    strbuf_s *obuf = os->buf[OBJSTM_BUF];
+    if (os->cur_objstm == 0)    /* no object stream started */
+        return;
+    n1 = (unsigned int) strbuf_offset(obuf);    /* remember end of collected object stream contents */
+    /* this is needed here to calculate /First for the ObjStm dict */
+    for (i = 0, j = 0; i < os->idx; i++) {      /* add object-number/byte-offset list to buffer */
+        pdf_print_int(pdf, (int) os->obj[i].num);
+        pdf_out(pdf, ' ');
+        pdf_print_int(pdf, (int) os->obj[i].off);
+        if (j == 9 || i == os->idx - 1) {       /* print out in groups of ten for better readability */
+            pdf_out(pdf, '\n');
+            j = 0;
+        } else {
+            pdf_out(pdf, ' ');
+            j++;
+        }
+    }
+    n2 = (unsigned int) strbuf_offset(obuf);    /* remember current buffer end */
+    pdf_begin_obj(pdf, (int) os->cur_objstm, OBJSTM_NEVER);     /* switch to PDF stream writing */
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "ObjStm");
+    pdf_dict_add_int(pdf, "N", (int) os->idx);  /* number of objects in ObjStm */
+    pdf_dict_add_int(pdf, "First", (int) (n2 - n1));
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    /* write object-number/byte-offset list */
+    pdf_out_block(pdf, (const char *) (obuf->data + n1), (size_t) (n2 - n1));
+    /* write collected object stream contents */
+    pdf_out_block(pdf, (const char *) obuf->data, (size_t) n1);
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    os->cur_objstm = 0;         /* to force object stream generation next time */
+}
+
+@ begin a PDF dictionary
+
+@c
+void pdf_begin_dict(PDF pdf)
+{
+    pdf_puts(pdf, "<<");
+    pdf->cave = 0;
+}
+
+@ end a PDF dictionary
+
+@c
+void pdf_end_dict(PDF pdf)
+{
+    pdf_puts(pdf, ">>");
+    pdf->cave = 0;
+}
+
+@ add integer object to dict
+
+@c
+void pdf_dict_add_bool(PDF pdf, const char *key, int i)
+{
+    pdf_add_name(pdf, key);
+    pdf_add_bool(pdf, i);
+}
+
+@ add integer object to dict
+
+@c
+void pdf_dict_add_int(PDF pdf, const char *key, int i)
+{
+    pdf_add_name(pdf, key);
+    pdf_add_int(pdf, i);
+}
+
+@ add name object to dict
+
+@c
+void pdf_dict_add_name(PDF pdf, const char *key, const char *val)
+{
+    pdf_add_name(pdf, key);
+    pdf_add_name(pdf, val);
+}
+
+@ add string object to dict
+
+@c
+void pdf_dict_add_string(PDF pdf, const char *key, const char *val)
+{
+    if (val == NULL)
+        return;
+    pdf_add_name(pdf, key);
+    if (pdf->cave > 0)
+        pdf_out(pdf, ' ');
+    pdf->cave = 0;
+    pdf_print_str(pdf, val);
+}
+
+@ add name reference to dict
+
+@c
+void pdf_dict_add_ref(PDF pdf, const char *key, int num)
+{
+    pdf_add_name(pdf, key);
+    pdf_add_ref(pdf, num);
+}
+
+@ add objects of different types
+
+@c
+void pdf_add_null(PDF pdf)
+{
+    if (pdf->cave > 0)
+        pdf_out(pdf, ' ');
+    pdf_puts(pdf, "null");
+    pdf->cave = 1;
+}
+
+void pdf_add_bool(PDF pdf, int i)
+{
+    if (pdf->cave > 0)
+        pdf_out(pdf, ' ');
+    if (i == 0)
+        pdf_puts(pdf, "false");
+    else
+        pdf_puts(pdf, "true");
+    pdf->cave = 1;
+}
+
+void pdf_add_int(PDF pdf, int i)
+{
+    if (pdf->cave > 0)
+        pdf_out(pdf, ' ');
+    pdf_print_int(pdf, i);
+    pdf->cave = 1;
+}
+
+void pdf_add_longint(PDF pdf, longinteger n)
+{
+    if (pdf->cave > 0)
+        pdf_out(pdf, ' ');
+    pdf_print_int(pdf, n);
+    pdf->cave = 1;
+}
+
+void pdf_add_string(PDF pdf, const char *s)
+{
+    if (pdf->cave > 0)
+        pdf_out(pdf, ' ');
+    pdf_print_str(pdf, s);
+    pdf->cave = 1;
+}
+
+void pdf_add_name(PDF pdf, const char *name)
+{
+    pdf_out(pdf, '/');
+    pdf_puts(pdf, name);
+    pdf->cave = 1;
+}
+
+void pdf_add_ref(PDF pdf, int num)
+{
+    if (pdf->cave > 0)
+        pdf_out(pdf, ' ');
+    pdf_print_int(pdf, num);
+    pdf_puts(pdf, " 0 R");
+    pdf->cave = 1;
+}
+
+@ add stream length and filter entries to a stream dictionary,
+remember file position for seek
+
+@c
+void pdf_dict_add_streaminfo(PDF pdf)
+{
+    pdf_add_name(pdf, "Length");
+    pdf->stream_length_offset = pdf_offset(pdf) + 1;
+    pdf->seek_write_length = true; /* fill in length at |pdf_end_stream| call */
+    pdf_puts(pdf, " x         ");  /* space for 10 decimal digits */
+    pdf->cave = 1;
+    if (pdf->compress_level > 0) {
+        pdf_dict_add_name(pdf, "Filter", "FlateDecode");
+        pdf->stream_deflate = true;
+    }
+}
+
+@ begin a PDF array
+
+@c
+void pdf_begin_array(PDF pdf)
+{
+    pdf_out(pdf, '[');
+    pdf->cave = 0;
+}
+
+@ end a PDF array
+
+@c
+void pdf_end_array(PDF pdf)
+{
+    pdf_out(pdf, ']');
+    pdf->cave = 0;
+}
+
+@ begin a PDF object
+
+@c
+void pdf_begin_obj(PDF pdf, int i, int pdf_os_threshold)
+{
+    os_struct *os = pdf->os;
+    ensure_output_state(pdf, ST_HEADER_WRITTEN);
+    pdf_prepare_obj(pdf, i, pdf_os_threshold);
+    switch (os->curbuf) {
+        case PDFOUT_BUF:
+            pdf_printf(pdf, "%d 0 obj\n", (int) i);
+            break;
+        case OBJSTM_BUF:
+            if (pdf->compress_level == 0)
+                pdf_printf(pdf, "%% %d 0 obj\n", (int) i);  /* debugging help */
+            break;
+        default:
+            normal_error("pdf backend","weird begin object");
+    }
+    pdf->cave = 0;
+}
+
+@ end a PDF object
+
+@c
+void pdf_end_obj(PDF pdf)
+{
+    os_struct *os = pdf->os;
+    switch (os->curbuf) {
+        case PDFOUT_BUF:
+            pdf_puts(pdf, "\nendobj\n"); /* end a PDF object */
+            break;
+        case OBJSTM_BUF:
+            os->idx++;   /* = number of objects collected so far in ObjStm */
+            os->o_ctr++; /* only for statistics */
+            if (os->idx == PDF_OS_MAX_OBJS)
+                pdf_os_write_objstream(pdf);
+            else
+                pdf_out(pdf, '\n'); /* Adobe Reader seems to need this */
+            break;
+        default:
+            normal_error("pdf backend","weird end object");
+    }
+}
+
+@ Converts any string given in in in an allowed PDF string which can be
+ handled by printf et.al.: \.{\\} is escaped to \.{\\\\}, parenthesis are escaped and
+ control characters are octal encoded.
+ This assumes that the string does not contain any already escaped
+ characters!
+
+@c
+char *convertStringToPDFString(const char *in, int len)
+{
+    static char pstrbuf[MAX_PSTRING_LEN];
+    char *out = pstrbuf;
+    int i, j, k;
+    char buf[5];
+    j = 0;
+    for (i = 0; i < len; i++) {
+        check_buf((unsigned) j + sizeof(buf), MAX_PSTRING_LEN);
+        if (((unsigned char) in[i] < '!') || ((unsigned char) in[i] > '~')) {
+            /* convert control characters into oct */
+            k = snprintf(buf, sizeof(buf), "\\%03o", (unsigned int) (unsigned char) in[i]);
+            check_nprintf(k, sizeof(buf));
+            out[j++] = buf[0];
+            out[j++] = buf[1];
+            out[j++] = buf[2];
+            out[j++] = buf[3];
+        } else if ((in[i] == '(') || (in[i] == ')')) {
+            /* escape paranthesis */
+            out[j++] = '\\';
+            out[j++] = in[i];
+        } else if (in[i] == '\\') {
+            /* escape backslash */
+            out[j++] = '\\';
+            out[j++] = '\\';
+        } else {
+            /* copy char :-) */
+            out[j++] = in[i];
+        }
+    }
+    out[j] = '\0';
+    return pstrbuf;
+}
+
+@ Converts any string given in in in an allowed PDF string which is hexadecimal
+encoded; |sizeof(out)| should be at least $|lin|*2+1$.
+
+@c
+static void convertStringToHexString(const char *in, char *out, int lin)
+{
+    int i, k;
+    char buf[3];
+    int j = 0;
+    for (i = 0; i < lin; i++) {
+        k = snprintf(buf, sizeof(buf), "%02X", (unsigned int) (unsigned char) in[i]);
+        check_nprintf(k, sizeof(buf));
+        out[j++] = buf[0];
+        out[j++] = buf[1];
+    }
+    out[j] = '\0';
+}
+
+@ Compute the ID string as per PDF1.4 9.3:
+
+File identifers are defined by the optional ID entry in a PDF file's trailer
+dictionary (see Section 3.4.4, "File Trailer"; see also implementation note 105
+in Appendix H). The value of this entry is an array of two strings. The first
+string is a permanent identifier based on the contents of the file at the time it
+was originally created, and does not change when the file is incrementally
+updated. The second string is a changing identifier based on the file's contents
+at the time it was last updated. When a file is first written, both identifiers
+are set to the same value. If both identifiers match when a file reference is
+resolved, it is very likely that the correct file has been found; if only the
+first identifier matches, then a different version of the correct file has been
+found. To help ensure the uniqueness of file identifiers, it is recommend that
+they be computed using a message digest algorithm such as MD5 (described in
+Internet RFC 1321, The MD5 Message-Digest Algorithm; see the Bibliography), using
+the following information (see implementation note 106 in Appendix H): - The
+current time
+
+- A string representation of the file's location, usually a pathname
+- The size of the file in bytes
+- The values of all entries in the file's document information
+  dictionary (see Section 9.2.1,  Document Information Dictionary )
+
+This stipulates only that the two IDs must be identical when the file is created
+and that they should be reasonably unique. Since it's difficult to get the file
+size at this point in the execution of pdfTeX and scanning the info dict is also
+difficult, we start with a simpler implementation using just the first two items.
+
+
+@c
+
+/*
+    A user supplied trailerid had better be an array! So maybe we need to check
+    for [] and error otherwise.
+*/
+
+static void print_ID(PDF pdf)
+{
+    if ((pdf_suppress_optional_info & 512) == 0) {
+        const char *p = NULL;
+        pdf_add_name(pdf, "ID");
+        p = get_pdf_table_string("trailerid");
+        if (p && strlen(p) > 0) {
+            pdf_puts(pdf,p);
+        } else if (pdf_trailer_id != 0) {
+            /* user provided one */
+            pdf_print_toks(pdf, pdf_trailer_id);
+        } else {
+            /* system provided one */
+            time_t t;
+            size_t size;
+            char time_str[32];
+            md5_state_t state;
+            md5_byte_t digest[16];
+            char id[64];
+            char pwd[4096];
+            /* start md5 */
+            md5_init(&state);
+            /* get the time */
+            t = pdf->start_time;
+            size = strftime(time_str, sizeof(time_str), "%Y%m%dT%H%M%SZ", gmtime(&t));
+            md5_append(&state, (const md5_byte_t *) time_str, (int) size);
+            /* get the file name */
+            if (getcwd(pwd, sizeof(pwd)) == NULL) {
+                formatted_error("pdf backend","getcwd() failed (%s), (path too long?)", strerror(errno));
+            }
+#ifdef WIN32
+            {
+                char *p;
+                for (p = pwd; *p; p++) {
+                    if (*p == '\\')
+                        *p = '/';
+                    else if (IS_KANJI(p))
+                        p++;
+                }
+            }
+#endif
+            md5_append(&state, (const md5_byte_t *) pwd, (int) strlen(pwd));
+            md5_append(&state, (const md5_byte_t *) "/", 1);
+            md5_append(&state, (const md5_byte_t *) pdf->file_name, (int) strlen(pdf->file_name));
+            /* finish md5 */
+            md5_finish(&state, digest);
+            /* write the IDs */
+            convertStringToHexString((char *) digest, id, 16);
+            pdf_begin_array(pdf);
+            pdf_printf(pdf, "<%s> <%s>", id, id);
+            pdf_end_array(pdf);
+        }
+    }
+}
+
+@ Print the /CreationDate entry.
+
+PDF Reference, third edition says about the expected date format:
+
+3.8.2 Dates
+
+PDF defines a standard date format, which closely follows that of the
+international standard ASN.1 (Abstract Syntax Notation One), defined in ISO/IEC
+8824 (see the Bibliography). A date is a string of the form
+
+(D:YYYYMMDDHHmmSSOHH'mm')
+
+where
+
+YYYY  is the year
+MM    is the month
+DD    is the day (01-31)
+HH    is the hour (00-23)
+mm    is the minute (00-59)
+SS    is the second (00-59)
+O     is the relationship of local time to Universal Time (UT), denoted by one
+      of the characters +, -, or Z (see below)
+HH    followed by ' is the absolute value of the offset from UT in hours (00-23)
+mm    followed by ' is the absolute value of the offset from UT in minutes (00-59)
+
+The apostrophe character (') after HH and mm is part of the syntax. All fields
+after the year are optional. (The prefix D:, although also optional, is strongly
+recommended.) The default values for MM and DD are both 01; all other numerical
+fields default to zero values. A plus sign (+) as the value of the O field
+signifies that local time is later than UT, a minus sign (-) that local time is
+earlier than UT, and the letter Z that local time is equal to UT. If no UT
+information is specified, the relationship of the specified time to UT is
+considered to be unknown. Whether or not the time zone is known, the rest of the
+date should be specified in local time.
+
+For example, December 23, 1998, at 7:52 PM, U.S. Pacific Standard Time, is
+represented by the string
+
+D:199812231952-08'00'
+
+The main difficulty is get the time zone offset. |strftime()| does this in ISO
+C99 (e.g. newer glibc) with \%z, but we have to work with other systems (e.g.
+Solaris 2.5).
+
+@c
+#define TIME_STR_SIZE 30 /* minimum size for |time_str| is 24: |"D:YYYYmmddHHMMSS+HH'MM'"| */
+
+static void makepdftime(PDF pdf)
+{
+    struct tm lt, gmt;
+    size_t size;
+    int i, off, off_hours, off_mins;
+    time_t t = pdf->start_time;
+    char *time_str = pdf->start_time_str;
+    /* get the time */
+    if (utc_option) {
+        lt = *gmtime(&t);
+    } else {
+        lt = *localtime(&t);
+    }
+    size = strftime(time_str, TIME_STR_SIZE, "D:%Y%m%d%H%M%S", &lt);
+    /* expected format: "YYYYmmddHHMMSS" */
+    if (size == 0) {
+        /* unexpected, contents of |time_str| is undefined */
+        time_str[0] = '\0';
+        return;
+    }
+    /*
+       correction for seconds: \%S can be in range 00..61, the PDF reference
+       expects 00..59, therefore we map "60" and "61" to "59"
+    */
+    if (time_str[14] == '6') {
+        time_str[14] = '5';
+        time_str[15] = '9';
+        /* for safety */
+        time_str[16] = '\0';
+    }
+    /* get the time zone offset */
+    gmt = *gmtime(&t);
+    /* this calculation method was found in exim's tod.c */
+    off = 60 * (lt.tm_hour - gmt.tm_hour) + lt.tm_min - gmt.tm_min;
+    if (lt.tm_year != gmt.tm_year) {
+        off += (lt.tm_year > gmt.tm_year) ? 1440 : -1440;
+    } else if (lt.tm_yday != gmt.tm_yday) {
+        off += (lt.tm_yday > gmt.tm_yday) ? 1440 : -1440;
+    }
+    if (off == 0) {
+        time_str[size++] = 'Z';
+        time_str[size] = 0;
+    } else {
+        off_hours = off / 60;
+        off_mins = abs(off - off_hours * 60);
+        i = snprintf(&time_str[size], 9, "%+03d'%02d'", off_hours, off_mins);
+        check_nprintf(i, 9);
+    }
+    pdf->start_time = t;
+}
+
+@ @c
+void initialize_start_time(PDF pdf)
+{
+    if (pdf->start_time == 0) {
+        pdf->start_time = get_start_time();
+        pdf->start_time_str = xtalloc(TIME_STR_SIZE, char);
+        makepdftime(pdf);
+    }
+}
+
+@ @c
+char *getcreationdate(PDF pdf)
+{
+    initialize_start_time(pdf);
+    return pdf->start_time_str;
+}
+
+@ @c
+void remove_pdffile(PDF pdf)
+{
+    if (pdf != NULL) {
+        if (!kpathsea_debug && pdf->file_name && (pdf->draftmode == 0)) {
+            xfclose(pdf->file, pdf->file_name);
+            remove(pdf->file_name);
+        }
+    }
+}
+
+@ Use |check_o_mode()| in the backend-specific "Implement..." chunks
+
+@c
+void check_o_mode(PDF pdf, const char *s, int o_mode_bitpattern, boolean strict) /* s ignored now */
+{
+
+    output_mode o_mode;
+    const char *m = NULL;
+    if (lua_only) {
+        normal_error("lua only","no backend present, needed for what you asked for");
+        return ;
+    }
+    /*
+        in warn mode (strict == false): only check, don't do |fix_o_mode()| here! |output_mode_used|
+        is left in possibly wrong state until real output, ok.
+    */
+    if (output_mode_used == OMODE_NONE)
+        o_mode = get_o_mode();
+    else
+        o_mode = output_mode_used;
+    pdf->o_mode = output_mode_used; /* used by synctex, we need to use output_mode_used there */
+    if (!((1 << o_mode) & o_mode_bitpattern)) { /* warning or error */
+        switch (o_mode) {
+            case OMODE_DVI:
+                m = "DVI";
+                break;
+            case OMODE_PDF:
+                m = "PDF";
+                break;
+            default:
+               normal_error("pdf backend","weird output state");
+         }
+        if (strict)
+            formatted_error("pdf backend", "%s not allowed in %s mode (outputmode = %d)",s, m, (int) output_mode_par);
+        else
+            formatted_warning("pdf backend", "%s not allowed in %s mode (outputmode = %d)",s, m, (int) output_mode_par);
+    } else if (strict)
+        ensure_output_state(pdf, ST_HEADER_WRITTEN);
+}
+
+@ @c
+void set_job_id(PDF pdf, int year, int month, int day, int time)
+{
+    char *name_string, *format_string, *s;
+    size_t slen;
+    int i;
+    if (pdf->job_id_string != NULL)
+        return;
+    name_string = makecstring(job_name);
+    format_string = makecstring(format_ident);
+    slen = SMALL_BUF_SIZE + strlen(name_string) + strlen(format_string) + strlen(luatex_banner);
+    s = xtalloc(slen, char);
+    /* The Web2c version string starts with a space.  */
+    i = snprintf(s, slen, "%.4d/%.2d/%.2d %.2d:%.2d %s %s %s", year, month, day, time / 60, time % 60, name_string, format_string, luatex_banner);
+    check_nprintf(i, slen);
+    pdf->job_id_string = xstrdup(s);
+    xfree(s);
+    xfree(name_string);
+    xfree(format_string);
+}
+
+@ @c
+char *get_resname_prefix(PDF pdf)
+{
+    static char name_str[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    static char prefix[7]; /* make a tag of 6 chars long */
+    short i;
+    size_t base = strlen(name_str);
+    unsigned long crc = crc32(0L, Z_NULL, 0);
+    crc = crc32(crc, (Bytef *) pdf->job_id_string, (uInt) strlen(pdf->job_id_string));
+    for (i = 0; i < 6; i++) {
+        prefix[i] = name_str[crc % base];
+        crc /= base;
+    }
+    prefix[6] = '\0';
+    return prefix;
+}
+
+@ @c
+void pdf_begin_page(PDF pdf)
+{
+    int xform_attributes;
+    int xform_type = 0;
+    scaled form_margin = pdf_xform_margin; /* was one_bp until SVN4066 */
+    ensure_output_state(pdf, ST_HEADER_WRITTEN);
+    init_pdf_pagecalculations(pdf);
+    if (pdf->page_resources == NULL) {
+        pdf->page_resources = xtalloc(1, pdf_resource_struct);
+        pdf->page_resources->resources_tree = NULL;
+    }
+    pdf->page_resources->last_resources = pdf_create_obj(pdf, obj_type_others, 0);
+    reset_page_resources(pdf);
+
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        pdf->last_page = pdf_get_obj(pdf, obj_type_page, total_pages + 1, 0);
+        set_obj_aux(pdf, pdf->last_page, 1);    /* mark that this page has been created */
+        pdf->last_stream = pdf_create_obj(pdf, obj_type_pagestream, 0);
+        pdf_begin_obj(pdf, pdf->last_stream, OBJSTM_NEVER);
+        pdf->last_thread = null;
+        pdf_begin_dict(pdf);
+    } else {
+        xform_type = obj_xform_type(pdf, pdf_cur_form) ;
+        pdf_begin_obj(pdf, pdf_cur_form, OBJSTM_NEVER);
+        pdf->last_stream = pdf_cur_form;
+        /* Write out Form stream header */
+        pdf_begin_dict(pdf);
+        if (xform_type == 0) {
+            pdf_dict_add_name(pdf, "Type", "XObject");
+            pdf_dict_add_name(pdf, "Subtype", "Form");
+            pdf_dict_add_int(pdf, "FormType", 1);
+        }
+        xform_attributes = pdf_xform_attr; /* lookup once */
+        form_margin = obj_xform_margin(pdf, pdf_cur_form); /* now stored in object */
+        if (xform_attributes != null)
+            pdf_print_toks(pdf, xform_attributes);
+        if (obj_xform_attr(pdf, pdf_cur_form) != null) {
+            pdf_print_toks(pdf, obj_xform_attr(pdf, pdf_cur_form));
+            delete_token_ref(obj_xform_attr(pdf, pdf_cur_form));
+            set_obj_xform_attr(pdf, pdf_cur_form, null);
+        }
+        if (obj_xform_attr_str(pdf, pdf_cur_form) != null) {
+            lua_pdf_literal(pdf, obj_xform_attr_str(pdf, pdf_cur_form));
+            luaL_unref(Luas, LUA_REGISTRYINDEX, obj_xform_attr_str(pdf, pdf_cur_form));
+            set_obj_xform_attr_str(pdf, pdf_cur_form, null);
+        }
+        if (xform_type == 0 || xform_type == 1 || xform_type == 3) {
+            pdf_add_name(pdf, "BBox");
+            pdf_begin_array(pdf);
+            pdf_add_bp(pdf, -form_margin);
+            pdf_add_bp(pdf, -form_margin);
+            pdf_add_bp(pdf, pdf->page_size.h + form_margin);
+            pdf_add_bp(pdf, pdf->page_size.v + form_margin);
+            pdf_end_array(pdf);
+        }
+        if (xform_type == 0 || xform_type == 2 || xform_type == 3) {
+            pdf_add_name(pdf, "Matrix");
+            pdf_begin_array(pdf);
+            pdf_add_int(pdf, 1);
+            pdf_add_int(pdf, 0);
+            pdf_add_int(pdf, 0);
+            pdf_add_int(pdf, 1);
+            pdf_add_int(pdf, 0);
+            pdf_add_int(pdf, 0);
+            pdf_end_array(pdf);
+        }
+        pdf_dict_add_ref(pdf, "Resources", pdf->page_resources->last_resources);
+    }
+    /* Start stream of page/form contents */
+    pdf_dict_add_streaminfo(pdf);
+    pdf_end_dict(pdf);
+    pdf_begin_stream(pdf);
+    pos_stack_used = 0; /* start with empty stack */
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        colorstackpagestart();
+    }
+    if (global_shipping_mode == SHIPPING_PAGE)
+        pdf_out_colorstack_startpage(pdf);
+}
+
+@ @c
+void print_pdf_table_string(PDF pdf, const char *s)
+{
+    size_t len;
+    const char *ls;
+    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lua_key_index(pdf_data));
+    lua_rawget(Luas, LUA_REGISTRYINDEX);
+    lua_pushstring(Luas, s);                  /* s t ... */
+    lua_rawget(Luas, -2);                     /* s? t ... */
+    if (lua_type(Luas, -1) == LUA_TSTRING) {  /* s t ... */
+        ls = lua_tolstring(Luas, -1, &len);
+        if (len > 0) {
+            if (pdf->cave == 1)
+                pdf_out(pdf, ' ');
+            pdf_out_block(pdf, ls, len);
+            pdf->cave = 1;
+        }
+    }
+    lua_pop(Luas, 2);
+}
+
+@ @c
+const char *get_pdf_table_string(const char *s)
+{
+    const_lstring ls;
+    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lua_key_index(pdf_data));
+    lua_rawget(Luas, LUA_REGISTRYINDEX);
+    lua_pushstring(Luas, s);                   /* s t ... */
+    lua_rawget(Luas, -2);                      /* s? t ... */
+    if (lua_type(Luas, -1) == LUA_TSTRING) {   /* s t ... */
+        ls.s = lua_tolstring(Luas, -1, &ls.l);
+        /*
+            s is supposed to be anchored (e.g in the registry)
+            so it's not garbage collected
+        */
+        lua_pop(Luas, 2);
+        return ls.s;
+    }
+    lua_pop(Luas, 2);
+    return NULL ;
+}
+
+@ @c
+void pdf_end_page(PDF pdf)
+{
+    char s[64], *p;
+    int j, annots = 0, beads = 0, callback_id;
+    pdf_resource_struct *res_p = pdf->page_resources;
+    pdf_resource_struct local_page_resources;
+    pdf_object_list *annot_list, *bead_list, *link_list, *ol, *ol1;
+    scaledpos save_cur_page_size; /* to save |pdf->page_size| during flushing pending forms */
+    shipping_mode_e save_shipping_mode;
+    int xform_resources;
+    int page_resources, page_attributes;
+    int procset = PROCSET_PDF;
+    /* Finish stream of page/form contents */
+    pdf_goto_pagemode(pdf);
+    if (pos_stack_used > 0) {
+        formatted_error("pdf backend","%u unmatched 'save' after %s shipout", (unsigned int) pos_stack_used,
+            ((global_shipping_mode == SHIPPING_PAGE) ? "page" : "form"));
+    }
+    pdf_end_stream(pdf);
+    pdf_end_obj(pdf);
+    callback_id = callback_defined(finish_pdfpage_callback);
+    if (callback_id > 0)
+      run_callback(callback_id, "b->",(global_shipping_mode == SHIPPING_PAGE));
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        pdf->last_pages = pdf_do_page_divert(pdf, pdf->last_page, 0);
+        /* Write out /Page object */
+        pdf_begin_obj(pdf, pdf->last_page, OBJSTM_ALWAYS);
+        pdf_begin_dict(pdf);
+        pdf_dict_add_name(pdf, "Type", "Page");
+        pdf_dict_add_ref(pdf, "Contents", pdf->last_stream);
+        pdf_dict_add_ref(pdf, "Resources", res_p->last_resources);
+        pdf_add_name(pdf, "MediaBox");
+        pdf_begin_array(pdf);
+        pdf_add_int(pdf, 0);
+        pdf_add_int(pdf, 0);
+        pdf_add_bp(pdf, pdf->page_size.h);
+        pdf_add_bp(pdf, pdf->page_size.v);
+        pdf_end_array(pdf);
+        page_attributes = pdf_page_attr ; /* lookup once */
+        if (page_attributes != null)
+            pdf_print_toks(pdf, page_attributes);
+        print_pdf_table_string(pdf, "pageattributes");
+        pdf_dict_add_ref(pdf, "Parent", pdf->last_pages);
+        if (pdf->img_page_group_val != 0) {
+            pdf_dict_add_ref(pdf, "Group", pdf->img_page_group_val);
+        }
+        annot_list = get_page_resources_list(pdf, obj_type_annot);
+        link_list = get_page_resources_list(pdf, obj_type_link);
+        if (annot_list != NULL || link_list != NULL) {
+            annots = pdf_create_obj(pdf, obj_type_annots, 0);
+            pdf_dict_add_ref(pdf, "Annots", annots);
+        }
+        bead_list = get_page_resources_list(pdf, obj_type_bead);
+        if (bead_list != NULL) {
+            beads = pdf_create_obj(pdf, obj_type_beads, 0);
+            pdf_dict_add_ref(pdf, "B", beads);
+        }
+        pdf_end_dict(pdf);
+        pdf_end_obj(pdf);
+        pdf->img_page_group_val = 0;
+        /* Generate array of annotations or beads in page */
+        if (annot_list != NULL || link_list != NULL) {
+            pdf_begin_obj(pdf, annots, OBJSTM_ALWAYS);
+            pdf_begin_array(pdf);
+            while (annot_list != NULL) {
+                pdf_add_ref(pdf, annot_list->info);
+                annot_list = annot_list->link;
+            }
+            while (link_list != NULL) {
+                pdf_add_ref(pdf, link_list->info);
+                link_list = link_list->link;
+            }
+            pdf_end_array(pdf);
+            pdf_end_obj(pdf);
+        }
+        if (bead_list != NULL) {
+            pdf_begin_obj(pdf, beads, OBJSTM_ALWAYS);
+            pdf_begin_array(pdf);
+            while (bead_list != NULL) {
+                pdf_add_ref(pdf, bead_list->info);
+                bead_list = bead_list->link;
+            }
+            pdf_end_array(pdf);
+            pdf_end_obj(pdf);
+        }
+    }
+    /* Write out resource lists and pending raw objects */
+    ol = get_page_resources_list(pdf, obj_type_obj);
+    while (ol != NULL) {
+        if (!is_obj_written(pdf, ol->info))
+            pdf_write_obj(pdf, ol->info);
+        ol = ol->link;
+    }
+
+    /*
+        When flushing pending forms we need to save and restore resource lists
+        which are also used by page shipping. Saving and restoring
+        |pdf->page_size| is needed for proper writing out pending PDF marks.
+    */
+    ol = get_page_resources_list(pdf, obj_type_xform);
+    while (ol != NULL) {
+        if (!is_obj_written(pdf, ol->info)) {
+            pdf_cur_form = ol->info;
+            save_cur_page_size = pdf->page_size;
+            save_shipping_mode = global_shipping_mode;
+            pdf->page_resources = &local_page_resources;
+            local_page_resources.resources_tree = NULL;
+            ship_out(pdf, obj_xform_box(pdf, pdf_cur_form), SHIPPING_FORM);
+            /* Restore page size and page resources */
+            pdf->page_size = save_cur_page_size;
+            global_shipping_mode = save_shipping_mode;
+            destroy_page_resources_tree(pdf);
+            pdf->page_resources = res_p;
+        }
+        ol = ol->link;
+    }
+    /* Write out pending images */
+    ol = get_page_resources_list(pdf, obj_type_ximage);
+    while (ol != NULL) {
+        if (!is_obj_written(pdf, ol->info))
+            pdf_write_image(pdf, ol->info);
+        ol = ol->link;
+    }
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        /* Write out pending PDF marks and annotations */
+        ol = get_page_resources_list(pdf, obj_type_annot);
+        while (ol != NULL) {
+            if (ol->info > 0 && obj_type(pdf, ol->info) == obj_type_annot) {
+                j = obj_annot_ptr(pdf, ol->info); /* |j| points to |pdf_annot_node| */
+                pdf_begin_obj(pdf, ol->info, OBJSTM_ALWAYS);
+                pdf_begin_dict(pdf);
+                pdf_dict_add_name(pdf, "Type", "Annot");
+                pdf_print_toks(pdf, pdf_annot_data(j));
+                pdf_rectangle(pdf, j);
+                pdf_end_dict(pdf);
+                pdf_end_obj(pdf);
+            }
+            ol = ol->link;
+        }
+        /* Write out PDF link annotations */
+        if ((ol = get_page_resources_list(pdf, obj_type_link)) != NULL) {
+            while (ol != NULL) {
+                j = obj_annot_ptr(pdf, ol->info);
+                pdf_begin_obj(pdf, ol->info, OBJSTM_ALWAYS);
+                pdf_begin_dict(pdf);
+                pdf_dict_add_name(pdf, "Type", "Annot");
+                if (pdf_action_type(pdf_link_action(j)) != pdf_action_user)
+                    pdf_dict_add_name(pdf, "Subtype", "Link");
+                if (pdf_link_attr(j) != null)
+                    pdf_print_toks(pdf, pdf_link_attr(j));
+                pdf_rectangle(pdf, j);
+                if (pdf_action_type(pdf_link_action(j)) != pdf_action_user)
+                    pdf_puts(pdf, "/A ");
+                write_action(pdf, pdf_link_action(j));
+                pdf_end_dict(pdf);
+                pdf_end_obj(pdf);
+                ol = ol->link;
+            }
+            /* Flush |pdf_start_link_node|'s created by |append_link| */
+            ol = get_page_resources_list(pdf, obj_type_link);
+            while (ol != NULL) {
+                j = obj_annot_ptr(pdf, ol->info);
+                /*
+                    nodes with |subtype = pdf_link_data_node| were created by |append_link| and
+                    must be flushed here, as they are not linked in any list
+                */
+                if (subtype(j) == pdf_link_data_node)
+                    flush_node(j);
+                ol = ol->link;
+            }
+        }
+        /* Write out PDF mark destinations */
+        write_out_pdf_mark_destinations(pdf);
+        /* Write out PDF bead rectangle specifications */
+        print_bead_rectangles(pdf);
+    }
+    /* Write out resources dictionary */
+    pdf_begin_obj(pdf, res_p->last_resources, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    /* Print additional resources */
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        page_resources = pdf_page_resources; /* lookup once */
+        if (page_resources != null)
+            pdf_print_toks(pdf, page_resources);
+        print_pdf_table_string(pdf, "pageresources");
+    } else {
+        xform_resources = pdf_xform_resources; /* lookup once */
+        if (xform_resources != null)
+            pdf_print_toks(pdf, xform_resources);
+        if (obj_xform_resources(pdf, pdf_cur_form) != null) {
+            pdf_print_toks(pdf, obj_xform_resources(pdf, pdf_cur_form));
+            delete_token_ref(obj_xform_resources(pdf, pdf_cur_form));
+            set_obj_xform_resources(pdf, pdf_cur_form, null);
+        }
+        if (obj_xform_resources_str(pdf, pdf_cur_form) != null) {
+            lua_pdf_literal(pdf, obj_xform_resources_str(pdf, pdf_cur_form));
+            luaL_unref(Luas, LUA_REGISTRYINDEX, obj_xform_resources_str(pdf, pdf_cur_form));
+            set_obj_xform_resources_str(pdf, pdf_cur_form, null);
+        }
+    }
+    /* Generate font resources */
+    if ((ol = get_page_resources_list(pdf, obj_type_font)) != NULL) {
+        pdf_add_name(pdf, "Font");
+        pdf_begin_dict(pdf);
+        while (ol != NULL) {
+            p = s;
+            p += snprintf(p, 20, "F%i", obj_info(pdf, ol->info));
+            if (pdf->resname_prefix != NULL)
+                p += snprintf(p, 20, "%s", pdf->resname_prefix);
+            pdf_dict_add_ref(pdf, s, ol->info);
+            ol = ol->link;
+        }
+        pdf_end_dict(pdf);
+        procset |= PROCSET_TEXT;
+    }
+    /* Generate XObject resources */
+    ol = get_page_resources_list(pdf, obj_type_xform);
+    ol1 = get_page_resources_list(pdf, obj_type_ximage);
+    if (ol != NULL || ol1 != NULL) {
+        pdf_add_name(pdf, "XObject");
+        pdf_begin_dict(pdf);
+        while (ol != NULL) {
+            p = s;
+            p += snprintf(p, 20, "Fm%i", obj_info(pdf, ol->info));
+            if (pdf->resname_prefix != NULL)
+                p += snprintf(p, 20, "%s", pdf->resname_prefix);
+            pdf_dict_add_ref(pdf, s, ol->info);
+            ol = ol->link;
+        }
+        while (ol1 != null) {
+            p = s;
+            p += snprintf(p, 20, "Im%i", obj_info(pdf, ol1->info));
+            if (pdf->resname_prefix != NULL)
+                p += snprintf(p, 20, "%s", pdf->resname_prefix);
+            pdf_dict_add_ref(pdf, s, ol1->info);
+            procset |= img_procset(idict_array[obj_data_ptr(pdf, ol1->info)]);
+            ol1 = ol1->link;
+        }
+        pdf_end_dict(pdf);
+    }
+    /* Generate ProcSet */
+    if (pdf->major_version == 1) {
+        pdf_add_name(pdf, "ProcSet");
+        pdf_begin_array(pdf);
+        if ((procset & PROCSET_PDF) != 0)
+            pdf_add_name(pdf, "PDF");
+        if ((procset & PROCSET_TEXT) != 0)
+            pdf_add_name(pdf, "Text");
+        if ((procset & PROCSET_IMAGE_B) != 0)
+            pdf_add_name(pdf, "ImageB");
+        if ((procset & PROCSET_IMAGE_C) != 0)
+            pdf_add_name(pdf, "ImageC");
+        if ((procset & PROCSET_IMAGE_I) != 0)
+            pdf_add_name(pdf, "ImageI");
+        pdf_end_array(pdf);
+    }
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+}
+
+@* Finishing the PDF output file.
+
+@ Destinations that have been referenced but don't exists have
+|obj_dest_ptr=null|. Leaving them undefined might cause troubles for PDF
+browsers, so we need to fix them; they point to the last page.
+
+@c
+static void check_nonexisting_destinations(PDF pdf)
+{
+    int k;
+    for (k = pdf->head_tab[obj_type_dest]; k != 0; k = obj_link(pdf, k)) {
+        if (obj_dest_ptr(pdf, k) == null) {
+            if (obj_info(pdf, k) < 0) {
+                char *ss = makecstring(-obj_info(pdf, k));
+                formatted_warning("pdf backend", "unreferenced destination with name '%s'",ss);
+            } else {
+                formatted_warning("pdf backend", "unreferenced destination with num '%d'",obj_info(pdf,k));
+            }
+
+            pdf_begin_obj(pdf, k, OBJSTM_ALWAYS);
+            pdf_begin_array(pdf);
+            pdf_add_ref(pdf, pdf->last_page);
+            pdf_add_name(pdf, "Fit");
+            pdf_end_array(pdf);
+            pdf_end_obj(pdf);
+        }
+    }
+}
+
+@ @c
+static void check_nonexisting_pages(PDF pdf)
+{
+    struct avl_traverser t;
+    oentry *p;
+    struct avl_table *page_tree = pdf->obj_tree[obj_type_page];
+    avl_t_init(&t, page_tree);
+    /* search from the end backward until the last real page is found */
+    for (p = avl_t_last(&t, page_tree); p != NULL && obj_aux(pdf, p->objptr) == 0; p = avl_t_prev(&t)) {
+        formatted_warning("pdf backend", "page %d has been referenced but does not exist",obj_info(pdf, p->objptr));
+    }
+}
+
+@ If the same keys in a dictionary are given several times, then it is not
+defined which value is choosen by an application. Therefore the keys |/Producer|
+and |/Creator| are only set if the token list |pdf_info_toks| converted to a
+string does not contain these key strings.
+
+@c
+static boolean substr_of_str(const char *s, const char *t)
+{
+    if (strstr(t, s) == NULL)
+        return false;
+    return true;
+}
+
+static int pdf_print_info(PDF pdf, int luatexversion,
+                          str_number luatexrevision)
+{                               /* print info object */
+    boolean creator_given = false;
+    boolean producer_given = false;
+    boolean creationdate_given = false;
+    boolean moddate_given = false;
+    boolean trapped_given = false;
+    char *s = NULL;
+    const char *p = NULL;
+    int k, len = 0;
+    k = pdf_create_obj(pdf, obj_type_info, 0);
+    pdf_begin_obj(pdf, k, 3);   /* keep Info readable unless explicitely forced */
+    pdf_begin_dict(pdf);
+    if (pdf_info_toks != 0) {
+        s = tokenlist_to_cstring(pdf_info_toks, true, &len);
+        creator_given = substr_of_str("/Creator", s);
+        producer_given = substr_of_str("/Producer", s);
+        creationdate_given = substr_of_str("/CreationDate", s);
+        moddate_given = substr_of_str("/ModDate", s);
+        trapped_given = substr_of_str("/Trapped", s);
+    }
+    p = get_pdf_table_string("info");
+    if (p && strlen(p) > 0) {
+        creator_given = creator_given || substr_of_str("/Creator", p);
+        producer_given = producer_given || substr_of_str("/Producer", p);
+        creationdate_given = creationdate_given || substr_of_str("/CreationDate", p);
+        moddate_given = moddate_given || substr_of_str("/ModDate", p);
+        trapped_given = trapped_given || substr_of_str("/Trapped", p);
+    }
+    if (pdf_info_toks != null) {
+        if (len > 0) {
+            pdf_out(pdf, '\n');
+            pdf_puts(pdf, s);
+            pdf_out(pdf, '\n');
+            xfree(s);
+        }
+        delete_token_ref(pdf_info_toks);
+        pdf_info_toks = null;
+    }
+    if (p && strlen(p) > 0) {
+        pdf_out(pdf, '\n');
+        pdf_puts(pdf, p); /* no free, pointer */
+        pdf_out(pdf, '\n');
+    }
+    if ((pdf_suppress_optional_info & 128) == 0 && !producer_given) {
+        pdf_add_name(pdf, "Producer");
+        pdf_puts(pdf, " (LuaTeX-");
+        pdf_puts(pdf, luatex_version_string);
+        pdf_out(pdf, ')');
+    }
+    if ((pdf_suppress_optional_info & 16) == 0 && !creator_given) {
+        pdf_dict_add_string(pdf, "Creator", "TeX");
+    }
+    if ((pdf_suppress_optional_info & 32) == 0 && !creationdate_given) {
+        initialize_start_time(pdf);
+        pdf_dict_add_string(pdf, "CreationDate", pdf->start_time_str);
+    }
+    if ((pdf_suppress_optional_info & 64) == 0 && !moddate_given) {
+        initialize_start_time(pdf);
+        pdf_dict_add_string(pdf, "ModDate", pdf->start_time_str);
+    }
+    if ((pdf_suppress_optional_info & 256) == 0 && !trapped_given) {
+        pdf_dict_add_name(pdf, "Trapped", "False");
+    }
+    if ((pdf_suppress_optional_info & 1) == 0) {
+        pdf_dict_add_string(pdf, "PTEX.FullBanner", luatex_banner);
+    }
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+    return k;
+}
+
+static void build_free_object_list(PDF pdf)
+{
+    int k;
+    int l = 0;
+    set_obj_fresh(pdf, l);      /* null object at begin of list of free objects */
+    for (k = 1; k <= pdf->obj_ptr; k++) {
+        if (!is_obj_written(pdf, k)) {
+            set_obj_link(pdf, l, k);
+            l = k;
+        }
+    }
+    set_obj_link(pdf, l, 0);
+}
+
+@ Now the finish of PDF output file. At this moment all Page objects
+are already written completely to PDF output file.
+
+@c
+void finish_pdf_file(PDF pdf, int luatexversion, str_number luatexrevision)
+{
+    int i, j, k;
+    int root, info, xref_stm = 0, outlines, threads, names_tree;
+    size_t xref_offset_width;
+    int callback_id = callback_defined(stop_run_callback);
+    int callback_id1 = callback_defined(finish_pdffile_callback);
+    if (total_pages == 0) {
+        if (callback_id == 0) {
+            normal_warning("pdf backend","no pages of output.");
+        } else if (callback_id > 0) {
+            run_callback(callback_id, "->");
+        }
+        if (pdf->gone > 0)
+            normal_error("pdf backend","dangling objects discarded, no output file produced.");
+    } else {
+        if (pdf->draftmode == 0) {
+            pdf_flush(pdf); /* to make sure that the output file name has been already created */
+            flush_jbig2_page0_objects(pdf); /* flush page 0 objects from JBIG2 images, if any */
+            if (callback_id1 > 0)
+                run_callback(callback_id1, "->");
+            check_nonexisting_pages(pdf);
+            check_nonexisting_destinations(pdf);
+            /* Output fonts definition */
+            for (k = 1; k <= max_font_id(); k++) {
+                if (font_used(k) && (pdf_font_num(k) < 0)) {
+                    i = -pdf_font_num(k);
+                    for (j = font_bc(k); j <= font_ec(k); j++)
+                        if (quick_char_exists(k, j) && pdf_char_marked(k, j))
+                            pdf_mark_char(i, j);
+                    if ((pdf_font_attr(i) == 0) && (pdf_font_attr(k) != 0)) {
+                        set_pdf_font_attr(i, pdf_font_attr(k));
+                    } else if ((pdf_font_attr(k) == 0) && (pdf_font_attr(i) != 0)) {
+                        set_pdf_font_attr(k, pdf_font_attr(i));
+                    } else if ((pdf_font_attr(i) != 0) && (pdf_font_attr(k) != 0) && (!str_eq_str(pdf_font_attr(i), pdf_font_attr(k)))) {
+                        formatted_warning("pdf backend","fontattr of font %d and %d are conflicting, %k is used",i,k,i);
+                    }
+                }
+            }
+            pdf->gen_tounicode = pdf_gen_tounicode;
+            pdf->omit_cidset = pdf_omit_cidset;
+            k = pdf->head_tab[obj_type_font];
+            while (k != 0) {
+                int f = obj_info(pdf, k);
+                do_pdf_font(pdf, f);
+                k = obj_link(pdf, k);
+            }
+            write_fontstuff(pdf);
+            pdf->last_pages = output_pages_tree(pdf);
+            /* Output outlines */
+            outlines = print_outlines(pdf);
+            /*
+                Output name tree. The name tree is very similiar to Pages tree so
+                its construction should be certain from Pages tree construction.
+                For intermediate node |obj_info| will be the first name and
+                |obj_link| will be the last name in \.{\\Limits} array. Note that
+                |pdf_dest_names_ptr| will be less than |obj_ptr|, so we test if
+                |k < pdf_dest_names_ptr| then |k| is index of leaf in
+                |dest_names|; else |k| will be index in |obj_tab| of some
+                intermediate node.
+             */
+            names_tree = output_name_tree(pdf);
+
+            /* Output article threads */
+            if (pdf->head_tab[obj_type_thread] != 0) {
+                threads = pdf_create_obj(pdf, obj_type_others, 0);
+                pdf_begin_obj(pdf, threads, OBJSTM_ALWAYS);
+                pdf_begin_array(pdf);
+                k = pdf->head_tab[obj_type_thread];
+                while (k != 0) {
+                    pdf_add_ref(pdf, k);
+                    k = obj_link(pdf, k);
+                }
+                pdf_end_array(pdf);
+                pdf_end_obj(pdf);
+                k = pdf->head_tab[obj_type_thread];
+                while (k != 0) {
+                    out_thread(pdf, k);
+                    k = obj_link(pdf, k);
+                }
+            } else {
+                threads = 0;
+            }
+            /* Output the /Catalog object */
+            root = pdf_create_obj(pdf, obj_type_catalog, 0);
+            pdf_begin_obj(pdf, root, OBJSTM_ALWAYS);
+            pdf_begin_dict(pdf);
+            pdf_dict_add_name(pdf, "Type", "Catalog");
+            pdf_dict_add_ref(pdf, "Pages", pdf->last_pages);
+            if (threads != 0)
+                pdf_dict_add_ref(pdf, "Threads", threads);
+            if (outlines != 0)
+                pdf_dict_add_ref(pdf, "Outlines", outlines);
+            if (names_tree != 0)
+                pdf_dict_add_ref(pdf, "Names", names_tree);
+            if (pdf_catalog_toks != null) {
+                pdf_print_toks(pdf, pdf_catalog_toks);
+                delete_token_ref(pdf_catalog_toks);
+                pdf_catalog_toks = null;
+            }
+            print_pdf_table_string(pdf, "catalog");
+            if (pdf_catalog_openaction != 0)
+                pdf_dict_add_ref(pdf, "OpenAction", pdf_catalog_openaction);
+            pdf_end_dict(pdf);
+            pdf_end_obj(pdf);
+            /* last candidate for object stream */
+            info = pdf_print_info(pdf, luatexversion, luatexrevision);
+            /* final object for pdf->os_enable == false */
+            if (pdf->os_enable) {
+                pdf_buffer_select(pdf, OBJSTM_BUF);
+                pdf_os_write_objstream(pdf);
+                pdf_flush(pdf);
+                pdf_buffer_select(pdf, PDFOUT_BUF);
+                /* Output the cross-reference stream dictionary */
+                xref_stm = pdf_create_obj(pdf, obj_type_others, 0);
+                pdf_begin_obj(pdf, xref_stm, OBJSTM_NEVER);     /* final object for pdf->os_enable == true */
+                if ((obj_offset(pdf, pdf->obj_ptr) / 256) > 16777215)
+                    xref_offset_width = 5;
+                else if (obj_offset(pdf, pdf->obj_ptr) > 16777215)
+                    xref_offset_width = 4;
+                else if (obj_offset(pdf, pdf->obj_ptr) > 65535)
+                    xref_offset_width = 3;
+                else
+                    xref_offset_width = 2;
+                /* Build a linked list of free objects */
+                build_free_object_list(pdf);
+                pdf_begin_dict(pdf);
+                pdf_dict_add_name(pdf, "Type", "XRef");
+                pdf_add_name(pdf, "Index");
+                pdf_begin_array(pdf);
+                pdf_add_int(pdf, 0);
+                pdf_add_int(pdf, pdf->obj_ptr + 1);
+                pdf_end_array(pdf);
+                pdf_dict_add_int(pdf, "Size", pdf->obj_ptr + 1);
+                pdf_add_name(pdf, "W");
+                pdf_begin_array(pdf);
+                pdf_add_int(pdf, 1);
+                pdf_add_int(pdf, (int) xref_offset_width);
+                pdf_add_int(pdf, 1);
+                pdf_end_array(pdf);
+                pdf_dict_add_ref(pdf, "Root", root);
+                pdf_dict_add_ref(pdf, "Info", info);
+                if (pdf_trailer_toks != null) {
+                    pdf_print_toks(pdf, pdf_trailer_toks);
+                    delete_token_ref(pdf_trailer_toks);
+                    pdf_trailer_toks = null;
+                }
+                print_pdf_table_string(pdf, "trailer");
+                print_ID(pdf);
+                pdf_dict_add_streaminfo(pdf);
+                pdf_end_dict(pdf);
+                pdf_begin_stream(pdf);
+                for (k = 0; k <= pdf->obj_ptr; k++) {
+                    if (!is_obj_written(pdf, k)) {      /* a free object */
+                        pdf_out(pdf, 0);
+                        pdf_out_bytes(pdf, obj_link(pdf, k), xref_offset_width);
+                        pdf_out(pdf, 255);
+                    } else if (obj_os_idx(pdf, k) == PDF_OS_MAX_OBJS) { /* object not in object stream */
+                        pdf_out(pdf, 1);
+                        pdf_out_bytes(pdf, obj_offset(pdf, k),
+                                      xref_offset_width);
+                        pdf_out(pdf, 0);
+                    } else {    /* object in object stream */
+                        pdf_out(pdf, 2);
+                        pdf_out_bytes(pdf, obj_offset(pdf, k),
+                                      xref_offset_width);
+                        pdf_out(pdf, obj_os_idx(pdf, k));
+                    }
+                }
+                pdf_end_stream(pdf);
+                pdf_end_obj(pdf);
+                pdf_flush(pdf);
+            } else {
+                /* Output the |obj_tab| and build a linked list of free objects */
+                build_free_object_list(pdf);
+                pdf_save_offset(pdf);
+                pdf_puts(pdf, "xref\n");
+                pdf_puts(pdf, "0 ");
+                pdf_print_int_ln(pdf, pdf->obj_ptr + 1);
+                pdf_print_fw_int(pdf, obj_link(pdf, 0));
+                pdf_puts(pdf, " 65535 f \n");
+                for (k = 1; k <= pdf->obj_ptr; k++) {
+                    if (!is_obj_written(pdf, k)) {
+                        pdf_print_fw_int(pdf, obj_link(pdf, k));
+                        pdf_puts(pdf, " 00000 f \n");
+                    } else {
+                        pdf_print_fw_int(pdf, obj_offset(pdf, k));
+                        pdf_puts(pdf, " 00000 n \n");
+                    }
+                }
+            }
+            /* Output the trailer */
+            if (!pdf->os_enable) {
+                pdf_puts(pdf, "trailer\n");
+                pdf_begin_dict(pdf);
+                pdf_dict_add_int(pdf, "Size", pdf->obj_ptr + 1);
+                pdf_dict_add_ref(pdf, "Root", root);
+                pdf_dict_add_ref(pdf, "Info", info);
+                if (pdf_trailer_toks != null) {
+                    pdf_print_toks(pdf, pdf_trailer_toks);
+                    delete_token_ref(pdf_trailer_toks);
+                    pdf_trailer_toks = null;
+                }
+                print_ID(pdf);
+                pdf_end_dict(pdf);
+                pdf_out(pdf, '\n');
+            }
+            pdf_puts(pdf, "startxref\n");
+            pdf->cave = 0;
+            if (pdf->os_enable)
+                pdf_add_longint(pdf, (longinteger) obj_offset(pdf, xref_stm));
+            else
+                pdf_add_longint(pdf, (longinteger) pdf->save_offset);
+            pdf_puts(pdf, "\n%%EOF\n");
+            pdf_flush(pdf);
+            if (callback_id == 0) {
+                tprint_nl("Output written on ");
+                tprint(pdf->file_name);
+                tprint(" (");
+                print_int(total_pages);
+                tprint(" page");
+                if (total_pages != 1)
+                    print_char('s');
+                tprint(", ");
+                print_int(pdf_offset(pdf));
+                tprint(" bytes).");
+                print_ln();
+            } else if (callback_id > 0) {
+                run_callback(callback_id, "->");
+            }
+        } else {
+            if (callback_id > 0) {
+                run_callback(callback_id, "->");
+            }
+        }
+        libpdffinish(pdf);
+        if (pdf->draftmode == 0)
+            close_file(pdf->file);
+        else
+            normal_warning("pdf backend","draftmode enabled, not changing output pdf");
+    }
+    if (callback_id == 0) {
+        if (log_opened_global) {
+            fprintf(log_file, "\nPDF statistics: %d PDF objects out of %d (max. %d)\n",
+                (int) pdf->obj_ptr, (int) pdf->obj_tab_size,
+                (int) sup_obj_tab_size);
+            if (pdf->os->ostm_ctr > 0) {
+                fprintf(log_file, " %d compressed objects within %d object stream%s\n",
+                    (int) pdf->os->o_ctr, (int) pdf->os->ostm_ctr,
+                    (pdf->os->ostm_ctr > 1 ? "s" : ""));
+            }
+            fprintf(log_file, " %d named destinations out of %d (max. %d)\n",
+                (int) pdf->dest_names_ptr, (int) pdf->dest_names_size,
+                (int) sup_dest_names_size);
+            fprintf(log_file, " %d words of extra memory for PDF output out of %d (max. %d)\n",
+                (int) pdf->mem_ptr, (int) pdf->mem_size,
+                (int) sup_pdf_mem_size);
+        }
+    }
+}
+
+@ @c
+void scan_pdfcatalog(PDF pdf)
+{
+    halfword p;
+    scan_toks(false, true);
+    pdf_catalog_toks = concat_tokens(pdf_catalog_toks, def_ref);
+    if (scan_keyword("openaction")) {
+        if (pdf_catalog_openaction != 0) {
+            normal_error("pdf backend", "duplicate of openaction");
+        } else {
+            check_o_mode(pdf, "catalog", 1 << OMODE_PDF, true);
+            p = scan_action(pdf);
+            pdf_catalog_openaction = pdf_create_obj(pdf, obj_type_others, 0);
+            pdf_begin_obj(pdf, pdf_catalog_openaction, OBJSTM_ALWAYS);
+            write_action(pdf, p);
+            pdf_end_obj(pdf);
+            delete_action_ref(p);
+        }
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfgen.c
+++ /dev/null
@@ -1,2516 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#include <kpathsea/c-dir.h>
-#include <kpathsea/c-ctype.h>
-#include "lua/luatex-api.h"
-#include "md5.h"
-
-#define check_nprintf(size_get, size_want) \
-    if ((unsigned)(size_get) >= (unsigned)(size_want)) \
-        formatted_error("pdf backend","snprintf() failed in file %s at line %d", __FILE__, __LINE__);
-
-PDF static_pdf = NULL;
-
-/*tex The commandline interface: */
-
-int output_mode_used;
-int output_mode_option;
-int output_mode_value;
-int draft_mode_option;
-int draft_mode_value;
-
-/*tex Additional keys of the |/Info| dictionary: */
-
-halfword pdf_info_toks;
-
-/*tex Additional keys of the |/Catalog| and its |/OpenAction| dictionary: */
-
-halfword pdf_catalog_toks;
-
-halfword pdf_catalog_openaction;
-
-/*tex Additional keys of the |/Names| dictionary: */
-
-halfword pdf_names_toks;
-
-/*tex Additional keys of the |/Trailer| dictionary" */
-
-halfword pdf_trailer_toks;
-
-/*tex Set to |shipping_mode| when |ship_out| starts */
-
-shipping_mode_e global_shipping_mode = NOT_SHIPPING;
-
-/*tex
-
-    Create a new buffer |strbuf_s| of size |size| and maximum allowed size
-    |limit|. Initialize it and set |p| to begin of data.
-
-*/
-
-strbuf_s *new_strbuf(size_t size, size_t limit)
-{
-    strbuf_s *b;
-    b = xtalloc(1, strbuf_s);
-    b->size = size;
-    b->limit = limit;
-    if (size > 0) {
-        b->p = b->data = xtalloc(b->size, unsigned char);
-    } else {
-        /*tex For other alloc: */
-        b->p = b->data = NULL;
-    }
-    return b;
-}
-
-/*tex Check that |n| bytes more fit into buffer; increase it if required. */
-
-static void strbuf_room(strbuf_s * b, size_t n)
-{
-    unsigned int a;
-    size_t l = (size_t) (b->p - b->data);
-    if (n > b->limit - l)
-        overflow("PDF buffer", (unsigned) b->size);
-    if (n + l > b->size) {
-        a = (unsigned int) (b->size >> 2);
-        if (n + l > b->size + a)
-            b->size = n + l;
-        else if (b->size < b->limit - a)
-            b->size = b->size + a;
-        else
-            b->size = b->limit;
-        b->data = xreallocarray(b->data, unsigned char, (unsigned) b->size);
-        b->p = b->data + l;
-    }
-}
-
-/*tex Seek to position |offset| within buffer. Position must be valid. */
-
-void strbuf_seek(strbuf_s * b, off_t offset)
-{
-    b->p = b->data + offset;
-}
-
-/*tex Get the current buffer fill level, the number of characters.*/
-
-size_t strbuf_offset(strbuf_s * b)
-{
-    return (size_t) (b->p - b->data);
-}
-
-/*tex Put one character into buffer. Make room before if needed. */
-
-void strbuf_putchar(strbuf_s * b, unsigned char c)
-{
-    if ((size_t) (b->p - b->data + 1) > b->size)
-        strbuf_room(b, 1);
-    *b->p++ = c;
-}
-
-/*tex Dump filled buffer part to the \PDF\ file. */
-
-void strbuf_flush(PDF pdf, strbuf_s * b)
-{
-    pdf_out_block(pdf, (const char *) b->data, strbuf_offset(b));
-    strbuf_seek(b, 0);
-}
-
-/*tex We free all dynamically allocated buffer structures. */
-
-void strbuf_free(strbuf_s * b)
-{
-    xfree(b->data);
-    xfree(b);
-}
-
-/*tex This |init_pdf_struct| is called early and only once. */
-
-PDF init_pdf_struct(PDF pdf)
-{
-    os_struct *os;
-    pdf = xtalloc(1, pdf_output_file);
-    memset(pdf, 0, sizeof(pdf_output_file));
-    pdf->job_name = makecstring(job_name);
-    /*tex This will be set by |fix_o_mode|: */
-    output_mode_used = OMODE_NONE;
-    /*tex Used by synctex, we need to use output_mode_used there. */
-    pdf->o_mode = output_mode_used;
-    pdf->o_state = ST_INITIAL;
-    /*tex Initialize \PDF\ and object stream writing */
-    pdf->os = os = xtalloc(1, os_struct);
-    memset(pdf->os, 0, sizeof(os_struct));
-    os->buf[PDFOUT_BUF] = new_strbuf(inf_pdfout_buf_size, sup_pdfout_buf_size);
-    os->buf[OBJSTM_BUF] = new_strbuf(inf_objstm_buf_size, sup_objstm_buf_size);
-    os->obj = xtalloc(PDF_OS_MAX_OBJS, os_obj_data);
-    os->cur_objstm = 0;
-    os->curbuf = PDFOUT_BUF;
-    pdf->buf = os->buf[os->curbuf];
-    /*tex
-        Later ttf_seek_outbuf(TABDIR_OFF + n * 4 * TTF_ULONG_SIZE); in
-        ttf_init_font we need 236 bytes, so we start with 256 bytes as in \PDFTEX.
-    */
-    pdf->fb = new_strbuf(256, 100000000);
-    pdf->stream_deflate = false;
-    pdf->stream_writing = false;
-    /*tex
-        Sometimes it is neccesary to allocate memory for PDF output that cannot be
-        deallocated then, so we use |mem| for this purpose.
-    */
-    pdf->mem_size = inf_pdf_mem_size;
-    pdf->mem = xtalloc(pdf->mem_size, int);
-    /*tex
-        The first word is not used so we can use zero as a value for testing
-        whether a pointer to |mem| is valid.
-    */
-    pdf->mem_ptr = 1;
-    pdf->pstruct = NULL;
-    pdf->posstruct = xtalloc(1, posstructure);
-    pdf->posstruct->pos.h = 0;
-    pdf->posstruct->pos.v = 0;
-    pdf->posstruct->dir = dir_TLT;
-    /*tex Allocated size of |obj_tab| array> */
-    pdf->obj_tab_size = (unsigned) inf_obj_tab_size;
-    pdf->obj_tab = xtalloc(pdf->obj_tab_size + 1, obj_entry);
-    memset(pdf->obj_tab, 0, sizeof(obj_entry));
-    pdf->minor_version = -1;
-    pdf->major_version = -1;
-    pdf->decimal_digits = 4;
-    pdf->gamma = 65536;
-    pdf->image_gamma = 65536;
-    pdf->image_hicolor = 1;
-    pdf->image_apply_gamma = 0;
-    pdf->objcompresslevel = 0;
-    pdf->compress_level = 0;
-    pdf->force_file = 0;
-    pdf->recompress = 0;
-    pdf->draftmode = 0;
-    pdf->inclusion_copy_font = 1;
-    pdf->pk_resolution = 0;
-    pdf->pk_fixed_dpi = 0;
-    pdf->pk_scale_factor = 0;
-    init_dest_names(pdf);
-    pdf->page_resources = NULL;
-    init_pdf_pagecalculations(pdf);
-    pdf->vfstruct = new_vfstruct();
-    return pdf;
-}
-
-/*tex We use |pdf_get_mem| to allocate memory in |mem|. */
-
-int pdf_get_mem(PDF pdf, int s)
-{
-    int a;
-    int ret;
-    if (s > sup_pdf_mem_size - pdf->mem_ptr)
-        overflow("pdf memory size (pdf_mem_size)", (unsigned) pdf->mem_size);
-    if (pdf->mem_ptr + s > pdf->mem_size) {
-        a = pdf->mem_size >> 2;
-        if (pdf->mem_ptr + s > pdf->mem_size + a) {
-            pdf->mem_size = pdf->mem_ptr + s;
-        } else if (pdf->mem_size < sup_pdf_mem_size - a) {
-            pdf->mem_size = pdf->mem_size + a;
-        } else {
-            pdf->mem_size = sup_pdf_mem_size;
-        }
-        pdf->mem = xreallocarray(pdf->mem, int, (unsigned) pdf->mem_size);
-    }
-    ret = pdf->mem_ptr;
-    pdf->mem_ptr = pdf->mem_ptr + s;
-    return ret;
-}
-
-/*tex
-
-    This ensures that |pdfmajorversion| and |pdfminorversion| are set only before
-    any bytes have been written to the generated \PDF\ file. Here also all
-    variables for \PDF\ output are initialized, the \PDF\ file is opened by
-    |ensure_pdf_open|, and the \PDF\ header is written.
-
-*/
-
-void fix_pdf_version(PDF pdf)
-{
-    if (pdf->major_version < 0) {
-        /*tex It is unset. */
-        if (pdf_major_version == 0) {
-            normal_warning("pdf backend","unset major version, using 1 instead");
-            pdf->major_version = 1;
-        } else if ((pdf_major_version < 0) || (pdf_major_version > 2)) {
-            formatted_warning("pdf backend","illegal major version %d, using 1 instead",pdf_major_version);
-            pdf->major_version = 1;
-        } else {
-            pdf->major_version = pdf_major_version;
-        }
-    } else if (pdf->major_version != pdf_major_version) {
-        normal_warning("pdf backend", "the major version cannot be changed after data is written to the PDF file");
-    }
-    if (pdf->minor_version < 0) {
-        /*tex It is unset. */
-        if ((pdf_minor_version < 0) || (pdf_minor_version > 9)) {
-            formatted_warning("pdf backend","illegal minor version %d, using 4 instead",pdf_minor_version);
-            pdf->minor_version = 4;
-        } else {
-            pdf->minor_version = pdf_minor_version;
-        }
-    } else if (pdf->minor_version != pdf_minor_version) {
-        normal_warning("pdf backend", "minorversion cannot be changed after data is written to the PDF file");
-    }
-}
-
-static void fix_pdf_draftmode(PDF pdf)
-{
-    if (pdf->draftmode != draft_mode_par)
-        normal_warning("pdf backend", "draftmode cannot be changed after data is written to the PDF file");
-    if (pdf->draftmode != 0) {
-        /*tex Fix these as they might have been changed in between. */
-        pdf->compress_level = 0;
-        pdf->objcompresslevel = 0;
-    }
-}
-
-#define ZIP_BUF_SIZE  32768
-
-#define check_err(f, fn) \
-    if (f != Z_OK) \
-        formatted_error("pdf backend","zlib %s() failed (error code %d)", fn, f)
-
-static void write_zip(PDF pdf)
-{
-    int flush, err = Z_OK;
-    uInt zip_len;
-    strbuf_s *buf = pdf->buf;
-    z_stream *s = pdf->c_stream;
-    boolean finish = pdf->zip_write_state == ZIP_FINISH;
-    if (pdf->stream_length == 0) {
-        if (s == NULL) {
-            s = pdf->c_stream = xtalloc(1, z_stream);
-            s->zalloc = (alloc_func) 0;
-            s->zfree = (free_func) 0;
-            s->opaque = (voidpf) 0;
-            check_err(deflateInit(s, pdf->compress_level), "deflateInit");
-            pdf->zipbuf = xtalloc(ZIP_BUF_SIZE, char);
-        } else
-            check_err(deflateReset(s), "deflateReset");
-        s->next_out = (Bytef *) pdf->zipbuf;
-        s->avail_out = ZIP_BUF_SIZE;
-    }
-    s->next_in = buf->data;
-    s->avail_in = (uInt) (buf->p - buf->data);
-    while (true) {
-        if (s->avail_out == 0 || (finish && s->avail_out < ZIP_BUF_SIZE)) {
-            zip_len = ZIP_BUF_SIZE - s->avail_out;
-            pdf->gone += (off_t) xfwrite(pdf->zipbuf, 1, zip_len, pdf->file);
-            pdf->last_byte = pdf->zipbuf[zip_len - 1];
-            s->next_out = (Bytef *) pdf->zipbuf;
-            s->avail_out = ZIP_BUF_SIZE;
-        }
-        if (finish) {
-            if (err == Z_STREAM_END) {
-                xfflush(pdf->file);
-                pdf->zip_write_state = NO_ZIP;
-                break;
-            }
-            flush = Z_FINISH;
-        } else {
-            if (s->avail_in == 0)
-                break;
-            flush = Z_NO_FLUSH;
-        }
-        err = deflate(s, flush);
-        if (err != Z_OK && err != Z_STREAM_END)
-            formatted_error("pdf backend","zlib deflate() failed (error code %d)", err);
-    }
-    pdf->stream_length = (off_t) s->total_out;
-}
-
-void zip_free(PDF pdf)
-{
-    if (pdf->zipbuf != NULL) {
-        check_err(deflateEnd(pdf->c_stream), "deflateEnd");
-        xfree(pdf->zipbuf);
-    }
-    xfree(pdf->c_stream);
-}
-
-static void write_nozip(PDF pdf)
-{
-    strbuf_s *buf = pdf->buf;
-    size_t l = strbuf_offset(buf);
-    if (l == 0)
-        return;
-    pdf->stream_length = pdf_offset(pdf) - pdf->save_offset;
-    pdf->gone += (off_t) xfwrite((char *) buf->data, sizeof(char), l, pdf->file);
-    pdf->last_byte = *(buf->p - 1);
-}
-
-/*tex
-
-    The PDF buffer is flushed by calling |pdf_flush|, which checks the variable
-    |zip_write_state| and will compress the buffer before flushing if neccesary.
-    We call |pdf_begin_stream| to begin a stream and |pdf_end_stream| to finish
-    it. The stream contents will be compressed if compression is turn on.
-
-*/
-
-void pdf_flush(PDF pdf)
-{
-    os_struct *os = pdf->os;
-    off_t saved_pdf_gone = pdf->gone;
-    switch (os->curbuf) {
-        case PDFOUT_BUF:
-            if (pdf->draftmode == 0) {
-                switch (pdf->zip_write_state) {
-                    case NO_ZIP:
-                        write_nozip(pdf);
-                        break;
-                    case ZIP_WRITING:
-                    case ZIP_FINISH:
-                        write_zip(pdf);
-                        break;
-                    default:
-                        normal_error("pdf backend", "bad zip state");
-                }
-            } else
-                pdf->zip_write_state = NO_ZIP;
-            strbuf_seek(pdf->buf, 0);
-            if (saved_pdf_gone > pdf->gone)
-                normal_error("pdf backend", "file size exceeds architectural limits (pdf_gone wraps around)");
-            break;
-        case OBJSTM_BUF:
-            break;
-        default:
-            normal_error("pdf backend", "bad buffer state");
-    }
-}
-
-static void pdf_buffer_select(PDF pdf, buffer_e buf)
-{
-    os_struct *os = pdf->os;
-    if (pdf->os_enable && buf == OBJSTM_BUF) {
-        /*tex Switch to object stream: */
-        os->curbuf = OBJSTM_BUF;
-    } else {
-        /*tex Switch to \PDF\ stream: */
-        os->curbuf = PDFOUT_BUF;
-    }
-    pdf->buf = os->buf[pdf->os->curbuf];
-}
-
-/*tex
-
-    We create new |/ObjStm| object if required, and set up cross reference info.
-
-*/
-
-static void pdf_prepare_obj(PDF pdf, int k, int pdf_os_threshold)
-{
-    os_struct *os = pdf->os;
-    strbuf_s *obuf = os->buf[OBJSTM_BUF];
-    if (pdf->objcompresslevel >= pdf_os_threshold)
-        pdf_buffer_select(pdf, OBJSTM_BUF);
-    else
-        pdf_buffer_select(pdf, PDFOUT_BUF);
-    switch (os->curbuf) {
-        case PDFOUT_BUF:
-            obj_offset(pdf, k) = pdf_offset(pdf);
-            /*tex Mark it as not included in any |ObjStm|. */
-            obj_os_idx(pdf, k) = PDF_OS_MAX_OBJS;
-            break;
-        case OBJSTM_BUF:
-            if (os->cur_objstm == 0) {
-                os->cur_objstm =
-                    (unsigned int) pdf_create_obj(pdf, obj_type_objstm, 0);
-                os->idx = 0;
-                /*tex Start a fresh object stream. */
-                obuf->p = obuf->data;
-                /*tex Keep some statistics. */
-                os->ostm_ctr++;
-            }
-            obj_os_idx(pdf, k) = (int) os->idx;
-            obj_os_objnum(pdf, k) = (int) os->cur_objstm;
-            os->obj[os->idx].num = k;
-            os->obj[os->idx].off = obuf->p - obuf->data;
-            break;
-        default:
-            normal_error("pdf backend", "bad object state");
-    }
-}
-
-/*tex
-
-    We set the active buffer pointer and make sure that there are at least |n|
-    bytes free in that buffer, flushing happens if needed.
-
-*/
-
-inline void pdf_room(PDF pdf, int n)
-{
-    strbuf_s *buf = pdf->buf;
-    if ((size_t) (n + buf->p - buf->data) <= buf->size)
-        return;
-    if (pdf->os->curbuf == PDFOUT_BUF) {
-        if ((size_t) n > buf->size)
-            overflow("PDF output buffer", (unsigned) buf->size);
-        if ((size_t) (n + buf->p - buf->data) < buf->limit)
-            strbuf_room(buf, (size_t) n);
-        else
-            pdf_flush(pdf);
-    } else {
-        strbuf_room(buf, (size_t) n);
-    }
-}
-
-void pdf_out_block(PDF pdf, const char *s, size_t n)
-{
-    size_t l;
-    strbuf_s *buf = pdf->buf;
-    do {
-        l = n;
-        if (l > buf->size)
-            l = buf->size;
-        pdf_room(pdf, (int) l);
-        (void) memcpy(buf->p, s, l);
-        buf->p += l;
-        s += l;
-        n -= l;
-    } while (n > 0);
-}
-
-__attribute__ ((format(printf, 2, 3)))
-void pdf_printf(PDF pdf, const char *fmt, ...)
-{
-    va_list args;
-    va_start(args, fmt);
-    if (pdf->printf_buf == NULL) {
-        pdf->printf_buf = xtalloc(PRINTF_BUF_SIZE, char);
-    }
-    (void) vsnprintf(pdf->printf_buf, PRINTF_BUF_SIZE, fmt, args);
-    pdf_puts(pdf, pdf->printf_buf);
-    va_end(args);
-}
-
-void pdf_print(PDF pdf, str_number s)
-{
-    const char *ss;
-    size_t l;
-    if (s >= STRING_OFFSET) {
-        ss = (const char *) str_string(s);
-        l = str_length(s);
-        pdf_out_block(pdf, ss, l);
-    } else {
-        pdf_out(pdf, s);
-    }
-}
-
-void pdf_print_int(PDF pdf, longinteger n)
-{
-    char s[24];
-    int w;
-    w = snprintf(s, 23, "%" LONGINTEGER_PRI "d", (LONGINTEGER_TYPE) n);
-    check_nprintf(w, 23);
-    pdf_out_block(pdf, (const char *) s, (size_t) w);
-}
-
-void print_pdffloat(PDF pdf, pdffloat f)
-{
-    int64_t m = f.m;
-    if (m == 0) {
-        pdf_out(pdf, '0');
-    } else {
-        int e = f.e;
-        if (e == 0) {
-            /*tex division by |ten_pow[0] == 1| */
-            if (m == 1) {
-                pdf_out(pdf, '1');
-            } else {
-                char a[24];
-                snprintf(a, 23, "%" LONGINTEGER_PRI "i", (LONGINTEGER_TYPE) m);
-                pdf_puts(pdf, a);
-            }
-        } else {
-            int t = ten_pow[e] ;
-            if (t == m) {
-                pdf_out(pdf, '1');
-            } else {
-                int i, l, w;
-                char a[24];
-                if (m < 0) {
-                    pdf_out(pdf, '-');
-                    m *= -1;
-                }
-                l = m / t;
-                w = snprintf(a, 23, "%i", l);
-                pdf_out_block(pdf, (const char *) a, (size_t) w);
-                l = m % t;
-                if (l != 0) {
-                    pdf_out(pdf, '.');
-                    snprintf(a, 23, "%d", l + t);
-                    for (i = e; i > 0; i--) {
-                        if (a[i] != '0')
-                            break;
-                        a[i] = '\0';
-                    }
-                    pdf_puts(pdf, (a + 1));
-                }
-            }
-        }
-    }
-}
-
-void pdf_print_str(PDF pdf, const char *s)
-{
-    const char *orig = s;
-    /*tex This initializes at the last string index. */
-    int l = (int) strlen(s) - 1;
-    if (l < 0) {
-        pdf_puts(pdf, "()");
-        return;
-    }
-    /*tex
-        The next might not really be safe as the string could be ``(a)xx(b)'' but
-        so far we never had an issue.
-    */
-    if ((s[0] == '(') && (s[l] == ')')) {
-        pdf_puts(pdf, s);
-        return;
-    }
-    if ((s[0] != '<') || (s[l] != '>') || odd((l + 1))) {
-        pdf_out(pdf, '(');
-        pdf_puts(pdf, s);
-        pdf_out(pdf, ')');
-        return;
-    }
-    s++;
-    while (isxdigit((unsigned char)*s))
-        s++;
-    if (s != orig + l) {
-        pdf_out(pdf, '(');
-        pdf_puts(pdf, orig);
-        pdf_out(pdf, ')');
-        return;
-    }
-    pdf_puts(pdf, orig);
-}
-
-/*tex A stream needs to have a stream dictionary also. */
-
-void pdf_begin_stream(PDF pdf)
-{
-    pdf_puts(pdf, "\nstream\n");
-    pdf_save_offset(pdf);
-    pdf_flush(pdf);
-    if (pdf->stream_deflate) {
-        pdf->zip_write_state = ZIP_WRITING;
-    }
-    pdf->stream_writing = true;
-    pdf->stream_length = 0;
-    pdf->last_byte = 0;
-}
-
-void pdf_end_stream(PDF pdf)
-{
-    os_struct *os = pdf->os;
-    switch (os->curbuf) {
-        case PDFOUT_BUF:
-            if (pdf->zip_write_state == ZIP_WRITING)
-                pdf->zip_write_state = ZIP_FINISH;
-            /*tex This sets| pdf->last_byte|. */
-            pdf_flush(pdf);
-            break;
-        case OBJSTM_BUF:
-            normal_error("pdf backend", "bad buffer in end stream, case 1");
-            break;
-        default:
-            normal_error("pdf backend", "bad buffer in end stream, case 2");
-    }
-    pdf->stream_deflate = false;
-    pdf->stream_writing = false;
-    /*tex This doesn't really belong to the stream: */
-    pdf_out(pdf, '\n');
-    pdf_puts(pdf, "endstream");
-    /*tex Write the stream |/Length|. */
-
-    if (pdf->seek_write_length && pdf->draftmode == 0) {
-        xfseeko(pdf->file, (off_t)pdf->stream_length_offset+12, SEEK_SET, pdf->job_name);
-        fprintf(pdf->file, "  ");
-        xfseeko(pdf->file, (off_t)pdf->stream_length_offset, SEEK_SET, pdf->job_name);
-        fprintf(pdf->file, "%" LONGINTEGER_PRI "i >>", (LONGINTEGER_TYPE) pdf->stream_length);
-        xfseeko(pdf->file, 0, SEEK_END, pdf->job_name);
-    }
-    pdf->seek_write_length = false;
-}
-
-/*tex
-
-    To print |scaled| value to \PDF\ output we need some subroutines to ensure
-    accurary.
-
-*/
-
-
-/*tex We max out at $2^{31}-1$. */
-
-#define max_integer 0x7FFFFFFF
-
-/*tex scaled value corresponds to 100in, exact, 473628672 */
-
-scaled one_hundred_inch = 7227 * 65536;
-
-/*tex scaled value corresponds to 1in (rounded to 4736287) */
-
-scaled one_inch = (7227 * 65536 + 50) / 100;
-
-/*tex scaled value corresponds to 1truein (rounded!) */
-
-scaled one_true_inch = (7227 * 65536 + 50) / 100;
-
-/*tex scaled value corresponds to 100bp */
-
-scaled one_hundred_bp = (7227 * 65536)      /  72;
-
-/*tex scaled value corresponds to 1bp (rounded to 65782) */
-
-scaled one_bp = 65781;
-
-/*tex
-
-    One basepoint is set to exactly 65781, as in \TEX\ itself, because this value
-    is also used for |\pdfpxdimen|.
-
-*/
-
-int ten_pow[10] = {
-    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 /* $10^0..10^9$ */
-};
-
-/*tex
-
-    The function |divide_scaled| divides |s| by |m| using |dd| decimal digits of
-    precision.
-
-*/
-
-scaled round_xn_over_d(scaled x, int n, unsigned int d)
-{
-    boolean positive = true;
-    unsigned t, u, v;
-    if (x < 0) {
-        positive = !positive;
-        x = -(x);
-    }
-    if (n < 0) {
-        positive = !positive;
-        n = -(n);
-    }
-    t = (unsigned) ((x % 0100000) * n);
-    u = (unsigned) (((unsigned) (x) / 0100000) * (unsigned) n + (t / 0100000));
-    v = (u % d) * 0100000 + (t % 0100000);
-    if (u / d >= 0100000)
-        arith_error = true;
-    else
-        u = 0100000 * (u / d) + (v / d);
-    v = v % d;
-    if (2 * v >= d)
-        u++;
-    if (positive)
-        return (scaled) u;
-    else
-        return (-(scaled) u);
-}
-
-void pdf_add_bp(PDF pdf, scaled s)
-{
-    pdffloat a;
-    pdfstructure *p = pdf->pstruct;
-    a.m = i64round(s * p->k1);
-    a.e = pdf->decimal_digits;
-    pdf_check_space(pdf);
-    print_pdffloat(pdf, a);
-    pdf_set_space(pdf);
-}
-
-typedef struct {
-    int obj_type;
-    pdf_object_list *list;
-} pr_entry;
-
-static int comp_page_resources(const void *pa, const void *pb, void *p)
-{
-    int a = ((const pr_entry *) pa)->obj_type;
-    int b = ((const pr_entry *) pb)->obj_type;
-    (void) p;
-    if (a > b)
-        return 1;
-    if (a < b)
-        return -1;
-    return 0;
-}
-
-void addto_page_resources(PDF pdf, pdf_obj_type t, int k)
-{
-    pdf_resource_struct *re;
-    pr_entry *pr, tmp;
-    void **pp;
-    pdf_object_list *p, *item = NULL;
-    re = pdf->page_resources;
-    if (re->resources_tree == NULL) {
-        re->resources_tree = avl_create(comp_page_resources, NULL, &avl_xallocator);
-        if (re->resources_tree == NULL)
-            formatted_error("pdf backend","addto_page_resources(): avl_create() page_resource_tree failed");
-    }
-    tmp.obj_type = t;
-    pr = (pr_entry *) avl_find(re->resources_tree, &tmp);
-    if (pr == NULL) {
-        pr = xtalloc(1, pr_entry);
-        pr->obj_type = t;
-        pr->list = NULL;
-        pp = avl_probe(re->resources_tree, pr);
-        if (pp == NULL)
-            formatted_error("pdf backend","addto_page_resources(): avl_probe() out of memory in insertion");
-    }
-    if (pr->list == NULL) {
-        item = xtalloc(1, pdf_object_list);
-        item->link = NULL;
-        item->info = k;
-        pr->list = item;
-        if (obj_type(pdf, k) == (int)t) {
-            /*tex |k| is an object number. */
-            set_obj_scheduled(pdf, k);
-        }
-    } else {
-        for (p = pr->list; p->info != k && p->link != NULL; p = p->link);
-        if (p->info != k) {
-            item = xtalloc(1, pdf_object_list);
-            item->link = NULL;
-            item->info = k;
-            p->link = item;
-            if (obj_type(pdf, k) == (int)t)
-                set_obj_scheduled(pdf, k);
-        }
-    }
-}
-
-pdf_object_list *get_page_resources_list(PDF pdf, pdf_obj_type t)
-{
-    pdf_resource_struct *re = pdf->page_resources;
-    pr_entry *pr, tmp;
-    if (re == NULL || re->resources_tree == NULL)
-        return NULL;
-    tmp.obj_type = t;
-    pr = (pr_entry *) avl_find(re->resources_tree, &tmp);
-    if (pr == NULL)
-        return NULL;
-    return pr->list;
-}
-
-static void reset_page_resources(PDF pdf)
-{
-    pdf_resource_struct *re = pdf->page_resources;
-    pr_entry *p;
-    struct avl_traverser t;
-    pdf_object_list *l1, *l2;
-    if (re == NULL || re->resources_tree == NULL)
-        return;
-    avl_t_init(&t, re->resources_tree);
-    for (p = avl_t_first(&t, re->resources_tree); p != NULL; p = avl_t_next(&t)) {
-        if (p->list != NULL) {
-            for (l1 = p->list; l1 != NULL; l1 = l2) {
-                l2 = l1->link;
-                free(l1);
-            }
-            /*tex We reset but the AVL tree remains! */
-            p->list = NULL;
-        }
-    }
-}
-
-static void destroy_pg_res_tree(void *pa, void *param)
-{
-    (void) param;
-    xfree(pa);
-}
-
-static void destroy_page_resources_tree(PDF pdf)
-{
-    pdf_resource_struct *re = pdf->page_resources;
-    reset_page_resources(pdf);
-    if (re->resources_tree != NULL)
-        avl_destroy(re->resources_tree, destroy_pg_res_tree);
-    re->resources_tree = NULL;
-}
-
-static void pdf_print_fw_int(PDF pdf, longinteger n)
-{
-    unsigned char digits[11];
-
-    int k = 10;
-    do {
-        k--;
-        digits[k] = (unsigned char) ('0' + (n % 10));
-        n /= 10;
-    } while (k != 0);
-    if (n!=0) {
-        /*tex The absolute value of $n$ is greater than 9999999999. */
-        normal_error("pdf backend", "offset exceeds 10 bytes, try enabling object compression.");
-    }
-    digits[10]='\0';
-    pdf_puts(pdf, (const char *) digits);
-}
-
-/*tex
-
-    We print out an integer |n| as a fixed number |w| of bytes,. This is used in
-    the |XRef| cross-reference stream creator.
-
-*/
-
-static void pdf_out_bytes(PDF pdf, longinteger n, size_t w)
-{
-    /*tex The number of digits in a number being output. */
-    unsigned char bytes[8];
-    int k = (int) w;
-    do {
-        k--;
-        bytes[k] = (unsigned char) (n % 256);
-        n /= 256;
-    } while (k != 0);
-    pdf_out_block(pdf, (const char *) bytes, w);
-}
-
-void pdf_print_str_ln(PDF pdf, const char *s)
-{
-    pdf_print_str(pdf, s);
-    pdf_out(pdf, '\n');
-}
-
-void pdf_print_toks(PDF pdf, halfword p)
-{
-    int len = 0;
-    char *s = tokenlist_to_cstring(p, true, &len);
-    if (len > 0) {
-        pdf_check_space(pdf);
-        pdf_puts(pdf, s);
-        pdf_set_space(pdf);
-    }
-    xfree(s);
-}
-
-void pdf_add_rect_spec(PDF pdf, halfword r)
-{
-    pdf_add_bp(pdf, pdf_ann_left(r));
-    pdf_add_bp(pdf, pdf_ann_bottom(r));
-    pdf_add_bp(pdf, pdf_ann_right(r));
-    pdf_add_bp(pdf, pdf_ann_top(r));
-}
-
-void pdf_rectangle(PDF pdf, halfword r)
-{
-    pdf_add_name(pdf, "Rect");
-    pdf_begin_array(pdf);
-    pdf_add_rect_spec(pdf, r);
-    pdf_end_array(pdf);
-}
-
-static void init_pdf_outputparameters(PDF pdf)
-{
-    int pk_mode;
-    pdf->draftmode = fix_int(draft_mode_par, 0, 1);
-    pdf->compress_level = fix_int(pdf_compress_level, 0, 9);
-    pdf->decimal_digits = fix_int(pdf_decimal_digits, 3, 5);
-    pdf->gamma = fix_int(pdf_gamma, 0, 1000000);
-    pdf->image_gamma = fix_int(pdf_image_gamma, 0, 1000000);
-    pdf->image_hicolor = fix_int(pdf_image_hicolor, 0, 1);
-    pdf->image_apply_gamma = fix_int(pdf_image_apply_gamma, 0, 1);
-    pdf->objcompresslevel = fix_int(pdf_obj_compress_level, 0, MAX_OBJ_COMPRESS_LEVEL);
-    pdf->recompress = fix_int(pdf_recompress, 0, 1);
-    pdf->inclusion_copy_font = fix_int(pdf_inclusion_copy_font, 0, 1);
-    pdf->pk_resolution = fix_int(pdf_pk_resolution, 72, 8000);
-    pdf->pk_fixed_dpi = fix_int(pdf_pk_fixed_dpi, 0, 1);
-    if ((pdf->minor_version >= 5) && (pdf->objcompresslevel > 0)) {
-        pdf->os_enable = true;
-    } else {
-        if (pdf->objcompresslevel > 0) {
-            normal_warning("pdf backend","objcompresslevel > 0 requires minorversion > 4");
-            pdf->objcompresslevel = 0;
-        }
-        pdf->os_enable = false;
-    }
-    if (pdf->pk_resolution == 0) {
-        /*tex If not set from format file or by user take it from \.{texmf.cnf}. */
-        pdf->pk_resolution = pk_dpi;
-    }
-    pdf->pk_scale_factor = divide_scaled(72, pdf->pk_resolution, pk_decimal_digits(pdf,5));
-    if (!callback_defined(read_pk_file_callback)) {
-        pk_mode = pdf_pk_mode;
-        if (pk_mode != null) {
-            char *s = tokenlist_to_cstring(pk_mode, true, NULL);
-            kpse_init_prog("LUATEX", (unsigned) pdf->pk_resolution, s, nil);
-            xfree(s);
-        } else {
-            kpse_init_prog("LUATEX", (unsigned) pdf->pk_resolution, nil, nil);
-        }
-        if (!kpse_var_value("MKTEXPK"))
-            kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_cmdline);
-    }
-    set_job_id(pdf, year_par, month_par, day_par, time_par);
-    if ((pdf_unique_resname > 0) && (pdf->resname_prefix == NULL))
-        pdf->resname_prefix = get_resname_prefix(pdf);
-}
-
-/*tex
-
-    This checks that we have a name for the generated PDF file and that it's
-    open.
-
-*/
-
-void pdf_write_header(PDF pdf)
-{
-    /*tex Initialize variables for \PDF\ output. */
-    fix_pdf_version(pdf);
-    init_pdf_outputparameters(pdf);
-    fix_pdf_draftmode(pdf);
-    /*tex Write \PDF\ header */
-    pdf_printf(pdf, "%%PDF-%d.%d\n", pdf->major_version, pdf->minor_version);
-    /* Some binary crap. */
-    pdf_out(pdf, '%');
-    pdf_out(pdf, 'L' + 128);
-    pdf_out(pdf, 'U' + 128);
-    pdf_out(pdf, 'A' + 128);
-    pdf_out(pdf, 'T' + 128);
-    pdf_out(pdf, 'E' + 128);
-    pdf_out(pdf, 'X' + 128);
-    pdf_out(pdf, 'P' + 128);
-    pdf_out(pdf, 'D' + 128);
-    pdf_out(pdf, 'F' + 128);
-    pdf_out(pdf, '\n');
-}
-
-void pdf_open_file(PDF pdf) {
-    ensure_output_file_open(pdf, ".pdf");
-}
-
-void ensure_output_state(PDF pdf, output_state s)
-{
-    if (pdf->o_state < s) {
-        if (s > ST_INITIAL) {
-            ensure_output_state(pdf, s - 1);
-        }
-        switch (s - 1) {
-            case ST_INITIAL:
-                fix_o_mode();
-                break;
-            case ST_OMODE_FIX:
-                backend_out_control[backend_control_open_file](pdf);
-                break;
-            case ST_FILE_OPEN:
-                backend_out_control[backend_control_write_header](pdf);
-                break;
-            case ST_HEADER_WRITTEN:
-                break;
-            case ST_FILE_CLOSED:
-                break;
-            default:
-                normal_error("pdf backend","weird output state");
-        }
-        pdf->o_state++;
-    }
-}
-
-/*tex
-
-    Write out an accumulated object stream. The object number and byte offset
-    pairs are generated and appended to the ready buffered object stream. By this
-    the value of \.{/First} can be calculated. Then a new \.{/ObjStm} object is
-    generated, and everything is copied to the PDF output buffer, where also
-    compression is done. When calling this procedure, |pdf_os_mode| must be
-    |true|.
-
-*/
-
-static void pdf_os_write_objstream(PDF pdf)
-{
-    os_struct *os = pdf->os;
-    /*tex |n1|, |n2|: |ObjStm| buffer may be reallocated! */
-    unsigned int i, j, n1, n2;
-    strbuf_s *obuf = os->buf[OBJSTM_BUF];
-    if (os->cur_objstm == 0) {
-        /*tex No object stream started. */
-        return;
-    }
-    /*tex Remember end of collected object stream contents. */
-    n1 = (unsigned int) strbuf_offset(obuf);
-    /*tex This is needed here to calculate |/First| for the |ObjStm| dict */
-    for (i = 0, j = 0; i < os->idx; i++) {
-        /*tex Add object-number/byte-offset list to buffer. */
-        pdf_print_int(pdf, (int) os->obj[i].num);
-        pdf_out(pdf, ' ');
-        pdf_print_int(pdf, (int) os->obj[i].off);
-        if (j == 9 || i == os->idx - 1) {
-            /*tex Print out in groups of ten for better readability. */
-            pdf_out(pdf, '\n');
-            j = 0;
-        } else {
-            pdf_out(pdf, ' ');
-            j++;
-        }
-    }
-    /*tex Remember current buffer end. */
-    n2 = (unsigned int) strbuf_offset(obuf);
-    /*tex Switch to \PDF\ stream writing. */
-    pdf_begin_obj(pdf, (int) os->cur_objstm, OBJSTM_NEVER);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "ObjStm");
-    /*tex The number of objects in |ObjStm|. */
-    pdf_dict_add_int(pdf, "N", (int) os->idx);
-    pdf_dict_add_int(pdf, "First", (int) (n2 - n1));
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    /*tex Write object-number/byte-offset list. */
-    pdf_out_block(pdf, (const char *) (obuf->data + n1), (size_t) (n2 - n1));
-    /*tex Write collected object stream contents. */
-    pdf_out_block(pdf, (const char *) obuf->data, (size_t) n1);
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    /*tex We force object stream generation next time. */
-    os->cur_objstm = 0;
-}
-
-/*tex Here comes a bunch of flushers: */
-
-void pdf_begin_dict(PDF pdf)
-{
-    pdf_check_space(pdf);
-    pdf_puts(pdf, "<<");
-    pdf_set_space(pdf);
-}
-
-void pdf_end_dict(PDF pdf)
-{
-    pdf_check_space(pdf);
-    pdf_puts(pdf, ">>");
-    pdf_set_space(pdf);
-}
-
-void pdf_dict_add_bool(PDF pdf, const char *key, int i)
-{
-    pdf_add_name(pdf, key);
-    pdf_add_bool(pdf, i);
-}
-
-void pdf_dict_add_int(PDF pdf, const char *key, int i)
-{
-    pdf_add_name(pdf, key);
-    pdf_add_int(pdf, i);
-}
-
-void pdf_dict_add_name(PDF pdf, const char *key, const char *val)
-{
-    pdf_add_name(pdf, key);
-    pdf_add_name(pdf, val);
-}
-
-void pdf_dict_add_string(PDF pdf, const char *key, const char *val)
-{
-    if (val == NULL)
-        return;
-    pdf_add_name(pdf, key);
-    pdf_check_space(pdf);
-    pdf_print_str(pdf, val);
-    pdf_set_space(pdf);
-}
-
-void pdf_dict_add_ref(PDF pdf, const char *key, int num)
-{
-    pdf_add_name(pdf, key);
-    pdf_add_ref(pdf, num);
-}
-
-void pdf_add_null(PDF pdf)
-{
-    pdf_check_space(pdf);
-    pdf_puts(pdf, "null");
-    pdf_set_space(pdf);
-}
-
-void pdf_add_bool(PDF pdf, int i)
-{
-    pdf_check_space(pdf);
-    if (i == 0)
-        pdf_puts(pdf, "false");
-    else
-        pdf_puts(pdf, "true");
-    pdf_set_space(pdf);
-}
-
-void pdf_add_int(PDF pdf, int i)
-{
-    pdf_check_space(pdf);
-    pdf_print_int(pdf, i);
-    pdf_set_space(pdf);
-}
-
-void pdf_add_longint(PDF pdf, longinteger n)
-{
-    pdf_check_space(pdf);
-    pdf_print_int(pdf, n);
-    pdf_set_space(pdf);
-}
-
-void pdf_add_string(PDF pdf, const char *s)
-{
-    pdf_check_space(pdf);
-    pdf_print_str(pdf, s);
-    pdf_set_space(pdf);
-}
-
-void pdf_add_name(PDF pdf, const char *name)
-{
-    pdf_check_space(pdf);
-    pdf_out(pdf, '/');
-    pdf_puts(pdf, name);
-    pdf_set_space(pdf);
-}
-
-void pdf_add_ref(PDF pdf, int num)
-{
-    pdf_check_space(pdf);
-    pdf_print_int(pdf, num);
-    pdf_puts(pdf, " 0 R");
-    pdf_set_space(pdf);
-}
-
-/*tex
-
-    When we add the stream length and filter entries to a stream dictionary,
-    remember file position for seek.
-
-*/
-
-void pdf_dict_add_streaminfo(PDF pdf)
-{
-    if (pdf->compress_level > 0) {
-        pdf_dict_add_name(pdf, "Filter", "FlateDecode");
-        pdf->stream_deflate = true;
-    }
-    pdf_add_name(pdf, "Length");
-    pdf->stream_length_offset = pdf_offset(pdf) + 1;
-    /*tex Fill in length at |pdf_end_stream| call. */
-    pdf->seek_write_length = true;
-    /*tex We reserve space for 10 decimal digits plus space. */
-    pdf_puts(pdf, " x          ");
-    pdf_set_space(pdf);
-}
-
-void pdf_begin_array(PDF pdf)
-{
-    pdf_check_space(pdf);
-    pdf_out(pdf, '[');
-    pdf_set_space(pdf);
-}
-
-void pdf_end_array(PDF pdf)
-{
-    pdf_check_space(pdf);
-    pdf_out(pdf, ']');
-    pdf_set_space(pdf);
-}
-
-void pdf_begin_obj(PDF pdf, int i, int pdf_os_threshold)
-{
-    os_struct *os = pdf->os;
-    ensure_output_state(pdf, ST_HEADER_WRITTEN);
-    pdf_prepare_obj(pdf, i, pdf_os_threshold);
-    switch (os->curbuf) {
-        case PDFOUT_BUF:
-            pdf_printf(pdf, "%d 0 obj\n", (int) i);
-            break;
-        case OBJSTM_BUF:
-            if (pdf->compress_level == 0) {
-                /*tex Some debugging help. */
-                pdf_printf(pdf, "%% %d 0 obj\n", (int) i);
-            }
-            break;
-        default:
-            normal_error("pdf backend","weird begin object");
-    }
-    pdf_reset_space(pdf);
-}
-
-void pdf_end_obj(PDF pdf)
-{
-    os_struct *os = pdf->os;
-    switch (os->curbuf) {
-        case PDFOUT_BUF:
-            /*tex End a \PDF\ object. */
-            pdf_puts(pdf, "\nendobj\n");
-            break;
-        case OBJSTM_BUF:
-            /*tex Tthe number of objects collected so far in ObjStm: */
-            os->idx++;
-            /*tex Only for statistics: */
-            os->o_ctr++;
-            if (os->idx == PDF_OS_MAX_OBJS) {
-                pdf_os_write_objstream(pdf);
-            } else {
-                /*tex Adobe Reader seems to need this. */
-                pdf_out(pdf, '\n');
-            }
-            break;
-        default:
-            normal_error("pdf backend","weird end object");
-    }
-}
-
-/*
-    Needed for embedding fonts.
-
-*/
-
-pdf_obj *pdf_new_stream(void)
-{
-    pdf_obj *stream = xmalloc(sizeof(pdf_obj));
-    stream->length = 0;
-    stream->data = NULL;
-    return stream;
-}
-
-void pdf_add_stream(pdf_obj * stream, unsigned char *buf, long len)
-{
-    int i;
-    assert(stream != NULL);
-    if (stream->data == NULL) {
-        stream->data = xmalloc((unsigned) len);
-    } else {
-        stream->data =
-            xrealloc(stream->data, (unsigned) len + (unsigned) stream->length);
-    }
-    for (i = 0; i < len; i++) {
-        *(stream->data + stream->length + i) = *(buf + i);
-    }
-    stream->length += (unsigned) len;
-}
-
-void pdf_release_obj(pdf_obj * stream)
-{
-    if (stream != NULL) {
-        if (stream->data != NULL) {
-            xfree(stream->data);
-        }
-        xfree(stream);
-    }
-}
-
-/*
-
-    This one converts any string given in in in an allowed PDF string which can
-    be handled by printf et.al.: \.{\\} is escaped to \.{\\\\}, parenthesis are
-    escaped and control characters are octal encoded. This assumes that the
-    string does not contain any already escaped characters!
-
-*/
-
-char *convertStringToPDFString(const char *in, int len)
-{
-    static char pstrbuf[MAX_PSTRING_LEN];
-    char *out = pstrbuf;
-    int i, j, k;
-    char buf[5];
-    j = 0;
-    for (i = 0; i < len; i++) {
-        check_buf((unsigned) j + sizeof(buf), MAX_PSTRING_LEN);
-        if (((unsigned char) in[i] < '!') || ((unsigned char) in[i] > '~')) {
-            /*tex Convert control characters into octal. */
-            k = snprintf(buf, sizeof(buf), "\\%03o", (unsigned int) (unsigned char) in[i]);
-            check_nprintf(k, sizeof(buf));
-            out[j++] = buf[0];
-            out[j++] = buf[1];
-            out[j++] = buf[2];
-            out[j++] = buf[3];
-        } else if ((in[i] == '(') || (in[i] == ')')) {
-            /*tex Escape parenthesis: */
-            out[j++] = '\\';
-            out[j++] = in[i];
-        } else if (in[i] == '\\') {
-            /*tex Escape backslash: */
-            out[j++] = '\\';
-            out[j++] = '\\';
-        } else {
-            /* Copy char : */
-            out[j++] = in[i];
-        }
-    }
-    out[j] = '\0';
-    return pstrbuf;
-}
-
-/*tex
-
-    This one converts any string given in in in an allowed PDF string which is
-    hexadecimal encoded; |sizeof(out)| should be at least $|lin|*2+1$.
-
-*/
-
-static void convertStringToHexString(const char *in, char *out, int lin)
-{
-    int i, k;
-    char buf[3];
-    int j = 0;
-    for (i = 0; i < lin; i++) {
-        k = snprintf(buf, sizeof(buf), "%02X", (unsigned int) (unsigned char) in[i]);
-        check_nprintf(k, sizeof(buf));
-        out[j++] = buf[0];
-        out[j++] = buf[1];
-    }
-    out[j] = '\0';
-}
-
-/*tex
-
-    We compute the ID string as per PDF specification 1.4 9.3 that stipulates
-    only that the two IDs must be identical when the file is created and that
-    they should be reasonably unique. Since it's difficult to get the file size
-    at this point in the execution of pdfTeX and scanning the info dict is also
-    difficult, we start with a simpler implementation using just the first two
-    items.
-
-    A user supplied trailerid had better be an array! So maybe we need to check
-    for |[]| and error otherwise.
-
-*/
-
-static void print_ID(PDF pdf)
-{
-    if ((pdf_suppress_optional_info & 512) == 0) {
-        const char *p = NULL;
-        pdf_add_name(pdf, "ID");
-        p = get_pdf_table_string("trailerid");
-        if (p && strlen(p) > 0) {
-            pdf_puts(pdf,p);
-        } else if (pdf_trailer_id != 0) {
-            /*tex The user provided one: */
-            pdf_print_toks(pdf, pdf_trailer_id);
-        } else {
-            /*tex The system provided one: */
-            time_t t;
-            size_t size;
-            char time_str[32];
-            md5_state_t state;
-            md5_byte_t digest[16];
-            char id[64];
-            char pwd[4096];
-            md5_init(&state);
-            t = pdf->start_time;
-            size = strftime(time_str, sizeof(time_str), "%Y%m%dT%H%M%SZ", gmtime(&t));
-            md5_append(&state, (const md5_byte_t *) time_str, (int) size);
-            if (getcwd(pwd, sizeof(pwd)) == NULL) {
-                formatted_error("pdf backend","getcwd() failed (%s), (path too long?)", strerror(errno));
-            }
-#ifdef WIN32
-            {
-                char *p;
-                for (p = pwd; *p; p++) {
-                    if (*p == '\\')
-                        *p = '/';
-                    else if (IS_KANJI(p))
-                        p++;
-                }
-            }
-#endif
-            md5_append(&state, (const md5_byte_t *) pwd, (int) strlen(pwd));
-            md5_append(&state, (const md5_byte_t *) "/", 1);
-            md5_append(&state, (const md5_byte_t *) pdf->file_name, (int) strlen(pdf->file_name));
-            md5_finish(&state, digest);
-            convertStringToHexString((char *) digest, id, 16);
-            pdf_begin_array(pdf);
-            pdf_check_space(pdf);
-            pdf_printf(pdf, "<%s> <%s>", id, id);
-            pdf_set_space(pdf);
-            pdf_end_array(pdf);
-        }
-    }
-}
-
-/*tex
-
-    Here we print the |/CreationDate| entry in the form
-    |(D:YYYYMMDDHHmmSSOHH'mm')|. The main difficulty is get the time zone offset.
-    |strftime()| does this in ISO C99 (e.g. newer glibc) with \%z, but we have to
-    work with other systems (e.g. Solaris 2.5).
-
-*/
-
-#define TIME_STR_SIZE 30
-
-static void makepdftime(PDF pdf)
-{
-    struct tm lt, gmt;
-    size_t size;
-    int i, off, off_hours, off_mins;
-    time_t t = pdf->start_time;
-    char *time_str = pdf->start_time_str;
-    /*tex Get the time. */
-    if (utc_option) {
-        lt = *gmtime(&t);
-    } else {
-        lt = *localtime(&t);
-    }
-    size = strftime(time_str, TIME_STR_SIZE, "D:%Y%m%d%H%M%S", &lt);
-    /*tex Expected format: |YYYYmmddHHMMSS|. */
-    if (size == 0) {
-        /*tex Unexpected, contents of |time_str| is undefined .*/
-        time_str[0] = '\0';
-        return;
-    }
-    /*tex
-       A correction for seconds. the PDF reference expects 00..59, therefore we
-       map 60 and 61 to 59.
-    */
-    if (time_str[14] == '6') {
-        time_str[14] = '5';
-        time_str[15] = '9';
-        /*tex For safety: */
-        time_str[16] = '\0';
-    }
-    /*tex Get the time zone offset. */
-    gmt = *gmtime(&t);
-    off = 60 * (lt.tm_hour - gmt.tm_hour) + lt.tm_min - gmt.tm_min;
-    if (lt.tm_year != gmt.tm_year) {
-        off += (lt.tm_year > gmt.tm_year) ? 1440 : -1440;
-    } else if (lt.tm_yday != gmt.tm_yday) {
-        off += (lt.tm_yday > gmt.tm_yday) ? 1440 : -1440;
-    }
-    if (off == 0) {
-        time_str[size++] = 'Z';
-        time_str[size] = 0;
-    } else {
-        off_hours = off / 60;
-        off_mins = abs(off - off_hours * 60);
-        i = snprintf(&time_str[size], 9, "%+03d'%02d'", off_hours, off_mins);
-        check_nprintf(i, 9);
-    }
-    pdf->start_time = t;
-}
-
-void initialize_start_time(PDF pdf)
-{
-    if (pdf->start_time == 0) {
-        pdf->start_time = get_start_time();
-        pdf->start_time_str = xtalloc(TIME_STR_SIZE, char);
-        makepdftime(pdf);
-    }
-}
-
-char *getcreationdate(PDF pdf)
-{
-    initialize_start_time(pdf);
-    return pdf->start_time_str;
-}
-
-void remove_pdffile(PDF pdf)
-{
-    if (pdf != NULL) {
-        if (!kpathsea_debug && pdf->file_name && (pdf->draftmode == 0)) {
-            xfclose(pdf->file, pdf->file_name);
-            remove(pdf->file_name);
-        }
-    }
-}
-
-/*tex We use this checker in other modules. It is not pdf specific. */
-
-void check_o_mode(PDF pdf, const char *s, int o_mode_bitpattern, boolean strict)
-{
-
-    output_mode o_mode;
-    const char *m = NULL;
-    if (lua_only) {
-        normal_error("lua only","no backend present, needed for what you asked for");
-        return ;
-    }
-    if (output_mode_used == OMODE_NONE)
-        o_mode = get_o_mode();
-    else
-        o_mode = output_mode_used;
-    /*tex This is used by synctex, we need to use output_mode_used there. */
-    pdf->o_mode = output_mode_used;
-    if (!((1 << o_mode) & o_mode_bitpattern)) {
-        switch (o_mode) {
-            case OMODE_DVI:
-                m = "DVI";
-                break;
-            case OMODE_PDF:
-                m = "PDF";
-                break;
-            default:
-               normal_error("pdf backend","weird output state");
-         }
-        if (strict)
-            formatted_error("pdf backend", "%s not allowed in %s mode (outputmode = %d)",s, m, (int) output_mode_par);
-        else
-            formatted_warning("pdf backend", "%s not allowed in %s mode (outputmode = %d)",s, m, (int) output_mode_par);
-    } else if (strict)
-        ensure_output_state(pdf, ST_HEADER_WRITTEN);
-}
-
-void ensure_output_file_open(PDF pdf, const char *ext)
-{
-    char *fn;
-    if (pdf->file_name != NULL)
-        return;
-    if (job_name == 0)
-        open_log_file();
-    fn = pack_job_name(ext);
-    if (pdf->draftmode == 0 || output_mode_used == OMODE_DVI) {
-        while (!lua_b_open_out(&pdf->file, fn))
-            fn = prompt_file_name("file name for output", ext);
-    }
-    pdf->file_name = fn;
-}
-
-/*tex till here */
-
-void set_job_id(PDF pdf, int year, int month, int day, int time)
-{
-    char *name_string, *format_string, *s;
-    size_t slen;
-    int i;
-    if (pdf->job_id_string != NULL)
-        return;
-    name_string = makecstring(job_name);
-    format_string = makecstring(format_ident);
-    slen = SMALL_BUF_SIZE + strlen(name_string) + strlen(format_string) + strlen(luatex_banner);
-    s = xtalloc(slen, char);
-    /*tex The \WEBC\ version string starts with a space. (Really?) */
-    i = snprintf(s, slen, "%.4d/%.2d/%.2d %.2d:%.2d %s %s %s", year, month, day, time / 60, time % 60, name_string, format_string, luatex_banner);
-    check_nprintf(i, slen);
-    pdf->job_id_string = xstrdup(s);
-    xfree(s);
-    xfree(name_string);
-    xfree(format_string);
-}
-
-char *get_resname_prefix(PDF pdf)
-{
-    static char name_str[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-    /*tex We make a tag of 6 characters long. */
-    static char prefix[7];
-    short i;
-    size_t base = strlen(name_str);
-    unsigned long crc = crc32(0L, Z_NULL, 0);
-    crc = crc32(crc, (Bytef *) pdf->job_id_string, (uInt) strlen(pdf->job_id_string));
-    for (i = 0; i < 6; i++) {
-        prefix[i] = name_str[crc % base];
-        crc /= base;
-    }
-    prefix[6] = '\0';
-    return prefix;
-}
-
-void pdf_begin_page(PDF pdf)
-{
-    int xform_attributes;
-    int xform_type = 0;
-    scaled form_margin = pdf_xform_margin;
-    ensure_output_state(pdf, ST_HEADER_WRITTEN);
-    init_pdf_pagecalculations(pdf);
-    if (pdf->page_resources == NULL) {
-        pdf->page_resources = xtalloc(1, pdf_resource_struct);
-        pdf->page_resources->resources_tree = NULL;
-    }
-    pdf->page_resources->last_resources = pdf_create_obj(pdf, obj_type_others, 0);
-    reset_page_resources(pdf);
-
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        pdf->last_page = pdf_get_obj(pdf, obj_type_page, total_pages + 1, 0);
-        /*tex Mark that this page has been created. */
-        set_obj_aux(pdf, pdf->last_page, 1);
-        pdf->last_stream = pdf_create_obj(pdf, obj_type_pagestream, 0);
-        pdf_begin_obj(pdf, pdf->last_stream, OBJSTM_NEVER);
-        pdf->last_thread = null;
-        pdf_begin_dict(pdf);
-    } else {
-        xform_type = obj_xform_type(pdf, pdf_cur_form) ;
-        pdf_begin_obj(pdf, pdf_cur_form, OBJSTM_NEVER);
-        pdf->last_stream = pdf_cur_form;
-        /*tex Write out the |Form| stream header */
-        pdf_begin_dict(pdf);
-        if (xform_type == 0) {
-            pdf_dict_add_name(pdf, "Type", "XObject");
-            pdf_dict_add_name(pdf, "Subtype", "Form");
-            pdf_dict_add_int(pdf, "FormType", 1);
-        }
-        xform_attributes = pdf_xform_attr;
-        /*tex Now stored in the object: */
-        form_margin = obj_xform_margin(pdf, pdf_cur_form);
-        if (xform_attributes != null)
-            pdf_print_toks(pdf, xform_attributes);
-        if (obj_xform_attr(pdf, pdf_cur_form) != null) {
-            pdf_print_toks(pdf, obj_xform_attr(pdf, pdf_cur_form));
-            delete_token_ref(obj_xform_attr(pdf, pdf_cur_form));
-            set_obj_xform_attr(pdf, pdf_cur_form, null);
-        }
-        if (obj_xform_attr_str(pdf, pdf_cur_form) != null) {
-            lua_pdf_literal(pdf, obj_xform_attr_str(pdf, pdf_cur_form),1);
-            luaL_unref(Luas, LUA_REGISTRYINDEX, obj_xform_attr_str(pdf, pdf_cur_form));
-            set_obj_xform_attr_str(pdf, pdf_cur_form, null);
-        }
-        if (xform_type == 0 || xform_type == 1 || xform_type == 3) {
-            pdf_add_name(pdf, "BBox");
-            pdf_begin_array(pdf);
-            pdf_add_bp(pdf, -form_margin);
-            pdf_add_bp(pdf, -form_margin);
-            pdf_add_bp(pdf, pdf->page_size.h + form_margin);
-            pdf_add_bp(pdf, pdf->page_size.v + form_margin);
-            pdf_end_array(pdf);
-        }
-        if (xform_type == 0 || xform_type == 2 || xform_type == 3) {
-            pdf_add_name(pdf, "Matrix");
-            pdf_begin_array(pdf);
-            pdf_add_int(pdf, 1);
-            pdf_add_int(pdf, 0);
-            pdf_add_int(pdf, 0);
-            pdf_add_int(pdf, 1);
-            pdf_add_int(pdf, 0);
-            pdf_add_int(pdf, 0);
-            pdf_end_array(pdf);
-        }
-        pdf_dict_add_ref(pdf, "Resources", pdf->page_resources->last_resources);
-    }
-    /*tex Start a stream of page or form contents: */
-    pdf_dict_add_streaminfo(pdf);
-    pdf_end_dict(pdf);
-    pdf_begin_stream(pdf);
-    /*tex Start with an empty stack: */
-    pos_stack_used = 0;
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        colorstackpagestart();
-    }
-    if (global_shipping_mode == SHIPPING_PAGE)
-        pdf_out_colorstack_startpage(pdf);
-}
-
-void print_pdf_table_string(PDF pdf, const char *s)
-{
-    size_t len;
-    const char *ls;
-    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lua_key_index(pdf_data));
-    lua_rawget(Luas, LUA_REGISTRYINDEX);
-    lua_pushstring(Luas, s);
-    lua_rawget(Luas, -2);
-    if (lua_type(Luas, -1) == LUA_TSTRING) {
-        ls = lua_tolstring(Luas, -1, &len);
-        if (len > 0) {
-            pdf_check_space(pdf);
-            pdf_out_block(pdf, ls, len);
-            pdf_set_space(pdf);
-        }
-    }
-    lua_pop(Luas, 2);
-}
-
-const char *get_pdf_table_string(const char *s)
-{
-    const_lstring ls;
-    lua_rawgeti(Luas, LUA_REGISTRYINDEX, lua_key_index(pdf_data));
-    lua_rawget(Luas, LUA_REGISTRYINDEX);
-    lua_pushstring(Luas, s);
-    lua_rawget(Luas, -2);
-    if (lua_type(Luas, -1) == LUA_TSTRING) {
-        ls.s = lua_tolstring(Luas, -1, &ls.l);
-        /*tex
-            Here |s| is supposed to be anchored (e.g.\ in the registry) so it's
-            not garbage collected.
-        */
-        lua_pop(Luas, 2);
-        return ls.s;
-    }
-    lua_pop(Luas, 2);
-    return NULL ;
-}
-
-void pdf_end_page(PDF pdf)
-{
-    char s[64], *p;
-    int j, annots = 0, beads = 0, callback_id;
-    pdf_resource_struct *res_p = pdf->page_resources;
-    pdf_resource_struct local_page_resources;
-    pdf_object_list *annot_list, *bead_list, *link_list, *ol, *ol1;
-    /*tex We save |pdf->page_size| during flushing pending forms: */
-    scaledpos save_cur_page_size;
-    shipping_mode_e save_shipping_mode;
-    int save_pdf_cur_form;
-    int xform_resources;
-    int page_resources, page_attributes;
-    int procset = PROCSET_PDF;
-    /*tex Finish the stream of page or form contents: */
-    pdf_goto_pagemode(pdf);
-    if (pos_stack_used > 0) {
-        formatted_error("pdf backend","%u unmatched 'save' after %s shipout", (unsigned int) pos_stack_used,
-            ((global_shipping_mode == SHIPPING_PAGE) ? "page" : "form"));
-    }
-    pdf_end_stream(pdf);
-    pdf_end_obj(pdf);
-    callback_id = callback_defined(finish_pdfpage_callback);
-    if (callback_id > 0)
-      run_callback(callback_id, "b->",(global_shipping_mode == SHIPPING_PAGE));
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        pdf->last_pages = pdf_do_page_divert(pdf, pdf->last_page, 0);
-        /*tex  Write out the |/Page| object. */
-        pdf_begin_obj(pdf, pdf->last_page, OBJSTM_ALWAYS);
-        pdf_begin_dict(pdf);
-        pdf_dict_add_name(pdf, "Type", "Page");
-        pdf_dict_add_ref(pdf, "Contents", pdf->last_stream);
-        pdf_dict_add_ref(pdf, "Resources", res_p->last_resources);
-        pdf_add_name(pdf, "MediaBox");
-        pdf_begin_array(pdf);
-        pdf_add_int(pdf, 0);
-        pdf_add_int(pdf, 0);
-        pdf_add_bp(pdf, pdf->page_size.h);
-        pdf_add_bp(pdf, pdf->page_size.v);
-        pdf_end_array(pdf);
-        page_attributes = pdf_page_attr ;
-        if (page_attributes != null)
-            pdf_print_toks(pdf, page_attributes);
-        print_pdf_table_string(pdf, "pageattributes");
-        pdf_dict_add_ref(pdf, "Parent", pdf->last_pages);
-        if (pdf->img_page_group_val != 0) {
-            pdf_dict_add_ref(pdf, "Group", pdf->img_page_group_val);
-        }
-        annot_list = get_page_resources_list(pdf, obj_type_annot);
-        link_list = get_page_resources_list(pdf, obj_type_link);
-        if (annot_list != NULL || link_list != NULL) {
-            annots = pdf_create_obj(pdf, obj_type_annots, 0);
-            pdf_dict_add_ref(pdf, "Annots", annots);
-        }
-        bead_list = get_page_resources_list(pdf, obj_type_bead);
-        if (bead_list != NULL) {
-            beads = pdf_create_obj(pdf, obj_type_beads, 0);
-            pdf_dict_add_ref(pdf, "B", beads);
-        }
-        pdf_end_dict(pdf);
-        pdf_end_obj(pdf);
-        pdf->img_page_group_val = 0;
-        /*tex Generate an array of annotations or beads in page. */
-        if (annot_list != NULL || link_list != NULL) {
-            pdf_begin_obj(pdf, annots, OBJSTM_ALWAYS);
-            pdf_begin_array(pdf);
-            while (annot_list != NULL) {
-                pdf_add_ref(pdf, annot_list->info);
-                annot_list = annot_list->link;
-            }
-            while (link_list != NULL) {
-                pdf_add_ref(pdf, link_list->info);
-                link_list = link_list->link;
-            }
-            pdf_end_array(pdf);
-            pdf_end_obj(pdf);
-        }
-        if (bead_list != NULL) {
-            pdf_begin_obj(pdf, beads, OBJSTM_ALWAYS);
-            pdf_begin_array(pdf);
-            while (bead_list != NULL) {
-                pdf_add_ref(pdf, bead_list->info);
-                bead_list = bead_list->link;
-            }
-            pdf_end_array(pdf);
-            pdf_end_obj(pdf);
-        }
-    }
-    /*tex Write out the resource lists and pending raw objects. */
-    ol = get_page_resources_list(pdf, obj_type_obj);
-    while (ol != NULL) {
-        if (!is_obj_written(pdf, ol->info))
-            pdf_write_obj(pdf, ol->info);
-        ol = ol->link;
-    }
-    /*tex
-        When flushing pending forms we need to save and restore resource lists
-        which are also used by page shipping. Saving and restoring
-        |pdf->page_size| is needed for proper writing out pending \PDF\ marks.
-    */
-    ol = get_page_resources_list(pdf, obj_type_xform);
-    while (ol != NULL) {
-        if (!is_obj_written(pdf, ol->info)) {
-            save_pdf_cur_form = pdf_cur_form;
-            pdf_cur_form = ol->info;
-            save_cur_page_size = pdf->page_size;
-            save_shipping_mode = global_shipping_mode;
-            pdf->page_resources = &local_page_resources;
-            local_page_resources.resources_tree = NULL;
-            ship_out(pdf, obj_xform_box(pdf, pdf_cur_form), SHIPPING_FORM);
-            /*tex Restore the page size and page resources. */
-            pdf->page_size = save_cur_page_size;
-            global_shipping_mode = save_shipping_mode;
-            destroy_page_resources_tree(pdf);
-            pdf->page_resources = res_p;
-            pdf_cur_form = save_pdf_cur_form;
-        }
-        ol = ol->link;
-    }
-    /*tex Write out pending images. */
-    ol = get_page_resources_list(pdf, obj_type_ximage);
-    while (ol != NULL) {
-        if (!is_obj_written(pdf, ol->info))
-            pdf_write_image(pdf, ol->info);
-        ol = ol->link;
-    }
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        /*tex Write out pending \PDF\ marks and annotations. */
-        ol = get_page_resources_list(pdf, obj_type_annot);
-        while (ol != NULL) {
-            if (ol->info > 0 && obj_type(pdf, ol->info) == obj_type_annot) {
-                /*tex Here |j| points to |pdf_annot_node|: */
-                j = obj_annot_ptr(pdf, ol->info);
-                pdf_begin_obj(pdf, ol->info, OBJSTM_ALWAYS);
-                pdf_begin_dict(pdf);
-                pdf_dict_add_name(pdf, "Type", "Annot");
-                pdf_print_toks(pdf, pdf_annot_data(j));
-                pdf_rectangle(pdf, j);
-                pdf_end_dict(pdf);
-                pdf_end_obj(pdf);
-            }
-            ol = ol->link;
-        }
-        /*tex Write out PDF link annotations. */
-        if ((ol = get_page_resources_list(pdf, obj_type_link)) != NULL) {
-            while (ol != NULL) {
-                j = obj_annot_ptr(pdf, ol->info);
-                pdf_begin_obj(pdf, ol->info, OBJSTM_ALWAYS);
-                pdf_begin_dict(pdf);
-                pdf_dict_add_name(pdf, "Type", "Annot");
-                if (pdf_action_type(pdf_link_action(j)) != pdf_action_user)
-                    pdf_dict_add_name(pdf, "Subtype", "Link");
-                if (pdf_link_attr(j) != null)
-                    pdf_print_toks(pdf, pdf_link_attr(j));
-                pdf_rectangle(pdf, j);
-                if (pdf_action_type(pdf_link_action(j)) != pdf_action_user)
-                    pdf_puts(pdf, "/A ");
-                write_action(pdf, pdf_link_action(j));
-                pdf_end_dict(pdf);
-                pdf_end_obj(pdf);
-                ol = ol->link;
-            }
-            /*tex Flush |pdf_start_link_node|'s created by |append_link|. */
-            ol = get_page_resources_list(pdf, obj_type_link);
-            while (ol != NULL) {
-                j = obj_annot_ptr(pdf, ol->info);
-                /*tex
-                    Nodes with |subtype = pdf_link_data_node| were created by
-                    |append_link| and must be flushed here, as they are not
-                    linked in any list.
-                */
-                if (subtype(j) == pdf_link_data_node)
-                    flush_node(j);
-                ol = ol->link;
-            }
-        }
-        /*tex Write out \PDF\ mark destinations. */
-        write_out_pdf_mark_destinations(pdf);
-        /*tex Write out \PDF\ bead rectangle specifications. */
-        print_bead_rectangles(pdf);
-    }
-    /*tex Write out resources dictionary. */
-    pdf_begin_obj(pdf, res_p->last_resources, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    /*tex Print additional resources. */
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        page_resources = pdf_page_resources;
-        if (page_resources != null) {
-            pdf_print_toks(pdf, page_resources);
-        }
-        print_pdf_table_string(pdf, "pageresources");
-    } else {
-        xform_resources = pdf_xform_resources;
-        if (xform_resources != null) {
-            pdf_print_toks(pdf, xform_resources);
-        }
-        if (obj_xform_resources(pdf, pdf_cur_form) != null) {
-            pdf_print_toks(pdf, obj_xform_resources(pdf, pdf_cur_form));
-            delete_token_ref(obj_xform_resources(pdf, pdf_cur_form));
-            set_obj_xform_resources(pdf, pdf_cur_form, null);
-        }
-        if (obj_xform_resources_str(pdf, pdf_cur_form) != null) {
-            lua_pdf_literal(pdf, obj_xform_resources_str(pdf, pdf_cur_form),1);
-            luaL_unref(Luas, LUA_REGISTRYINDEX, obj_xform_resources_str(pdf, pdf_cur_form));
-            set_obj_xform_resources_str(pdf, pdf_cur_form, null);
-        }
-    }
-    /*tex Generate font resources. */
-    if ((ol = get_page_resources_list(pdf, obj_type_font)) != NULL) {
-        pdf_add_name(pdf, "Font");
-        pdf_begin_dict(pdf);
-        while (ol != NULL) {
-            p = s;
-            p += snprintf(p, 20, "F%i", obj_info(pdf, ol->info));
-            if (pdf->resname_prefix != NULL)
-                p += snprintf(p, 20, "%s", pdf->resname_prefix);
-            pdf_dict_add_ref(pdf, s, ol->info);
-            ol = ol->link;
-        }
-        pdf_end_dict(pdf);
-        procset |= PROCSET_TEXT;
-    }
-    /*tex Generate |XObject| resources. */
-    ol = get_page_resources_list(pdf, obj_type_xform);
-    ol1 = get_page_resources_list(pdf, obj_type_ximage);
-    if (ol != NULL || ol1 != NULL) {
-        pdf_add_name(pdf, "XObject");
-        pdf_begin_dict(pdf);
-        while (ol != NULL) {
-            p = s;
-            p += snprintf(p, 20, "Fm%i", obj_info(pdf, ol->info));
-            if (pdf->resname_prefix != NULL)
-                p += snprintf(p, 20, "%s", pdf->resname_prefix);
-            pdf_dict_add_ref(pdf, s, ol->info);
-            ol = ol->link;
-        }
-        while (ol1 != null) {
-            p = s;
-            p += snprintf(p, 20, "Im%i", obj_info(pdf, ol1->info));
-            if (pdf->resname_prefix != NULL)
-                p += snprintf(p, 20, "%s", pdf->resname_prefix);
-            pdf_dict_add_ref(pdf, s, ol1->info);
-            procset |= img_procset(idict_array[obj_data_ptr(pdf, ol1->info)]);
-            ol1 = ol1->link;
-        }
-        pdf_end_dict(pdf);
-    }
-    /*tex Generate |ProcSet| in version 1.*/
-    if (pdf->major_version == 1) {
-        pdf_add_name(pdf, "ProcSet");
-        pdf_begin_array(pdf);
-        if ((procset & PROCSET_PDF) != 0)
-            pdf_add_name(pdf, "PDF");
-        if ((procset & PROCSET_TEXT) != 0)
-            pdf_add_name(pdf, "Text");
-        if ((procset & PROCSET_IMAGE_B) != 0)
-            pdf_add_name(pdf, "ImageB");
-        if ((procset & PROCSET_IMAGE_C) != 0)
-            pdf_add_name(pdf, "ImageC");
-        if ((procset & PROCSET_IMAGE_I) != 0)
-            pdf_add_name(pdf, "ImageI");
-        pdf_end_array(pdf);
-    }
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-}
-
-/*tex
-
-    We're now ready to wrap up the output file. Destinations that have been
-    referenced but don't exists have |obj_dest_ptr=null|. Leaving them undefined
-    might cause troubles for PDF browsers, so we need to fix them; they point to
-    the last page.
-
-*/
-
-static void check_nonexisting_destinations(PDF pdf)
-{
-    int k;
-    for (k = pdf->head_tab[obj_type_dest]; k != 0; k = obj_link(pdf, k)) {
-        if (obj_dest_ptr(pdf, k) == null) {
-            if (obj_info(pdf, k) < 0) {
-                char *ss = makecstring(-obj_info(pdf, k));
-                formatted_warning("pdf backend", "unreferenced destination with name '%s'",ss);
-            } else {
-                formatted_warning("pdf backend", "unreferenced destination with num '%d'",obj_info(pdf,k));
-            }
-
-            pdf_begin_obj(pdf, k, OBJSTM_ALWAYS);
-            pdf_begin_array(pdf);
-            pdf_add_ref(pdf, pdf->last_page);
-            pdf_add_name(pdf, "Fit");
-            pdf_end_array(pdf);
-            pdf_end_obj(pdf);
-        }
-    }
-}
-
-static void check_nonexisting_pages(PDF pdf)
-{
-    struct avl_traverser t;
-    oentry *p;
-    struct avl_table *page_tree = pdf->obj_tree[obj_type_page];
-    avl_t_init(&t, page_tree);
-    /*tex Search from the end backward until the last real page is found. */
-    for (p = avl_t_last(&t, page_tree); p != NULL && obj_aux(pdf, p->objptr) == 0; p = avl_t_prev(&t)) {
-        formatted_warning("pdf backend", "page %d has been referenced but does not exist",obj_info(pdf, p->objptr));
-    }
-}
-
-/*tex
-
-    If the same keys in a dictionary are given several times, then it is not
-    defined which value is choosen by an application. Therefore the keys
-    |/Producer| and |/Creator| are only set if the token list |pdf_info_toks|
-    converted to a string does not contain these key strings.
-
-*/
-
-static boolean substr_of_str(const char *s, const char *t)
-{
-    if (strstr(t, s) == NULL)
-        return false;
-    return true;
-}
-
-static int pdf_print_info(PDF pdf, int luatexversion, str_number luatexrevision)
-{
-    boolean creator_given = false;
-    boolean producer_given = false;
-    boolean creationdate_given = false;
-    boolean moddate_given = false;
-    boolean trapped_given = false;
-    char *s = NULL;
-    const char *p = NULL;
-    int k, len = 0;
-    k = pdf_create_obj(pdf, obj_type_info, 0);
-    pdf_begin_obj(pdf, k, 3);
-    pdf_begin_dict(pdf);
-    if (pdf_info_toks != 0) {
-        s = tokenlist_to_cstring(pdf_info_toks, true, &len);
-        creator_given = substr_of_str("/Creator", s);
-        producer_given = substr_of_str("/Producer", s);
-        creationdate_given = substr_of_str("/CreationDate", s);
-        moddate_given = substr_of_str("/ModDate", s);
-        trapped_given = substr_of_str("/Trapped", s);
-    }
-    p = get_pdf_table_string("info");
-    if (p && strlen(p) > 0) {
-        creator_given = creator_given || substr_of_str("/Creator", p);
-        producer_given = producer_given || substr_of_str("/Producer", p);
-        creationdate_given = creationdate_given || substr_of_str("/CreationDate", p);
-        moddate_given = moddate_given || substr_of_str("/ModDate", p);
-        trapped_given = trapped_given || substr_of_str("/Trapped", p);
-    }
-    if (pdf_info_toks != null) {
-        if (len > 0) {
-            pdf_check_space(pdf);
-            pdf_puts(pdf, s);
-            pdf_set_space(pdf);
-            xfree(s);
-        }
-        delete_token_ref(pdf_info_toks);
-        pdf_info_toks = null;
-    }
-    if (p && strlen(p) > 0) {
-        pdf_check_space(pdf);
-        pdf_puts(pdf, p);
-        pdf_set_space(pdf);
-    }
-    if ((pdf_suppress_optional_info & 128) == 0 && !producer_given) {
-        pdf_add_name(pdf, "Producer");
-        pdf_puts(pdf, " (LuaTeX-");
-        pdf_puts(pdf, luatex_version_string);
-        pdf_out(pdf, ')');
-    }
-    if ((pdf_suppress_optional_info & 16) == 0 && !creator_given) {
-        pdf_dict_add_string(pdf, "Creator", "TeX");
-    }
-    if ((pdf_suppress_optional_info & 32) == 0 && !creationdate_given) {
-        initialize_start_time(pdf);
-        pdf_dict_add_string(pdf, "CreationDate", pdf->start_time_str);
-    }
-    if ((pdf_suppress_optional_info & 64) == 0 && !moddate_given) {
-        initialize_start_time(pdf);
-        pdf_dict_add_string(pdf, "ModDate", pdf->start_time_str);
-    }
-    if ((pdf_suppress_optional_info & 256) == 0 && !trapped_given) {
-        pdf_dict_add_name(pdf, "Trapped", "False");
-    }
-    if ((pdf_suppress_optional_info & 1) == 0) {
-        pdf_dict_add_string(pdf, "PTEX.FullBanner", luatex_banner);
-    }
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-    return k;
-}
-
-static void build_free_object_list(PDF pdf)
-{
-    int k;
-    int l = 0;
-    /*tex A |null| object at the begin of a list of free objects. */
-    set_obj_fresh(pdf, l);
-    for (k = 1; k <= pdf->obj_ptr; k++) {
-        if (!is_obj_written(pdf, k)) {
-            set_obj_link(pdf, l, k);
-            l = k;
-        }
-    }
-    set_obj_link(pdf, l, 0);
-}
-
-/*tex
-
-    Now we can the finish of \PDF\ output file. At this moment all |/Page|
-    objects are already written completely to \PDF\ output file.
-
-*/
-
-void pdf_finish_file(PDF pdf, int fatal_error) {
-    if (fatal_error) {
-        remove_pdffile(static_pdf); /* will become remove_output_file */
-        print_err(" ==> Fatal error occurred, no output PDF file produced!");
-    } else {
-        int i, j, k;
-        int root, info;
-        int xref_stm = 0;
-        int outlines = 0;
-        int threads = 0;
-        int names_tree = 0;
-        size_t xref_offset_width;
-        int luatexversion = luatex_version;
-        str_number luatexrevision = get_luatexrevision();
-        int callback_id = callback_defined(stop_run_callback);
-        int callback_id1 = callback_defined(finish_pdffile_callback);
-        if (total_pages == 0 && !pdf->force_file) {
-            if (callback_id == 0) {
-                normal_warning("pdf backend","no pages of output.");
-            } else if (callback_id > 0) {
-                run_callback(callback_id, "->");
-            }
-            if (pdf->gone > 0) {
-                /* number of bytes gone */
-                normal_error("pdf backend","already written content discarded, no output file produced.");
-            }
-        } else {
-            if (pdf->draftmode == 0) {
-                /*tex We make sure that the output file name has been already created. */
-                pdf_flush(pdf);
-                /*tex Flush page 0 objects from JBIG2 images, if any. */
-                flush_jbig2_page0_objects(pdf);
-                if (callback_id1 > 0) {
-                    run_callback(callback_id1, "->");
-                }
-                if (total_pages > 0) {
-                    check_nonexisting_pages(pdf);
-                    check_nonexisting_destinations(pdf);
-                }
-                /*tex Output fonts definition. */
-                for (k = 1; k <= max_font_id(); k++) {
-                    if (font_used(k) && (pdf_font_num(k) < 0)) {
-                        i = -pdf_font_num(k);
-                        for (j = font_bc(k); j <= font_ec(k); j++)
-                            if (quick_char_exists(k, j) && pdf_char_marked(k, j))
-                                pdf_mark_char(i, j);
-                        if ((pdf_font_attr(i) == 0) && (pdf_font_attr(k) != 0)) {
-                            set_pdf_font_attr(i, pdf_font_attr(k));
-                        } else if ((pdf_font_attr(k) == 0) && (pdf_font_attr(i) != 0)) {
-                            set_pdf_font_attr(k, pdf_font_attr(i));
-                        } else if ((pdf_font_attr(i) != 0) && (pdf_font_attr(k) != 0) && (!str_eq_str(pdf_font_attr(i), pdf_font_attr(k)))) {
-                            formatted_warning("pdf backend","fontattr of font %d and %d are conflicting, %k is used",i,k,i);
-                        }
-                    }
-                }
-                pdf->gen_tounicode = pdf_gen_tounicode;
-                pdf->omit_cidset = pdf_omit_cidset;
-                pdf->omit_charset = pdf_omit_charset;
-                k = pdf->head_tab[obj_type_font];
-                while (k != 0) {
-                    int f = obj_info(pdf, k);
-                    do_pdf_font(pdf, f);
-                    k = obj_link(pdf, k);
-                }
-                write_fontstuff(pdf);
-                if (total_pages > 0) {
-                    pdf->last_pages = output_pages_tree(pdf);
-                    /*tex Output outlines. */
-                    outlines = print_outlines(pdf);
-                    /*tex
-                        The name tree is very similiar to Pages tree so its construction
-                        should be certain from Pages tree construction. For intermediate
-                        node |obj_info| will be the first name and |obj_link| will be the
-                        last name in \.{\\Limits} array. Note that |pdf_dest_names_ptr|
-                        will be less than |obj_ptr|, so we test if |k <
-                        pdf_dest_names_ptr| then |k| is index of leaf in |dest_names|;
-                        else |k| will be index in |obj_tab| of some intermediate node.
-                     */
-                    names_tree = output_name_tree(pdf);
-                    /*tex Output article threads. */
-                    if (pdf->head_tab[obj_type_thread] != 0) {
-                        threads = pdf_create_obj(pdf, obj_type_others, 0);
-                        pdf_begin_obj(pdf, threads, OBJSTM_ALWAYS);
-                        pdf_begin_array(pdf);
-                        k = pdf->head_tab[obj_type_thread];
-                        while (k != 0) {
-                            pdf_add_ref(pdf, k);
-                            k = obj_link(pdf, k);
-                        }
-                        pdf_end_array(pdf);
-                        pdf_end_obj(pdf);
-                        k = pdf->head_tab[obj_type_thread];
-                        while (k != 0) {
-                            out_thread(pdf, k);
-                            k = obj_link(pdf, k);
-                        }
-                    } else {
-                        threads = 0;
-                    }
-                }
-                /*tex Output the |/Catalog| object. */
-                root = pdf_create_obj(pdf, obj_type_catalog, 0);
-                pdf_begin_obj(pdf, root, OBJSTM_ALWAYS);
-                pdf_begin_dict(pdf);
-                pdf_dict_add_name(pdf, "Type", "Catalog");
-                if (total_pages > 0) {
-                    pdf_dict_add_ref(pdf, "Pages", pdf->last_pages);
-                    if (threads != 0) {
-                        pdf_dict_add_ref(pdf, "Threads", threads);
-                    }
-                    if (outlines != 0) {
-                        pdf_dict_add_ref(pdf, "Outlines", outlines);
-                    }
-                    if (names_tree != 0) {
-                        pdf_dict_add_ref(pdf, "Names", names_tree);
-                    }
-                    if (pdf_catalog_toks != null) {
-                        pdf_print_toks(pdf, pdf_catalog_toks);
-                        delete_token_ref(pdf_catalog_toks);
-                        pdf_catalog_toks = null;
-                    }
-                }
-                if (pdf_catalog_openaction != 0) {
-                    pdf_dict_add_ref(pdf, "OpenAction", pdf_catalog_openaction);
-                }
-                print_pdf_table_string(pdf, "catalog");
-                pdf_end_dict(pdf);
-                pdf_end_obj(pdf);
-                info = pdf_print_info(pdf, luatexversion, luatexrevision);
-                if (pdf->os_enable) {
-                    pdf_buffer_select(pdf, OBJSTM_BUF);
-                    pdf_os_write_objstream(pdf);
-                    pdf_flush(pdf);
-                    pdf_buffer_select(pdf, PDFOUT_BUF);
-                    /*tex Output the cross-reference stream dictionary. */
-                    xref_stm = pdf_create_obj(pdf, obj_type_others, 0);
-                    pdf_begin_obj(pdf, xref_stm, OBJSTM_NEVER);
-                    if ((obj_offset(pdf, pdf->obj_ptr) / 256) > 16777215)
-                        xref_offset_width = 5;
-                    else if (obj_offset(pdf, pdf->obj_ptr) > 16777215)
-                        xref_offset_width = 4;
-                    else if (obj_offset(pdf, pdf->obj_ptr) > 65535)
-                        xref_offset_width = 3;
-                    else
-                        xref_offset_width = 2;
-                    /*tex Build a linked list of free objects. */
-                    build_free_object_list(pdf);
-                    pdf_begin_dict(pdf);
-                    pdf_dict_add_name(pdf, "Type", "XRef");
-                    pdf_add_name(pdf, "Index");
-                    pdf_begin_array(pdf);
-                    pdf_add_int(pdf, 0);
-                    pdf_add_int(pdf, pdf->obj_ptr + 1);
-                    pdf_end_array(pdf);
-                    pdf_dict_add_int(pdf, "Size", pdf->obj_ptr + 1);
-                    pdf_add_name(pdf, "W");
-                    pdf_begin_array(pdf);
-                    pdf_add_int(pdf, 1);
-                    pdf_add_int(pdf, (int) xref_offset_width);
-                    pdf_add_int(pdf, 1);
-                    pdf_end_array(pdf);
-                    pdf_dict_add_ref(pdf, "Root", root);
-                    pdf_dict_add_ref(pdf, "Info", info);
-                    if (pdf_trailer_toks != null) {
-                        pdf_print_toks(pdf, pdf_trailer_toks);
-                        delete_token_ref(pdf_trailer_toks);
-                        pdf_trailer_toks = null;
-                    }
-                    print_pdf_table_string(pdf, "trailer");
-                    print_ID(pdf);
-                    pdf_dict_add_streaminfo(pdf);
-                    pdf_end_dict(pdf);
-                    pdf_begin_stream(pdf);
-                    for (k = 0; k <= pdf->obj_ptr; k++) {
-                        if (!is_obj_written(pdf, k)) {
-                            /*tex A free object: */
-                            pdf_out(pdf, 0);
-                            pdf_out_bytes(pdf, obj_link(pdf, k), xref_offset_width);
-                            pdf_out(pdf, 255);
-                        } else if (obj_os_idx(pdf, k) == PDF_OS_MAX_OBJS) {
-                            /*tex  An object not in object stream: */
-                            pdf_out(pdf, 1);
-                            pdf_out_bytes(pdf, obj_offset(pdf, k), xref_offset_width);
-                            pdf_out(pdf, 0);
-                        } else {
-                            /*tex An object in object stream: */
-                            pdf_out(pdf, 2);
-                            pdf_out_bytes(pdf, obj_offset(pdf, k), xref_offset_width);
-                            pdf_out(pdf, obj_os_idx(pdf, k));
-                        }
-                    }
-                    pdf_end_stream(pdf);
-                    pdf_end_obj(pdf);
-                    pdf_flush(pdf);
-                } else {
-                    /*tex Output the |obj_tab| and build a linked list of free objects. */
-                    build_free_object_list(pdf);
-                    pdf_save_offset(pdf);
-                    pdf_puts(pdf, "xref\n");
-                    pdf_puts(pdf, "0 ");
-                    pdf_print_int_ln(pdf, pdf->obj_ptr + 1);
-                    pdf_print_fw_int(pdf, obj_link(pdf, 0));
-                    pdf_puts(pdf, " 65535 f \n");
-                    for (k = 1; k <= pdf->obj_ptr; k++) {
-                        if (!is_obj_written(pdf, k)) {
-                            pdf_print_fw_int(pdf, obj_link(pdf, k));
-                            pdf_puts(pdf, " 00000 f \n");
-                        } else {
-                            pdf_print_fw_int(pdf, obj_offset(pdf, k));
-                            pdf_puts(pdf, " 00000 n \n");
-                        }
-                    }
-                }
-                /*tex Output the trailer. */
-                if (!pdf->os_enable) {
-                    pdf_puts(pdf, "trailer\n");
-                    pdf_reset_space(pdf);
-                    pdf_begin_dict(pdf);
-                    pdf_dict_add_int(pdf, "Size", pdf->obj_ptr + 1);
-                    pdf_dict_add_ref(pdf, "Root", root);
-                    pdf_dict_add_ref(pdf, "Info", info);
-                    if (pdf_trailer_toks != null) {
-                        pdf_print_toks(pdf, pdf_trailer_toks);
-                        delete_token_ref(pdf_trailer_toks);
-                        pdf_trailer_toks = null;
-                    }
-                    print_ID(pdf);
-                    pdf_end_dict(pdf);
-                    pdf_out(pdf, '\n');
-                }
-                pdf_puts(pdf, "startxref\n");
-                pdf_reset_space(pdf);
-                if (pdf->os_enable)
-                    pdf_add_longint(pdf, (longinteger) obj_offset(pdf, xref_stm));
-                else
-                    pdf_add_longint(pdf, (longinteger) pdf->save_offset);
-                pdf_puts(pdf, "\n%%EOF\n");
-                pdf_flush(pdf);
-                if (callback_id == 0) {
-                    tprint_nl("Output written on ");
-                    tprint(pdf->file_name);
-                    tprint(" (");
-                    print_int(total_pages);
-                    tprint(" page");
-                    if (total_pages != 1)
-                        print_char('s');
-                    tprint(", ");
-                    print_int(pdf_offset(pdf));
-                    tprint(" bytes).");
-                    print_ln();
-                } else if (callback_id > 0) {
-                    run_callback(callback_id, "->");
-                }
-                libpdffinish(pdf);
-                close_file(pdf->file);
-            } else {
-                if (callback_id > 0) {
-                    run_callback(callback_id, "->");
-                }
-                libpdffinish(pdf);
-                normal_warning("pdf backend","draftmode enabled, not changing output pdf");
-            }
-        }
-        if (callback_id == 0) {
-            if (log_opened_global) {
-                fprintf(log_file, "\nPDF statistics: %d PDF objects out of %d (max. %d)\n",
-                    (int) pdf->obj_ptr, (int) pdf->obj_tab_size,
-                    (int) sup_obj_tab_size);
-                if (pdf->os->ostm_ctr > 0) {
-                    fprintf(log_file, " %d compressed objects within %d object stream%s\n",
-                        (int) pdf->os->o_ctr, (int) pdf->os->ostm_ctr,
-                        (pdf->os->ostm_ctr > 1 ? "s" : ""));
-                }
-                fprintf(log_file, " %d named destinations out of %d (max. %d)\n",
-                    (int) pdf->dest_names_ptr, (int) pdf->dest_names_size,
-                    (int) sup_dest_names_size);
-                fprintf(log_file, " %d words of extra memory for PDF output out of %d (max. %d)\n",
-                    (int) pdf->mem_ptr, (int) pdf->mem_size,
-                    (int) sup_pdf_mem_size);
-            }
-        }
-    }
-}
-
-void scan_pdfcatalog(PDF pdf)
-{
-    halfword p;
-    scan_toks(false, true);
-    pdf_catalog_toks = concat_tokens(pdf_catalog_toks, def_ref);
-    if (scan_keyword("openaction")) {
-        if (pdf_catalog_openaction != 0) {
-            normal_error("pdf backend", "duplicate of openaction");
-        } else {
-            check_o_mode(pdf, "catalog", 1 << OMODE_PDF, true);
-            p = scan_action(pdf);
-            pdf_catalog_openaction = pdf_create_obj(pdf, obj_type_others, 0);
-            pdf_begin_obj(pdf, pdf_catalog_openaction, OBJSTM_ALWAYS);
-            write_action(pdf, p);
-            pdf_end_obj(pdf);
-            delete_action_ref(p);
-        }
-    }
-}
-
-/*tex
-
-    This function converts double to pdffloat; very small and very large numbers
-    are {\em not} converted to scientific notation. Here n must be a number or
-    real conforming to the implementation limits of \PDF\ as specified in
-    appendix C.1 of the \PDF\ standard. The maximum value of ints is |+2^32|, the
-    maximum value of reals is |+2^15| and the smallest values of reals is
-    |1/(2^16)|.
-
-*/
-
-static pdffloat conv_double_to_pdffloat(double n)
-{
-    pdffloat a;
-    a.e = 6;
-    a.m = i64round(n * ten_pow[a.e]);
-    return a;
-}
-
-void pdf_add_real(PDF pdf, double d)
-{
-    pdf_check_space(pdf);
-    print_pdffloat(pdf, conv_double_to_pdffloat(d));
-    pdf_set_space(pdf);
-}
-
-void pdf_push_list(PDF pdf, scaledpos *saved_pos, int *saved_loc) {
-    /* nothing */
-}
-
-void pdf_pop_list(PDF pdf, scaledpos *saved_pos, int *saved_loc) {
-    /* nothing */
-}
-
-extern void pdf_set_reference_point(PDF pdf, posstructure *refpoint)
-{
-    refpoint->pos.h = pdf_h_origin;
-    refpoint->pos.v = pdf->page_size.v - pdf_v_origin;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfglyph.w
@@ -0,0 +1,254 @@
+% pdfglyph.w
+%
+% Copyright 2009-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 "pdf/pdfpage.h"
+
+@ eternal constants
+
+@c
+#define e_tj 3 /* must be 3; movements in []TJ are in fontsize/$10^3$ units */
+
+@ @c
+static int64_t pdf_char_width(pdfstructure * p, internal_font_number f, int i)
+{
+    /* use exactly this formula also for calculating the /Width array values */
+    return i64round((double) char_width(f, i) / font_size(f) * ten_pow[e_tj + p->cw.e]);
+}
+
+@ @c
+void pdf_print_charwidth(PDF pdf, internal_font_number f, int i)
+{
+    pdffloat cw;
+    pdfstructure *p = pdf->pstruct;
+    cw.m = pdf_char_width(p, f, i);
+    cw.e = p->cw.e;
+    print_pdffloat(pdf, cw);
+}
+
+@ @c
+static void setup_fontparameters(PDF pdf, internal_font_number f, int ex_glyph)
+{
+    float slant, extend, expand, scale = 1.0;
+    float u = 1.0;
+    pdfstructure *p = pdf->pstruct;
+    /* fix mantis bug \# 0000200 (acroread "feature") */
+    if ((font_format(f) == opentype_format || (font_format(f) == type1_format && font_encodingbytes(f) == 2))  && font_units_per_em(f) > 0)
+        u = font_units_per_em(f) / 1000.0;
+    pdf->f_cur = f;
+    p->f_pdf = pdf_set_font(pdf, f);
+    p->fs.m = i64round(font_size(f) / u / by_one_bp * ten_pow[p->fs.e]);
+    slant = font_slant(f) / 1000.0;
+    extend = font_extend(f) / 1000.0;
+    expand = 1.0 + (ex_glyph/1) / 1000.0;
+    p->tj_delta.e = p->cw.e - 1;        /* "- 1" makes less corrections inside []TJ */
+    /* no need to be more precise than TeX (1sp) */
+    while (p->tj_delta.e > 0 && (double) font_size(f) / ten_pow[p->tj_delta.e + e_tj] < 0.5)
+        p->tj_delta.e--;        /* happens for very tiny fonts */
+    p->tm[0].m = i64round(scale * expand * extend * ten_pow[p->tm[0].e]);
+    p->tm[2].m = i64round(slant * ten_pow[p->tm[2].e]);
+    p->tm[3].m = i64round(scale * ten_pow[p->tm[3].e]);
+    p->k2 = ten_pow[e_tj + p->cw.e] * scale / (ten_pow[p->pdf.h.e] * pdf2double(p->fs) * pdf2double(p->tm[0]));
+    p->cur_ex = ex_glyph ;  /* we keep track of the state of ex */
+}
+
+
+@ @c
+static void set_font(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    pdf_printf(pdf, "/F%d", (int) p->f_pdf);
+    pdf_print_resname_prefix(pdf);
+    pdf_out(pdf, ' ');
+    print_pdffloat(pdf, p->fs);
+    pdf_puts(pdf, " Tf ");
+    p->f_pdf_cur = p->f_pdf;
+    p->fs_cur.m = p->fs.m;
+    p->need_tf = false;
+    p->need_tm = true; /* always follow Tf by Tm */
+}
+
+@ @c
+static void set_textmatrix(PDF pdf, scaledpos pos)
+{
+    boolean move;
+    pdfstructure *p = pdf->pstruct;
+    if (!is_textmode(p))
+        normal_error("pdf backend","text mode expected in set_textmatrix");
+    move = calc_pdfpos(p, pos);
+    if (p->need_tm || move) {
+        print_pdf_matrix(pdf, p->tm);
+        pdf_puts(pdf, " Tm ");
+        p->pdf.h.m = p->pdf_bt_pos.h.m + p->tm[4].m; /* Tm replaces */
+        p->pdf.v.m = p->pdf_bt_pos.v.m + p->tm[5].m;
+        p->need_tm = false;
+    }
+    p->tm0_cur.m = p->tm[0].m;
+}
+
+@ Print out a character to PDF buffer; the character will be printed in octal
+form in the following cases: chars <= 32, backslash (92), left parenthesis
+(40), and right parenthesis (41).
+
+@c
+static void pdf_print_char(PDF pdf, int c)
+{
+    if (c > 255)
+        return;
+    /* pdf_print_escaped(c) */
+    if (c <= 32 || c == '\\' || c == '(' || c == ')' || c > 127) {
+        pdf_room(pdf, 4);
+        pdf_quick_out(pdf, '\\');
+        pdf_quick_out(pdf, (unsigned char) ('0' + ((c >> 6) & 0x3)));
+        pdf_quick_out(pdf, (unsigned char) ('0' + ((c >> 3) & 0x7)));
+        pdf_quick_out(pdf, (unsigned char) ('0' + (c & 0x7)));
+    } else
+        pdf_out(pdf, c);
+}
+
+static void pdf_print_wide_char(PDF pdf, int c)
+{
+    char hex[5];
+    snprintf(hex, 5, "%04X", c);
+    pdf_out_block(pdf, (const char *) hex, 4);
+}
+
+@ @c
+static void begin_charmode(PDF pdf, internal_font_number f, pdfstructure * p)
+{
+    if (!is_chararraymode(p))
+        normal_error("pdf backend","char array mode expected in begin_char_mode");
+    if (font_encodingbytes(f) == 2) {
+        p->ishex = 1;
+        pdf_out(pdf, '<');
+    } else {
+        p->ishex = 0;
+        pdf_out(pdf, '(');
+    }
+    p->mode = PMODE_CHAR;
+}
+
+@ @c
+void end_charmode(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    if (!is_charmode(p))
+        normal_error("pdf backend","char mode expected in end_char_mode");
+    if (p->ishex == 1) {
+        p->ishex = 0;
+        pdf_out(pdf, '>');
+    } else {
+        pdf_out(pdf, ')');
+    }
+    p->mode = PMODE_CHARARRAY;
+}
+
+@ @c
+static void begin_chararray(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    if (!is_textmode(p))
+        normal_error("pdf backend","text mode expected in begin_char_array");
+    p->pdf_tj_pos = p->pdf;
+    p->cw.m = 0;
+    pdf_out(pdf, '[');
+    p->mode = PMODE_CHARARRAY;
+}
+
+@ @c
+void end_chararray(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    if (!is_chararraymode(p))
+        normal_error("pdf backend","char array mode expected in end_char_array");
+    pdf_puts(pdf, "]TJ\n");
+    p->pdf = p->pdf_tj_pos;
+    p->mode = PMODE_TEXT;
+}
+
+@ We need to adapt the tm when a font changes. A change can be a change in id
+(frontend) or pdf reference (backend, as we share font resources). At such a
+change we also need to adapt to the slant and extend. Initially we also need to
+take the exfactor of a glyph into account. When the font is unchanged, we still
+need to check each glyph for a change in exfactor. We store the current one on
+the state record so that we can minimize testing.
+
+@c
+void pdf_place_glyph(PDF pdf, internal_font_number f, int c, int ex)
+{
+    boolean move;
+    pdfstructure *p = pdf->pstruct;
+    scaledpos pos = pdf->posstruct->pos;
+    /* already done:
+        if (!char_exists(f, c)) {
+            return;
+        }
+    */
+    if (font_writingmode(f) == vertical_writingmode) {
+        if (p->wmode != WMODE_V) {
+            p->wmode = WMODE_V;
+            p->need_tm = true;
+        }
+    } else {
+        if (p->wmode != WMODE_H) {
+            p->wmode = WMODE_H;
+            p->need_tm = true;
+        }
+    }
+    if (p->need_tf || f != pdf->f_cur || p->f_pdf != p->f_pdf_cur || p->fs.m != p->fs_cur.m || is_pagemode(p)) {
+         pdf_goto_textmode(pdf);
+         setup_fontparameters(pdf, f, ex);
+         set_font(pdf);
+    } else if (p->tm0_cur.m != p->tm[0].m || p->cur_ex != ex) {
+         setup_fontparameters(pdf, f, ex);
+         p->need_tm = true;
+    }
+    /* all movements */
+    move = calc_pdfpos(p, pos); /* within text or chararray or char mode */
+    if (move || p->need_tm) {
+        if (p->need_tm
+        || (p->wmode == WMODE_H && (p->pdf_bt_pos.v.m + p->tm[5].m) != p->pdf.v.m)
+        || (p->wmode == WMODE_V && (p->pdf_bt_pos.h.m + p->tm[4].m) != p->pdf.h.m)
+        || abs(p->tj_delta.m) >= 1000000) {
+            pdf_goto_textmode(pdf);
+            set_textmatrix(pdf, pos);
+            begin_chararray(pdf);
+            move = calc_pdfpos(p, pos); /* for fine adjustment */
+        }
+        if (move) {
+            if (is_charmode(p))
+                end_charmode(pdf);
+            print_pdffloat(pdf, p->tj_delta);
+            p->cw.m -= p->tj_delta.m * ten_pow[p->cw.e - p->tj_delta.e];
+        }
+    }
+    /* glyph output */
+    if (is_chararraymode(p))
+        begin_charmode(pdf, f, p);
+    else if (!is_charmode(p))
+        normal_error("pdf backend","char (array) mode expected in place_glyph");
+    pdf_mark_char(f, c);
+    if (font_encodingbytes(f) == 2)
+        pdf_print_wide_char(pdf, char_index(f, c));
+    else
+        pdf_print_char(pdf, c);
+    p->cw.m += pdf_char_width(p, p->f_pdf, c);  /* aka |adv_char_width()| */
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfglyph.c
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "pdf/pdfpage.h"
-
-/*tex
-
-    The |e_tj| step must be 3 because movements in |[]TJ| are in fontsize/$10^3$
-    units.
-
-*/
-
-#define e_tj 3
-
-/*tex
-
-    Use exactly this formula also for calculating the |/Width| array values.
-
-*/
-
-static int64_t pdf_char_width(pdfstructure * p, internal_font_number f, int i)
-{
-    return i64round((double) char_width(f, i) / font_size(f) * ten_pow[e_tj + p->cw.e]);
-}
-
-void pdf_print_charwidth(PDF pdf, internal_font_number f, int i)
-{
-    pdffloat cw;
-    pdfstructure *p = pdf->pstruct;
-    cw.m = pdf_char_width(p, f, i);
-    cw.e = p->cw.e;
-    print_pdffloat(pdf, cw);
-}
-
-static void setup_fontparameters(PDF pdf, internal_font_number f, int ex_glyph)
-{
-    float slant, extend, squeeze, expand, scale = 1.0;
-    float u = 1.0;
-    pdfstructure *p = pdf->pstruct;
-    if ((font_format(f) == opentype_format || (font_format(f) == type1_format && font_encodingbytes(f) == 2))  && font_units_per_em(f) > 0)
-        u = font_units_per_em(f) / 1000.0;
-    pdf->f_cur = f;
-    p->f_pdf = pdf_set_font(pdf, f);
-    p->fs.m = i64round(font_size(f) / u / by_one_bp * ten_pow[p->fs.e]);
-    slant = font_slant(f) / 1000.0;
-    extend = font_extend(f) / 1000.0;
-    squeeze = font_squeeze(f) / 1000.0;
-    expand = 1.0 + (ex_glyph/1) / 1000.0;
-    /*tex The |-1| makes less corrections inside |[]TJ|: */
-    p->tj_delta.e = p->cw.e - 1;
-    /*tex There is no need to be more precise than \TEX\ (1sp). */
-    while (p->tj_delta.e > 0 && (double) font_size(f) / ten_pow[p->tj_delta.e + e_tj] < 0.5) {
-        /*tex This happens for very tiny fonts. */
-        p->tj_delta.e--;
-    }
-    p->tm[0].m = i64round(scale * expand * extend * ten_pow[p->tm[0].e]);
-    p->tm[2].m = i64round(slant * ten_pow[p->tm[2].e]);
-    p->tm[3].m = i64round(scale * squeeze * ten_pow[p->tm[3].e]);
-    p->k2 = ten_pow[e_tj + p->cw.e] * scale / (ten_pow[p->pdf.h.e] * pdf2double(p->fs) * pdf2double(p->tm[0]));
-    /*tex We keep track of the state of ex. */
-    p->cur_ex = ex_glyph ;
-    p->need_width = font_width(f);
-    p->need_mode = font_mode(f);
-}
-
-static void set_font(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-
-    if (p->need_width != 0) {
-        pdf_printf(pdf, "%0.3f w\n",((7227.0/7200.0)/1000.0) * p->need_width );
-        p->done_width = 1;
-    } else if (p->done_width) {
-        pdf_puts(pdf, "0 w\n");
-        p->done_width = 0;
-    }
-    if (p->need_mode != 0) {
-        pdf_printf(pdf, "%d Tr\n", (int) p->need_mode);
-        p->done_mode = 1;
-    } else if (p->done_mode) {
-        pdf_puts(pdf, "0 Tr\n");
-        p->done_mode = 0;
-    }
-    pdf_printf(pdf, "/F%d", (int) p->f_pdf);
-    pdf_print_resname_prefix(pdf);
-    pdf_out(pdf, ' ');
-    print_pdffloat(pdf, p->fs);
-    pdf_puts(pdf, " Tf\n");
-    p->f_pdf_cur = p->f_pdf;
-    p->fs_cur.m = p->fs.m;
-    p->need_tf = false;
-    /*tex Always follow |Tf| by |Tm|: */
-    p->need_tm = true;
-}
-
-static void set_textmatrix(PDF pdf, scaledpos pos)
-{
-    boolean move;
-    pdfstructure *p = pdf->pstruct;
-    if (!is_textmode(p))
-        normal_error("pdf backend","text mode expected in set_textmatrix");
-    move = calc_pdfpos(p, pos);
-    if (p->need_tm || move) {
-        print_pdf_matrix(pdf, p->tm);
-        pdf_puts(pdf, " Tm ");
-        /*tex |Tm| replaces */
-        p->pdf.h.m = p->pdf_bt_pos.h.m + p->tm[4].m;
-        p->pdf.v.m = p->pdf_bt_pos.v.m + p->tm[5].m;
-        p->need_tm = false;
-    }
-    p->tm0_cur.m = p->tm[0].m;
-}
-
-/*tex
-
-    Print out a character to PDF buffer; the character will be printed in octal
-    form in the following cases: chars <= 32, backslash (92), left parenthesis
-    (40), and right parenthesis (41).
-
-*/
-
-static void pdf_print_char(PDF pdf, int c)
-{
-    if (c > 255) {
-        return;
-    } else if (c <= 32 || c == '\\' || c == '(' || c == ')' || c > 127) {
-        pdf_room(pdf, 4);
-        pdf_quick_out(pdf, '\\');
-        pdf_quick_out(pdf, (unsigned char) ('0' + ((c >> 6) & 0x3)));
-        pdf_quick_out(pdf, (unsigned char) ('0' + ((c >> 3) & 0x7)));
-        pdf_quick_out(pdf, (unsigned char) ('0' + (c & 0x7)));
-    } else
-        pdf_out(pdf, c);
-}
-
-static void pdf_print_wide_char(PDF pdf, int c)
-{
-    char hex[5];
-    snprintf(hex, 5, "%04X", c);
-    pdf_out_block(pdf, (const char *) hex, 4);
-}
-
-static void begin_charmode(PDF pdf, internal_font_number f, pdfstructure * p)
-{
-    if (!is_chararraymode(p))
-        normal_error("pdf backend","char array mode expected in begin_char_mode");
-    if (font_encodingbytes(f) == 2) {
-        p->ishex = 1;
-        pdf_out(pdf, '<');
-    } else {
-        p->ishex = 0;
-        pdf_out(pdf, '(');
-    }
-    p->mode = PMODE_CHAR;
-}
-
-void end_charmode(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-    if (!is_charmode(p))
-        normal_error("pdf backend","char mode expected in end_char_mode");
-    if (p->ishex == 1) {
-        p->ishex = 0;
-        pdf_out(pdf, '>');
-    } else {
-        pdf_out(pdf, ')');
-    }
-    p->mode = PMODE_CHARARRAY;
-}
-
-static void begin_chararray(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-    if (!is_textmode(p))
-        normal_error("pdf backend","text mode expected in begin_char_array");
-    p->pdf_tj_pos = p->pdf;
-    p->cw.m = 0;
-    pdf_out(pdf, '[');
-    p->mode = PMODE_CHARARRAY;
-}
-
-void end_chararray(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-    if (!is_chararraymode(p))
-        normal_error("pdf backend","char array mode expected in end_char_array");
-    pdf_puts(pdf, "]TJ\n");
-    p->pdf = p->pdf_tj_pos;
-    p->mode = PMODE_TEXT;
-}
-
-/*tex
-
-    We need to adapt the tm when a font changes. A change can be a change in id
-    (frontend) or pdf reference (backend, as we share font resources). At such a
-    change we also need to adapt to the slant and extend. Initially we also need
-    to take the exfactor of a glyph into account. When the font is unchanged, we
-    still need to check each glyph for a change in exfactor. We store the current
-    one on the state record so that we can minimize testing.
-
-*/
-
-void pdf_place_glyph(PDF pdf, internal_font_number f, int c, int ex)
-{
-    boolean move;
-    pdfstructure *p = pdf->pstruct;
-    scaledpos pos = pdf->posstruct->pos;
-    /*tex
-
-        This is already done:
-
-        \startyping
-        if (!char_exists(f, c)) {
-            return;
-        }
-        \stoptyping
-
-    */
-    if (font_writingmode(f) == vertical_writingmode) {
-        if (p->wmode != WMODE_V) {
-            p->wmode = WMODE_V;
-            p->need_tm = true;
-        }
-    } else {
-        if (p->wmode != WMODE_H) {
-            p->wmode = WMODE_H;
-            p->need_tm = true;
-        }
-    }
-    if (p->need_tf || f != pdf->f_cur || p->f_pdf != p->f_pdf_cur || p->fs.m != p->fs_cur.m || is_pagemode(p)) {
-         pdf_goto_textmode(pdf);
-         setup_fontparameters(pdf, f, ex);
-         set_font(pdf);
-    } else if (p->tm0_cur.m != p->tm[0].m || p->cur_ex != ex) {
-         setup_fontparameters(pdf, f, ex);
-         p->need_tm = true;
-    }
-    /*tex All movements within text or chararray or char mode: */
-    move = calc_pdfpos(p, pos);
-    if (move || p->need_tm) {
-        if (p->need_tm
-        || (p->wmode == WMODE_H && (p->pdf_bt_pos.v.m + p->tm[5].m) != p->pdf.v.m)
-        || (p->wmode == WMODE_V && (p->pdf_bt_pos.h.m + p->tm[4].m) != p->pdf.h.m)
-        || abs(p->tj_delta.m) >= 1000000) {
-            pdf_goto_textmode(pdf);
-            set_textmatrix(pdf, pos);
-            begin_chararray(pdf);
-            /*tex For fine adjustment: */
-            move = calc_pdfpos(p, pos);
-        }
-        if (move) {
-            if (is_charmode(p))
-                end_charmode(pdf);
-            print_pdffloat(pdf, p->tj_delta);
-            p->cw.m -= p->tj_delta.m * ten_pow[p->cw.e - p->tj_delta.e];
-        }
-    }
-    /*tex Glyph output: */
-    if (is_chararraymode(p))
-        begin_charmode(pdf, f, p);
-    else if (!is_charmode(p))
-        normal_error("pdf backend","char (array) mode expected in place_glyph");
-    pdf_mark_char(f, c);
-    if (font_encodingbytes(f) == 2)
-        pdf_print_wide_char(pdf, char_index(f, c));
-    else
-        pdf_print_char(pdf, c);
-    /*tex Also known as |adv_char_width()|: */
-    p->cw.m += pdf_char_width(p, p->f_pdf, c);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfimage.w
@@ -0,0 +1,150 @@
+% pdfimage.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"
+
+@ @c
+void place_img(PDF pdf, image_dict * idict, scaled_whd dim, int transform)
+{
+    float a[6];                 /* transformation matrix */
+    float xoff, yoff, tmp;
+    pdfstructure *p = pdf->pstruct;
+    scaledpos pos = pdf->posstruct->pos;
+    int r;                      /* number of digits after the decimal point */
+    int k;
+    scaledpos tmppos;
+    pdffloat cm[6];
+    int groupref;               /* added from web for 1.40.8 */
+    a[0] = a[3] = 1.0e6;
+    a[1] = a[2] = 0;
+    if (img_type(idict) == IMG_TYPE_PDF || img_type(idict) == IMG_TYPE_PDFMEMSTREAM
+        || img_type(idict) == IMG_TYPE_PDFSTREAM) {
+        a[0] /= (float) img_xsize(idict);
+        a[3] /= (float) img_ysize(idict);
+        xoff = (float) img_xorig(idict) / (float) img_xsize(idict);
+        yoff = (float) img_yorig(idict) / (float) img_ysize(idict);
+        r = 6;
+    } else {
+        /* added from web for 1.40.8 */
+        if (img_type(idict) == IMG_TYPE_PNG) {
+            groupref = img_group_ref(idict);
+            if ((groupref > 0) && (pdf->img_page_group_val == 0))
+                pdf->img_page_group_val = groupref;
+        }
+        /* /added from web */
+        a[0] /= (float) one_hundred_bp;
+        a[3] = a[0];
+        xoff = yoff = 0;
+        r = 4;
+    }
+    if ((transform & 7) > 3) { /* mirror cases */
+        a[0] *= -1;
+        xoff *= -1;
+    }
+    switch ((transform + img_rotation(idict)) & 3) {
+    case 0:                    /* no transform */
+        break;
+    case 1:                    /* rot. 90 deg. (counterclockwise) */
+        a[1] = a[0];
+        a[2] = -a[3];
+        a[3] = a[0] = 0;
+        tmp = yoff;
+        yoff = xoff;
+        xoff = -tmp;
+        break;
+    case 2:                    /* rot. 180 deg. (counterclockwise) */
+        a[0] *= -1;
+        a[3] *= -1;
+        xoff *= -1;
+        yoff *= -1;
+        break;
+    case 3:                    /* rot. 270 deg. (counterclockwise) */
+        a[1] = -a[0];
+        a[2] = a[3];
+        a[3] = a[0] = 0;
+        tmp = yoff;
+        yoff = -xoff;
+        xoff = tmp;
+        break;
+    default:;
+    }
+    xoff *= (float) dim.wd;
+    yoff *= (float) (dim.ht + dim.dp);
+    a[0] *= (float) dim.wd;
+    a[1] *= (float) (dim.ht + dim.dp);
+    a[2] *= (float) dim.wd;
+    a[3] *= (float) (dim.ht + dim.dp);
+    a[4] = (float) pos.h - xoff;
+    a[5] = (float) pos.v - yoff;
+    k = transform + img_rotation(idict);
+    if ((transform & 7) > 3)
+        k++;
+    switch (k & 3) {
+    case 0:                    /* no transform */
+        break;
+    case 1:                    /* rot. 90 deg. (counterclockwise) */
+        a[4] += (float) dim.wd;
+        break;
+    case 2:                    /* rot. 180 deg. */
+        a[4] += (float) dim.wd;
+        a[5] += (float) (dim.ht + dim.dp);
+        break;
+    case 3:                    /* rot. 270 deg. */
+        a[5] += (float) (dim.ht + dim.dp);
+        break;
+    default:;
+    }
+    /* the following is a kludge, TODO: use pdfpage.c functions */
+    setpdffloat(cm[0], i64round(a[0]), r);
+    setpdffloat(cm[1], i64round(a[1]), r);
+    setpdffloat(cm[2], i64round(a[2]), r);
+    setpdffloat(cm[3], i64round(a[3]), r);
+    tmppos.h = round(a[4]);
+    tmppos.v = round(a[5]);
+    pdf_goto_pagemode(pdf);
+    (void) calc_pdfpos(p, tmppos);
+    cm[4] = p->cm[4];
+    cm[5] = p->cm[5];
+    if (pdf->img_page_group_val == 0)
+        pdf->img_page_group_val = img_group_ref(idict); /* added from web for 1.40.8 */
+    pdf_puts(pdf, "q\n");
+    pdf_print_cm(pdf, cm);
+    pdf_puts(pdf, "/Im");
+    pdf_print_int(pdf, img_index(idict));
+    pdf_print_resname_prefix(pdf);
+    pdf_puts(pdf, " Do\nQ\n");
+    addto_page_resources(pdf, obj_type_ximage, img_objnum(idict));
+    if (img_state(idict) < DICT_OUTIMG)
+        img_state(idict) = DICT_OUTIMG;
+}
+
+@ for normal output, see \.{pdflistout.w}
+
+@c
+void pdf_place_image(PDF pdf, halfword p)
+{
+    scaled_whd dim;
+    image_dict *idict = idict_array[rule_index(p)];
+    dim.wd = width(p);
+    dim.ht = height(p);
+    dim.dp = depth(p);
+    place_img(pdf, idict, dim, rule_transform(p));
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfimage.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-void place_img(PDF pdf, image_dict * idict, scaled_whd dim, int transform)
-{
-    /*tex A transformation matrix: */
-    float a[6];
-    float xoff, yoff, tmp;
-    pdfstructure *p = pdf->pstruct;
-    scaledpos pos = pdf->posstruct->pos;
-    /*tex The number of digits after the decimal point: */
-    int r;
-    int k;
-    scaledpos tmppos;
-    pdffloat cm[6];
-    int groupref;
-    a[0] = a[3] = 1.0e6;
-    a[1] = a[2] = 0;
-    if (img_type(idict) == IMG_TYPE_PDF || img_type(idict) == IMG_TYPE_PDFMEMSTREAM
-        || img_type(idict) == IMG_TYPE_PDFSTREAM) {
-        a[0] /= (float) img_xsize(idict);
-        a[3] /= (float) img_ysize(idict);
-        xoff = (float) img_xorig(idict) / (float) img_xsize(idict);
-        yoff = (float) img_yorig(idict) / (float) img_ysize(idict);
-        r = 6;
-    } else {
-        if (img_type(idict) == IMG_TYPE_PNG) {
-            groupref = img_group_ref(idict);
-            if ((groupref > 0) && (pdf->img_page_group_val == 0))
-                pdf->img_page_group_val = groupref;
-        }
-        a[0] /= (float) one_hundred_bp;
-        a[3] = a[0];
-        xoff = yoff = 0;
-        r = 4;
-    }
-    if ((transform & 7) > 3) {
-        /*tex Mirror cases: */
-        a[0] *= -1;
-        xoff *= -1;
-    }
-    switch ((transform + img_rotation(idict)) & 3) {
-        case 0:
-            /*tex No transform. */
-            break;
-        case 1:
-            /*tex rotation over 90 degrees (counterclockwise) */
-            a[1] = a[0];
-            a[2] = -a[3];
-            a[3] = a[0] = 0;
-            tmp = yoff;
-            yoff = xoff;
-            xoff = -tmp;
-            break;
-        case 2:
-            /*tex rotation over 180 degrees (counterclockwise) */
-            a[0] *= -1;
-            a[3] *= -1;
-            xoff *= -1;
-            yoff *= -1;
-            break;
-        case 3:
-            /*tex rotation over 270 degrees (counterclockwise) */
-            a[1] = -a[0];
-            a[2] = a[3];
-            a[3] = a[0] = 0;
-            tmp = yoff;
-            yoff = -xoff;
-            xoff = tmp;
-            break;
-        default:;
-    }
-    xoff *= (float) dim.wd;
-    yoff *= (float) (dim.ht + dim.dp);
-    a[0] *= (float) dim.wd;
-    a[1] *= (float) (dim.ht + dim.dp);
-    a[2] *= (float) dim.wd;
-    a[3] *= (float) (dim.ht + dim.dp);
-    a[4] = (float) pos.h - xoff;
-    a[5] = (float) pos.v - yoff;
-    k = transform + img_rotation(idict);
-    if ((transform & 7) > 3)
-        k++;
-    switch (k & 3) {
-        case 0:
-            /*tex No transform */
-            break;
-        case 1:
-            /*tex rotation over 90 degrees (counterclockwise) */
-            a[4] += (float) dim.wd;
-            break;
-        case 2:
-            /*tex rotation over 180 degrees (counterclockwise) */
-            a[4] += (float) dim.wd;
-            a[5] += (float) (dim.ht + dim.dp);
-            break;
-        case 3:
-            /*tex rotation over 270 degrees (counterclockwise) */
-            a[5] += (float) (dim.ht + dim.dp);
-            break;
-        default:;
-    }
-    setpdffloat(cm[0], i64round(a[0]), r);
-    setpdffloat(cm[1], i64round(a[1]), r);
-    setpdffloat(cm[2], i64round(a[2]), r);
-    setpdffloat(cm[3], i64round(a[3]), r);
-    tmppos.h = round(a[4]);
-    tmppos.v = round(a[5]);
-    pdf_goto_pagemode(pdf);
-    (void) calc_pdfpos(p, tmppos);
-    cm[4] = p->cm[4];
-    cm[5] = p->cm[5];
-    if (pdf->img_page_group_val == 0)
-        pdf->img_page_group_val = img_group_ref(idict);
-    pdf_puts(pdf, "q\n");
-    pdf_print_cm(pdf, cm);
-    pdf_puts(pdf, "/Im");
-    pdf_print_int(pdf, img_index(idict));
-    pdf_print_resname_prefix(pdf);
-    pdf_puts(pdf, " Do\nQ\n");
-    addto_page_resources(pdf, obj_type_ximage, img_objnum(idict));
-    if (img_state(idict) < DICT_OUTIMG)
-        img_state(idict) = DICT_OUTIMG;
-}
-
-/*tex For normal output see |pdflistout.c|: */
-
-void pdf_place_image(PDF pdf, halfword p)
-{
-    scaled_whd dim;
-    image_dict *idict = idict_array[rule_index(p)];
-    dim.wd = width(p);
-    dim.ht = height(p);
-    dim.dp = depth(p);
-    place_img(pdf, idict, dim, rule_transform(p));
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdflink.w
@@ -0,0 +1,164 @@
+% pdflink.w
+%
+% Copyright 2009-2012 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"
+
+@ To implement nested link annotations, we need a stack to hold copy of
+|pdf_start_link_node|'s that are being written out, together with their box
+nesting level.
+
+@c
+void push_link_level(PDF pdf, halfword p)
+{
+    if (pdf->link_stack_ptr >= pdf_max_link_level)
+        overflow("pdf link stack size", pdf_max_link_level);
+    pdf->link_stack_ptr++;
+    pdf->link_stack[pdf->link_stack_ptr].nesting_level = cur_s;
+    pdf->link_stack[pdf->link_stack_ptr].link_node = copy_node_list(p);
+    pdf->link_stack[pdf->link_stack_ptr].ref_link_node = p;
+}
+
+@ @c
+void pop_link_level(PDF pdf)
+{
+    flush_node_list(pdf->link_stack[pdf->link_stack_ptr].link_node);
+    pdf->link_stack_ptr--;
+}
+
+@ @c
+void do_link(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
+{
+    scaled_whd alt_rule;
+    int k;
+    if (type(p) == vlist_node)
+        normal_error("pdf backend", "'startlink' ended up in vlist");
+    if (global_shipping_mode == SHIPPING_FORM)
+        normal_error("pdf backend", "link annotations cannot be inside an xform");
+    if (is_obj_scheduled(pdf, pdf_link_objnum(p)))
+        pdf_link_objnum(p) = pdf_create_obj(pdf, obj_type_others, 0);
+    push_link_level(pdf, p);
+    alt_rule.wd = width(p);
+    alt_rule.ht = height(p);
+    alt_rule.dp = depth(p);
+    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_link_margin);
+    obj_annot_ptr(pdf, pdf_link_objnum(p)) = p; /* the reference for the pdf annot object must be set here */
+    k = pdf_link_objnum(p);
+    set_obj_scheduled(pdf, pdf_link_objnum(p));
+    addto_page_resources(pdf, obj_type_link, k);
+}
+
+@ @c
+void end_link(PDF pdf, halfword p)
+{
+    halfword q;
+    scaledpos pos = pdf->posstruct->pos;
+    if (type(p) == vlist_node)
+        normal_error("pdf backend","'endlink' ended up in vlist");
+    if (pdf->link_stack_ptr < 1)
+        normal_error("pdf backend","pdf link_stack empty, 'endlink' used without 'startlink'");
+    if (pdf->link_stack[pdf->link_stack_ptr].nesting_level != cur_s)
+        normal_error("pdf backend","'endlink' ended up in different nesting level than 'startlink'");
+    /*
+        NOTA BENE: test for running link must be done on |link_node| and not
+        |ref_link_node|, as |ref_link_node| can be set by |do_link| or
+        |append_link| already
+    */
+    if (is_running(width(pdf->link_stack[pdf->link_stack_ptr].link_node))) {
+        q = pdf->link_stack[pdf->link_stack_ptr].ref_link_node;
+        if (global_shipping_mode == SHIPPING_PAGE && matrixused()) {
+            matrixrecalculate(pos.h + pdf_link_margin);
+            pdf_ann_left(q) = getllx() - pdf_link_margin;
+            pdf_ann_top(q) = getlly() - pdf_link_margin;
+            pdf_ann_right(q) = geturx() + pdf_link_margin;
+            pdf_ann_bottom(q) = getury() + pdf_link_margin;
+        } else {
+            switch (pdf->posstruct->dir) {
+                case dir_TLT:
+                    pdf_ann_right(q) = pos.h + pdf_link_margin;
+                    break;
+                case dir_TRT:
+                    pdf_ann_left(q) = pos.h - pdf_link_margin;
+                    break;
+                case dir_LTL:
+                case dir_RTT:
+                    pdf_ann_bottom(q) = pos.v - pdf_link_margin;
+                    break;
+                default:
+                    pdf_ann_right(q) = pos.h + pdf_link_margin;
+                    formatted_warning("pdf backend","forcing bad dir %i to TLT in link",pdf->posstruct->dir);
+            }
+        }
+    }
+    pop_link_level(pdf);
+}
+
+@ For ``running'' annotations we must append a new node when the end of
+annotation is in other box than its start. The new created node is identical to
+corresponding whatsit node representing the start of annotation, but its |info|
+field is |max_halfword|. We set |info| field just before destroying the node, in
+order to use |flush_node_list| to do the job.
+
+@ Append a new pdf annot to |pdf_link_list|.
+
+@c
+void append_link(PDF pdf, halfword parent_box, scaledpos cur, small_number i)
+{
+    halfword p;
+    int k;
+    scaled_whd alt_rule;
+    p = copy_node(pdf->link_stack[(int) i].link_node);
+    pdf->link_stack[(int) i].ref_link_node = p;
+    subtype(p) = pdf_link_data_node;    /* this node is not a normal link node */
+    alt_rule.wd = width(p);
+    alt_rule.ht = height(p);
+    alt_rule.dp = depth(p);
+    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_link_margin);
+    k = pdf_create_obj(pdf, obj_type_others, 0);
+    obj_annot_ptr(pdf, k) = p;
+    set_obj_scheduled(pdf, pdf_link_objnum(p));
+    addto_page_resources(pdf, obj_type_link, k);
+}
+
+@ @c
+void scan_startlink(PDF pdf)
+{
+    int k;
+    halfword r;
+    if (abs(cur_list.mode_field) == vmode)
+        normal_error("pdf backend", "startlink cannot be used in vertical mode");
+    k = pdf_create_obj(pdf, obj_type_others, 0);
+    new_annot_whatsit(pdf_start_link_node);
+    set_pdf_link_attr(cur_list.tail_field, null);
+    if (scan_keyword("attr")) {
+        scan_toks(false, true);
+        set_pdf_link_attr(cur_list.tail_field, def_ref);
+    }
+    r = scan_action(pdf);
+    set_pdf_link_action(cur_list.tail_field, r);
+    set_pdf_link_objnum(cur_list.tail_field, k);
+    pdf_last_link = k;
+    /*
+        NOTA BENE: although it is possible to set |obj_annot_ptr(k) := tail|
+        here, it is not safe if nodes are later copied/destroyed/moved; a better
+        place to do this is inside |do_link|, when the whatsit node is written
+        out
+    */
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdflink.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
-
-Copyright 2009-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    To implement nested link annotations, we need a stack to hold copy of
-    |pdf_start_link_node|'s that are being written out, together with their box
-    nesting level.
-
-*/
-
-void push_link_level(PDF pdf, halfword p)
-{
-    if (pdf->link_stack_ptr >= pdf_max_link_level)
-        overflow("pdf link stack size", pdf_max_link_level);
-    pdf->link_stack_ptr++;
-    pdf->link_stack[pdf->link_stack_ptr].nesting_level = cur_s;
-    pdf->link_stack[pdf->link_stack_ptr].link_node = copy_node_list(p);
-    pdf->link_stack[pdf->link_stack_ptr].ref_link_node = p;
-}
-
-void pop_link_level(PDF pdf)
-{
-    flush_node_list(pdf->link_stack[pdf->link_stack_ptr].link_node);
-    pdf->link_stack_ptr--;
-}
-
-void do_link(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
-{
-    scaled_whd alt_rule;
-    int k;
-    if (type(p) == vlist_node)
-        normal_error("pdf backend", "'startlink' ended up in vlist");
-    if (global_shipping_mode == SHIPPING_FORM)
-        normal_error("pdf backend", "link annotations cannot be inside an xform");
-    if (is_obj_scheduled(pdf, pdf_link_objnum(p)))
-        pdf_link_objnum(p) = pdf_create_obj(pdf, obj_type_others, 0);
-    push_link_level(pdf, p);
-    alt_rule.wd = width(p);
-    alt_rule.ht = height(p);
-    alt_rule.dp = depth(p);
-    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_link_margin);
-    /*tex The reference for the annot object must be set here. */
-    obj_annot_ptr(pdf, pdf_link_objnum(p)) = p;
-    k = pdf_link_objnum(p);
-    set_obj_scheduled(pdf, pdf_link_objnum(p));
-    addto_page_resources(pdf, obj_type_link, k);
-}
-
-void end_link(PDF pdf, halfword p)
-{
-    halfword q;
-    scaledpos pos = pdf->posstruct->pos;
-    if (type(p) == vlist_node)
-        normal_error("pdf backend","'endlink' ended up in vlist");
-    if (pdf->link_stack_ptr < 1)
-        normal_error("pdf backend","pdf link_stack empty, 'endlink' used without 'startlink'");
-    if (pdf->link_stack[pdf->link_stack_ptr].nesting_level != cur_s)
-        normal_error("pdf backend","'endlink' ended up in different nesting level than 'startlink'");
-    /*tex
-        The test for running link must be done on |link_node| and not
-        |ref_link_node|, as |ref_link_node| can be set by |do_link| or
-        |append_link| already.
-    */
-    if (is_running(width(pdf->link_stack[pdf->link_stack_ptr].link_node))) {
-        q = pdf->link_stack[pdf->link_stack_ptr].ref_link_node;
-        if (global_shipping_mode == SHIPPING_PAGE && matrixused()) {
-            matrixrecalculate(pos.h + pdf_link_margin);
-            pdf_ann_left(q) = getllx() - pdf_link_margin;
-            pdf_ann_top(q) = getlly() - pdf_link_margin;
-            pdf_ann_right(q) = geturx() + pdf_link_margin;
-            pdf_ann_bottom(q) = getury() + pdf_link_margin;
-        } else {
-            switch (pdf->posstruct->dir) {
-                case dir_TLT:
-                    pdf_ann_right(q) = pos.h + pdf_link_margin;
-                    break;
-                case dir_TRT:
-                    pdf_ann_left(q) = pos.h - pdf_link_margin;
-                    break;
-                case dir_LTL:
-                case dir_RTT:
-                    pdf_ann_bottom(q) = pos.v - pdf_link_margin;
-                    break;
-                default:
-                    pdf_ann_right(q) = pos.h + pdf_link_margin;
-                    formatted_warning("pdf backend","forcing bad dir %i to TLT in link",pdf->posstruct->dir);
-            }
-        }
-    }
-    pop_link_level(pdf);
-}
-
-/*tex
-
-    For ``running'' annotations we must append a new node when the end of
-    annotation is in other box than its start. The new created node is identical
-    to corresponding whatsit node representing the start of annotation, but its
-    |info| field is |max_halfword|. We set |info| field just before destroying
-    the node, in order to use |flush_node_list| to do the job.
-
-*/
-
-void append_link(PDF pdf, halfword parent_box, scaledpos cur, small_number i)
-{
-    halfword p;
-    int k;
-    scaled_whd alt_rule;
-    p = copy_node(pdf->link_stack[(int) i].link_node);
-    pdf->link_stack[(int) i].ref_link_node = p;
-    /*tex This node is not a normal link node. */
-    subtype(p) = pdf_link_data_node;
-    alt_rule.wd = width(p);
-    alt_rule.ht = height(p);
-    alt_rule.dp = depth(p);
-    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_link_margin);
-    k = pdf_create_obj(pdf, obj_type_others, 0);
-    obj_annot_ptr(pdf, k) = p;
-    set_obj_scheduled(pdf, pdf_link_objnum(p));
-    addto_page_resources(pdf, obj_type_link, k);
-}
-
-void scan_startlink(PDF pdf)
-{
-    int k;
-    halfword r;
-    if (abs(cur_list.mode_field) == vmode)
-        normal_error("pdf backend", "startlink cannot be used in vertical mode");
-    k = pdf_create_obj(pdf, obj_type_others, 0);
-    new_annot_whatsit(pdf_start_link_node);
-    set_pdf_link_attr(cur_list.tail_field, null);
-    if (scan_keyword("attr")) {
-        scan_toks(false, true);
-        set_pdf_link_attr(cur_list.tail_field, def_ref);
-    }
-    r = scan_action(pdf);
-    set_pdf_link_action(cur_list.tail_field, r);
-    set_pdf_link_objnum(cur_list.tail_field, k);
-    pdf_last_link = k;
-    /*tex
-        Although it is possible to set |obj_annot_ptr(k) := tail| here, it is not
-        safe if nodes are later copied/destroyed/moved; a better place to do this
-        is inside |do_link|, when the whatsit node is written out.
-    */
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdflistout.w
@@ -0,0 +1,1078 @@
+% pdflistout.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\pdfTeX{pdf\TeX}
+
+@ @c
+#include "ptexlib.h"
+
+@ @c
+#define kern_width(q) width(q) + ex_kern(q)
+
+/*
+    ext_xn_over_d(width(q), 1000000+ex_kern(q), 1000000);
+*/
+
+@ @c
+pos_info_structure pos_info; /* to be accessed from Lua */
+
+static backend_struct *backend = NULL;
+backend_function *backend_out, *backend_out_whatsit;
+
+@ @c
+static void missing_backend_function(PDF pdf, halfword p)
+{
+    const char *n = get_node_name(type(p), subtype(p));
+    if (type(p) == whatsit_node)
+        formatted_error("pdf backend","no output function for whatsit %s",n);
+    else
+        formatted_error("pdf backend","no output function for node %s",n);
+}
+
+@ @c
+static void init_none_backend_functions(void)
+{
+    backend_struct *p = &backend[OMODE_NONE];
+    p->name = strdup("(None)");
+}
+
+@ @c
+static void init_pdf_backend_functions(void)
+{
+    backend_struct *p = &backend[OMODE_PDF];
+    p->name = strdup("PDF");
+    p->node_fu[rule_node] = &pdf_place_rule;
+    p->node_fu[glyph_node] = &pdf_place_glyph;
+    p->whatsit_fu[special_node] = &pdf_special;
+    p->whatsit_fu[pdf_literal_node] = &pdf_out_literal;
+    p->whatsit_fu[pdf_refobj_node] = &pdf_ref_obj;
+    p->whatsit_fu[pdf_annot_node] = &do_annot;
+    p->whatsit_fu[pdf_start_link_node] = &do_link;
+    p->whatsit_fu[pdf_end_link_node] = &end_link;
+    p->whatsit_fu[pdf_dest_node] = &do_dest;
+    p->whatsit_fu[pdf_thread_node] = &do_thread;
+    p->whatsit_fu[pdf_end_thread_node] = &end_thread;
+    p->whatsit_fu[late_lua_node] = &late_lua;
+    p->whatsit_fu[pdf_colorstack_node] = &pdf_out_colorstack;
+    p->whatsit_fu[pdf_setmatrix_node] = &pdf_out_setmatrix;
+    p->whatsit_fu[pdf_save_node] = &pdf_out_save;
+    p->whatsit_fu[pdf_restore_node] = &pdf_out_restore;
+}
+
+@ @c
+static void init_dvi_backend_functions(void)
+{
+    backend_struct *p = &backend[OMODE_DVI];
+    p->name = strdup("DVI");
+    p->node_fu[rule_node] = &dvi_place_rule;
+    p->node_fu[glyph_node] = &dvi_place_glyph;
+    p->whatsit_fu[special_node] = &dvi_special;
+    p->whatsit_fu[late_lua_node] = &late_lua;
+}
+
+@ @c
+void init_backend_functionpointers(output_mode o_mode)
+{
+    int i, j;
+    if (backend == NULL) {
+        backend = xmalloc((MAX_OMODE + 1) * sizeof(backend_struct));
+        for (i = 0; i <= MAX_OMODE; i++) {
+            backend[i].node_fu = xmalloc((MAX_NODE_TYPE + 1) * sizeof(backend_function));
+            backend[i].whatsit_fu = xmalloc((MAX_WHATSIT_TYPE + 1) * sizeof(backend_function));
+            for (j = 0; j < MAX_NODE_TYPE + 1; j++)
+                backend[i].node_fu[j] = &missing_backend_function;
+            for (j = 0; j < MAX_WHATSIT_TYPE + 1; j++)
+                backend[i].whatsit_fu[j] = &missing_backend_function;
+        }
+        init_none_backend_functions();
+        init_dvi_backend_functions();
+        init_pdf_backend_functions();
+    }
+    backend_out = backend[o_mode].node_fu;
+    backend_out_whatsit = backend[o_mode].whatsit_fu;
+}
+
+@ This code scans forward to the ending |dir_node| while keeping
+track of the needed width in |w|. When it finds the node that will end
+this segment, it stores the accumulated with in the |dir_dvi_h| field
+of that end node, so that when that node is found later in the
+processing, the correct glue correction can be applied (obsolete).
+
+@c
+static scaled simple_advance_width(halfword p)
+{
+    halfword q = p;
+    scaled w = 0;
+    while ((q != null) && (vlink(q) != null)) {
+        q = vlink(q);
+        switch (type(q)) {
+            case glyph_node:
+                w += glyph_width(q);
+/* experimental */
+w += x_advance(q);
+                break;
+            case hlist_node:
+            case vlist_node:
+            case rule_node:
+            case margin_kern_node:
+                w += width(q);
+                break;
+            case kern_node:
+                /* officially we should check the subtype */
+                w += kern_width(q);
+                break;
+            case disc_node:
+                /* hh: the frontend should append already */
+                if (vlink(no_break(q)) != null)
+                    w += simple_advance_width(no_break(q));
+            default:
+                break;
+        }
+    }
+    return w;
+}
+
+@ @c
+static halfword calculate_width_to_enddir(halfword p, real cur_glue, scaled cur_g, halfword this_box)
+{
+    int dir_nest = 1;
+    halfword q = p, enddir_ptr = p;
+    scaled w = 0;
+    real glue_temp; /* glue value before rounding */
+    int g_sign = glue_sign(this_box);
+    int g_order = glue_order(this_box);
+    while ((q != null) && (vlink(q) != null)) {
+        q = vlink(q);
+        if (is_char_node(q))
+            w += pack_width(box_dir(this_box), dir_TRT, q, true);
+        else {
+            switch (type(q)) {
+                case hlist_node:
+                case vlist_node:
+                    w += pack_width(box_dir(this_box), box_dir(q), q, false);
+                    break;
+                case rule_node:
+                case margin_kern_node:
+                    w += width(q);
+                    break;
+                case kern_node:
+                    /* officially we should check the subtype */
+                    w += kern_width(q);
+                    break;
+                case math_node:
+                    /* begin mathskip code */
+                    if (glue_is_zero(q)) {
+                        w += surround(q);
+                        break;
+                    } else {
+                        /* fall through: mathskip */
+                    }
+                    /* end mathskip code */
+                case glue_node:
+                    w += width(q) - cur_g;
+                    if (g_sign != normal) {
+                        if (g_sign == stretching) {
+                            if (stretch_order(q) == g_order) {
+                                cur_glue = cur_glue + stretch(q);
+                                vet_glue(float_cast(glue_set(this_box)) * cur_glue);
+                                cur_g = float_round(glue_temp);
+                            }
+                        } else if (shrink_order(q) == g_order) {
+                            cur_glue = cur_glue - shrink(q);
+                            vet_glue(float_cast(glue_set(this_box)) * cur_glue);
+                            cur_g = float_round(glue_temp);
+                        }
+                    }
+                    w += cur_g;
+                    break;
+                case disc_node:
+                    /* hh: the frontend should append already */
+                    if (vlink(no_break(q)) != null)
+                        w += simple_advance_width(no_break(q));
+                    break;
+                case dir_node:
+                    if (dir_dir(q) >= 0)
+                        dir_nest++;
+                    else
+                        dir_nest--;
+                    if (dir_nest == 0) {
+                        enddir_ptr = q;
+                        dir_cur_h(enddir_ptr) = w;
+                        q = null;
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+    if (enddir_ptr == p)
+        /* no enddir found, just transport w by begindir */
+        dir_cur_h(enddir_ptr) = w;
+    return enddir_ptr;
+}
+
+@ The |out_what| procedure takes care of outputting the whatsit nodes for
+|vlist_out| and |hlist_out|, which are similar for either case.
+
+We don't implement \.{\\write} inside of leaders. (The reason is that
+the number of times a leader box appears might be different in different
+implementations, due to machine-dependent rounding in the glue calculations.)
+@^leaders@>
+
+@c
+void out_what(PDF pdf, halfword p)
+{
+    switch (subtype(p)) {
+        case special_node:         /* |pdf_special(pdf, p)|; */
+        case late_lua_node:        /* |late_lua(pdf, p)|; */
+        case pdf_save_node:        /* |pdf_out_save(pdf, p)|; */
+        case pdf_restore_node:     /* |pdf_out_restore(pdf, p)|; */
+        case pdf_end_link_node:    /* |end_link(pdf, p)|; */
+        case pdf_end_thread_node:  /* |end_thread(pdf, p)|; */
+        case pdf_literal_node:     /* |pdf_out_literal(pdf, p)|; */
+        case pdf_colorstack_node:  /* |pdf_out_colorstack(pdf, p)|; */
+        case pdf_setmatrix_node:   /* |pdf_out_setmatrix(pdf, p)|; */
+        case pdf_refobj_node:      /* |pdf_ref_obj(pdf, p)| */
+            backend_out_whatsit[subtype(p)] (pdf, p);
+            break;
+        case open_node:
+        case write_node:
+        case close_node:
+            /* do some work that has been queued up for \.{\\write} */
+            if (!doing_leaders) {
+                int j = write_stream(p);
+                if (subtype(p) == write_node) {
+                    write_out(p);
+                } else if (subtype(p) == close_node) {
+                    close_write_file(j);
+                } else if (valid_write_file(j)) {
+                    char *fn;
+                    close_write_file(j);
+                    cur_name = open_name(p);
+                    cur_area = open_area(p);
+                    cur_ext = open_ext(p);
+                    if (cur_ext == get_nullstr())
+                        cur_ext = maketexstring(".tex");
+                    fn = pack_file_name(cur_name, cur_area, cur_ext);
+                    while (! open_write_file(j,fn)) {
+                        fn = prompt_file_name("output file name", ".tex");
+                    }
+                }
+            }
+            break;
+        case user_defined_node:
+            break;
+        default:
+            /* this should give an error about missing whatsit backend function */
+            backend_out_whatsit[subtype(p)] (pdf, p);
+    }
+}
+
+@ @c
+void hlist_out(PDF pdf, halfword this_box, int rule_callback_id)
+{
+    posstructure localpos;      /* the position structure local within this function */
+    posstructure *refpos;       /* the list origin pos. on the page, provided by the caller */
+    scaledpos cur = { 0, 0 }, tmpcur, basepoint;
+    scaledpos size = { 0, 0 };  /* rule dimensions */
+    scaled effective_horizontal;
+    scaled save_h;              /* what |cur.h| should pop to */
+    scaled edge;                /* right edge of sub-box or leader space */
+    halfword enddir_ptr;        /* temporary pointer to enddir node */
+    int g_order;                /* applicable order of infinity for glue */
+    int g_sign;                 /* selects type of glue */
+    halfword p, q;              /* current position in the hlist */
+    halfword leader_box;        /* the leader box being replicated */
+    scaled leader_wd;           /* width of leader box being replicated */
+    scaled lx;                  /* extra space between leader boxes */
+    boolean outer_doing_leaders;        /* were we doing leaders? */
+    real glue_temp;             /* glue value before rounding */
+    real cur_glue = 0.0;        /* glue seen so far */
+    scaled cur_g = 0;           /* rounded equivalent of |cur_glue| times the glue ratio */
+    scaled_whd rule, ci;
+    int i;                      /* index to scan |pdf_link_stack| */
+    int save_loc = 0;           /* DVI! \.{DVI} byte location upon entry */
+    scaledpos save_dvi = { 0, 0 };      /* DVI! what |dvi| should pop to */
+    int synctex = synctex_par ;
+
+    g_order = glue_order(this_box);
+    g_sign = glue_sign(this_box);
+    p = list_ptr(this_box);
+
+    refpos = pdf->posstruct;
+    pdf->posstruct = &localpos; /* use local structure for recursion */
+    localpos.pos = refpos->pos;
+    localpos.dir = box_dir(this_box);
+
+    cur_s++;
+    if (cur_s > max_push)
+        max_push = cur_s;
+
+    if (output_mode_used == OMODE_DVI) {
+        if (cur_s > 0) {
+            dvi_push();
+            save_dvi = dvi;
+        }
+        save_loc = dvi_offset + dvi_ptr;
+    }
+
+    for (i = 1; i <= pdf->link_stack_ptr; i++) {
+        if (pdf->link_stack[i].nesting_level == cur_s)
+            append_link(pdf, this_box, cur, (small_number) i);
+    }
+
+    if (synctex)
+        synctexhlist(this_box);
+    while (p != null) {
+        if (is_char_node(p)) {
+            do {
+                if (x_displace(p) != 0 || y_displace(p) != 0) {
+                    tmpcur.h = cur.h + x_displace(p);
+                    tmpcur.v = cur.v - y_displace(p);
+                    synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
+                }
+                ci = output_one_char(pdf, p);
+                if (textdir_parallel(localpos.dir, dir_TLT)) {
+                    cur.h += ci.wd;
+/* experimental */
+cur.h += x_advance(p);
+                } else {
+                    cur.h += ci.ht + ci.dp;
+                }
+                synch_pos_with_cur(pdf->posstruct, refpos, cur);
+                p = vlink(p);
+            } while (is_char_node(p));
+            if (synctex)
+                synctexcurrent();
+        } else {
+            /* output the non-|char_node| |p| for |hlist_out| and move to the next node */
+            switch (type(p)) {
+                case hlist_node:
+                case vlist_node:
+                    if (textdir_parallel(box_dir(p), localpos.dir)) {
+                        effective_horizontal = width(p);
+                        basepoint.v = 0;
+                        if (textdir_opposite(box_dir(p), localpos.dir))
+                            basepoint.h = width(p);
+                        else
+                            basepoint.h = 0;
+                    } else {
+                        effective_horizontal = height(p) + depth(p);
+                        if (!is_mirrored(box_dir(p))) {
+                            if (partextdir_eq(box_dir(p), localpos.dir))
+                                basepoint.h = height(p);
+                            else
+                                basepoint.h = depth(p);
+                        } else {
+                            if (partextdir_eq(box_dir(p), localpos.dir))
+                                basepoint.h = depth(p);
+                            else
+                                basepoint.h = height(p);
+                        }
+                        if (is_rotated(localpos.dir)) {
+                            if (partextdir_eq(localpos.dir, box_dir(p)))
+                                basepoint.v = -width(p) / 2; /* up */
+                            else
+                                basepoint.v = width(p) / 2;  /* down */
+                        } else if (is_mirrored(localpos.dir)) {
+                            if (partextdir_eq(localpos.dir, box_dir(p)))
+                                basepoint.v = 0;
+                            else
+                                basepoint.v = width(p);      /* down */
+                        } else {
+                            if (partextdir_eq(localpos.dir, box_dir(p)))
+                                basepoint.v = -width(p);     /* up */
+                            else
+                                basepoint.v = 0;
+                        }
+                    }
+                    if (!is_mirrored(localpos.dir))
+                        basepoint.v = basepoint.v + shift_amount(p); /* shift the box down */
+                    else
+                        basepoint.v = basepoint.v - shift_amount(p); /* shift the box up */
+                    if (list_ptr(p) == null) {
+                        if (synctex) {
+                            if (type(p) == vlist_node)
+                                synctexvoidvlist(p, this_box);
+                            else
+                                synctexvoidhlist(p, this_box);
+                        }
+                    } else {
+                        tmpcur.h = cur.h + basepoint.h;
+                        tmpcur.v = basepoint.v;
+                        synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
+                        if (type(p) == vlist_node)
+                            vlist_out(pdf, p, rule_callback_id);
+                        else
+                            hlist_out(pdf, p, rule_callback_id);
+                    }
+                    cur.h += effective_horizontal;
+                    break;
+                case disc_node:
+                    /* hh: the frontend should append already */
+                    if (vlink(no_break(p)) != null) {
+                        if (subtype(p) != select_disc) {
+                            q = tail_of_list(vlink(no_break(p))); /* this could be a tlink */
+                            vlink(q) = vlink(p);
+                            q = vlink(no_break(p));
+                            vlink(no_break(p)) = null;
+                            vlink(p) = q;
+                        }
+                    }
+                    break;
+                case math_node:
+                    if (synctex) {
+                        synctexmath(p, this_box);
+                    }
+                    /* begin mathskip code */
+                    if (glue_is_zero(p)) {
+                        cur.h += surround(p);
+                        break;
+                    } else {
+                        /* fall through: mathskip */
+                    }
+                    /* end mathskip code */
+                case glue_node:
+                    {
+                        /* move right or output leaders, we use real multiplication */
+                        rule.wd = width(p) - cur_g;
+                        if (g_sign != normal) {
+                            if (g_sign == stretching) {
+                                if (stretch_order(p) == g_order) {
+                                    cur_glue = cur_glue + stretch(p);
+                                    vet_glue(float_cast(glue_set(this_box)) * cur_glue);
+                                    cur_g = float_round(glue_temp);
+                                }
+                            } else if (shrink_order(p) == g_order) {
+                                cur_glue = cur_glue - shrink(p);
+                                vet_glue(float_cast(glue_set(this_box)) * cur_glue);
+                                cur_g = float_round(glue_temp);
+                            }
+                    }
+                    rule.wd = rule.wd + cur_g;
+                    if (subtype(p) >= a_leaders) {
+                        /* output leaders in an hlist, |goto fin_rule| if a rule or to |next_p| if done */
+                        leader_box = leader_ptr(p);
+                        if (type(leader_box) == rule_node) {
+                            rule.ht = height(leader_box);
+                            rule.dp = depth(leader_box);
+                            goto FIN_RULE;
+                        }
+                        if (textdir_parallel(box_dir(leader_box), localpos.dir))
+                            leader_wd = width(leader_box);
+                        else
+                            leader_wd = height(leader_box) + depth(leader_box);
+                        if ((leader_wd > 0) && (rule.wd > 0)) {
+                            rule.wd = rule.wd + 10; /* compensate for floating-point rounding */
+                            edge = cur.h + rule.wd;
+                            lx = 0;
+                            /*
+                                let |cur.h| be the position of the first box, and set |leader_wd+lx|
+                                to the spacing between corresponding parts of boxes
+                            */
+                            if (subtype(p) == g_leaders) {
+                                save_h = cur.h;
+                                switch (localpos.dir) {
+                                    case dir_TLT:
+                                        cur.h += refpos->pos.h - shipbox_refpos.h;
+                                        cur.h = leader_wd * (cur.h / leader_wd);
+                                        cur.h -= refpos->pos.h - shipbox_refpos.h;
+                                        break;
+                                    case dir_TRT:
+                                        cur.h = refpos->pos.h - shipbox_refpos.h - cur.h;
+                                        cur.h = leader_wd * (cur.h / leader_wd);
+                                        cur.h = refpos->pos.h - shipbox_refpos.h - cur.h;
+                                        break;
+                                    case dir_LTL:
+                                    case dir_RTT:
+                                        cur.h = refpos->pos.v - shipbox_refpos.v - cur.h;
+                                        cur.h = leader_wd * (cur.h / leader_wd);
+                                        cur.h = refpos->pos.v - shipbox_refpos.v - cur.h;
+                                        break;
+                                    default:
+                                        formatted_warning("pdf backend","forcing bad dir %i to TLT in hlist case 1",localpos.dir);
+                                        localpos.dir = dir_TLT;
+                                        cur.h += refpos->pos.h - shipbox_refpos.h;
+                                        cur.h = leader_wd * (cur.h / leader_wd);
+                                        cur.h -= refpos->pos.h - shipbox_refpos.h;
+                                        break;
+                                }
+                                if (cur.h < save_h)
+                                    cur.h += leader_wd;
+                            } else if (subtype(p) == a_leaders) {
+                                save_h = cur.h;
+                                cur.h = leader_wd * (cur.h / leader_wd);
+                                if (cur.h < save_h)
+                                    cur.h += leader_wd;
+                            } else {
+                                lq = rule.wd / leader_wd; /* the number of box copies */
+                                lr = rule.wd % leader_wd; /* the remaining space */
+                                if (subtype(p) == c_leaders) {
+                                    cur.h += lr / 2;
+                                } else {
+                                    lx = lr / (lq + 1);
+                                    cur.h += (lr - (lq - 1) * lx) / 2;
+                                }
+                            }
+                            while (cur.h + leader_wd <= edge) {
+                                /* output a leader box at |cur.h|, then advance |cur.h| by |leader_wd+lx| */
+                                if (pardir_parallel(box_dir(leader_box), localpos.dir)) {
+                                    basepoint.v = 0;
+                                    if (textdir_opposite(box_dir(leader_box), localpos.dir))
+                                        basepoint.h = width(leader_box);
+                                    else
+                                        basepoint.h = 0;
+                                } else {
+                                    if (!is_mirrored(box_dir(leader_box))) {
+                                        if (partextdir_eq(box_dir(leader_box), localpos.dir))
+                                            basepoint.h = height(leader_box);
+                                        else
+                                            basepoint.h = depth(leader_box);
+                                    } else {
+                                        if (partextdir_eq(box_dir(leader_box), localpos.dir))
+                                            basepoint.h = depth(leader_box);
+                                        else
+                                            basepoint.h = height(leader_box);
+                                    }
+                                    if (partextdir_eq(localpos.dir, box_dir(leader_box)))
+                                        basepoint.v = -(width(leader_box) / 2);
+                                    else
+                                        basepoint.v = (width(leader_box) / 2);
+                                }
+                                if (!is_mirrored(localpos.dir))
+                                    basepoint.v = basepoint.v + shift_amount(leader_box); /* shift the box down */
+                                else
+                                    basepoint.v = basepoint.v - shift_amount(leader_box); /* shift the box up */
+                                tmpcur.h = cur.h + basepoint.h;
+                                tmpcur.v = basepoint.v;
+                                synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
+                                outer_doing_leaders = doing_leaders;
+                                doing_leaders = true;
+                                if (type(leader_box) == vlist_node)
+                                    vlist_out(pdf, leader_box, rule_callback_id);
+                                else
+                                    hlist_out(pdf, leader_box, rule_callback_id);
+                                doing_leaders = outer_doing_leaders;
+                                cur.h += leader_wd + lx;
+                            }
+                            cur.h = edge - 10;
+                            goto NEXTP;
+                        }
+                    }
+                    }
+                    goto MOVE_PAST;
+                    break;
+                case kern_node:
+                    if (synctex)
+                        synctexkern(p, this_box);
+                    /* officially we should check the subtype */
+                    cur.h += kern_width(p);
+                    break;
+                case rule_node:
+                    if (rule_dir(p) < 0)
+                        rule_dir(p) = localpos.dir;
+                    if (pardir_parallel(rule_dir(p), localpos.dir)) {
+                        rule.ht = height(p);
+                        rule.dp = depth(p);
+                        rule.wd = width(p);
+                    } else {
+                        rule.ht = width(p) / 2;
+                        rule.dp = width(p) / 2;
+                        rule.wd = height(p) + depth(p);
+                    }
+                    goto FIN_RULE;
+                    break;
+                case dir_node:
+                    /* output a reflection instruction if the direction has changed */
+                    if (dir_dir(p) >= 0) {
+                        /*
+                            Calculate the needed width to the matching |enddir|, return the
+                            |enddir| node, with width info
+                        */
+                        enddir_ptr = calculate_width_to_enddir(p, cur_glue, cur_g, this_box);
+                        if (textdir_parallel(dir_dir(p), localpos.dir)) {
+                            dir_cur_h(enddir_ptr) += cur.h;
+                            if (textdir_opposite(dir_dir(p), localpos.dir))
+                                cur.h = dir_cur_h(enddir_ptr);
+                        } else
+                            dir_cur_h(enddir_ptr) = cur.h;
+                        if (enddir_ptr != p) {
+                            /* only if it is an enddir */
+                            dir_cur_v(enddir_ptr) = cur.v;
+                            dir_refpos_h(enddir_ptr) = refpos->pos.h;
+                            dir_refpos_v(enddir_ptr) = refpos->pos.v;
+                            /* negative: mark it as |enddir| */
+                            dir_dir(enddir_ptr) = localpos.dir - dir_swap;
+                        }
+                        /* fake a nested |hlist_out| */
+                        synch_pos_with_cur(pdf->posstruct, refpos, cur);
+                        refpos->pos = pdf->posstruct->pos;
+                        localpos.dir = dir_dir(p);
+                        cur.h = 0;
+                        cur.v = 0;
+                    } else {
+                        refpos->pos.h = dir_refpos_h(p);
+                        refpos->pos.v = dir_refpos_v(p);
+                        localpos.dir = dir_dir(p) + dir_swap;
+                        cur.h = dir_cur_h(p);
+                        cur.v = dir_cur_v(p);
+                    }
+                    break;
+                case whatsit_node:
+                    /* output the whatsit node |p| in |hlist_out| */
+                    switch (subtype(p)) {
+                        case save_pos_node:
+                            last_position = pdf->posstruct->pos;
+                            pos_info.curpos = pdf->posstruct->pos;
+                            pos_info.boxpos.pos = refpos->pos;
+                            pos_info.boxpos.dir = localpos.dir;
+                            pos_info.boxdim.wd = width(this_box);
+                            pos_info.boxdim.ht = height(this_box);
+                            pos_info.boxdim.dp = depth(this_box);
+                            break;
+                        case pdf_annot_node:
+                        case pdf_start_link_node:
+                        case pdf_dest_node:
+                        case pdf_start_thread_node:
+                        case pdf_thread_node:
+                            backend_out_whatsit[subtype(p)] (pdf, p, this_box, cur);
+                            break;
+                        default:
+                            out_what(pdf, p);
+                    }
+                    break;
+                case margin_kern_node:
+                    cur.h += width(p);
+                    break;
+                default:
+                    break;
+            }
+            goto NEXTP;
+          FIN_RULE:
+            /* output a rule in an hlist */
+            if (is_running(rule.ht))
+                rule.ht = height(this_box);
+            if (is_running(rule.dp))
+                rule.dp = depth(this_box);
+            /* we don't output empty rules */
+            if ((rule.ht + rule.dp) > 0 && rule.wd > 0) {
+                switch (localpos.dir) {
+                    case dir_TLT:
+                        size.h = rule.wd;
+                        size.v = rule.ht + rule.dp;
+                        pos_down(rule.dp);
+                        break;
+                    case dir_TRT:
+                        size.h = rule.wd;
+                        size.v = rule.ht + rule.dp;
+                        pos_left(size.h);
+                        pos_down(rule.dp);
+                        break;
+                    case dir_LTL:
+                        size.h = rule.ht + rule.dp;
+                        size.v = rule.wd;
+                        pos_left(rule.ht);
+                        pos_down(size.v);
+                        break;
+                    case dir_RTT:
+                        size.h = rule.ht + rule.dp;
+                        size.v = rule.wd;
+                        pos_left(rule.dp);
+                        pos_down(size.v);
+                        break;
+                    default:
+                        formatted_warning("pdf backend","forcing bad dir %i to TLT in hlist case 2",localpos.dir);
+                        localpos.dir = dir_TLT;
+                        size.h = rule.wd;
+                        size.v = rule.ht + rule.dp;
+                        pos_down(rule.dp);
+                        break;
+                }
+                if (type(p) == glue_node) {
+                    q = leader_ptr(p);
+                    if ((q) && (type(q) == rule_node)) {
+                        backend_out[rule_node] (pdf, q, size, rule_callback_id);
+                    }
+                } else {
+                    backend_out[rule_node] (pdf, p, size, rule_callback_id);
+                }
+            }
+          MOVE_PAST:
+            cur.h += rule.wd;
+            if (synctex) {
+                synch_pos_with_cur(pdf->posstruct, refpos, cur);
+                synctexhorizontalruleorglue(p, this_box);
+            }
+          NEXTP:
+            p = vlink(p);
+            synch_pos_with_cur(pdf->posstruct, refpos, cur);
+        }
+    }
+    if (synctex)
+        synctextsilh(this_box);
+    if (output_mode_used == OMODE_DVI) {
+        prune_movements(save_loc);
+        if (cur_s > 0) {
+            dvi_pop(save_loc);
+            dvi = save_dvi;
+        }
+    }
+    cur_s--;
+    pdf->posstruct = refpos;
+}
+
+@ @c
+void vlist_out(PDF pdf, halfword this_box, int rule_callback_id)
+{
+    posstructure localpos; /* the position structure local within this function */
+    posstructure *refpos;  /* the list origin pos. on the page, provided by the caller */
+
+    scaledpos cur, tmpcur, basepoint;
+    scaledpos size = { 0, 0 };     /* rule dimensions */
+    scaled effective_vertical;
+    scaled save_v;                 /* what |cur.v| should pop to */
+    scaled top_edge;               /* the top coordinate for this box */
+    scaled edge;                   /* bottom boundary of leader space */
+    glue_ord g_order;              /* applicable order of infinity for glue */
+    int g_sign;                    /* selects type of glue */
+    halfword p;                    /* current position in the vlist */
+    halfword q;                    /* temp */
+    halfword leader_box;           /* the leader box being replicated */
+    scaled leader_ht;              /* height of leader box being replicated */
+    scaled lx;                     /* extra space between leader boxes */
+    boolean outer_doing_leaders;   /* were we doing leaders? */
+    real glue_temp;                /* glue value before rounding */
+    real cur_glue = 0.0;           /* glue seen so far */
+    scaled cur_g = 0;              /* rounded equivalent of |cur_glue| times the glue ratio */
+    scaled_whd rule;
+    int save_loc = 0;              /* DVI byte location upon entry */
+    scaledpos save_dvi = { 0, 0 }; /* DVI! what |dvi| should pop to */
+    int synctex = synctex_par ;
+
+    g_order = (glue_ord) glue_order(this_box);
+    g_sign = glue_sign(this_box);
+    p = list_ptr(this_box);
+
+    cur.h = 0;
+    cur.v = -height(this_box);
+    top_edge = cur.v;
+
+    refpos = pdf->posstruct;
+    pdf->posstruct = &localpos; /* use local structure for recursion */
+    localpos.dir = box_dir(this_box);
+    synch_pos_with_cur(pdf->posstruct, refpos, cur);
+
+    cur_s++;
+    if (cur_s > max_push)
+        max_push = cur_s;
+
+    if (output_mode_used == OMODE_DVI) {
+        if (cur_s > 0) {
+            dvi_push();
+            save_dvi = dvi;
+        }
+        save_loc = dvi_offset + dvi_ptr;
+    }
+
+    if (synctex)
+        synctexvlist(this_box);
+
+    /* create thread for the current vbox if needed */
+    check_running_thread(pdf, this_box, cur);
+
+    while (p != null) {
+        switch (type(p)) {
+            case hlist_node:
+            case vlist_node:
+                /*
+                    output a box in a vlist:
+
+                    todo: the direct test to switch between |width(p)| and |-width(p)|
+                    is definately wrong, because it does not nest properly. But at least
+                    it fixes a very obvious problem that otherwise occured with
+                    \.{\\pardir TLT} in a document with \.{\\bodydir TRT}, and so it
+                    will have to do for now. (hh: is this still true?)
+
+                */
+                if (pardir_parallel(box_dir(p), localpos.dir)) {
+                    effective_vertical = height(p) + depth(p);
+                    if ((type(p) == hlist_node) && is_mirrored(box_dir(p)))
+                        basepoint.v = depth(p);
+                    else
+                        basepoint.v = height(p);
+                    if (textdir_opposite(box_dir(p), localpos.dir))
+                        basepoint.h = width(p);
+                    else
+                        basepoint.h = 0;
+                } else {
+                    effective_vertical = width(p);
+                    if (!is_mirrored(box_dir(p))) {
+                        if (partextdir_eq(box_dir(p), localpos.dir))
+                            basepoint.h = height(p);
+                        else
+                            basepoint.h = depth(p);
+                    } else {
+                        if (partextdir_eq(box_dir(p), localpos.dir))
+                            basepoint.h = depth(p);
+                        else
+                            basepoint.h = height(p);
+                    }
+                    if (partextdir_eq(localpos.dir, box_dir(p)))
+                        basepoint.v = 0;
+                    else
+                        basepoint.v = width(p);
+                }
+                basepoint.h = basepoint.h + shift_amount(p); /* shift the box right */
+                if (list_ptr(p) == null) {
+                    cur.v += effective_vertical;
+                    if (synctex) {
+                        synch_pos_with_cur(pdf->posstruct, refpos, cur);
+                        if (type(p) == vlist_node)
+                            synctexvoidvlist(p, this_box);
+                        else
+                            synctexvoidhlist(p, this_box);
+                    }
+                } else {
+                    tmpcur.h = basepoint.h;
+                    tmpcur.v = cur.v + basepoint.v;
+                    synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
+                    if (type(p) == vlist_node)
+                        vlist_out(pdf, p, rule_callback_id);
+                    else
+                        hlist_out(pdf, p, rule_callback_id);
+                    cur.v += effective_vertical;
+                }
+                break;
+            case rule_node:
+                if (rule_dir(p) < 0)
+                    rule_dir(p) = localpos.dir;
+                if (pardir_parallel(rule_dir(p), localpos.dir)) {
+                    rule.ht = height(p);
+                    rule.dp = depth(p);
+                    rule.wd = width(p);
+                } else {
+                    rule.ht = width(p) / 2;
+                    rule.dp = width(p) / 2;
+                    rule.wd = height(p) + depth(p);
+                }
+                goto FIN_RULE;
+                break;
+            case glue_node:
+                {
+                    /* move down or output leaders, we use real multiplication */
+                    rule.ht = width(p) - cur_g;
+                    if (g_sign != normal) {
+                        if (g_sign == stretching) {
+                            if (stretch_order(p) == g_order) {
+                                cur_glue = cur_glue + stretch(p);
+                                vet_glue(float_cast(glue_set(this_box)) * cur_glue);
+                                cur_g = float_round(glue_temp);
+                            }
+                        } else if (shrink_order(p) == g_order) {
+                            cur_glue = cur_glue - shrink(p);
+                            vet_glue(float_cast(glue_set(this_box)) * cur_glue);
+                            cur_g = float_round(glue_temp);
+                        }
+                }
+                rule.ht = rule.ht + cur_g;
+                if (subtype(p) >= a_leaders) {
+                    /* output leaders in a vlist, |goto fin_rulefin_rule| if a rule or to |next_p| if done */
+                    leader_box = leader_ptr(p);
+                    if (type(leader_box) == rule_node) {
+                        rule.wd = width(leader_box);
+                        rule.dp = 0;
+                        goto FIN_RULE;
+                    }
+                    leader_ht = height(leader_box) + depth(leader_box);
+                    if ((leader_ht > 0) && (rule.ht > 0)) {
+                        rule.ht = rule.ht + 10; /* compensate for floating-point rounding */
+                        edge = cur.v + rule.ht;
+                        lx = 0;
+                        /*
+                            let |cur.v| be the position of the first box, and set |leader_ht+lx|
+                            to the spacing between corresponding parts of boxes
+                        */
+                        if (subtype(p) == g_leaders) {
+                            save_v = cur.v;
+                            switch (localpos.dir) {
+                                case dir_LTL:
+                                    cur.v += refpos->pos.h - shipbox_refpos.h;
+                                    cur.v = leader_ht * (cur.v / leader_ht);
+                                    cur.v -= refpos->pos.h - shipbox_refpos.h;
+                                    break;
+                                case dir_RTT:
+                                    cur.v = refpos->pos.h - shipbox_refpos.h - cur.v;
+                                    cur.v = leader_ht * (cur.v / leader_ht);
+                                    cur.v = refpos->pos.h - shipbox_refpos.h - cur.v;
+                                    break;
+                                case dir_TLT:
+                                case dir_TRT:
+                                    cur.v = refpos->pos.v - shipbox_refpos.v - cur.v;
+                                    cur.v = leader_ht * (cur.v / leader_ht);
+                                    cur.v = refpos->pos.v - shipbox_refpos.v - cur.v;
+                                    break;
+                                default:
+                                    formatted_warning("pdf backend","forcing bad dir %i to TLT in vlist case 1",localpos.dir);
+                                    localpos.dir = dir_TLT;
+                                    cur.v += refpos->pos.h - shipbox_refpos.h;
+                                    cur.v = leader_ht * (cur.v / leader_ht);
+                                    cur.v -= refpos->pos.h - shipbox_refpos.h;
+                                    break;
+                            }
+                            if (cur.v < save_v)
+                                cur.v += leader_ht;
+                        } else if (subtype(p) == a_leaders) {
+                            save_v = cur.v;
+                            cur.v = top_edge + leader_ht * ((cur.v - top_edge) / leader_ht);
+                            if (cur.v < save_v)
+                                cur.v += leader_ht;
+                        } else {
+                            lq = rule.ht / leader_ht; /* the number of box copies */
+                            lr = rule.ht % leader_ht; /* the remaining space */
+                            if (subtype(p) == c_leaders) {
+                                cur.v += lr / 2;
+                            } else {
+                                lx = lr / (lq + 1);
+                                cur.v += (lr - (lq - 1) * lx) / 2;
+                            }
+                        }
+
+                        while (cur.v + leader_ht <= edge) {
+                            /* output a leader box at |cur.v|, then advance |cur.v| by |leader_ht+lx| */
+                            tmpcur.h = shift_amount(leader_box);
+                            tmpcur.v = cur.v + height(leader_box);
+                            synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
+                            outer_doing_leaders = doing_leaders;
+                            doing_leaders = true;
+                            if (type(leader_box) == vlist_node)
+                                vlist_out(pdf, leader_box, rule_callback_id);
+                            else
+                                hlist_out(pdf, leader_box, rule_callback_id);
+                            doing_leaders = outer_doing_leaders;
+                            cur.v += leader_ht + lx;
+                        }
+                        cur.v = edge - 10;
+                        goto NEXTP;
+                    }
+                }
+                }
+                goto MOVE_PAST;
+                break;
+            case kern_node:
+                cur.v += width(p);
+                break;
+            case whatsit_node:
+                /* output the whatsit node |p| in |vlist_out| */
+                switch (subtype(p)) {
+                    case save_pos_node:
+                        last_position = pdf->posstruct->pos;
+                        pos_info.curpos = pdf->posstruct->pos;
+                        pos_info.boxpos.pos = refpos->pos;
+                        pos_info.boxpos.dir = localpos.dir;
+                        pos_info.boxdim.wd = width(this_box);
+                        pos_info.boxdim.ht = height(this_box);
+                        pos_info.boxdim.dp = depth(this_box);
+                        break;
+                    case pdf_annot_node:
+                    case pdf_start_link_node:
+                    case pdf_dest_node:
+                    case pdf_start_thread_node:
+                    case pdf_thread_node:
+                        backend_out_whatsit[subtype(p)] (pdf, p, this_box, cur);
+                        break;
+                    default:
+                        out_what(pdf, p);
+                }
+                break;
+            case glyph_node:
+            case disc_node:
+                confusion("vlistout"); /* this can't happen */
+                break;
+            default:
+                break;
+        }
+        goto NEXTP;
+      FIN_RULE:
+        /* output a rule in a vlist, |goto next_p| */
+        if (is_running(rule.wd))
+            rule.wd = width(this_box);
+        /* we don't output empty rules */
+        if ((rule.ht + rule.dp) > 0 && rule.wd > 0) {
+            switch (localpos.dir) {
+                case dir_TLT:
+                    size.h = rule.wd;
+                    size.v = rule.ht + rule.dp;
+                    pos_down(size.v);
+                    break;
+                case dir_TRT:
+                    size.h = rule.wd;
+                    size.v = rule.ht + rule.dp;
+                    pos_left(size.h);
+                    pos_down(size.v);
+                    break;
+                case dir_LTL:
+                    size.h = rule.ht + rule.dp;
+                    size.v = rule.wd;
+                    pos_down(size.v);
+                    break;
+                case dir_RTT:
+                    size.h = rule.ht + rule.dp;
+                    size.v = rule.wd;
+                    pos_left(size.h);
+                    pos_down(size.v);
+                    break;
+                default:
+                    formatted_warning("pdf backend","forcing bad dir %i to TLT in vlist case 2",localpos.dir);
+                    localpos.dir = dir_TLT;
+                    size.h = rule.wd;
+                    size.v = rule.ht + rule.dp;
+                    pos_down(size.v);
+                    break;
+            }
+            if (type(p) == glue_node) {
+                q = leader_ptr(p);
+                if ((q) && (type(q) == rule_node)) {
+                    backend_out[rule_node] (pdf, q, size, rule_callback_id);
+                }
+            } else {
+                backend_out[rule_node] (pdf, p, size, rule_callback_id);
+            }
+        }
+        cur.v += rule.ht + rule.dp;
+        goto NEXTP;
+      MOVE_PAST:
+        cur.v += rule.ht;
+      NEXTP:
+        p = vlink(p);
+        synch_pos_with_cur(pdf->posstruct, refpos, cur);
+    }
+    if (synctex)
+        synctextsilv(this_box);
+
+    if (output_mode_used == OMODE_DVI) {
+        prune_movements(save_loc);
+        if (cur_s > 0) {
+            dvi_pop(save_loc);
+            dvi = save_dvi;
+        }
+    }
+    cur_s--;
+    pdf->posstruct = refpos;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdflistout.c
+++ /dev/null
@@ -1,1072 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#define kern_width(q) width(q) + ex_kern(q)
-
-#define billion 1000000000.0
-
-#define vet_glue(A) do {         \
-    glue_temp=A;                 \
-    if (glue_temp>billion)       \
-      glue_temp=billion;         \
-    else if (glue_temp<-billion) \
-      glue_temp=-billion;        \
-  } while (0)
-
-/*tex
-
-    This code scans forward to the ending |dir_node| while keeping track of the
-    needed width in |w|. When it finds the node that will end this segment, it
-    stores the accumulated with in the |dir_dvi_h| field of that end node, so
-    that when that node is found later in the processing, the correct glue
-    correction can be applied (obsolete).
-
-*/
-
-static scaled simple_advance_width(halfword p)
-{
-    halfword q = p;
-    scaled w = 0;
-    while ((q != null) && (vlink(q) != null)) {
-        q = vlink(q);
-        switch (type(q)) {
-            case glyph_node:
-                w += glyph_width(q);
-                break;
-            case hlist_node:
-            case vlist_node:
-            case rule_node:
-            case margin_kern_node:
-                w += width(q);
-                break;
-            case kern_node:
-                /*tex Officially we should check the subtype. */
-                w += kern_width(q);
-                break;
-            case disc_node:
-                /* (HH): The frontend should append already. */
-                if (vlink(no_break(q)) != null)
-                    w += simple_advance_width(no_break(q));
-            default:
-                break;
-        }
-    }
-    return w;
-}
-
-static halfword calculate_width_to_enddir(halfword p, real cur_glue, scaled cur_g, halfword this_box)
-{
-    int dir_nest = 1;
-    halfword q = p, enddir_ptr = p;
-    scaled w = 0;
-    /*tex The glue value before rounding: */
-    real glue_temp;
-    int g_sign = glue_sign(this_box);
-    int g_order = glue_order(this_box);
-    while ((q != null) && (vlink(q) != null)) {
-        q = vlink(q);
-        if (is_char_node(q))
-            w += pack_width(box_dir(this_box), dir_TRT, q, true);
-        else {
-            switch (type(q)) {
-                case hlist_node:
-                case vlist_node:
-                    w += pack_width(box_dir(this_box), box_dir(q), q, false);
-                    break;
-                case rule_node:
-                case margin_kern_node:
-                    w += width(q);
-                    break;
-                case kern_node:
-                    /*tex Officially we should check the subtype. */
-                    w += kern_width(q);
-                    break;
-                case math_node:
-                    /*tex Begin of |mathskip| code. */
-                    if (glue_is_zero(q)) {
-                        w += surround(q);
-                        break;
-                    } else {
-                        /*tex Fall through |mathskip|. */
-                    }
-                    /*tex End of |mathskip| code. */
-                case glue_node:
-                    w += width(q) - cur_g;
-                    if (g_sign != normal) {
-                        if (g_sign == stretching) {
-                            if (stretch_order(q) == g_order) {
-                                cur_glue = cur_glue + stretch(q);
-                                vet_glue(float_cast(glue_set(this_box)) * cur_glue);
-                                cur_g = float_round(glue_temp);
-                            }
-                        } else if (shrink_order(q) == g_order) {
-                            cur_glue = cur_glue - shrink(q);
-                            vet_glue(float_cast(glue_set(this_box)) * cur_glue);
-                            cur_g = float_round(glue_temp);
-                        }
-                    }
-                    w += cur_g;
-                    break;
-                case disc_node:
-                    /* (HH): The frontend should append already. */
-                    if (vlink(no_break(q)) != null)
-                        w += simple_advance_width(no_break(q));
-                    break;
-                case dir_node:
-                    if (subtype(q) == normal_dir)
-                        dir_nest++;
-                    else
-                        dir_nest--;
-                    if (dir_nest == 0) {
-                        enddir_ptr = q;
-                        dir_cur_h(enddir_ptr) = w;
-                        q = null;
-                    }
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-    if (enddir_ptr == p)
-        /*tex No enddir found, just transport |w| by |begindir|. */
-        dir_cur_h(enddir_ptr) = w;
-    return enddir_ptr;
-}
-
-/*tex
-
-    The |out_what| procedure takes care of outputting the whatsit nodes for
-    |vlist_out| and |hlist_out|, which are similar for either case.
-
-    We don't implement \.{\\write} inside of leaders. (The reason is that the
-    number of times a leader box appears might be different in different
-    implementations, due to machine-dependent rounding in the glue calculations.)
-
-*/
-
-static void handle_backend_whatsit(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
-{
-    if (output_mode_used == OMODE_PDF) {
-        switch (subtype(p)) {
-            case pdf_literal_node:
-            case pdf_save_node:
-            case pdf_restore_node:
-            case pdf_setmatrix_node:
-            case pdf_colorstack_node:
-            case pdf_refobj_node:
-            case pdf_end_link_node:
-            case pdf_end_thread_node:
-                backend_out_whatsit[subtype(p)](pdf, p);
-                break;
-            case pdf_annot_node:
-            case pdf_start_link_node:
-            case pdf_dest_node:
-            case pdf_start_thread_node:
-            case pdf_thread_node:
-                backend_out_whatsit[subtype(p)](pdf, p, parent_box, cur);
-                break;
-            default:
-                /*tex We ignore bad ones. */
-                break;
-        }
-    }
-}
-
-void hlist_out(PDF pdf, halfword this_box, int rule_callback_id)
-{
-    /*tex the position structure local within this function */
-    posstructure localpos;
-    /*tex the list origin pos. on the page, provided by the caller */
-    posstructure *refpos;
-    /*tex A few status variables. */
-    scaledpos cur = { 0, 0 }, tmpcur, basepoint;
-    /*tex rule dimensions */
-    scaledpos size = { 0, 0 };
-    scaled effective_horizontal;
-    /*tex what |cur.h| should pop to */
-    scaled save_h;
-    /*tex right edge of sub-box or leader space */
-    scaled edge;
-    /*tex temporary pointer to enddir node */
-    halfword enddir_ptr;
-    /*tex applicable order of infinity for glue */
-    int g_order;
-    /*tex selects type of glue */
-    int g_sign;
-    /*tex glue variables */
-    int lq, lr;
-    /*tex current position in the hlist */
-    halfword p, q;
-    /*tex the leader box being replicated */
-    halfword leader_box;
-    /*tex width of leader box being replicated */
-    scaled leader_wd;
-    /*tex extra space between leader boxes */
-    scaled lx;
-    /*tex were we doing leaders? */
-    boolean outer_doing_leaders;
-    /*tex glue value before rounding */
-    real glue_temp;
-    /*tex glue seen so far */
-    real cur_glue = 0.0;
-    /*tex rounded equivalent of |cur_glue| times the glue ratio */
-    scaled cur_g = 0;
-    scaled_whd rule, ci;
-    /*tex index to scan |pdf_link_stack| */
-    int i;
-    /*tex DVI! \.{DVI} byte location upon entry */
-    int saved_loc = 0;
-    /*tex DVI! what |dvi| should pop to */
-    scaledpos saved_pos = { 0, 0 };
-    int synctex = synctex_par ;
-    scaled rleft, rright;
-    g_order = glue_order(this_box);
-    g_sign = glue_sign(this_box);
-    p = list_ptr(this_box);
-    refpos = pdf->posstruct;
-    /*tex use local structure for recursion */
-    pdf->posstruct = &localpos;
-    localpos.pos = refpos->pos;
-    localpos.dir = box_dir(this_box);
-    cur_s++;
-    backend_out_control[backend_control_push_list](pdf,&saved_pos,&saved_loc);
-    for (i = 1; i <= pdf->link_stack_ptr; i++) {
-        if (pdf->link_stack[i].nesting_level == cur_s)
-            append_link(pdf, this_box, cur, (small_number) i);
-    }
-    if (synctex) {
-        synctexhlist(this_box);
-    }
-    while (p != null) {
-        if (is_char_node(p)) {
-            do {
-                if (x_displace(p) != 0 || y_displace(p) != 0) {
-                    tmpcur.h = cur.h + x_displace(p);
-                    tmpcur.v = cur.v - y_displace(p);
-                    synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
-                }
-                ci = output_one_char(pdf, p);
-                if (textdir_parallel(localpos.dir, dir_TLT)) {
-                    cur.h += ci.wd;
-                } else {
-                    cur.h += ci.ht + ci.dp;
-                }
-                synch_pos_with_cur(pdf->posstruct, refpos, cur);
-                p = vlink(p);
-            } while (is_char_node(p));
-            if (synctex)
-                synctexcurrent();
-        } else {
-            /*tex
-                Output the non-|char_node| |p| for |hlist_out| and move to the
-                next node.
-            */
-            switch (type(p)) {
-                case hlist_node:
-                case vlist_node:
-                    if (textdir_parallel(box_dir(p), localpos.dir)) {
-                        effective_horizontal = width(p);
-                        basepoint.v = 0;
-                        if (textdir_opposite(box_dir(p), localpos.dir))
-                            basepoint.h = width(p);
-                        else
-                            basepoint.h = 0;
-                    } else {
-                        effective_horizontal = height(p) + depth(p);
-                        if (!is_mirrored(box_dir(p))) {
-                            if (partextdir_eq(box_dir(p), localpos.dir))
-                                basepoint.h = height(p);
-                            else
-                                basepoint.h = depth(p);
-                        } else {
-                            if (partextdir_eq(box_dir(p), localpos.dir))
-                                basepoint.h = depth(p);
-                            else
-                                basepoint.h = height(p);
-                        }
-                        if (is_rotated(localpos.dir)) {
-                            if (partextdir_eq(localpos.dir, box_dir(p))) {
-                                /*tex up */
-                                basepoint.v = -width(p) / 2;
-                            } else {
-                                /*tex down */
-                                basepoint.v = width(p) / 2;
-                            }
-                        } else if (is_mirrored(localpos.dir)) {
-                            if (partextdir_eq(localpos.dir, box_dir(p))) {
-                                basepoint.v = 0;
-                            } else {
-                                /*tex down */
-                                basepoint.v = width(p);
-                            }
-                        } else {
-                            if (partextdir_eq(localpos.dir, box_dir(p))) {
-                                /*tex up */
-                                basepoint.v = -width(p);
-                            } else {
-                                basepoint.v = 0;
-                            }
-                        }
-                    }
-                    if (!is_mirrored(localpos.dir)) {
-                        /*tex Shift the box down. */
-                        basepoint.v = basepoint.v + shift_amount(p);
-                    } else {
-                        /*tex Shift the box up. */
-                        basepoint.v = basepoint.v - shift_amount(p);
-                    }
-                    if (list_ptr(p) == null) {
-                        if (synctex) {
-                            if (type(p) == vlist_node)
-                                synctexvoidvlist(p, this_box);
-                            else
-                                synctexvoidhlist(p, this_box);
-                        }
-                    } else {
-                        tmpcur.h = cur.h + basepoint.h;
-                        tmpcur.v = basepoint.v;
-                        synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
-                        if (type(p) == vlist_node)
-                            vlist_out(pdf, p, rule_callback_id);
-                        else
-                            hlist_out(pdf, p, rule_callback_id);
-                    }
-                    cur.h += effective_horizontal;
-                    break;
-                case disc_node:
-                    /*tex (HH): the frontend should append already */
-                    if (vlink(no_break(p)) != null) {
-                        if (subtype(p) != select_disc) {
-                            /*tex This could be a |tlink|. */
-                            q = tail_of_list(vlink(no_break(p)));
-                            vlink(q) = vlink(p);
-                            q = vlink(no_break(p));
-                            vlink(no_break(p)) = null;
-                            vlink(p) = q;
-                        }
-                    }
-                    break;
-                case math_node:
-                    if (synctex) {
-                        synctexmath(p, this_box);
-                    }
-                    /*tex Begin |mathskip| code. */
-                    if (glue_is_zero(p)) {
-                        cur.h += surround(p);
-                        break;
-                    } else {
-                        /*tex Fall through |mathskip|. */
-                    }
-                    /*tex End |mathskip| code. */
-                case glue_node:
-                    {
-                        /*tex
-                            Move right or output leaders, we use real
-                            multiplication.
-                        */
-                        rule.wd = width(p) - cur_g;
-                        if (g_sign != normal) {
-                            if (g_sign == stretching) {
-                                if (stretch_order(p) == g_order) {
-                                    cur_glue = cur_glue + stretch(p);
-                                    vet_glue(float_cast(glue_set(this_box)) * cur_glue);
-                                    cur_g = float_round(glue_temp);
-                                }
-                            } else if (shrink_order(p) == g_order) {
-                                cur_glue = cur_glue - shrink(p);
-                                vet_glue(float_cast(glue_set(this_box)) * cur_glue);
-                                cur_g = float_round(glue_temp);
-                            }
-                    }
-                    rule.wd = rule.wd + cur_g;
-                    if (subtype(p) >= a_leaders) {
-                        /*tex
-                            Output leaders in an hlist, |goto fin_rule| if a rule
-                            or to |next_p| if done.
-                        */
-                        leader_box = leader_ptr(p);
-                        if (type(leader_box) == rule_node) {
-                            rule.ht = height(leader_box);
-                            rule.dp = depth(leader_box);
-                            rleft = 0;
-                            rright = 0;
-                            goto FIN_RULE;
-                        }
-                        if (textdir_parallel(box_dir(leader_box), localpos.dir))
-                            leader_wd = width(leader_box);
-                        else
-                            leader_wd = height(leader_box) + depth(leader_box);
-                        if ((leader_wd > 0) && (rule.wd > 0)) {
-                            /*tex Compensate for floating-point rounding. */
-                            rule.wd = rule.wd + 10;
-                            edge = cur.h + rule.wd;
-                            lx = 0;
-                            /*tex
-                                Let |cur.h| be the position of the first box, and
-                                set |leader_wd+lx| to the spacing between
-                                corresponding parts of boxes.
-                            */
-                            if (subtype(p) == g_leaders) {
-                                save_h = cur.h;
-                                switch (localpos.dir) {
-                                    case dir_TLT:
-                                        cur.h += refpos->pos.h - shipbox_refpos.h;
-                                        cur.h = leader_wd * (cur.h / leader_wd);
-                                        cur.h -= refpos->pos.h - shipbox_refpos.h;
-                                        break;
-                                    case dir_TRT:
-                                        cur.h = refpos->pos.h - shipbox_refpos.h - cur.h;
-                                        cur.h = leader_wd * (cur.h / leader_wd);
-                                        cur.h = refpos->pos.h - shipbox_refpos.h - cur.h;
-                                        break;
-                                    case dir_LTL:
-                                    case dir_RTT:
-                                        cur.h = refpos->pos.v - shipbox_refpos.v - cur.h;
-                                        cur.h = leader_wd * (cur.h / leader_wd);
-                                        cur.h = refpos->pos.v - shipbox_refpos.v - cur.h;
-                                        break;
-                                    default:
-                                        formatted_warning("pdf backend","forcing bad dir %i to TLT in hlist case 1",localpos.dir);
-                                        localpos.dir = dir_TLT;
-                                        cur.h += refpos->pos.h - shipbox_refpos.h;
-                                        cur.h = leader_wd * (cur.h / leader_wd);
-                                        cur.h -= refpos->pos.h - shipbox_refpos.h;
-                                        break;
-                                }
-                                if (cur.h < save_h)
-                                    cur.h += leader_wd;
-                            } else if (subtype(p) == a_leaders) {
-                                save_h = cur.h;
-                                cur.h = leader_wd * (cur.h / leader_wd);
-                                if (cur.h < save_h)
-                                    cur.h += leader_wd;
-                            } else {
-                                /*tex The number of box copies: */
-                                lq = rule.wd / leader_wd;
-                                /*tex The remaining space: */
-                                lr = rule.wd % leader_wd;
-                                if (subtype(p) == c_leaders) {
-                                    cur.h += lr / 2;
-                                } else {
-                                    lx = lr / (lq + 1);
-                                    cur.h += (lr - (lq - 1) * lx) / 2;
-                                }
-                            }
-                            while (cur.h + leader_wd <= edge) {
-                                /*tex
-                                    Output a leader box at |cur.h|, then advance
-                                    |cur.h| by |leader_wd+lx|.
-                                */
-                                if (pardir_parallel(box_dir(leader_box), localpos.dir)) {
-                                    basepoint.v = 0;
-                                    if (textdir_opposite(box_dir(leader_box), localpos.dir))
-                                        basepoint.h = width(leader_box);
-                                    else
-                                        basepoint.h = 0;
-                                } else {
-                                    if (!is_mirrored(box_dir(leader_box))) {
-                                        if (partextdir_eq(box_dir(leader_box), localpos.dir))
-                                            basepoint.h = height(leader_box);
-                                        else
-                                            basepoint.h = depth(leader_box);
-                                    } else {
-                                        if (partextdir_eq(box_dir(leader_box), localpos.dir))
-                                            basepoint.h = depth(leader_box);
-                                        else
-                                            basepoint.h = height(leader_box);
-                                    }
-                                    if (partextdir_eq(localpos.dir, box_dir(leader_box)))
-                                        basepoint.v = -(width(leader_box) / 2);
-                                    else
-                                        basepoint.v = (width(leader_box) / 2);
-                                }
-                                if (!is_mirrored(localpos.dir))
-                                    basepoint.v = basepoint.v + shift_amount(leader_box); /* shift the box down */
-                                else
-                                    basepoint.v = basepoint.v - shift_amount(leader_box); /* shift the box up */
-                                tmpcur.h = cur.h + basepoint.h;
-                                tmpcur.v = basepoint.v;
-                                synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
-                                outer_doing_leaders = doing_leaders;
-                                doing_leaders = true;
-                                if (type(leader_box) == vlist_node)
-                                    vlist_out(pdf, leader_box, rule_callback_id);
-                                else
-                                    hlist_out(pdf, leader_box, rule_callback_id);
-                                doing_leaders = outer_doing_leaders;
-                                cur.h += leader_wd + lx;
-                            }
-                            cur.h = edge - 10;
-                            goto NEXTP;
-                        }
-                    }
-                    }
-                    goto MOVE_PAST;
-                    break;
-                case kern_node:
-                    if (synctex)
-                        synctexkern(p, this_box);
-                    /*tex officially we should check the subtype */
-                    cur.h += kern_width(p);
-                    break;
-                case rule_node:
-                    if (rule_dir(p) < 0)
-                        rule_dir(p) = localpos.dir;
-                    if (pardir_parallel(rule_dir(p), localpos.dir)) {
-                        rule.ht = height(p);
-                        rule.dp = depth(p);
-                        rule.wd = width(p);
-                    } else {
-                        rule.ht = width(p) / 2;
-                        rule.dp = width(p) / 2;
-                        rule.wd = height(p) + depth(p);
-                    }
-                    rleft = rule_left(p);
-                    rright = rule_right(p);
-                    goto FIN_RULE;
-                    break;
-                case dir_node:
-                    /*tex
-                        Output a reflection instruction if the direction has changed.
-                    */
-                    if (subtype(p) == normal_dir) {
-                        /*tex
-                            Calculate the needed width to the matching |enddir|, return the
-                            |enddir| node, with width info.
-                        */
-                        enddir_ptr = calculate_width_to_enddir(p, cur_glue, cur_g, this_box);
-                        if (textdir_parallel(dir_dir(p), localpos.dir)) {
-                            dir_cur_h(enddir_ptr) += cur.h;
-                            if (textdir_opposite(dir_dir(p), localpos.dir))
-                                cur.h = dir_cur_h(enddir_ptr);
-                        } else
-                            dir_cur_h(enddir_ptr) = cur.h;
-                        if (enddir_ptr != p) {
-                            /*tex Only if it is an |enddir|. */
-                            dir_cur_v(enddir_ptr) = cur.v;
-                            dir_refpos_h(enddir_ptr) = refpos->pos.h;
-                            dir_refpos_v(enddir_ptr) = refpos->pos.v;
-                            /*tex Negative: mark it as |enddir|. */
-                            dir_dir(enddir_ptr) = localpos.dir;
-                        }
-                        /*tex fake a nested |hlist_out|. */
-                        synch_pos_with_cur(pdf->posstruct, refpos, cur);
-                        refpos->pos = pdf->posstruct->pos;
-                        localpos.dir = dir_dir(p);
-                        cur.h = 0;
-                        cur.v = 0;
-                    } else {
-                        refpos->pos.h = dir_refpos_h(p);
-                        refpos->pos.v = dir_refpos_v(p);
-                        localpos.dir = dir_dir(p);
-                        cur.h = dir_cur_h(p);
-                        cur.v = dir_cur_v(p);
-                    }
-                    break;
-                case whatsit_node:
-                    /*tex Output the whatsit node |p| in |hlist_out|. */
-                    if (subtype(p) <= last_common_whatsit) {
-                        switch (subtype(p)) {
-                            case save_pos_node:
-                                last_position = pdf->posstruct->pos;
-                                /*
-                                pos_info.curpos = pdf->posstruct->pos;
-                                pos_info.boxpos.pos = refpos->pos;
-                                pos_info.boxpos.dir = localpos.dir;
-                                pos_info.boxdim.wd = width(this_box);
-                                pos_info.boxdim.ht = height(this_box);
-                                pos_info.boxdim.dp = depth(this_box);
-                                */
-                                break;
-                            case user_defined_node:
-                                break;
-                            case open_node:
-                            case write_node:
-                            case close_node:
-                                wrapup_leader(p);
-                                break;
-                            case special_node:
-                                /*tex |pdf_special(pdf, p)| */
-                            case late_lua_node:
-                                /*tex |late_lua(pdf, p)| */
-                                backend_out_whatsit[subtype(p)] (pdf, p);
-                                break;
-                            default:
-                                break;
-                        }
-                    } else {
-                        handle_backend_whatsit(pdf, p, this_box, cur);
-                    }
-                    break;
-                case margin_kern_node:
-                    cur.h += width(p);
-                    break;
-                default:
-                    break;
-            }
-            goto NEXTP;
-          FIN_RULE:
-            /*tex Output a rule in an hlist. */
-            if (is_running(rule.ht)) {
-                rule.ht = height(this_box);
-            }
-            if (rleft != 0) {
-                rule.ht -= rleft;
-                pos_down(-rleft);
-            }
-            if (is_running(rule.dp)) {
-                rule.dp = depth(this_box);
-            }
-            if (rright != 0) {
-                rule.dp -= rright;
-            }
-            /*tex We don't output empty rules. */
-            if ((rule.ht + rule.dp) > 0 && rule.wd > 0) {
-                switch (localpos.dir) {
-                    case dir_TLT:
-                        size.h = rule.wd;
-                        size.v = rule.ht + rule.dp;
-                        pos_down(rule.dp);
-                        break;
-                    case dir_TRT:
-                        size.h = rule.wd;
-                        size.v = rule.ht + rule.dp;
-                        pos_left(size.h);
-                        pos_down(rule.dp);
-                        break;
-                    case dir_LTL:
-                        size.h = rule.ht + rule.dp;
-                        size.v = rule.wd;
-                        pos_left(rule.ht);
-                        pos_down(size.v);
-                        break;
-                    case dir_RTT:
-                        size.h = rule.ht + rule.dp;
-                        size.v = rule.wd;
-                        pos_left(rule.dp);
-                        pos_down(size.v);
-                        break;
-                    default:
-                        formatted_warning("pdf backend","forcing bad dir %i to TLT in hlist case 2",localpos.dir);
-                        localpos.dir = dir_TLT;
-                        size.h = rule.wd;
-                        size.v = rule.ht + rule.dp;
-                        pos_down(rule.dp);
-                        break;
-                }
-                if (type(p) == glue_node) {
-                    q = leader_ptr(p);
-                    if ((q) && (type(q) == rule_node)) {
-                        backend_out[rule_node] (pdf, q, size, rule_callback_id);
-                    }
-                } else {
-                    backend_out[rule_node] (pdf, p, size, rule_callback_id);
-                }
-            }
-          MOVE_PAST:
-            cur.h += rule.wd;
-            if (synctex) {
-                synch_pos_with_cur(pdf->posstruct, refpos, cur);
-                synctexhorizontalruleorglue(p, this_box);
-            }
-          NEXTP:
-            p = vlink(p);
-            synch_pos_with_cur(pdf->posstruct, refpos, cur);
-        }
-    }
-    if (synctex) {
-        synctextsilh(this_box);
-    }
-    backend_out_control[backend_control_pop_list](pdf,&saved_pos,&saved_loc);
-    cur_s--;
-    pdf->posstruct = refpos;
-}
-
-void vlist_out(PDF pdf, halfword this_box, int rule_callback_id)
-{
-    /*tex The position structure local within this function: */
-    posstructure localpos;
-    /*tex The list origin pos. on the page, provided by the caller: */
-    posstructure *refpos;
-    /*tex a few status variables */
-    scaledpos cur, tmpcur, basepoint;
-    /*tex rule dimensions */
-    scaledpos size = { 0, 0 };
-    scaled effective_vertical;
-    /*tex what |cur.v| should pop to */
-    scaled save_v;
-    /*tex the top coordinate for this box */
-    scaled top_edge;
-    /*tex bottom boundary of leader space */
-    scaled edge;
-    /*tex applicable order of infinity for glue */
-    glue_ord g_order;
-    /*tex selects type of glue */
-    int g_sign;
-    /*tex glue variables */
-    int lq, lr;
-    /*tex current position in the vlist */
-    halfword p;
-    /*tex temp */
-    halfword q;
-    /*tex the leader box being replicated */
-    halfword leader_box;
-    /*tex height of leader box being replicated */
-    scaled leader_ht;
-    /*tex extra space between leader boxes */
-    scaled lx;
-    /*tex were we doing leaders? */
-    boolean outer_doing_leaders;
-    /*tex glue value before rounding */
-    real glue_temp;
-    /*tex glue seen so far */
-    real cur_glue = 0.0;
-    /*tex rounded equivalent of |cur_glue| times the glue ratio */
-    scaled cur_g = 0;
-    scaled_whd rule;
-    /*tex \DVI\ byte location upon entry */
-    int saved_loc = 0;
-    /*tex \DVI\ what |dvi| should pop to */
-    scaledpos saved_pos = { 0, 0 };
-    int synctex = synctex_par ;
-    int rleft, rright;
-    g_order = (glue_ord) glue_order(this_box);
-    g_sign = glue_sign(this_box);
-    p = list_ptr(this_box);
-    cur.h = 0;
-    cur.v = -height(this_box);
-    top_edge = cur.v;
-    refpos = pdf->posstruct;
-    /*tex Use local structure for recursion. */
-    pdf->posstruct = &localpos;
-    localpos.dir = box_dir(this_box);
-    synch_pos_with_cur(pdf->posstruct, refpos, cur);
-    cur_s++;
-    backend_out_control[backend_control_push_list](pdf,&saved_pos,&saved_loc);
-    if (synctex) {
-        synctexvlist(this_box);
-    }
-    /*tex Create thread for the current vbox if needed. */
-    check_running_thread(pdf, this_box, cur);
-    while (p != null) {
-        switch (type(p)) {
-            case hlist_node:
-            case vlist_node:
-                /*tex Output a box in a vlist. */
-                if (pardir_parallel(box_dir(p), localpos.dir)) {
-                    effective_vertical = height(p) + depth(p);
-                    if ((type(p) == hlist_node) && is_mirrored(box_dir(p)))
-                        basepoint.v = depth(p);
-                    else
-                        basepoint.v = height(p);
-                    if (textdir_opposite(box_dir(p), localpos.dir))
-                        basepoint.h = width(p);
-                    else
-                        basepoint.h = 0;
-                } else {
-                    effective_vertical = width(p);
-                    if (!is_mirrored(box_dir(p))) {
-                        if (partextdir_eq(box_dir(p), localpos.dir))
-                            basepoint.h = height(p);
-                        else
-                            basepoint.h = depth(p);
-                    } else {
-                        if (partextdir_eq(box_dir(p), localpos.dir))
-                            basepoint.h = depth(p);
-                        else
-                            basepoint.h = height(p);
-                    }
-                    if (partextdir_eq(localpos.dir, box_dir(p)))
-                        basepoint.v = 0;
-                    else
-                        basepoint.v = width(p);
-                }
-                /*tex Shift the box right. */
-                basepoint.h = basepoint.h + shift_amount(p);
-                if (list_ptr(p) == null) {
-                    cur.v += effective_vertical;
-                    if (synctex) {
-                        synch_pos_with_cur(pdf->posstruct, refpos, cur);
-                        if (type(p) == vlist_node)
-                            synctexvoidvlist(p, this_box);
-                        else
-                            synctexvoidhlist(p, this_box);
-                    }
-                } else {
-                    tmpcur.h = basepoint.h;
-                    tmpcur.v = cur.v + basepoint.v;
-                    synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
-                    if (type(p) == vlist_node)
-                        vlist_out(pdf, p, rule_callback_id);
-                    else
-                        hlist_out(pdf, p, rule_callback_id);
-                    cur.v += effective_vertical;
-                }
-                break;
-            case rule_node:
-                if (rule_dir(p) < 0)
-                    rule_dir(p) = localpos.dir;
-                if (pardir_parallel(rule_dir(p), localpos.dir)) {
-                    rule.ht = height(p);
-                    rule.dp = depth(p);
-                    rule.wd = width(p);
-                } else {
-                    rule.ht = width(p) / 2;
-                    rule.dp = width(p) / 2;
-                    rule.wd = height(p) + depth(p);
-                }
-                rleft = rule_left(p);
-                rright = rule_right(p);
-                goto FIN_RULE;
-                break;
-            case glue_node:
-                {
-                    /*tex
-                        Move down or output leaders, we use real multiplication.
-                    */
-                    rule.ht = width(p) - cur_g;
-                    if (g_sign != normal) {
-                        if (g_sign == stretching) {
-                            if (stretch_order(p) == g_order) {
-                                cur_glue = cur_glue + stretch(p);
-                                vet_glue(float_cast(glue_set(this_box)) * cur_glue);
-                                cur_g = float_round(glue_temp);
-                            }
-                        } else if (shrink_order(p) == g_order) {
-                            cur_glue = cur_glue - shrink(p);
-                            vet_glue(float_cast(glue_set(this_box)) * cur_glue);
-                            cur_g = float_round(glue_temp);
-                        }
-                }
-                rule.ht = rule.ht + cur_g;
-                if (subtype(p) >= a_leaders) {
-                    /*tex
-                        Output leaders in a vlist, |goto fin_rulefin_rule| if a
-                        rule or to |next_p| if done.
-                    */
-                    leader_box = leader_ptr(p);
-                    if (type(leader_box) == rule_node) {
-                        rule.wd = width(leader_box);
-                        rule.dp = 0;
-                        rleft = 0;
-                        rright = 0;
-                        goto FIN_RULE;
-                    }
-                    leader_ht = height(leader_box) + depth(leader_box);
-                    if ((leader_ht > 0) && (rule.ht > 0)) {
-                        /*tex Compensate for floating-point rounding: */
-                        rule.ht = rule.ht + 10;
-                        edge = cur.v + rule.ht;
-                        lx = 0;
-                        /*tex
-                            Let |cur.v| be the position of the first box, and set
-                            |leader_ht+lx| to the spacing between corresponding
-                            parts of boxes.
-                        */
-                        if (subtype(p) == g_leaders) {
-                            save_v = cur.v;
-                            switch (localpos.dir) {
-                                case dir_TLT:
-                                case dir_TRT:
-                                    cur.v = refpos->pos.v - shipbox_refpos.v - cur.v;
-                                    cur.v = leader_ht * (cur.v / leader_ht);
-                                    cur.v = refpos->pos.v - shipbox_refpos.v - cur.v;
-                                    break;
-                                case dir_LTL:
-                                    cur.v += refpos->pos.h - shipbox_refpos.h;
-                                    cur.v = leader_ht * (cur.v / leader_ht);
-                                    cur.v -= refpos->pos.h - shipbox_refpos.h;
-                                    break;
-                                case dir_RTT:
-                                    cur.v = refpos->pos.h - shipbox_refpos.h - cur.v;
-                                    cur.v = leader_ht * (cur.v / leader_ht);
-                                    cur.v = refpos->pos.h - shipbox_refpos.h - cur.v;
-                                    break;
-                                default:
-                                    formatted_warning("pdf backend","forcing bad dir %i to TLT in vlist case 1",localpos.dir);
-                                    localpos.dir = dir_TLT;
-                                    cur.v += refpos->pos.h - shipbox_refpos.h;
-                                    cur.v = leader_ht * (cur.v / leader_ht);
-                                    cur.v -= refpos->pos.h - shipbox_refpos.h;
-                                    break;
-                            }
-                            if (cur.v < save_v)
-                                cur.v += leader_ht;
-                        } else if (subtype(p) == a_leaders) {
-                            save_v = cur.v;
-                            cur.v = top_edge + leader_ht * ((cur.v - top_edge) / leader_ht);
-                            if (cur.v < save_v)
-                                cur.v += leader_ht;
-                        } else {
-                            /*tex The number of box copies. */
-                            lq = rule.ht / leader_ht;
-                            /*tex The remaining space. */
-                            lr = rule.ht % leader_ht;
-                            if (subtype(p) == c_leaders) {
-                                cur.v += lr / 2;
-                            } else {
-                                lx = lr / (lq + 1);
-                                cur.v += (lr - (lq - 1) * lx) / 2;
-                            }
-                        }
-                        while (cur.v + leader_ht <= edge) {
-                            /*tex
-                                Output a leader box at |cur.v|, then advance
-                                |cur.v| by |leader_ht+lx|.
-                            */
-                            tmpcur.h = shift_amount(leader_box);
-                            tmpcur.v = cur.v + height(leader_box);
-                            synch_pos_with_cur(pdf->posstruct, refpos, tmpcur);
-                            outer_doing_leaders = doing_leaders;
-                            doing_leaders = true;
-                            if (type(leader_box) == vlist_node)
-                                vlist_out(pdf, leader_box, rule_callback_id);
-                            else
-                                hlist_out(pdf, leader_box, rule_callback_id);
-                            doing_leaders = outer_doing_leaders;
-                            cur.v += leader_ht + lx;
-                        }
-                        cur.v = edge - 10;
-                        goto NEXTP;
-                    }
-                }
-                }
-                goto MOVE_PAST;
-                break;
-            case kern_node:
-                cur.v += width(p);
-                break;
-            case whatsit_node:
-                /*tex Output the whatsit node |p| in |vlist_out|. */
-                if (subtype(p) <= last_common_whatsit) {
-                    switch (subtype(p)) {
-                        case save_pos_node:
-                            last_position = pdf->posstruct->pos;
-                            /*
-                            pos_info.curpos = pdf->posstruct->pos;
-                            pos_info.boxpos.pos = refpos->pos;
-                            pos_info.boxpos.dir = localpos.dir;
-                            pos_info.boxdim.wd = width(this_box);
-                            pos_info.boxdim.ht = height(this_box);
-                            pos_info.boxdim.dp = depth(this_box);
-                            */
-                            break;
-                        case user_defined_node:
-                            break;
-                        case open_node:
-                        case write_node:
-                        case close_node:
-                            wrapup_leader(p);
-                            break;
-                        case special_node:
-                            /*tex |pdf_special(pdf, p)|; */
-                        case late_lua_node:
-                            /*tex |late_lua(pdf, p)|; */
-                            backend_out_whatsit[subtype(p)] (pdf, p);
-                            break;
-                        default:
-                            break;
-                    }
-                } else {
-                    handle_backend_whatsit(pdf, p, this_box, cur);
-                }
-                break;
-            case glyph_node:
-            case disc_node:
-                /*tex This can't happen unless one messes up in \LUA. */
-                confusion("vlistout");
-                break;
-            default:
-                break;
-        }
-        goto NEXTP;
-      FIN_RULE:
-        /*tex Output a rule in a vlist and |goto next_p|. */
-        if (is_running(rule.wd)) {
-            rule.wd = width(this_box);
-        }
-        if (rleft != 0) {
-            rule.wd -= rleft;
-            pos_left(-rleft);
-        }
-        if (rright != 0) {
-            rule.wd -= rright;
-        }
-        /*tex We don't output empty rules. */
-        if ((rule.ht + rule.dp) > 0 && rule.wd > 0) {
-            switch (localpos.dir) {
-                case dir_TLT:
-                    size.h = rule.wd;
-                    size.v = rule.ht + rule.dp;
-                    pos_down(size.v);
-                    break;
-                case dir_TRT:
-                    size.h = rule.wd;
-                    size.v = rule.ht + rule.dp;
-                    pos_left(size.h);
-                    pos_down(size.v);
-                    break;
-                case dir_LTL:
-                    size.h = rule.ht + rule.dp;
-                    size.v = rule.wd;
-                    pos_down(size.v);
-                    break;
-                case dir_RTT:
-                    size.h = rule.ht + rule.dp;
-                    size.v = rule.wd;
-                    pos_left(size.h);
-                    pos_down(size.v);
-                    break;
-                default:
-                    formatted_warning("pdf backend","forcing bad dir %i to TLT in vlist case 2",localpos.dir);
-                    localpos.dir = dir_TLT;
-                    size.h = rule.wd;
-                    size.v = rule.ht + rule.dp;
-                    pos_down(size.v);
-                    break;
-            }
-            if (type(p) == glue_node) {
-                q = leader_ptr(p);
-                if ((q) && (type(q) == rule_node)) {
-                    backend_out[rule_node] (pdf, q, size, rule_callback_id);
-                }
-            } else {
-                backend_out[rule_node] (pdf, p, size, rule_callback_id);
-            }
-        }
-        cur.v += rule.ht + rule.dp;
-        goto NEXTP;
-      MOVE_PAST:
-        cur.v += rule.ht;
-      NEXTP:
-        p = vlink(p);
-        synch_pos_with_cur(pdf->posstruct, refpos, cur);
-    }
-    if (synctex) {
-        synctextsilv(this_box);
-    }
-    backend_out_control[backend_control_pop_list](pdf,&saved_pos,&saved_loc);
-    cur_s--;
-    pdf->posstruct = refpos;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfliteral.w
@@ -0,0 +1,201 @@
+% pdfliteral.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"
+
+@ @c
+void pdf_special(PDF pdf, halfword p)
+{
+    int old_setting = selector;
+    str_number s;
+    selector = new_string;
+    show_token_list(token_link(write_tokens(p)), null, -1);
+    selector = old_setting;
+    s = make_string();
+    pdf_literal(pdf, s, scan_special, true);
+    flush_str(s);
+}
+
+@ To ship out a \TeX\ box to PDF page description we need to implement
+|hlist_out|, |vlist_out| and |ship_out|, which are equivalent to the \TeX'
+original |hlist_out|, |vlist_out| and |ship_out| resp. But first we need to
+declare some procedures needed in |hlist_out| and |vlist_out|.
+
+@c
+void pdf_out_literal(PDF pdf, halfword p)
+{
+    int old_setting;            /* holds print |selector| */
+    str_number s;
+    pdfstructure *ps = pdf->pstruct;
+    if (pdf_literal_type(p) == normal) {
+        old_setting = selector;
+        selector = new_string;
+        show_token_list(token_link(pdf_literal_data(p)), null, -1);
+        selector = old_setting;
+        s = make_string();
+        pdf_literal(pdf, s, pdf_literal_mode(p), false);
+        flush_str(s);
+    } else {
+        switch (pdf_literal_mode(p)) {
+            case set_origin:
+                pdf_goto_pagemode(pdf);
+                pdf_set_pos(pdf, pdf->posstruct->pos);
+                break;
+            case direct_page:
+                pdf_goto_pagemode(pdf);
+                break;
+            case direct_text:
+                pdf_goto_textmode(pdf);
+                break;
+            case direct_font:
+                pdf_goto_fontmode(pdf);
+                break;
+            case direct_always:
+                pdf_end_string_nl(pdf);
+                ps->need_tm = true;
+                break;
+            case direct_raw:
+                pdf_end_string_nl(pdf);
+                break;
+            default:
+                normal_error("pdf backend","bad literal mode");
+                break;
+        }
+        lua_pdf_literal(pdf, pdf_literal_data(p));
+    }
+}
+
+@ test equality of start of strings
+@c
+static boolean str_in_cstr(str_number s, const char *r, unsigned i)
+{
+    const unsigned char *k, *l;
+    if ((unsigned) str_length(s) < i + strlen(r))
+        return false;
+    k = (const unsigned char *) r;
+    l = str_string(s) + i;
+    while ((*l) && (*k)) {
+        if (*l++ != *k++)
+            return false;
+    }
+    return true;
+}
+
+@ @c
+void pdf_literal(PDF pdf, str_number s, int literal_mode, boolean warn)
+{
+    unsigned char *ss;
+    size_t l;
+    pool_pointer j = 0;         /* current character code position, initialized to make the compiler happy */
+    pdfstructure *p = pdf->pstruct;
+    if (s >= STRING_OFFSET) {
+        /* needed for |out_save| */
+        j = 0;
+        /* unfortunately we always go through this when we have vf specials (and also via temp strings) */
+        if (literal_mode == scan_special) {
+            if (!(str_in_cstr(s, "pdf:", 0) || str_in_cstr(s, "PDF:", 0))) {
+                if (warn && ((!(str_in_cstr(s, "src:", 0) || str_in_cstr(s, "SRC:", 0))) || (str_length(s) == 0)))
+                    tprint_nl("Non-PDF special ignored!");
+                return;
+            }
+            j = j + (pool_pointer) 4;                   /* strlen("PDF:") */
+            if (str_in_cstr(s, "direct:", 4)) {         /* strlen("PDF:") */
+                j = j + (pool_pointer) 7;               /* strlen("direct:") */
+                literal_mode = direct_always;
+            } else if (str_in_cstr(s, "page:", 4)) {    /* strlen("PDF:") */
+                j = j + (pool_pointer) 5;               /* strlen("page:") */
+                literal_mode = direct_page;
+            } else if (str_in_cstr(s, "text:", 4)) {    /* strlen("PDF:") */
+                j = j + (pool_pointer) 5;               /* strlen("text:") */
+                literal_mode = direct_text;
+            } else if (str_in_cstr(s, "raw:", 4)) {     /* strlen("PDF:") */
+                j = j + (pool_pointer) 4;               /* strlen("raw:") */
+                literal_mode = direct_raw;
+            } else if (str_in_cstr(s, "origin:", 4)) {  /* strlen("PDF:") */
+                j = j + (pool_pointer) 7;               /* strlen("origin:") */
+                literal_mode = set_origin;
+            } else {
+                literal_mode = set_origin;
+            }
+        }
+    }
+    switch (literal_mode) {
+        case set_origin:
+            pdf_goto_pagemode(pdf);
+            pdf_set_pos(pdf, pdf->posstruct->pos);
+            break;
+        case direct_page:
+            pdf_goto_pagemode(pdf);
+            break;
+        case direct_text:
+            pdf_goto_fontmode(pdf);
+//            pdf_goto_textmode(pdf);
+            break;
+        case direct_always:
+            pdf_end_string_nl(pdf);
+            p->need_tm = true;
+            break;
+        case direct_raw:
+            pdf_end_string_nl(pdf);
+            break;
+        default:
+            normal_error("pdf backend","bad literal mode");
+            break;
+    }
+    if (s >= STRING_OFFSET) {
+        ss = str_string(s);
+        l = str_length(s) - (size_t) j;
+        pdf_out_block(pdf, (const char *) (ss + j), l);
+    } else {
+        pdf_out(pdf, s);
+    }
+    pdf_out(pdf, '\n');
+}
+
+void pdf_literal_set_mode(PDF pdf, int literal_mode)
+{
+    pdfstructure *p = pdf->pstruct;
+    switch (literal_mode) {
+        case set_origin:
+            pdf_goto_pagemode(pdf);
+            pdf_set_pos(pdf, pdf->posstruct->pos);
+            break;
+        case direct_page:
+            pdf_goto_pagemode(pdf);
+            break;
+        case direct_text:
+            pdf_goto_textmode(pdf);
+            break;
+        case direct_font:
+            pdf_goto_fontmode(pdf);
+            break;
+        case direct_always:
+            pdf_end_string_nl(pdf);
+            p->need_tm = true;
+            break;
+        case direct_raw:
+            pdf_end_string_nl(pdf);
+            break;
+        default:
+            normal_error("pdf backend","bad literal mode");
+            break;
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfliteral.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-void pdf_special(PDF pdf, halfword p)
-{
-    int old_setting = selector;
-    str_number s;
-    selector = new_string;
-    show_token_list(token_link(write_tokens(p)), null, -1);
-    selector = old_setting;
-    s = make_string();
-    pdf_literal(pdf, s, scan_special, true);
-    flush_str(s);
-}
-
-/*tex
-
-    To ship out a \TeX\ box to PDF page description we need to implement
-    |hlist_out|, |vlist_out| and |ship_out|, which are equivalent to the \TeX'
-    original |hlist_out|, |vlist_out| and |ship_out| resp. But first we need to
-    declare some procedures needed in |hlist_out| and |vlist_out|.
-
-*/
-
-void pdf_out_literal(PDF pdf, halfword p)
-{
-    int old_setting;
-    str_number s;
-    int t = pdf_literal_type(p);
-    pdfstructure *ps = pdf->pstruct;
-    if (t == normal) {
-        old_setting = selector;
-        selector = new_string;
-        show_token_list(token_link(pdf_literal_data(p)), null, -1);
-        selector = old_setting;
-        s = make_string();
-        pdf_literal(pdf, s, pdf_literal_mode(p), false);
-        flush_str(s);
-    } else if (t == lua_refid_literal) {
-        switch (pdf_literal_mode(p)) {
-            case set_origin:
-                pdf_goto_pagemode(pdf);
-                pdf_set_pos(pdf, pdf->posstruct->pos);
-                break;
-            case direct_page:
-                pdf_goto_pagemode(pdf);
-                break;
-            case direct_text:
-                pdf_goto_textmode(pdf);
-                break;
-            case direct_font:
-                pdf_goto_fontmode(pdf);
-                break;
-            case direct_always:
-                pdf_end_string_nl(pdf);
-                ps->need_tm = true;
-                break;
-            case direct_raw:
-                pdf_end_string_nl(pdf);
-                break;
-            default:
-                normal_error("pdf backend","bad literal mode");
-                break;
-        }
-        lua_pdf_literal(pdf, pdf_literal_data(p), 0);
-    }
-}
-
-/*tex Test equality of start of strings: */
-
-static boolean str_in_cstr(str_number s, const char *r, unsigned i)
-{
-    const unsigned char *k, *l;
-    if ((unsigned) str_length(s) < i + strlen(r))
-        return false;
-    k = (const unsigned char *) r;
-    l = str_string(s) + i;
-    while ((*l) && (*k)) {
-        if (*l++ != *k++)
-            return false;
-    }
-    return true;
-}
-
-void pdf_literal(PDF pdf, str_number s, int literal_mode, boolean warn)
-{
-    unsigned char *ss;
-    size_t l;
-    /*tex
-        The current character code position, initialized to make the compiler
-        happy:
-    */
-    pool_pointer j = 0;
-    pdfstructure *p = pdf->pstruct;
-    if (s >= STRING_OFFSET) {
-        /*tex Needed for |out_save|: */
-        j = 0;
-        /*tex
-            Unfortunately we always go through this when we have vf specials (and
-            also via temp strings):
-        */
-        if (literal_mode == scan_special) {
-            if (!(str_in_cstr(s, "pdf:", 0) || str_in_cstr(s, "PDF:", 0))) {
-                if (warn && ((!(str_in_cstr(s, "src:", 0) || str_in_cstr(s, "SRC:", 0))) || (str_length(s) == 0)))
-                    tprint_nl("Non-PDF special ignored!");
-                return;
-            }
-            /*tex |strlen("PDF:")| */
-            j = j + (pool_pointer) 4;
-            if (str_in_cstr(s, "direct:", 4)) {
-                /*tex |strlen("direct:")| */
-                j = j + (pool_pointer) 7;
-                literal_mode = direct_always;
-            } else if (str_in_cstr(s, "page:", 4)) {
-                /*tex |strlen("page:")| */
-                j = j + (pool_pointer) 5;
-                literal_mode = direct_page;
-            } else if (str_in_cstr(s, "text:", 4)) {
-                /*tex |strlen("text:")| */
-                j = j + (pool_pointer) 5;
-                literal_mode = direct_text;
-            } else if (str_in_cstr(s, "raw:", 4)) {
-                /*tex |strlen("raw:")| */
-                j = j + (pool_pointer) 4;
-                literal_mode = direct_raw;
-            } else if (str_in_cstr(s, "origin:", 4)) {
-                /*tex |strlen("origin:")| */
-                j = j + (pool_pointer) 7;
-                literal_mode = set_origin;
-            } else {
-                literal_mode = set_origin;
-            }
-        }
-    }
-    switch (literal_mode) {
-        case set_origin:
-            pdf_goto_pagemode(pdf);
-            pdf_set_pos(pdf, pdf->posstruct->pos);
-            break;
-        case direct_page:
-            pdf_goto_pagemode(pdf);
-            break;
-        case direct_text:
-            pdf_goto_fontmode(pdf);
-            /*tex not: |pdf_goto_textmode(pdf);| */
-            break;
-        case direct_always:
-            pdf_end_string_nl(pdf);
-            p->need_tm = true;
-            break;
-        case direct_raw:
-            pdf_end_string_nl(pdf);
-            break;
-        default:
-            normal_error("pdf backend","bad literal mode");
-            break;
-    }
-    if (s >= STRING_OFFSET) {
-        ss = str_string(s);
-        l = str_length(s) - (size_t) j;
-        pdf_out_block(pdf, (const char *) (ss + j), l);
-    } else {
-        pdf_out(pdf, s);
-    }
-    pdf_out(pdf, '\n');
-}
-
-void pdf_literal_set_mode(PDF pdf, int literal_mode)
-{
-    pdfstructure *p = pdf->pstruct;
-    switch (literal_mode) {
-        case set_origin:
-            pdf_goto_pagemode(pdf);
-            pdf_set_pos(pdf, pdf->posstruct->pos);
-            break;
-        case direct_page:
-            pdf_goto_pagemode(pdf);
-            break;
-        case direct_text:
-            pdf_goto_textmode(pdf);
-            break;
-        case direct_font:
-            pdf_goto_fontmode(pdf);
-            break;
-        case direct_always:
-            pdf_end_string_nl(pdf);
-            p->need_tm = true;
-            break;
-        case direct_raw:
-            pdf_end_string_nl(pdf);
-            break;
-        default:
-            normal_error("pdf backend","bad literal mode");
-            break;
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfobj.w
@@ -0,0 +1,221 @@
+% pdfobj.w
+%
+% Copyright 2009-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"
+
+@ write a raw PDF object
+
+@c
+void pdf_write_obj(PDF pdf, int k)
+{
+    lstring data;
+    const_lstring st;
+    size_t li;                  /* index into |data.s| */
+    int saved_compress_level ;
+    int os_threshold = OBJSTM_ALWAYS;   /* gives compressed objects for \.{\\pdfvariable objcompresslevel} >= |OBJSTM_ALWAYS| */
+    int l = 0;                          /* possibly a lua registry reference */
+    int ll = 0;
+    data.s = NULL;
+    /* we can have an immediate object before we are initialized */
+    ensure_output_state(pdf, ST_HEADER_WRITTEN);
+    saved_compress_level = pdf->compress_level;
+    /* end of ugly hack */
+    if (obj_obj_pdfcompresslevel(pdf, k) > -1) { /* -1 = "unset" */
+        pdf->compress_level = obj_obj_pdfcompresslevel(pdf, k);
+        if (pdf->compress_level == 0) {
+            pdf->objcompresslevel = 0;
+        }
+    }
+    if (obj_obj_objstm_threshold(pdf, k) != OBJSTM_UNSET)
+        os_threshold = obj_obj_objstm_threshold(pdf, k);
+    if (obj_obj_is_stream(pdf, k)) {
+        pdf_begin_obj(pdf, k, OBJSTM_NEVER);
+        pdf_begin_dict(pdf);
+        l = obj_obj_stream_attr(pdf, k);
+        if (l != LUA_NOREF) {
+            lua_rawgeti(Luas, LUA_REGISTRYINDEX, l);
+            if (lua_type(Luas,-1) != LUA_TSTRING)
+                normal_error("pdf backend","invalid object");
+            st.s = lua_tolstring(Luas, -1, &li);
+            st.l = li;
+            pdf_out_block(pdf, st.s, st.l);
+            if (st.s[st.l - 1] != '\n')
+                pdf_out(pdf, '\n');
+            luaL_unref(Luas, LUA_REGISTRYINDEX, l);
+            obj_obj_stream_attr(pdf, k) = LUA_NOREF;
+        }
+        pdf_dict_add_streaminfo(pdf);
+        pdf_end_dict(pdf);
+        pdf_begin_stream(pdf);
+    } else
+        pdf_begin_obj(pdf, k, os_threshold);
+    l = obj_obj_data(pdf, k);
+    lua_rawgeti(Luas, LUA_REGISTRYINDEX, l);
+    if (lua_type(Luas,-1) != LUA_TSTRING)
+        normal_error("pdf backend","invalid object");
+    st.s = lua_tolstring(Luas, -1, &li);
+    st.l = li;
+    lua_pop(Luas, 1);
+    if (obj_obj_is_file(pdf, k)) {
+        boolean res = false;      /* callback status value */
+        const char *fnam = NULL;  /* callback found filename */
+        int callback_id;
+        /* st.s is also |\0|-terminated, even as lstring */
+        fnam = luatex_find_file(st.s, find_data_file_callback);
+        callback_id = callback_defined(read_data_file_callback);
+        if (fnam && callback_id > 0) {
+            boolean file_opened = false;
+            res = run_callback(callback_id, "S->bSd", fnam, &file_opened, &data.s, &ll);
+            data.l = (size_t) ll;
+            if (!file_opened)
+                normal_error("pdf backend", "cannot open file for embedding");
+        } else {
+            byte_file f;        /* the data file's FILE* */
+            if (!fnam)
+                fnam = st.s;
+            if (!luatex_open_input(&f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, true))
+                normal_error("pdf backend", "cannot open file for embedding");
+            res = read_data_file(f, &data.s, &ll);
+            data.l = (size_t) ll;
+            close_file(f);
+        }
+        if (data.l == 0L)
+            normal_error("pdf backend", "empty file for embedding");
+        if (!res)
+            normal_error("pdf backend", "error reading file for embedding");
+        tprint("<<");
+        tprint(st.s);
+        pdf_out_block(pdf, (const char *) data.s, data.l);
+        xfree(data.s);
+        tprint(">>");
+    } else {
+        pdf_out_block(pdf, st.s, st.l);
+    }
+    if (obj_obj_is_stream(pdf, k)) {
+        pdf_end_stream(pdf);
+        pdf_end_obj(pdf);
+    } else /* here we do the \n */
+        pdf_end_obj(pdf);
+    luaL_unref(Luas, LUA_REGISTRYINDEX, l);
+    obj_obj_data(pdf, k) = LUA_NOREF;
+    pdf->compress_level = saved_compress_level;
+}
+
+@ @c
+void init_obj_obj(PDF pdf, int k)
+{
+    obj_obj_stream_attr(pdf, k) = LUA_NOREF;
+    obj_obj_data(pdf, k) = LUA_NOREF;
+    unset_obj_obj_is_stream(pdf, k);
+    unset_obj_obj_is_file(pdf, k);
+    obj_obj_pdfcompresslevel(pdf, k) = -1; /* unset */
+    obj_obj_objstm_threshold(pdf, k) = OBJSTM_UNSET; /* unset */
+}
+
+@ The \.{\\pdfextension obj} primitive is used to create a ``raw'' object in the
+PDF output file. The object contents will be hold in memory and will be written
+out only when the object is referenced by \.{\\pdfextension refobj}. When
+\.{\\pdfextension obj} is used with \.{\\immediate}, the object contents will be
+written out immediately. Objects referenced in the current page are appended into
+|pdf_obj_list|.
+
+@c
+void scan_obj(PDF pdf)
+{
+    int k;
+    lstring *st = NULL;
+    if (scan_keyword("reserveobjnum")) {
+        get_x_token();
+        if (cur_cmd != spacer_cmd)
+            back_input();
+        pdf->obj_count++;
+        k = pdf_create_obj(pdf, obj_type_obj, 0);
+    } else {
+        if (scan_keyword("useobjnum")) {
+            scan_int();
+            k = cur_val;
+            check_obj_type(pdf, obj_type_obj, k);
+            if (is_obj_scheduled(pdf, k) || obj_data_ptr(pdf, k) != 0)
+                luaL_error(Luas, "object in use");
+        } else {
+            pdf->obj_count++;
+            k = pdf_create_obj(pdf, obj_type_obj, 0);
+        }
+        obj_data_ptr(pdf, k) = pdf_get_mem(pdf, pdfmem_obj_size);
+        init_obj_obj(pdf, k);
+        if (scan_keyword("uncompressed")) {
+            obj_obj_pdfcompresslevel(pdf, k) = 0;
+            obj_obj_objstm_threshold(pdf, k) = OBJSTM_NEVER;
+        }
+        if (scan_keyword("stream")) {
+            set_obj_obj_is_stream(pdf, k);
+            if (scan_keyword("attr")) {
+                scan_toks(false, true);
+                st = tokenlist_to_lstring(def_ref, true);
+                flush_list(def_ref);
+                lua_pushlstring(Luas, (char *) st->s, st->l);
+                obj_obj_stream_attr(pdf, k) = luaL_ref(Luas, LUA_REGISTRYINDEX);
+                free_lstring(st);
+                st = NULL;
+            }
+        }
+        if (scan_keyword("file"))
+            set_obj_obj_is_file(pdf, k);
+        scan_toks(false, true);
+        st = tokenlist_to_lstring(def_ref, true);
+        flush_list(def_ref);
+        lua_pushlstring(Luas, (char *) st->s, st->l);
+        obj_obj_data(pdf, k) = luaL_ref(Luas, LUA_REGISTRYINDEX);
+        free_lstring(st);
+        st = NULL;
+    }
+    pdf_last_obj = k;
+}
+
+@ @c
+void scan_refobj(PDF pdf)
+{
+    scan_int();
+    check_obj_type(pdf, obj_type_obj, cur_val);
+    new_whatsit(pdf_refobj_node);
+    pdf_obj_objnum(tail_par) = cur_val;
+}
+
+void scan_refobj_lua(PDF pdf, int k)
+{
+    check_obj_type(pdf, obj_type_obj, k);
+    new_whatsit(pdf_refobj_node);
+    pdf_obj_objnum(tail_par) = k;
+}
+
+@ @c
+void pdf_ref_obj(PDF pdf, halfword p)
+{
+    if (!is_obj_scheduled(pdf, pdf_obj_objnum(p)))
+        addto_page_resources(pdf, obj_type_obj, pdf_obj_objnum(p));
+}
+
+@ @c
+void pdf_ref_obj_lua(PDF pdf, int k)
+{
+    if (!is_obj_scheduled(pdf, k))
+        addto_page_resources(pdf, obj_type_obj, k);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfobj.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-/*tex Write a raw \PDF\ object: */
-
-void pdf_write_obj(PDF pdf, int k)
-{
-    lstring data;
-    const_lstring st;
-    /*tex The index into |data.s|: */
-    size_t li;
-    int saved_compress_level ;
-    /*tex Gives compressed objects for |\pdfvariable objcompresslevel| >= |OBJSTM_ALWAYS|: */
-    int os_threshold = OBJSTM_ALWAYS;
-    /*tex Possibly a \LUA\ registry reference: */
-    int l = 0;
-    int ll = 0;
-    data.s = NULL;
-    /*tex We can have an immediate object before we are initialized. */
-    ensure_output_state(pdf, ST_HEADER_WRITTEN);
-    saved_compress_level = pdf->compress_level;
-    /*tex End of a ugly hack. */
-    if (obj_obj_pdfcompresslevel(pdf, k) > -1) {
-        /*tex A value of |-1| means ``unset''. */
-        pdf->compress_level = obj_obj_pdfcompresslevel(pdf, k);
-        if (pdf->compress_level == 0) {
-            pdf->objcompresslevel = 0;
-        }
-    }
-    if (obj_obj_objstm_threshold(pdf, k) != OBJSTM_UNSET)
-        os_threshold = obj_obj_objstm_threshold(pdf, k);
-    if (obj_obj_is_stream(pdf, k)) {
-        pdf_begin_obj(pdf, k, OBJSTM_NEVER);
-        pdf_begin_dict(pdf);
-        l = obj_obj_stream_attr(pdf, k);
-        if (l != LUA_NOREF) {
-            lua_rawgeti(Luas, LUA_REGISTRYINDEX, l);
-            if (lua_type(Luas,-1) != LUA_TSTRING)
-                normal_error("pdf backend","invalid object");
-            st.s = lua_tolstring(Luas, -1, &li);
-            st.l = li;
-            lua_pop(Luas,1);
-            pdf_check_space(pdf);
-            pdf_out_block(pdf, st.s, st.l);
-            pdf_set_space(pdf);
-            luaL_unref(Luas, LUA_REGISTRYINDEX, l);
-            obj_obj_stream_attr(pdf, k) = LUA_NOREF;
-        }
-        pdf_dict_add_streaminfo(pdf);
-        pdf_end_dict(pdf);
-        pdf_begin_stream(pdf);
-    } else
-        pdf_begin_obj(pdf, k, os_threshold);
-    l = obj_obj_data(pdf, k);
-    lua_rawgeti(Luas, LUA_REGISTRYINDEX, l);
-    if (lua_type(Luas,-1) != LUA_TSTRING)
-        normal_error("pdf backend","invalid object");
-    st.s = lua_tolstring(Luas, -1, &li);
-    st.l = li;
-    lua_pop(Luas, 1);
-    if (obj_obj_is_file(pdf, k)) {
-        /*tex The callback status value: */
-        boolean res = false;
-        /*tex The callback found filename: */
-        const char *fnam = NULL;
-        int callback_id;
-        /*tex |st.s| is also |\0|-terminated, even as |lstring| */
-        fnam = luatex_find_file(st.s, find_data_file_callback);
-        callback_id = callback_defined(read_data_file_callback);
-        if (fnam && callback_id > 0) {
-            boolean file_opened = false;
-            res = run_callback(callback_id, "S->bSd", fnam, &file_opened, &data.s, &ll);
-            data.l = (size_t) ll;
-            if (!file_opened)
-                normal_error("pdf backend", "cannot open file for embedding");
-        } else {
-            /*tex The data file's |FILE*|: */
-            byte_file f;
-            if (!fnam)
-                fnam = st.s;
-            if (!luatex_open_input(&f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, true))
-                normal_error("pdf backend", "cannot open file for embedding");
-            res = read_data_file(f, &data.s, &ll);
-            data.l = (size_t) ll;
-            close_file(f);
-        }
-        if (data.l == 0L)
-            normal_error("pdf backend", "empty file for embedding");
-        if (!res)
-            normal_error("pdf backend", "error reading file for embedding");
-        tprint("<<");
-        tprint(st.s);
-        pdf_out_block(pdf, (const char *) data.s, data.l);
-        xfree(data.s);
-        tprint(">>");
-    } else {
-        pdf_out_block(pdf, st.s, st.l);
-    }
-    if (obj_obj_is_stream(pdf, k)) {
-        pdf_end_stream(pdf);
-        pdf_end_obj(pdf);
-    } else {
-        /*tex Here we do the |\n|. */
-        pdf_end_obj(pdf);
-    }
-    luaL_unref(Luas, LUA_REGISTRYINDEX, l);
-    obj_obj_data(pdf, k) = LUA_NOREF;
-    pdf->compress_level = saved_compress_level;
-}
-
-void init_obj_obj(PDF pdf, int k)
-{
-    obj_obj_stream_attr(pdf, k) = LUA_NOREF;
-    obj_obj_data(pdf, k) = LUA_NOREF;
-    unset_obj_obj_is_stream(pdf, k);
-    unset_obj_obj_is_file(pdf, k);
-    unset_obj_obj_no_length(pdf, k);
-    obj_obj_pdfcompresslevel(pdf, k) = -1; /* unset */
-    obj_obj_objstm_threshold(pdf, k) = OBJSTM_UNSET; /* unset */
-}
-
-/*tex
-
-    The |\pdfextension obj| primitive is used to create a ``raw'' object in
-    the PDF output file. The object contents will be hold in memory and will be
-    written out only when the object is referenced by |\pdfextension refobj|.
-    When |\pdfextension obj| is used with |\immediate|, the object contents
-    will be written out immediately. Objects referenced in the current page are
-    appended into |pdf_obj_list|.
-
-*/
-
-void scan_obj(PDF pdf)
-{
-    int k;
-    lstring *st = NULL;
-    if (scan_keyword("reserveobjnum")) {
-        get_x_token();
-        if (cur_cmd != spacer_cmd)
-            back_input();
-        pdf->obj_count++;
-        k = pdf_create_obj(pdf, obj_type_obj, 0);
-    } else {
-        if (scan_keyword("useobjnum")) {
-            scan_int();
-            k = cur_val;
-            check_obj_type(pdf, obj_type_obj, k);
-            if (is_obj_scheduled(pdf, k) || obj_data_ptr(pdf, k) != 0)
-                normal_error("pdf backend", "scheduled object is already used");
-        } else {
-            pdf->obj_count++;
-            k = pdf_create_obj(pdf, obj_type_obj, 0);
-        }
-        obj_data_ptr(pdf, k) = pdf_get_mem(pdf, pdfmem_obj_size);
-        init_obj_obj(pdf, k);
-        if (scan_keyword("uncompressed")) {
-            obj_obj_pdfcompresslevel(pdf, k) = 0;
-            obj_obj_objstm_threshold(pdf, k) = OBJSTM_NEVER;
-        }
-        if (scan_keyword("stream")) {
-            set_obj_obj_is_stream(pdf, k);
-            if (scan_keyword("attr")) {
-                scan_toks(false, true);
-                st = tokenlist_to_lstring(def_ref, true);
-                flush_list(def_ref);
-                lua_pushlstring(Luas, (char *) st->s, st->l);
-                obj_obj_stream_attr(pdf, k) = luaL_ref(Luas, LUA_REGISTRYINDEX);
-                free_lstring(st);
-                st = NULL;
-            }
-        }
-        if (scan_keyword("file"))
-            set_obj_obj_is_file(pdf, k);
-        scan_toks(false, true);
-        st = tokenlist_to_lstring(def_ref, true);
-        flush_list(def_ref);
-        lua_pushlstring(Luas, (char *) st->s, st->l);
-        obj_obj_data(pdf, k) = luaL_ref(Luas, LUA_REGISTRYINDEX);
-        free_lstring(st);
-        st = NULL;
-    }
-    pdf_last_obj = k;
-}
-
-void scan_refobj(PDF pdf)
-{
-    scan_int();
-    check_obj_type(pdf, obj_type_obj, cur_val);
-    new_whatsit(pdf_refobj_node);
-    pdf_obj_objnum(tail_par) = cur_val;
-}
-
-void scan_refobj_lua(PDF pdf, int k)
-{
-    check_obj_type(pdf, obj_type_obj, k);
-    new_whatsit(pdf_refobj_node);
-    pdf_obj_objnum(tail_par) = k;
-}
-
-void pdf_ref_obj(PDF pdf, halfword p)
-{
-    if (!is_obj_scheduled(pdf, pdf_obj_objnum(p)))
-        addto_page_resources(pdf, obj_type_obj, pdf_obj_objnum(p));
-}
-
-void pdf_ref_obj_lua(PDF pdf, int k)
-{
-    if (!is_obj_scheduled(pdf, k))
-        addto_page_resources(pdf, obj_type_obj, k);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfoutline.w
@@ -0,0 +1,251 @@
+% pdfoutline.w
+%
+% Copyright 2009-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"
+
+@ Data structure of outlines; it's not able to write out outline entries before
+all outline entries are defined, so memory allocated for outline entries can't
+not be deallocated and will stay in memory. For this reason we will store data of
+outline entries in |pdf->mem| instead of |mem|
+
+@c
+#define pdfmem_outline_size 8 /* size of memory in |pdf->mem| which |obj_outline_ptr| points to */
+
+#define obj_outline_count                obj_info      /* count of all opened children */
+#define obj_outline_ptr                  obj_aux       /* pointer to |pdf->mem| */
+
+#define obj_outline_title(pdf,A)         pdf->mem[obj_outline_ptr(pdf,A)]
+#define obj_outline_parent(pdf,A)        pdf->mem[obj_outline_ptr(pdf,A) + 1]
+#define obj_outline_prev(pdf,A)          pdf->mem[obj_outline_ptr(pdf,A) + 2]
+#define obj_outline_next(pdf,A)          pdf->mem[obj_outline_ptr(pdf,A) + 3]
+#define obj_outline_first(pdf,A)         pdf->mem[obj_outline_ptr(pdf,A) + 4]
+#define obj_outline_last(pdf,A)          pdf->mem[obj_outline_ptr(pdf,A) + 5]
+#define obj_outline_action_objnum(pdf,A) pdf->mem[obj_outline_ptr(pdf,A) + 6]  /* object number of action */
+#define obj_outline_attr(pdf,A)          pdf->mem[obj_outline_ptr(pdf,A) + 7]
+
+#define set_obj_outline_count(pdf,A,B)         obj_outline_count(pdf,A)=B
+#define set_obj_outline_ptr(pdf,A,B)           obj_outline_ptr(pdf,A)=B
+#define set_obj_outline_action_objnum(pdf,A,B) obj_outline_action_objnum(pdf,A)=B
+#define set_obj_outline_title(pdf,A,B)         obj_outline_title(pdf,A)=B
+#define set_obj_outline_prev(pdf,A,B)          obj_outline_prev(pdf,A)=B
+#define set_obj_outline_next(pdf,A,B)          obj_outline_next(pdf,A)=B
+#define set_obj_outline_first(pdf,A,B)         obj_outline_first(pdf,A)=B
+#define set_obj_outline_last(pdf,A,B)          obj_outline_last(pdf,A)=B
+#define set_obj_outline_parent(pdf,A,B)        obj_outline_parent(pdf,A)=B
+#define set_obj_outline_attr(pdf,A,B)          obj_outline_attr(pdf,A)=B
+
+@ @c
+static int open_subentries(PDF pdf, halfword p)
+{
+    int c, l, r;
+    int k = 0;
+    if (obj_outline_first(pdf, p) != 0) {
+        l = obj_outline_first(pdf, p);
+        do {
+            k++;
+            c = open_subentries(pdf, l);
+            if (obj_outline_count(pdf, l) > 0)
+                k = k + c;
+            obj_outline_parent(pdf, l) = p;
+            r = obj_outline_next(pdf, l);
+            if (r == 0)
+                obj_outline_last(pdf, p) = l;
+            l = r;
+        } while (l != 0);
+    }
+    if (obj_outline_count(pdf, p) > 0)
+        obj_outline_count(pdf, p) = k;
+    else
+        obj_outline_count(pdf, p) = -k;
+    return k;
+}
+
+@ return number of outline entries in the same level with |p|
+
+@c
+static int outline_list_count(PDF pdf, pointer p)
+{
+    int k = 1;
+    while (obj_outline_prev(pdf, p) != 0) {
+        k++;
+        p = obj_outline_prev(pdf, p);
+    }
+    return k;
+}
+
+@ @c
+void scan_pdfoutline(PDF pdf)
+{
+    halfword q, r;
+    int i, k, l;
+    int j = 0;
+    halfword p = null;
+    if (scan_keyword("attr")) {
+        scan_toks(false, true);
+        r = def_ref;
+    } else {
+        r = 0;
+    }
+    if (scan_keyword("useobjnum")) {
+        scan_int();
+        j = cur_val;
+    } else {
+        p = scan_action(pdf);
+    }
+    if (scan_keyword("count")) {
+        scan_int();
+        i = cur_val;
+    } else {
+        i = 0;
+    }
+    scan_toks(false, true);
+    q = def_ref;
+    if (j == 0) {
+        j = pdf_create_obj(pdf, obj_type_others, 0);
+        pdf_begin_obj(pdf, j, OBJSTM_ALWAYS);
+        write_action(pdf, p);
+        pdf_end_obj(pdf);
+        delete_action_ref(p);
+    }
+    k = pdf_create_obj(pdf, obj_type_outline, 0);
+    set_obj_outline_ptr(pdf, k, pdf_get_mem(pdf, pdfmem_outline_size));
+    set_obj_outline_action_objnum(pdf, k, j);
+    set_obj_outline_count(pdf, k, i);
+    l = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_begin_obj(pdf, l, OBJSTM_ALWAYS);
+    {
+        char *s = tokenlist_to_cstring(q, true, NULL);
+        pdf_print_str_ln(pdf, s);
+        xfree(s);
+    }
+    delete_token_ref(q);
+    pdf_end_obj(pdf);
+    set_obj_outline_title(pdf, k, l);
+    set_obj_outline_prev(pdf, k, 0);
+    set_obj_outline_next(pdf, k, 0);
+    set_obj_outline_first(pdf, k, 0);
+    set_obj_outline_last(pdf, k, 0);
+    set_obj_outline_parent(pdf, k, pdf->parent_outline);
+    set_obj_outline_attr(pdf, k, r);
+    if (pdf->first_outline == 0)
+        pdf->first_outline = k;
+    if (pdf->last_outline == 0) {
+        if (pdf->parent_outline != 0)
+            set_obj_outline_first(pdf, pdf->parent_outline, k);
+    } else {
+        set_obj_outline_next(pdf, pdf->last_outline, k);
+        set_obj_outline_prev(pdf, k, pdf->last_outline);
+    }
+    pdf->last_outline = k;
+    if (obj_outline_count(pdf, k) != 0) {
+        pdf->parent_outline = k;
+        pdf->last_outline = 0;
+    } else if ((pdf->parent_outline != 0) &&
+               (outline_list_count(pdf, k) ==
+                abs(obj_outline_count(pdf, pdf->parent_outline)))) {
+        j = pdf->last_outline;
+        do {
+            set_obj_outline_last(pdf, pdf->parent_outline, j);
+            j = pdf->parent_outline;
+            pdf->parent_outline = obj_outline_parent(pdf, pdf->parent_outline);
+        } while (!((pdf->parent_outline == 0) ||
+                   (outline_list_count(pdf, j) <
+                    abs(obj_outline_count(pdf, pdf->parent_outline)))));
+        if (pdf->parent_outline == 0)
+            pdf->last_outline = pdf->first_outline;
+        else
+            pdf->last_outline = obj_outline_first(pdf, pdf->parent_outline);
+        while (obj_outline_next(pdf, pdf->last_outline) != 0)
+            pdf->last_outline = obj_outline_next(pdf, pdf->last_outline);
+    }
+}
+
+@ In the end we must flush PDF objects that cannot be written out immediately
+after shipping out pages.
+
+@c
+int print_outlines(PDF pdf)
+{
+    int k, l, a;
+    int outlines;
+    if (pdf->first_outline != 0) {
+        outlines = pdf_create_obj(pdf, obj_type_others, 0);
+        l = pdf->first_outline;
+        k = 0;
+        do {
+            k++;
+            a = open_subentries(pdf, l);
+            if (obj_outline_count(pdf, l) > 0)
+                k = k + a;
+            set_obj_outline_parent(pdf, l, pdf->obj_ptr);
+            l = obj_outline_next(pdf, l);
+        } while (l != 0);
+        pdf_begin_obj(pdf, outlines, OBJSTM_ALWAYS);
+        pdf_begin_dict(pdf);
+        pdf_dict_add_name(pdf, "Type", "Outlines");
+        pdf_dict_add_ref(pdf, "First", pdf->first_outline);
+        pdf_dict_add_ref(pdf, "Last", pdf->last_outline);
+        pdf_dict_add_int(pdf, "Count", k);
+        pdf_end_dict(pdf);
+        pdf_end_obj(pdf);
+        /* Output PDF outline entries */
+
+        k = pdf->head_tab[obj_type_outline];
+        while (k != 0) {
+            if (obj_outline_parent(pdf, k) == pdf->parent_outline) {
+                if (obj_outline_prev(pdf, k) == 0)
+                    pdf->first_outline = k;
+                if (obj_outline_next(pdf, k) == 0)
+                    pdf->last_outline = k;
+            }
+            pdf_begin_obj(pdf, k, OBJSTM_ALWAYS);
+            pdf_begin_dict(pdf);
+            pdf_dict_add_ref(pdf, "Title", obj_outline_title(pdf, k));
+            pdf_dict_add_ref(pdf, "A", obj_outline_action_objnum(pdf, k));
+            if (obj_outline_parent(pdf, k) != 0)
+                pdf_dict_add_ref(pdf, "Parent", obj_outline_parent(pdf, k));
+            if (obj_outline_prev(pdf, k) != 0)
+                pdf_dict_add_ref(pdf, "Prev", obj_outline_prev(pdf, k));
+            if (obj_outline_next(pdf, k) != 0)
+                pdf_dict_add_ref(pdf, "Next", obj_outline_next(pdf, k));
+            if (obj_outline_first(pdf, k) != 0)
+                pdf_dict_add_ref(pdf, "First", obj_outline_first(pdf, k));
+            if (obj_outline_last(pdf, k) != 0)
+                pdf_dict_add_ref(pdf, "Last", obj_outline_last(pdf, k));
+            if (obj_outline_count(pdf, k) != 0)
+                pdf_dict_add_int(pdf, "Count", obj_outline_count(pdf, k));
+            if (obj_outline_attr(pdf, k) != 0) {
+                pdf_out(pdf, '\n');
+                pdf_print_toks(pdf, obj_outline_attr(pdf, k));
+                pdf_out(pdf, '\n');
+                delete_token_ref(obj_outline_attr(pdf, k));
+                set_obj_outline_attr(pdf, k, null);
+            }
+            pdf_end_dict(pdf);
+            pdf_end_obj(pdf);
+            k = obj_link(pdf, k);
+        }
+
+    } else {
+        outlines = 0;
+    }
+    return outlines;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfoutline.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    Data structure of outlines; it's not able to write out outline entries before
-    all outline entries are defined, so memory allocated for outline entries
-    can't not be deallocated and will stay in memory. For this reason we will
-    store data of outline entries in |pdf->mem| instead of |mem|.
-
-*/
-
-/*tex The size of memory in |pdf->mem| which |obj_outline_ptr| points to: */
-
-#define pdfmem_outline_size 8
-
-/*tex The count of all opened children: */
-
-#define obj_outline_count                obj_info
-
-/*tex The pointer to |pdf->mem|: */
-
-#define obj_outline_ptr                  obj_aux
-
-#define obj_outline_title(pdf,A)         pdf->mem[obj_outline_ptr(pdf,A)]
-#define obj_outline_parent(pdf,A)        pdf->mem[obj_outline_ptr(pdf,A) + 1]
-#define obj_outline_prev(pdf,A)          pdf->mem[obj_outline_ptr(pdf,A) + 2]
-#define obj_outline_next(pdf,A)          pdf->mem[obj_outline_ptr(pdf,A) + 3]
-#define obj_outline_first(pdf,A)         pdf->mem[obj_outline_ptr(pdf,A) + 4]
-#define obj_outline_last(pdf,A)          pdf->mem[obj_outline_ptr(pdf,A) + 5]
-#define obj_outline_action_objnum(pdf,A) pdf->mem[obj_outline_ptr(pdf,A) + 6]
-#define obj_outline_attr(pdf,A)          pdf->mem[obj_outline_ptr(pdf,A) + 7]
-
-#define set_obj_outline_count(pdf,A,B)         obj_outline_count(pdf,A)=B
-#define set_obj_outline_ptr(pdf,A,B)           obj_outline_ptr(pdf,A)=B
-#define set_obj_outline_action_objnum(pdf,A,B) obj_outline_action_objnum(pdf,A)=B
-#define set_obj_outline_title(pdf,A,B)         obj_outline_title(pdf,A)=B
-#define set_obj_outline_prev(pdf,A,B)          obj_outline_prev(pdf,A)=B
-#define set_obj_outline_next(pdf,A,B)          obj_outline_next(pdf,A)=B
-#define set_obj_outline_first(pdf,A,B)         obj_outline_first(pdf,A)=B
-#define set_obj_outline_last(pdf,A,B)          obj_outline_last(pdf,A)=B
-#define set_obj_outline_parent(pdf,A,B)        obj_outline_parent(pdf,A)=B
-#define set_obj_outline_attr(pdf,A,B)          obj_outline_attr(pdf,A)=B
-
-static int open_subentries(PDF pdf, halfword p)
-{
-    int c, l, r;
-    int k = 0;
-    if (obj_outline_first(pdf, p) != 0) {
-        l = obj_outline_first(pdf, p);
-        do {
-            k++;
-            c = open_subentries(pdf, l);
-            if (obj_outline_count(pdf, l) > 0)
-                k = k + c;
-            obj_outline_parent(pdf, l) = p;
-            r = obj_outline_next(pdf, l);
-            if (r == 0)
-                obj_outline_last(pdf, p) = l;
-            l = r;
-        } while (l != 0);
-    }
-    if (obj_outline_count(pdf, p) > 0)
-        obj_outline_count(pdf, p) = k;
-    else
-        obj_outline_count(pdf, p) = -k;
-    return k;
-}
-
-/*tex Return the number of outline entries in the same level with |p|: */
-
-static int outline_list_count(PDF pdf, pointer p)
-{
-    int k = 1;
-    while (obj_outline_prev(pdf, p) != 0) {
-        k++;
-        p = obj_outline_prev(pdf, p);
-    }
-    return k;
-}
-
-void scan_pdfoutline(PDF pdf)
-{
-    halfword q, r;
-    int i, k, l;
-    int j = 0;
-    halfword p = null;
-    if (scan_keyword("attr")) {
-        scan_toks(false, true);
-        r = def_ref;
-    } else {
-        r = 0;
-    }
-    if (scan_keyword("useobjnum")) {
-        scan_int();
-        j = cur_val;
-    } else {
-        p = scan_action(pdf);
-    }
-    if (scan_keyword("count")) {
-        scan_int();
-        i = cur_val;
-    } else {
-        i = 0;
-    }
-    scan_toks(false, true);
-    q = def_ref;
-    if (j == 0) {
-        j = pdf_create_obj(pdf, obj_type_others, 0);
-        pdf_begin_obj(pdf, j, OBJSTM_ALWAYS);
-        write_action(pdf, p);
-        pdf_end_obj(pdf);
-        delete_action_ref(p);
-    }
-    k = pdf_create_obj(pdf, obj_type_outline, 0);
-    set_obj_outline_ptr(pdf, k, pdf_get_mem(pdf, pdfmem_outline_size));
-    set_obj_outline_action_objnum(pdf, k, j);
-    set_obj_outline_count(pdf, k, i);
-    l = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_begin_obj(pdf, l, OBJSTM_ALWAYS);
-    {
-        char *s = tokenlist_to_cstring(q, true, NULL);
-        pdf_print_str_ln(pdf, s);
-        xfree(s);
-    }
-    delete_token_ref(q);
-    pdf_end_obj(pdf);
-    set_obj_outline_title(pdf, k, l);
-    set_obj_outline_prev(pdf, k, 0);
-    set_obj_outline_next(pdf, k, 0);
-    set_obj_outline_first(pdf, k, 0);
-    set_obj_outline_last(pdf, k, 0);
-    set_obj_outline_parent(pdf, k, pdf->parent_outline);
-    set_obj_outline_attr(pdf, k, r);
-    if (pdf->first_outline == 0)
-        pdf->first_outline = k;
-    if (pdf->last_outline == 0) {
-        if (pdf->parent_outline != 0)
-            set_obj_outline_first(pdf, pdf->parent_outline, k);
-    } else {
-        set_obj_outline_next(pdf, pdf->last_outline, k);
-        set_obj_outline_prev(pdf, k, pdf->last_outline);
-    }
-    pdf->last_outline = k;
-    if (obj_outline_count(pdf, k) != 0) {
-        pdf->parent_outline = k;
-        pdf->last_outline = 0;
-    } else if ((pdf->parent_outline != 0) &&
-               (outline_list_count(pdf, k) ==
-                abs(obj_outline_count(pdf, pdf->parent_outline)))) {
-        j = pdf->last_outline;
-        do {
-            set_obj_outline_last(pdf, pdf->parent_outline, j);
-            j = pdf->parent_outline;
-            pdf->parent_outline = obj_outline_parent(pdf, pdf->parent_outline);
-        } while (!((pdf->parent_outline == 0) ||
-                   (outline_list_count(pdf, j) <
-                    abs(obj_outline_count(pdf, pdf->parent_outline)))));
-        if (pdf->parent_outline == 0)
-            pdf->last_outline = pdf->first_outline;
-        else
-            pdf->last_outline = obj_outline_first(pdf, pdf->parent_outline);
-        while (obj_outline_next(pdf, pdf->last_outline) != 0)
-            pdf->last_outline = obj_outline_next(pdf, pdf->last_outline);
-    }
-}
-
-/*tex
-
-    In the end we must flush \PDF\F objects that cannot be written out
-    immediately after shipping out pages.
-
-*/
-
-int print_outlines(PDF pdf)
-{
-    int k, l, a;
-    int outlines;
-    if (pdf->first_outline != 0) {
-        outlines = pdf_create_obj(pdf, obj_type_others, 0);
-        l = pdf->first_outline;
-        k = 0;
-        do {
-            k++;
-            a = open_subentries(pdf, l);
-            if (obj_outline_count(pdf, l) > 0)
-                k = k + a;
-            set_obj_outline_parent(pdf, l, pdf->obj_ptr);
-            l = obj_outline_next(pdf, l);
-        } while (l != 0);
-        pdf_begin_obj(pdf, outlines, OBJSTM_ALWAYS);
-        pdf_begin_dict(pdf);
-        pdf_dict_add_name(pdf, "Type", "Outlines");
-        pdf_dict_add_ref(pdf, "First", pdf->first_outline);
-        pdf_dict_add_ref(pdf, "Last", pdf->last_outline);
-        pdf_dict_add_int(pdf, "Count", k);
-        pdf_end_dict(pdf);
-        pdf_end_obj(pdf);
-        /*tex Output \PDF\ outline entries. */
-        k = pdf->head_tab[obj_type_outline];
-        while (k != 0) {
-            if (obj_outline_parent(pdf, k) == pdf->parent_outline) {
-                if (obj_outline_prev(pdf, k) == 0)
-                    pdf->first_outline = k;
-                if (obj_outline_next(pdf, k) == 0)
-                    pdf->last_outline = k;
-            }
-            pdf_begin_obj(pdf, k, OBJSTM_ALWAYS);
-            pdf_begin_dict(pdf);
-            pdf_dict_add_ref(pdf, "Title", obj_outline_title(pdf, k));
-            pdf_dict_add_ref(pdf, "A", obj_outline_action_objnum(pdf, k));
-            if (obj_outline_parent(pdf, k) != 0)
-                pdf_dict_add_ref(pdf, "Parent", obj_outline_parent(pdf, k));
-            if (obj_outline_prev(pdf, k) != 0)
-                pdf_dict_add_ref(pdf, "Prev", obj_outline_prev(pdf, k));
-            if (obj_outline_next(pdf, k) != 0)
-                pdf_dict_add_ref(pdf, "Next", obj_outline_next(pdf, k));
-            if (obj_outline_first(pdf, k) != 0)
-                pdf_dict_add_ref(pdf, "First", obj_outline_first(pdf, k));
-            if (obj_outline_last(pdf, k) != 0)
-                pdf_dict_add_ref(pdf, "Last", obj_outline_last(pdf, k));
-            if (obj_outline_count(pdf, k) != 0)
-                pdf_dict_add_int(pdf, "Count", obj_outline_count(pdf, k));
-            if (obj_outline_attr(pdf, k) != 0) {
-                pdf_out(pdf, '\n');
-                pdf_print_toks(pdf, obj_outline_attr(pdf, k));
-                pdf_out(pdf, '\n');
-                delete_token_ref(obj_outline_attr(pdf, k));
-                set_obj_outline_attr(pdf, k, null);
-            }
-            pdf_end_dict(pdf);
-            pdf_end_obj(pdf);
-            k = obj_link(pdf, k);
-        }
-    } else {
-        outlines = 0;
-    }
-    return outlines;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfpage.w
@@ -0,0 +1,274 @@
+% pdfpage.w
+%
+% Copyright 2006-2012 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 <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+
+@ @c
+void init_pdf_pagecalculations(PDF pdf)
+{
+    pdfstructure *p;
+    int decimal_digits = pdf->decimal_digits;
+    if (pdf->pstruct == NULL)
+        pdf->pstruct = xmalloc(sizeof(pdfstructure));
+    p = pdf->pstruct;
+    setpdffloat(p->pdf.h, 0, decimal_digits);
+    setpdffloat(p->pdf.v, 0, decimal_digits);
+    p->cw.e = 1;
+    p->fs_cur.e = p->fs.e = (decimal_digits < 4 ? 5 : 6); /* "+ 2" makes less corrections inside []TJ */
+    /* for placement outside BT...ET */
+    setpdffloat(p->cm[0], 1, 0);
+    setpdffloat(p->cm[1], 0, 0);
+    setpdffloat(p->cm[2], 0, 0);
+    setpdffloat(p->cm[3], 1, 0);
+    setpdffloat(p->cm[4], 0, decimal_digits);   /* horizontal movement on page */
+    setpdffloat(p->cm[5], 0, decimal_digits);   /* vertical movement on page */
+    /* for placement inside BT...ET */
+    setpdffloat(p->tm0_cur, 0, 6);              /* mantissa holds HZ expand * ExtendFont */
+    setpdffloat(p->tm[0], ten_pow[6], 6);       /* mantissa holds HZ expand * ExtendFont */
+    setpdffloat(p->tm[1], 0, 0);
+    setpdffloat(p->tm[2], 0, 3);                /* mantissa holds SlantFont, 0 = default */
+    setpdffloat(p->tm[3], ten_pow[6], 6);
+    setpdffloat(p->tm[4], 0, decimal_digits);   /* mantissa holds delta from |pdf_bt_pos.h| */
+    setpdffloat(p->tm[5], 0, decimal_digits);   /* mantissa holds delta from |pdf_bt_pos.v| */
+    /*  */
+    p->f_pdf_cur = p->f_pdf = null_font;
+    p->fs_cur.m = p->fs.m = 0;
+    p->wmode = WMODE_H;
+    p->mode = PMODE_PAGE;
+    p->ishex = 0;
+    p->need_tf = false;
+    p->need_tm = false;
+    p->k1 = ten_pow[p->pdf.h.e] / by_one_bp;
+}
+
+@ @c
+void synch_pos_with_cur(posstructure * pos, posstructure * refpos, scaledpos cur)
+{
+    switch (pos->dir) {
+        case dir_TLT:
+            pos->pos.h = refpos->pos.h + cur.h;
+            pos->pos.v = refpos->pos.v - cur.v;
+            break;
+        case dir_TRT:
+            pos->pos.h = refpos->pos.h - cur.h;
+            pos->pos.v = refpos->pos.v - cur.v;
+            break;
+        case dir_LTL:
+            pos->pos.h = refpos->pos.h + cur.v;
+            pos->pos.v = refpos->pos.v - cur.h;
+            break;
+        case dir_RTT:
+            pos->pos.h = refpos->pos.h - cur.v;
+            pos->pos.v = refpos->pos.v - cur.h;
+            break;
+        default:
+            formatted_warning("pdf backend","forcing bad dir %i to TLT in synch_pos_with_cur",pos->dir);
+            pos->dir = dir_TLT;
+            pos->pos.h = refpos->pos.h + cur.h;
+            pos->pos.v = refpos->pos.v - cur.v;
+            break;
+    }
+}
+
+@ @c
+boolean calc_pdfpos(pdfstructure * p, scaledpos pos)
+{
+    scaledpos new;
+    boolean move_pdfpos = false;
+    switch (p->mode) {
+        case PMODE_PAGE:
+            new.h = i64round(pos.h * p->k1);
+            new.v = i64round(pos.v * p->k1);
+            p->cm[4].m = new.h - p->pdf.h.m;        /* cm is concatenated */
+            p->cm[5].m = new.v - p->pdf.v.m;
+            if (new.h != p->pdf.h.m || new.v != p->pdf.v.m)
+                move_pdfpos = true;
+            break;
+        case PMODE_TEXT:
+            new.h = i64round(pos.h * p->k1);
+            new.v = i64round(pos.v * p->k1);
+            p->tm[4].m = new.h - p->pdf_bt_pos.h.m; /* Tm replaces */
+            p->tm[5].m = new.v - p->pdf_bt_pos.v.m;
+            if (new.h != p->pdf.h.m || new.v != p->pdf.v.m)
+                move_pdfpos = true;
+            break;
+        case PMODE_CHAR:
+        case PMODE_CHARARRAY:
+            switch (p->wmode) {
+                case WMODE_H:
+                    new.h = i64round((pos.h * p->k1 - (double) p->pdf_tj_pos.h.m) * p->k2);
+                    new.v = i64round(pos.v * p->k1);
+                    p->tj_delta.m = -i64round((double) ((new.h - p->cw.m) / ten_pow[p->cw.e - p->tj_delta.e]));
+                    p->tm[5].m = new.v - p->pdf_bt_pos.v.m;     /* p->tm[4] is meaningless */
+                    if (p->tj_delta.m != 0 || new.v != p->pdf.v.m)
+                        move_pdfpos = true;
+                    break;
+                case WMODE_V:
+                    new.h = i64round(pos.h * p->k1);
+                    new.v = i64round(((double) p->pdf_tj_pos.v.m - pos.v * p->k1) * p->k2);
+                    p->tm[4].m = new.h - p->pdf_bt_pos.h.m;     /* p->tm[5] is meaningless */
+                    p->tj_delta.m = -i64round((double) ((new.v - p->cw.m) / ten_pow[p->cw.e - p->tj_delta.e]));
+                    if (p->tj_delta.m != 0 || new.h != p->pdf.h.m)
+                        move_pdfpos = true;
+                    break;
+                default:
+                    normal_error("pdf backend","unknown mode in char array in calc_pos");
+                    break;
+            }
+            break;
+        default:
+            normal_error("pdf backend","unknown mode in calc_pos");
+    }
+    return move_pdfpos;
+}
+
+@ @c
+void print_pdf_matrix(PDF pdf, pdffloat * tm)
+{
+    int i;
+    for (i = 0; i < 5; i++) {
+        print_pdffloat(pdf, tm[i]);
+        pdf_out(pdf, ' ');
+    }
+    print_pdffloat(pdf, tm[i]);
+}
+
+@ @c
+void pdf_print_cm(PDF pdf, pdffloat * cm)
+{
+    print_pdf_matrix(pdf, cm);
+    pdf_puts(pdf, " cm\n");
+}
+
+@ @c
+void pdf_set_pos(PDF pdf, scaledpos pos)
+{
+    boolean move;
+    pdfstructure *p = pdf->pstruct;
+    if (!is_pagemode(p))
+        normal_error("pdf backend","page mode expected in set_pos");
+    move = calc_pdfpos(p, pos);
+    if (move) {
+        pdf_print_cm(pdf, p->cm);
+        p->pdf.h.m += p->cm[4].m;
+        p->pdf.v.m += p->cm[5].m;
+    }
+}
+
+@ @c
+void pdf_set_pos_temp(PDF pdf, scaledpos pos)
+{
+    boolean move;
+    pdfstructure *p = pdf->pstruct;
+    if (!is_pagemode(p))
+        normal_error("pdf backend","page mode expected in set_pos_temp");
+    move = calc_pdfpos(p, pos);
+    if (move) {
+        pdf_print_cm(pdf, p->cm);
+    }
+}
+
+@ @c
+static void begin_text(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    if (!is_pagemode(p))
+        normal_error("pdf backend","page mode expected in begin_text");
+    p->pdf_bt_pos = p->pdf;
+    pdf_puts(pdf, "BT\n");
+    p->mode = PMODE_TEXT;
+    p->need_tf = true;
+}
+
+static void end_text(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    if (!is_textmode(p))
+        normal_error("pdf backend","text mode expected in end_text");
+    pdf_puts(pdf, "ET\n");
+    p->pdf = p->pdf_bt_pos;
+    p->mode = PMODE_PAGE;
+}
+
+void pdf_end_string_nl(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    if (is_charmode(p))
+        end_charmode(pdf);
+    if (is_chararraymode(p))
+        end_chararray(pdf);
+}
+
+@ @c
+void pdf_goto_pagemode(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    if (!is_pagemode(p)) {
+        if (is_charmode(p))
+            end_charmode(pdf);
+        if (is_chararraymode(p))
+            end_chararray(pdf);
+        if (is_textmode(p))
+            end_text(pdf);
+        if (!is_pagemode(p))
+            normal_error("pdf backend","page mode expected in goto_page_mode");
+    }
+}
+
+void pdf_goto_textmode(PDF pdf)
+{
+    pdfstructure *p = pdf->pstruct;
+    const scaledpos origin = {
+        0, 0
+    };
+    if (is_pagemode(p)) {
+        /* reset to page origin */
+        pdf_set_pos(pdf, origin);
+        begin_text(pdf);
+    } else if (!is_textmode(p)) {
+        if (is_charmode(p))
+            end_charmode(pdf);
+        if (is_chararraymode(p))
+            end_chararray(pdf);
+        if (!is_textmode(p))
+            normal_error("pdf backend","text mode expected in goto_text_mode");
+    }
+}
+
+void pdf_goto_fontmode(PDF pdf){
+    pdfstructure *p = pdf->pstruct;
+    const scaledpos origin = {
+        0, 0
+    };
+    if (is_charmode(p))
+        end_charmode(pdf);
+    if (is_chararraymode(p))
+        end_chararray(pdf);
+    if (is_textmode(p))
+        end_text(pdf);
+    pdf_set_pos(pdf, origin);
+    p->mode = PMODE_PAGE;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfpage.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
-
-Copyright 2006-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <math.h>
-
-void init_pdf_pagecalculations(PDF pdf)
-{
-    pdfstructure *p;
-    int decimal_digits = pdf->decimal_digits;
-    if (pdf->pstruct == NULL)
-        pdf->pstruct = xmalloc(sizeof(pdfstructure));
-    p = pdf->pstruct;
-    setpdffloat(p->pdf.h, 0, decimal_digits);
-    setpdffloat(p->pdf.v, 0, decimal_digits);
-    p->cw.e = 1;
-    /*tex |+ 2| makes less corrections inside []TJ */
-    p->fs_cur.e = p->fs.e = (decimal_digits < 4 ? 5 : 6);
-    /*tex for placement outside BT...ET */
-    setpdffloat(p->cm[0], 1, 0);
-    setpdffloat(p->cm[1], 0, 0);
-    setpdffloat(p->cm[2], 0, 0);
-    setpdffloat(p->cm[3], 1, 0);
-    /*tex horizontal movement on page */
-    setpdffloat(p->cm[4], 0, decimal_digits);
-    /*tex vertical movement on page */
-    setpdffloat(p->cm[5], 0, decimal_digits);
-    /*tex for placement inside BT...ET */
-    /*tex mantissa holds HZ expand * ExtendFont */
-    setpdffloat(p->tm0_cur, 0, 6);
-    /*tex mantissa holds HZ expand * ExtendFont */
-    setpdffloat(p->tm[0], ten_pow[6], 6);
-    setpdffloat(p->tm[1], 0, 0);
-    /*tex mantissa holds SlantFont, 0 = default */
-    setpdffloat(p->tm[2], 0, 3);
-    setpdffloat(p->tm[3], ten_pow[6], 6);
-    /*tex mantissa holds delta from |pdf_bt_pos.h| */
-    setpdffloat(p->tm[4], 0, decimal_digits);
-    /*tex mantissa holds delta from |pdf_bt_pos.v| */
-    setpdffloat(p->tm[5], 0, decimal_digits);
-    p->f_pdf_cur = p->f_pdf = null_font;
-    p->fs_cur.m = p->fs.m = 0;
-    p->wmode = WMODE_H;
-    p->mode = PMODE_PAGE;
-    p->ishex = 0;
-    p->need_tf = false;
-    p->need_tm = false;
-    p->done_width = false;
-    p->done_mode = false;
-    p->k1 = ten_pow[p->pdf.h.e] / by_one_bp;
-}
-
-void synch_pos_with_cur(posstructure * pos, posstructure * refpos, scaledpos cur)
-{
-    switch (pos->dir) {
-        case dir_TLT:
-            pos->pos.h = refpos->pos.h + cur.h;
-            pos->pos.v = refpos->pos.v - cur.v;
-            break;
-        case dir_TRT:
-            pos->pos.h = refpos->pos.h - cur.h;
-            pos->pos.v = refpos->pos.v - cur.v;
-            break;
-        case dir_LTL:
-            pos->pos.h = refpos->pos.h + cur.v;
-            pos->pos.v = refpos->pos.v - cur.h;
-            break;
-        case dir_RTT:
-            pos->pos.h = refpos->pos.h - cur.v;
-            pos->pos.v = refpos->pos.v - cur.h;
-            break;
-        default:
-            formatted_warning("pdf backend","forcing bad dir %i to TLT in synch_pos_with_cur",pos->dir);
-            pos->dir = dir_TLT;
-            pos->pos.h = refpos->pos.h + cur.h;
-            pos->pos.v = refpos->pos.v - cur.v;
-            break;
-    }
-}
-
-boolean calc_pdfpos(pdfstructure * p, scaledpos pos)
-{
-    scaledpos new;
-    boolean move_pdfpos = false;
-    switch (p->mode) {
-        case PMODE_PAGE:
-            new.h = i64round(pos.h * p->k1);
-            new.v = i64round(pos.v * p->k1);
-            /*tex cm is concatenated */
-            p->cm[4].m = new.h - p->pdf.h.m;
-            p->cm[5].m = new.v - p->pdf.v.m;
-            if (new.h != p->pdf.h.m || new.v != p->pdf.v.m)
-                move_pdfpos = true;
-            break;
-        case PMODE_TEXT:
-            new.h = i64round(pos.h * p->k1);
-            new.v = i64round(pos.v * p->k1);
-            /*tex Tm replaces */
-            p->tm[4].m = new.h - p->pdf_bt_pos.h.m;
-            p->tm[5].m = new.v - p->pdf_bt_pos.v.m;
-            if (new.h != p->pdf.h.m || new.v != p->pdf.v.m)
-                move_pdfpos = true;
-            break;
-        case PMODE_CHAR:
-        case PMODE_CHARARRAY:
-            switch (p->wmode) {
-                case WMODE_H:
-                    new.h = i64round((pos.h * p->k1 - (double) p->pdf_tj_pos.h.m) * p->k2);
-                    new.v = i64round(pos.v * p->k1);
-                    p->tj_delta.m = -i64round((double) ((new.h - p->cw.m) / ten_pow[p->cw.e - p->tj_delta.e]));
-                    /*tex p->tm[4] is meaningless */
-                    p->tm[5].m = new.v - p->pdf_bt_pos.v.m;
-                    if (p->tj_delta.m != 0 || new.v != p->pdf.v.m)
-                        move_pdfpos = true;
-                    break;
-                case WMODE_V:
-                    new.h = i64round(pos.h * p->k1);
-                    new.v = i64round(((double) p->pdf_tj_pos.v.m - pos.v * p->k1) * p->k2);
-                    /*tex p->tm[5] is meaningless */
-                    p->tm[4].m = new.h - p->pdf_bt_pos.h.m;
-                    p->tj_delta.m = -i64round((double) ((new.v - p->cw.m) / ten_pow[p->cw.e - p->tj_delta.e]));
-                    if (p->tj_delta.m != 0 || new.h != p->pdf.h.m)
-                        move_pdfpos = true;
-                    break;
-                default:
-                    normal_error("pdf backend","unknown mode in char array in calc_pos");
-                    break;
-            }
-            break;
-        default:
-            normal_error("pdf backend","unknown mode in calc_pos");
-    }
-    return move_pdfpos;
-}
-
-void print_pdf_matrix(PDF pdf, pdffloat * tm)
-{
-    int i;
-    for (i = 0; i < 5; i++) {
-        print_pdffloat(pdf, tm[i]);
-        pdf_out(pdf, ' ');
-    }
-    print_pdffloat(pdf, tm[i]);
-}
-
-void pdf_print_cm(PDF pdf, pdffloat * cm)
-{
-    print_pdf_matrix(pdf, cm);
-    pdf_puts(pdf, " cm\n");
-}
-
-void pdf_set_pos(PDF pdf, scaledpos pos)
-{
-    boolean move;
-    pdfstructure *p = pdf->pstruct;
-    if (!is_pagemode(p))
-        normal_error("pdf backend","page mode expected in set_pos");
-    move = calc_pdfpos(p, pos);
-    if (move) {
-        pdf_print_cm(pdf, p->cm);
-        p->pdf.h.m += p->cm[4].m;
-        p->pdf.v.m += p->cm[5].m;
-    }
-}
-
-void pdf_set_pos_temp(PDF pdf, scaledpos pos)
-{
-    boolean move;
-    pdfstructure *p = pdf->pstruct;
-    if (!is_pagemode(p))
-        normal_error("pdf backend","page mode expected in set_pos_temp");
-    move = calc_pdfpos(p, pos);
-    if (move) {
-        pdf_print_cm(pdf, p->cm);
-    }
-}
-
-static void begin_text(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-    if (!is_pagemode(p))
-        normal_error("pdf backend","page mode expected in begin_text");
-    p->pdf_bt_pos = p->pdf;
-    pdf_puts(pdf, "BT\n");
-    p->mode = PMODE_TEXT;
-    p->need_tf = true;
-    p->need_width = 0;
-    p->need_mode = 0;
-}
-
-static void end_text(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-    if (!is_textmode(p))
-        normal_error("pdf backend","text mode expected in end_text");
-
-    if (p->done_width != 0) {
-        pdf_puts(pdf, "0 w\n");
-        p->done_width = 0;
-    }
-    if (p->done_mode != 0) {
-        pdf_puts(pdf, "0 Tr\n");
-        p->done_mode = 0;
-    }
-
-    pdf_puts(pdf, "ET\n");
-    p->pdf = p->pdf_bt_pos;
-    p->mode = PMODE_PAGE;
-}
-
-void pdf_end_string_nl(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-    if (is_charmode(p))
-        end_charmode(pdf);
-    if (is_chararraymode(p))
-        end_chararray(pdf);
-}
-
-void pdf_goto_pagemode(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-    if (!is_pagemode(p)) {
-        if (is_charmode(p))
-            end_charmode(pdf);
-        if (is_chararraymode(p))
-            end_chararray(pdf);
-        if (is_textmode(p))
-            end_text(pdf);
-        if (!is_pagemode(p))
-            normal_error("pdf backend","page mode expected in goto_page_mode");
-    }
-}
-
-void pdf_goto_textmode(PDF pdf)
-{
-    pdfstructure *p = pdf->pstruct;
-    const scaledpos origin = {
-        0, 0
-    };
-    if (is_pagemode(p)) {
-        /*tex Reset to the page origin: */
-        pdf_set_pos(pdf, origin);
-        begin_text(pdf);
-    } else if (!is_textmode(p)) {
-        if (is_charmode(p))
-            end_charmode(pdf);
-        if (is_chararraymode(p))
-            end_chararray(pdf);
-        if (!is_textmode(p))
-            normal_error("pdf backend","text mode expected in goto_text_mode");
-    }
-}
-
-void pdf_goto_fontmode(PDF pdf){
-    pdfstructure *p = pdf->pstruct;
-    const scaledpos origin = {
-        0, 0
-    };
-    if (is_charmode(p))
-        end_charmode(pdf);
-    if (is_chararraymode(p))
-        end_chararray(pdf);
-    if (is_textmode(p))
-        end_text(pdf);
-    pdf_set_pos(pdf, origin);
-    p->mode = PMODE_PAGE;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfpagetree.w
@@ -0,0 +1,230 @@
+% pdfpagetree.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"
+
+@* Page diversions.
+
+@ @c
+# define PAGES_TREE_KIDSMAX 10
+
+static struct avl_table *divert_list_tree = NULL;
+
+typedef struct pages_entry_ {
+    int objnum;                   /* object number of this /Pages object */
+    int number_of_pages;          /* total number of all pages below */
+    int number_of_kids;           /* number of direct kid objects */
+    int kids[PAGES_TREE_KIDSMAX]; /* array of kid object numbers */
+    struct pages_entry_ *next;
+} pages_entry;
+
+typedef struct divert_list_entry_ {
+    int divnum;
+    pages_entry *first;
+    pages_entry *last;
+} divert_list_entry;
+
+static int comp_divert_list_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    if (((const divert_list_entry *) pa)->divnum > ((const divert_list_entry *) pb)->divnum)
+        return 1;
+    if (((const divert_list_entry *) pa)->divnum < ((const divert_list_entry *) pb)->divnum)
+        return -1;
+    return 0;
+}
+
+@ @c
+static pages_entry *new_pages_entry(PDF pdf)
+{
+    int i;
+    pages_entry *p = xtalloc(1, pages_entry);
+    p->number_of_pages = p->number_of_kids = 0;
+    for (i = 0; i < PAGES_TREE_KIDSMAX; i++)
+        p->kids[i] = 0;
+    p->next = NULL;
+    p->objnum = pdf_create_obj(pdf, obj_type_pages, 0);
+    return p;
+}
+
+@ @c
+static divert_list_entry *new_divert_list_entry(void)
+{
+    divert_list_entry *d;
+    d = xtalloc(1, divert_list_entry);
+    d->first = d->last = NULL;
+    return d;
+}
+
+@ @c
+static void ensure_list_tree(void)
+{
+    if (divert_list_tree == NULL) {
+        divert_list_tree = avl_create(comp_divert_list_entry, NULL, &avl_xallocator);
+    }
+}
+
+@ @c
+static divert_list_entry *get_divert_list(int divnum)
+{
+    divert_list_entry *d, tmp;
+    void **aa;
+    tmp.divnum = divnum;
+    d = (divert_list_entry *) avl_find(divert_list_tree, &tmp);
+    if (d == NULL) {
+        d = new_divert_list_entry();
+        d->divnum = divnum;
+        /* the next bit of code can actually be removed */
+        aa = avl_probe(divert_list_tree, d);
+        if (aa==NULL) {
+            normal_error("pdf backend","page list lookup error");
+        }
+    }
+    return d;
+}
+
+@ |pdf_do_page_divert()| returns the current /Parent object number
+@c
+int pdf_do_page_divert(PDF pdf, int objnum, int divnum)
+{
+    divert_list_entry *d;
+    pages_entry *p;
+    /* initialize the tree */
+    ensure_list_tree();
+    /* make sure we have a list for this diversion */
+    d = get_divert_list(divnum);
+    if (d->first == NULL || d->last->number_of_kids == PAGES_TREE_KIDSMAX) {
+        /* append a new |pages_entry| */
+        p = new_pages_entry(pdf);
+        if (d->first == NULL)
+            d->first = p;
+        else
+            d->last->next = p;
+        d->last = p;
+    }
+    p = d->last;
+    p->kids[p->number_of_kids++] = objnum;
+    p->number_of_pages++;
+    return p->objnum;
+}
+
+@ @c
+static void movelist(divert_list_entry * d, divert_list_entry * dto)
+{
+    if (d != NULL && d->first != NULL && d->divnum != dto->divnum) {
+        /* no undivert of empty list or into self */
+        if (dto->first == NULL)
+            dto->first = d->first;
+        else
+            dto->last->next = d->first;
+        dto->last = d->last;
+        /* one could as well remove this |divert_list_entry| */
+        d->first = d->last = NULL;
+    }
+}
+
+@ undivert from diversion |divnum| into diversion |curdivnum|
+@c
+void pdf_do_page_undivert(int divnum, int curdivnum)
+{
+    divert_list_entry *d, *dto, tmp;
+    struct avl_traverser t;
+    /*  initialize the tree */
+    ensure_list_tree();
+    /* find the diversion |curdivnum| list where diversion |divnum| should go */
+    dto = get_divert_list(curdivnum);
+    if (divnum == 0) {
+        /* 0 = special case: undivert {\it all\/} lists */
+        avl_t_init(&t, divert_list_tree);
+        for (d = avl_t_first(&t, divert_list_tree); d != NULL;
+             d = avl_t_next(&t))
+            movelist(d, dto);
+    } else {
+        tmp.divnum = divnum;
+        d = (divert_list_entry *) avl_find(divert_list_tree, &tmp);
+        movelist(d, dto);
+    }
+}
+
+@ write a /Pages object
+@c
+
+static void write_pages(PDF pdf, pages_entry * p, int parent)
+{
+    int i;
+    int pages_attributes ;
+    pdf_begin_obj(pdf, p->objnum, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_name(pdf, "Type", "Pages");
+    if (parent == 0) {
+        /* it's root */
+        pages_attributes = pdf_pages_attr; /* lookup once */
+        if (pages_attributes != null) {
+            pdf_print_toks(pdf, pages_attributes);
+            pdf_out(pdf, ' ');
+        }
+        print_pdf_table_string(pdf, "pagesattributes");
+        pdf_out(pdf, ' ');
+    } else
+        pdf_dict_add_ref(pdf, "Parent", parent);
+    pdf_dict_add_int(pdf, "Count", (int) p->number_of_pages);
+    pdf_add_name(pdf, "Kids");
+    pdf_begin_array(pdf);
+    for (i = 0; i < p->number_of_kids; i++)
+        pdf_add_ref(pdf, (int) p->kids[i]);
+    pdf_end_array(pdf);
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+}
+
+@ loop over all /Pages objects, output them, create their parents,
+recursing bottom up, return the /Pages root object number
+
+@c
+static int output_pages_list(PDF pdf, pages_entry * pe)
+{
+    pages_entry *p, *q, *r;
+    if (pe->next == NULL) {
+        /* everything fits into one |pages_entry| */
+        write_pages(pdf, pe, 0); /* /Pages root found */
+        return pe->objnum;
+    }
+    q = r = new_pages_entry(pdf); /* one level higher needed */
+    for (p = pe; p != NULL; p = p->next) {
+        if (q->number_of_kids == PAGES_TREE_KIDSMAX) {
+            q->next = new_pages_entry(pdf);
+            q = q->next;
+        }
+        q->kids[q->number_of_kids++] = p->objnum;
+        q->number_of_pages += p->number_of_pages;
+        write_pages(pdf, p, q->objnum);
+    }
+    return output_pages_list(pdf, r); /* recurse through next higher level */
+}
+
+@ @c
+int output_pages_tree(PDF pdf)
+{
+    divert_list_entry *d;
+    pdf_do_page_undivert(0, 0); /* concatenate all diversions into diversion 0 */
+    d = get_divert_list(0);     /* get diversion 0 */
+    return output_pages_list(pdf, d->first);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfpagetree.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#define PAGES_TREE_KIDSMAX 10
-
-static struct avl_table *divert_list_tree = NULL;
-
-typedef struct pages_entry_ {
-    /*tex The object number of this |/Pages| object. */
-    int objnum;
-    /*tex The total number of all pages below. */
-    int number_of_pages;
-    /*tex The number of direct kid objects. */
-    int number_of_kids;
-    /*tex The array of kid object numbers. */
-    int kids[PAGES_TREE_KIDSMAX];
-    struct pages_entry_ *next;
-} pages_entry;
-
-typedef struct divert_list_entry_ {
-    int divnum;
-    pages_entry *first;
-    pages_entry *last;
-} divert_list_entry;
-
-static int comp_divert_list_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    if (((const divert_list_entry *) pa)->divnum > ((const divert_list_entry *) pb)->divnum)
-        return 1;
-    if (((const divert_list_entry *) pa)->divnum < ((const divert_list_entry *) pb)->divnum)
-        return -1;
-    return 0;
-}
-
-static pages_entry *new_pages_entry(PDF pdf)
-{
-    int i;
-    pages_entry *p = xtalloc(1, pages_entry);
-    p->number_of_pages = p->number_of_kids = 0;
-    for (i = 0; i < PAGES_TREE_KIDSMAX; i++)
-        p->kids[i] = 0;
-    p->next = NULL;
-    p->objnum = pdf_create_obj(pdf, obj_type_pages, 0);
-    return p;
-}
-
-static divert_list_entry *new_divert_list_entry(void)
-{
-    divert_list_entry *d;
-    d = xtalloc(1, divert_list_entry);
-    d->first = d->last = NULL;
-    return d;
-}
-
-static void ensure_list_tree(void)
-{
-    if (divert_list_tree == NULL) {
-        divert_list_tree = avl_create(comp_divert_list_entry, NULL, &avl_xallocator);
-    }
-}
-
-static divert_list_entry *get_divert_list(int divnum)
-{
-    divert_list_entry *d, tmp;
-    void **aa;
-    tmp.divnum = divnum;
-    d = (divert_list_entry *) avl_find(divert_list_tree, &tmp);
-    if (d == NULL) {
-        d = new_divert_list_entry();
-        d->divnum = divnum;
-        /*tex The next bit of code can actually be removed. */
-        aa = avl_probe(divert_list_tree, d);
-        if (aa==NULL) {
-            normal_error("pdf backend","page list lookup error");
-        }
-    }
-    return d;
-}
-
-/*tex |pdf_do_page_divert| returns the current |/Parent| object number. */
-
-int pdf_do_page_divert(PDF pdf, int objnum, int divnum)
-{
-    divert_list_entry *d;
-    pages_entry *p;
-    /*tex Initialize the tree. */
-    ensure_list_tree();
-    /*tex Make sure we have a list for this diversion. */
-    d = get_divert_list(divnum);
-    if (d->first == NULL || d->last->number_of_kids == PAGES_TREE_KIDSMAX) {
-        /*tex Append a new |pages_entry|. */
-        p = new_pages_entry(pdf);
-        if (d->first == NULL)
-            d->first = p;
-        else
-            d->last->next = p;
-        d->last = p;
-    }
-    p = d->last;
-    p->kids[p->number_of_kids++] = objnum;
-    p->number_of_pages++;
-    return p->objnum;
-}
-
-static void movelist(divert_list_entry * d, divert_list_entry * dto)
-{
-    if (d != NULL && d->first != NULL && d->divnum != dto->divnum) {
-        /*tex No undivert of empty list or into self. */
-        if (dto->first == NULL)
-            dto->first = d->first;
-        else
-            dto->last->next = d->first;
-        dto->last = d->last;
-        /*tex One could as well remove this |divert_list_entry|. */
-        d->first = d->last = NULL;
-    }
-}
-
-/*tex Undivert from diversion |divnum| into diversion |curdivnum|. */
-
-void pdf_do_page_undivert(int divnum, int curdivnum)
-{
-    divert_list_entry *d, *dto, tmp;
-    struct avl_traverser t;
-    /*tex Initialize the tree. */
-    ensure_list_tree();
-    /*tex Find the diversion |curdivnum| list where diversion |divnum| should go. */
-    dto = get_divert_list(curdivnum);
-    if (divnum == 0) {
-        /*tex Zero is a special case: undivert {\em all} lists. */
-        avl_t_init(&t, divert_list_tree);
-        for (d = avl_t_first(&t, divert_list_tree); d != NULL;
-             d = avl_t_next(&t))
-            movelist(d, dto);
-    } else {
-        tmp.divnum = divnum;
-        d = (divert_list_entry *) avl_find(divert_list_tree, &tmp);
-        movelist(d, dto);
-    }
-}
-
-/*tex Write a |/Pages| object. */
-
-static void write_pages(PDF pdf, pages_entry * p, int parent, int callback_id)
-{
-    int i;
-    int pages_attributes ;
-    pdf_begin_obj(pdf, p->objnum, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_name(pdf, "Type", "Pages");
-    if (parent == 0) {
-        /*tex It's root. Lookup the attributes once. */
-        pages_attributes = pdf_pages_attr;
-        if (pages_attributes != null) {
-            pdf_print_toks(pdf, pages_attributes);
-            pdf_out(pdf, ' ');
-        }
-        print_pdf_table_string(pdf, "pagesattributes");
-        pdf_out(pdf, ' ');
-    } else {
-        pdf_dict_add_ref(pdf, "Parent", parent);
-    }
-    pdf_dict_add_int(pdf, "Count", (int) p->number_of_pages);
-    pdf_add_name(pdf, "Kids");
-    pdf_begin_array(pdf);
-    for (i = 0; i < p->number_of_kids; i++) {
-        if (callback_id) {
-            /* new */
-            int objnum = (int) p->kids[i];
-            if (obj_type(pdf, objnum) == obj_type_page) {
-                run_callback(callback_id, "d->d", objnum, &objnum);
-                check_obj_exists(pdf, objnum);
-                pdf_add_ref(pdf, (int) objnum);
-            } else {
-                pdf_add_ref(pdf, (int) p->kids[i]);
-            }
-        } else {
-            pdf_add_ref(pdf, (int) p->kids[i]);
-        }
-    }
-    pdf_end_array(pdf);
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-}
-
-/*tex
-
-    Loop over all |/Pages| objects, output them, create their parents, recursing
-    bottom up, return the |/Pages| root object number.
-
-*/
-
-static int output_pages_list(PDF pdf, pages_entry * pe, int callback_id)
-{
-    pages_entry *p, *q, *r;
-    if (pe->next == NULL) {
-        /*tex Everything fits into one |pages_entry|. */
-        write_pages(pdf, pe, 0, callback_id);
-        return pe->objnum;
-    }
-    /*tex One level higher needed. */
-    q = r = new_pages_entry(pdf);
-    for (p = pe; p != NULL; p = p->next) {
-        if (q->number_of_kids == PAGES_TREE_KIDSMAX) {
-            q->next = new_pages_entry(pdf);
-            q = q->next;
-        }
-        q->kids[q->number_of_kids++] = p->objnum;
-        q->number_of_pages += p->number_of_pages;
-        write_pages(pdf, p, q->objnum, callback_id);
-    }
-    /*tex Recurse through next higher level. */
-    return output_pages_list(pdf, r, callback_id);
-}
-
-int output_pages_tree(PDF pdf)
-{
-    int callback_id = callback_defined(page_objnum_provider_callback);
-    divert_list_entry *d;
-    /*tex Concatenate all diversions into diversion 0. */
-    pdf_do_page_undivert(0, 0);
-    /*tex Get diversion 0. */
-    d = get_divert_list(0);
-    return output_pages_list(pdf, d->first, callback_id);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfrule.w
@@ -0,0 +1,90 @@
+% pdfrule.w
+%
+% Copyright 2010-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 "pdf/pdfpage.h"
+
+@ @c
+
+/* maybe we should have an extra callback on normal rules or on any rule in 2.0+ */
+
+void pdf_place_rule(PDF pdf, halfword q, scaledpos size, int callback_id)
+{
+    pdfpos dim;
+    pdfstructure *p = pdf->pstruct;
+    scaledpos pos = pdf->posstruct->pos;
+    halfword s = subtype(q);
+    /*  (void) q; */
+    if (s >= math_over_rule && s <= math_radical_rule) {
+        if (callback_id == 0) {
+            s = normal_rule;
+        } else {
+            s = user_rule;
+        }
+    }
+    if (s == box_rule) {
+        pdf_place_form(pdf,q);
+    } else if (s == image_rule) {
+        pdf_place_image(pdf,q);
+    } else if (s == empty_rule) {
+        /* place nothing, only take space */
+    } else if (s == user_rule) {
+        if (callback_id != 0) {
+            pdf_goto_pagemode(pdf);
+            pdf_puts(pdf, "q\n");
+            pdf_set_pos_temp(pdf, pos);
+            run_callback(callback_id, "Ndd->",q,size.h,size.v);
+            pdf_puts(pdf, "\nQ\n");
+        }
+    } else {
+        /* normal_rule or >= 100 being a leader rule */
+        pdf_goto_pagemode(pdf);
+        dim.h.m = i64round(size.h * p->k1);
+        dim.h.e = p->pdf.h.e;
+        dim.v.m = i64round(size.v * p->k1);
+        dim.v.e = p->pdf.v.e;
+        pdf_puts(pdf, "q\n");
+        if (size.v <= one_bp) {
+            pos.v += i64round(0.5 * size.v);
+            pdf_set_pos_temp(pdf, pos);
+            pdf_puts(pdf, "[]0 d 0 J ");
+            print_pdffloat(pdf, dim.v);
+            pdf_puts(pdf, " w 0 0 m ");
+            print_pdffloat(pdf, dim.h);
+            pdf_puts(pdf, " 0 l S\n");
+        } else if (size.h <= one_bp) {
+            pos.h += i64round(0.5 * size.h);
+            pdf_set_pos_temp(pdf, pos);
+            pdf_puts(pdf, "[]0 d 0 J ");
+            print_pdffloat(pdf, dim.h);
+            pdf_puts(pdf, " w 0 0 m 0 ");
+            print_pdffloat(pdf, dim.v);
+            pdf_puts(pdf, " l S\n");
+        } else {
+            pdf_set_pos_temp(pdf, pos);
+            pdf_puts(pdf, "0 0 ");
+            print_pdffloat(pdf, dim.h);
+            pdf_out(pdf, ' ');
+            print_pdffloat(pdf, dim.v);
+            pdf_puts(pdf, " re f\n");
+        }
+        pdf_puts(pdf, "Q\n");
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfrule.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
-
-Copyright 2010-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "pdf/pdfpage.h"
-
-void pdf_place_rule(PDF pdf, halfword q, scaledpos size, int callback_id)
-{
-    pdfpos dim;
-    pdfstructure *p = pdf->pstruct;
-    scaledpos pos = pdf->posstruct->pos;
-    halfword s = subtype(q);
-    if (s >= math_over_rule && s <= math_radical_rule) {
-        if (callback_id == 0) {
-            s = normal_rule;
-        } else {
-            s = user_rule;
-        }
-    }
-    if (s == box_rule) {
-        pdf_place_form(pdf,q);
-    } else if (s == image_rule) {
-        pdf_place_image(pdf,q);
-    } else if (s == empty_rule) {
-        /*tex Place nothing, only take space. */
-    } else if (s == user_rule) {
-        if (callback_id != 0) {
-            pdf_goto_pagemode(pdf);
-            pdf_puts(pdf, "q\n");
-            pdf_set_pos_temp(pdf, pos);
-            run_callback(callback_id, "Ndd->",q,size.h,size.v);
-            pdf_puts(pdf, "\nQ\n");
-        }
-    } else {
-        /*tex |normal_rule| or >= 100 being a leader rule */
-        pdf_goto_pagemode(pdf);
-        dim.h.m = i64round(size.h * p->k1);
-        dim.h.e = p->pdf.h.e;
-        dim.v.m = i64round(size.v * p->k1);
-        dim.v.e = p->pdf.v.e;
-        pdf_puts(pdf, "q\n");
-        if (size.v <= one_bp) {
-            pos.v += i64round(0.5 * size.v);
-            pdf_set_pos_temp(pdf, pos);
-            pdf_puts(pdf, "[] 0 d 0 J ");
-            print_pdffloat(pdf, dim.v);
-            pdf_puts(pdf, " w 0 0 m ");
-            print_pdffloat(pdf, dim.h);
-            pdf_puts(pdf, " 0 l S\n");
-        } else if (size.h <= one_bp) {
-            pos.h += i64round(0.5 * size.h);
-            pdf_set_pos_temp(pdf, pos);
-            pdf_puts(pdf, "[] 0 d 0 J ");
-            print_pdffloat(pdf, dim.h);
-            pdf_puts(pdf, " w 0 0 m 0 ");
-            print_pdffloat(pdf, dim.v);
-            pdf_puts(pdf, " l S\n");
-        } else {
-            pdf_set_pos_temp(pdf, pos);
-            if (s == outline_rule) {
-                pdf_puts(pdf, "[] 0 d 0 J ");
-                if (rule_transform(q) > 0) {
-                    pdfpos temp ;
-                    temp.h.m = i64round(rule_transform(q) * p->k1);
-                    temp.h.e = p->pdf.h.e;
-                    print_pdffloat(pdf, temp.h);
-                    pdf_puts(pdf, " w ");
-                }
-            }
-            pdf_puts(pdf, "0 0 ");
-            print_pdffloat(pdf, dim.h);
-            pdf_out(pdf, ' ');
-            print_pdffloat(pdf, dim.v);
-            if (s == outline_rule) {
-                pdf_puts(pdf, " re S\n");
-            } else {
-                pdf_puts(pdf, " re f\n");
-            }
-        }
-        pdf_puts(pdf, "Q\n");
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfsaverestore.w
@@ -0,0 +1,81 @@
+% pdfsaverestore.w
+%
+% Copyright 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
+pos_entry *pos_stack = 0; /* the stack */
+int pos_stack_size = 0;   /* initially empty */
+int pos_stack_used = 0;   /* used entries */
+
+@ @c
+static void checkpdfsave(scaledpos pos)
+{
+    pos_entry *new_stack;
+    if (pos_stack_used >= pos_stack_size) {
+        pos_stack_size += STACK_INCREMENT;
+        new_stack = xtalloc((unsigned) pos_stack_size, pos_entry);
+        memcpy((void *) new_stack, (void *) pos_stack, (unsigned) pos_stack_used * sizeof(pos_entry));
+        xfree(pos_stack);
+        pos_stack = new_stack;
+    }
+    pos_stack[pos_stack_used].pos.h = pos.h;
+    pos_stack[pos_stack_used].pos.v = pos.v;
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        pos_stack[pos_stack_used].matrix_stack = matrix_stack_used;
+    }
+    pos_stack_used++;
+}
+
+@ @c
+static void checkpdfrestore(scaledpos pos)
+{
+    scaledpos diff;
+    if (pos_stack_used == 0) {
+        normal_warning("pdf backend", "'restore' is missing a 'save'");
+        return;
+    }
+    pos_stack_used--;
+    diff.h = pos.h - pos_stack[pos_stack_used].pos.h;
+    diff.v = pos.v - pos_stack[pos_stack_used].pos.v;
+    if (diff.h != 0 || diff.v != 0) {
+        formatted_warning("pdf backend","misplaced 'restore' by (%dsp, %dsp)", (int) diff.h, (int) diff.v);
+    }
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        matrix_stack_used = pos_stack[pos_stack_used].matrix_stack;
+    }
+}
+
+@ @c
+void pdf_out_save(PDF pdf, halfword p)
+{
+    (void) p;
+    checkpdfsave(pdf->posstruct->pos);
+    pdf_literal(pdf, 'q', set_origin, false);
+}
+
+@ @c
+void pdf_out_restore(PDF pdf, halfword p)
+{
+    (void) p;
+    checkpdfrestore(pdf->posstruct->pos);
+    pdf_literal(pdf, 'Q', set_origin, false);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfsaverestore.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-
-Copyright 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-pos_entry *pos_stack = 0;
-int pos_stack_size = 0;
-int pos_stack_used = 0;
-
-static void checkpdfsave(scaledpos pos)
-{
-    pos_entry *new_stack;
-    if (pos_stack_used >= pos_stack_size) {
-        pos_stack_size += STACK_INCREMENT;
-        new_stack = xtalloc((unsigned) pos_stack_size, pos_entry);
-        memcpy((void *) new_stack, (void *) pos_stack, (unsigned) pos_stack_used * sizeof(pos_entry));
-        xfree(pos_stack);
-        pos_stack = new_stack;
-    }
-    pos_stack[pos_stack_used].pos.h = pos.h;
-    pos_stack[pos_stack_used].pos.v = pos.v;
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        pos_stack[pos_stack_used].matrix_stack = matrix_stack_used;
-    }
-    pos_stack_used++;
-}
-
-static void checkpdfrestore(scaledpos pos)
-{
-    scaledpos diff;
-    if (pos_stack_used == 0) {
-        normal_warning("pdf backend", "'restore' is missing a 'save'");
-        return;
-    }
-    pos_stack_used--;
-    diff.h = pos.h - pos_stack[pos_stack_used].pos.h;
-    diff.v = pos.v - pos_stack[pos_stack_used].pos.v;
-    if (diff.h != 0 || diff.v != 0) {
-        formatted_warning("pdf backend","misplaced 'restore' by (%dsp, %dsp)", (int) diff.h, (int) diff.v);
-    }
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        matrix_stack_used = pos_stack[pos_stack_used].matrix_stack;
-    }
-}
-
-void pdf_out_save(PDF pdf, halfword p)
-{
-    (void) p;
-    checkpdfsave(pdf->posstruct->pos);
-    pdf_literal(pdf, 'q', set_origin, false);
-}
-
-void pdf_out_restore(PDF pdf, halfword p)
-{
-    (void) p;
-    checkpdfrestore(pdf->posstruct->pos);
-    pdf_literal(pdf, 'Q', set_origin, false);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfsetmatrix.w
@@ -0,0 +1,228 @@
+% pdfsetmatrix.w
+%
+% Copyright 2009 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"
+
+@ stack for \.{\\pdfextension setmatrix}
+
+@c
+matrix_entry *matrix_stack = NULL;
+int matrix_stack_size = 0;
+int matrix_stack_used = 0;
+
+@ @c
+boolean matrixused(void)
+{
+    return matrix_stack_used > 0;
+}
+
+static void matrix_stack_room(void)
+{
+    matrix_entry *new_stack;
+    if (matrix_stack_used >= matrix_stack_size) {
+        matrix_stack_size += STACK_INCREMENT;
+        new_stack = xtalloc((unsigned) matrix_stack_size, matrix_entry);
+        memcpy((void *) new_stack, (void *) matrix_stack, (unsigned) matrix_stack_used * sizeof(matrix_entry));
+        xfree(matrix_stack);
+        matrix_stack = new_stack;
+    }
+}
+
+@ \.{\\pdfextension setmatrix{a b c d}}
+
+|e| := pos.h
+
+|f| := pos.v
+
+|M_top|: current active matrix at the top of the matrix stack
+
+The origin of \.{\\pdfextension setmatrix} is the current point. The annotation
+coordinate system is the original page coordinate system. When pdfTeX calculates
+annotation rectangles it does not take into account this transformations, it uses
+the original coordinate system. To get the corrected values, first we go back to
+the origin, perform the transformation and go back:
+
+{\obeylines\obeyspaces\tt
+    (  1   0  0 )   ( a b 0 )   ( 1 0 0 )
+    (  0   1  0 ) x ( c d 0 ) x ( 0 1 0 ) x M\_top
+    ( -e  -f  1 )   ( 0 0 1 )   ( e f 1 )
+
+    ( 1  0  0 )   (  a  b 0 )
+  = ( 0  1  0 ) x (  c  d 0 ) x M\_top
+    ( e  f  1 )   ( -e -f 1 )
+
+    ( a         b         0 )
+  = ( c         d         0 ) x M\_top
+    ( e(1-a)-fc f(1-d)-eb 1 )
+}
+
+@c
+static void pdfsetmatrix(const char *in, scaledpos pos)
+{
+    /*
+        Argument of \.{\\pdfextension setmatrix} starts with |str_pool[in]| and ends
+        before |str_pool[pool_ptr]|.
+    */
+    matrix_entry x, *y, *z;
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        if (sscanf((const char *) in, " %lf %lf %lf %lf ", &x.a, &x.b, &x.c, &x.d) != 4) {
+            formatted_warning("pdf backend","unrecognized format of setmatrix: %s", in);
+            return;
+        }
+        /* calculate this transformation matrix */
+        x.e = (double) pos.h * (1.0 - x.a) - (double) pos.v * x.c;
+        x.f = (double) pos.v * (1.0 - x.d) - (double) pos.h * x.b;
+        matrix_stack_room();
+        z = &matrix_stack[matrix_stack_used];
+        if (matrix_stack_used > 0) {
+            y = &matrix_stack[matrix_stack_used - 1];
+            z->a = x.a * y->a + x.b * y->c;
+            z->b = x.a * y->b + x.b * y->d;
+            z->c = x.c * y->a + x.d * y->c;
+            z->d = x.c * y->b + x.d * y->d;
+            z->e = x.e * y->a + x.f * y->c + y->e;
+            z->f = x.e * y->b + x.f * y->d + y->f;
+        } else {
+            z->a = x.a;
+            z->b = x.b;
+            z->c = x.c;
+            z->d = x.d;
+            z->e = x.e;
+            z->f = x.f;
+        }
+        matrix_stack_used++;
+    }
+}
+
+@ Apply matrix to point (x,y)
+
+{\obeylines\obeyspaces\tt
+               ( a b 0 )
+   ( x y 1 ) x ( c d 0 ) = ( xa+yc+e xb+yd+f 1 )
+               ( e f 1 )
+}
+
+If \.{\\pdfextension setmatrix} wasn't used, then return the value unchanged.
+
+@ Return values for matrix tranform functions:
+
+@c
+static scaled ret_llx;
+static scaled ret_lly;
+static scaled ret_urx;
+static scaled ret_ury;
+
+scaled getllx(void)
+{
+    return ret_llx;
+}
+
+scaled getlly(void)
+{
+    return ret_lly;
+}
+
+scaled geturx(void)
+{
+    return ret_urx;
+}
+
+scaled getury(void)
+{
+    return ret_ury;
+}
+
+@ @c
+static int last_llx;
+static int last_lly;
+static int last_urx;
+static int last_ury;
+
+#define DO_ROUND(x)  ((x > 0) ? (x + .5) : (x - .5))
+#define DO_MIN(a, b) ((a < b) ? a : b)
+#define DO_MAX(a, b) ((a > b) ? a : b)
+
+static void do_matrixtransform(scaled x, scaled y, scaled * retx, scaled * rety)
+{
+    matrix_entry *m = &matrix_stack[matrix_stack_used - 1];
+    double x_old = x;
+    double y_old = y;
+    double x_new = x_old * m->a + y_old * m->c + m->e;
+    double y_new = x_old * m->b + y_old * m->d + m->f;
+    *retx = (scaled) DO_ROUND(x_new);
+    *rety = (scaled) DO_ROUND(y_new);
+}
+
+void matrixtransformrect(scaled llx, scaled lly, scaled urx, scaled ury)
+{
+    scaled x1, x2, x3, x4, y1, y2, y3, y4;
+    if (global_shipping_mode == SHIPPING_PAGE && matrix_stack_used > 0) {
+        last_llx = llx;
+        last_lly = lly;
+        last_urx = urx;
+        last_ury = ury;
+        do_matrixtransform(llx, lly, &x1, &y1);
+        do_matrixtransform(llx, ury, &x2, &y2);
+        do_matrixtransform(urx, lly, &x3, &y3);
+        do_matrixtransform(urx, ury, &x4, &y4);
+        ret_llx = DO_MIN(DO_MIN(x1, x2), DO_MIN(x3, x4));
+        ret_lly = DO_MIN(DO_MIN(y1, y2), DO_MIN(y3, y4));
+        ret_urx = DO_MAX(DO_MAX(x1, x2), DO_MAX(x3, x4));
+        ret_ury = DO_MAX(DO_MAX(y1, y2), DO_MAX(y3, y4));
+    } else {
+        ret_llx = llx;
+        ret_lly = lly;
+        ret_urx = urx;
+        ret_ury = ury;
+    }
+}
+
+void matrixtransformpoint(scaled x, scaled y)
+{
+    if (global_shipping_mode == SHIPPING_PAGE && matrix_stack_used > 0) {
+        do_matrixtransform(x, y, &ret_llx, &ret_lly);
+    } else {
+        ret_llx = x;
+        ret_lly = y;
+    }
+}
+
+void matrixrecalculate(scaled urx)
+{
+    matrixtransformrect(last_llx, last_lly, urx, last_ury);
+}
+
+@ @c
+void pdf_out_setmatrix(PDF pdf, halfword p)
+{
+    scaledpos pos = pdf->posstruct->pos;
+    int old_setting;            /* holds print |selector| */
+    str_number s;
+    old_setting = selector;
+    selector = new_string;
+    show_token_list(token_link(pdf_setmatrix_data(p)), null, -1);
+    pdfsetmatrix((char *) cur_string, pos);
+    tprint(" 0 0 cm");
+    selector = old_setting;
+    s = make_string();
+    pdf_literal(pdf, s, set_origin, false);
+    flush_str(s);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfsetmatrix.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
-
-Copyright 2009 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    We keep a stack for |pdfextension setmatrix|:
-
-*/
-
-matrix_entry *matrix_stack = NULL;
-int matrix_stack_size = 0;
-int matrix_stack_used = 0;
-
-boolean matrixused(void)
-{
-    return matrix_stack_used > 0;
-}
-
-static void matrix_stack_room(void)
-{
-    matrix_entry *new_stack;
-    if (matrix_stack_used >= matrix_stack_size) {
-        matrix_stack_size += STACK_INCREMENT;
-        new_stack = xtalloc((unsigned) matrix_stack_size, matrix_entry);
-        memcpy((void *) new_stack, (void *) matrix_stack, (unsigned) matrix_stack_used * sizeof(matrix_entry));
-        xfree(matrix_stack);
-        matrix_stack = new_stack;
-    }
-}
-
-/*tex
-
-    The matrix specification has four entries and gets translated to |e| being
-    |pos.h and |f| being pos.v. The current active matrix at the top of the
-    matrix stack is kept in |M_top|.
-
-    The origin of \.{\\pdfextension setmatrix} is the current point. The
-    annotation coordinate system is the original page coordinate system. When
-    pdfTeX calculates annotation rectangles it does not take into account this
-    transformations, it uses the original coordinate system. To get the corrected
-    values, first we go back to the origin, perform the transformation and go
-    back:
-
-    \starttyping
-      (  1   0  0 )   ( a b 0 )   ( 1 0 0 )
-      (  0   1  0 ) x ( c d 0 ) x ( 0 1 0 ) x M\_top
-      ( -e  -f  1 )   ( 0 0 1 )   ( e f 1 )
-
-      ( 1  0  0 )   (  a  b 0 )
-    = ( 0  1  0 ) x (  c  d 0 ) x M\_top
-      ( e  f  1 )   ( -e -f 1 )
-
-      ( a         b         0 )
-    = ( c         d         0 ) x M\_top
-      ( e(1-a)-fc f(1-d)-eb 1 )
-    \stoptyping
-
-*/
-
-static void pdfsetmatrix(const char *in, scaledpos pos)
-{
-    /*tex
-        The argument of |pdfextension setmatrix| starts with |str_pool[in]|
-        and ends before |str_pool[pool_ptr]|.
-    */
-    matrix_entry x, *y, *z;
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        if (sscanf((const char *) in, " %lf %lf %lf %lf ", &x.a, &x.b, &x.c, &x.d) != 4) {
-            formatted_warning("pdf backend","unrecognized format of setmatrix: %s", in);
-            return;
-        }
-        /*tex Calculate this transformation matrix. */
-        x.e = (double) pos.h * (1.0 - x.a) - (double) pos.v * x.c;
-        x.f = (double) pos.v * (1.0 - x.d) - (double) pos.h * x.b;
-        matrix_stack_room();
-        z = &matrix_stack[matrix_stack_used];
-        if (matrix_stack_used > 0) {
-            y = &matrix_stack[matrix_stack_used - 1];
-            z->a = x.a * y->a + x.b * y->c;
-            z->b = x.a * y->b + x.b * y->d;
-            z->c = x.c * y->a + x.d * y->c;
-            z->d = x.c * y->b + x.d * y->d;
-            z->e = x.e * y->a + x.f * y->c + y->e;
-            z->f = x.e * y->b + x.f * y->d + y->f;
-        } else {
-            z->a = x.a;
-            z->b = x.b;
-            z->c = x.c;
-            z->d = x.d;
-            z->e = x.e;
-            z->f = x.f;
-        }
-        matrix_stack_used++;
-    }
-}
-
-/*tex
-
-    Apply matrix to point (x,y)
-
-    \starttyping
-                ( a b 0 )
-    ( x y 1 ) x ( c d 0 ) = ( xa+yc+e xb+yd+f 1 )
-                ( e f 1 )
-    \stoptyping
-
-    If \.{\\pdfextension setmatrix} wasn't used, then return the value unchanged.
-    The return values for matrix tranform functions are:
-
-*/
-
-static scaled ret_llx;
-static scaled ret_lly;
-static scaled ret_urx;
-static scaled ret_ury;
-
-scaled getllx(void)
-{
-    return ret_llx;
-}
-
-scaled getlly(void)
-{
-    return ret_lly;
-}
-
-scaled geturx(void)
-{
-    return ret_urx;
-}
-
-scaled getury(void)
-{
-    return ret_ury;
-}
-
-static int last_llx;
-static int last_lly;
-static int last_urx;
-static int last_ury;
-
-#define DO_ROUND(x)  ((x > 0) ? (x + .5) : (x - .5))
-#define DO_MIN(a, b) ((a < b) ? a : b)
-#define DO_MAX(a, b) ((a > b) ? a : b)
-
-static void do_matrixtransform(scaled x, scaled y, scaled * retx, scaled * rety)
-{
-    matrix_entry *m = &matrix_stack[matrix_stack_used - 1];
-    double x_old = x;
-    double y_old = y;
-    double x_new = x_old * m->a + y_old * m->c + m->e;
-    double y_new = x_old * m->b + y_old * m->d + m->f;
-    *retx = (scaled) DO_ROUND(x_new);
-    *rety = (scaled) DO_ROUND(y_new);
-}
-
-void matrixtransformrect(scaled llx, scaled lly, scaled urx, scaled ury)
-{
-    scaled x1, x2, x3, x4, y1, y2, y3, y4;
-    if (global_shipping_mode == SHIPPING_PAGE && matrix_stack_used > 0) {
-        last_llx = llx;
-        last_lly = lly;
-        last_urx = urx;
-        last_ury = ury;
-        do_matrixtransform(llx, lly, &x1, &y1);
-        do_matrixtransform(llx, ury, &x2, &y2);
-        do_matrixtransform(urx, lly, &x3, &y3);
-        do_matrixtransform(urx, ury, &x4, &y4);
-        ret_llx = DO_MIN(DO_MIN(x1, x2), DO_MIN(x3, x4));
-        ret_lly = DO_MIN(DO_MIN(y1, y2), DO_MIN(y3, y4));
-        ret_urx = DO_MAX(DO_MAX(x1, x2), DO_MAX(x3, x4));
-        ret_ury = DO_MAX(DO_MAX(y1, y2), DO_MAX(y3, y4));
-    } else {
-        ret_llx = llx;
-        ret_lly = lly;
-        ret_urx = urx;
-        ret_ury = ury;
-    }
-}
-
-void matrixtransformpoint(scaled x, scaled y)
-{
-    if (global_shipping_mode == SHIPPING_PAGE && matrix_stack_used > 0) {
-        do_matrixtransform(x, y, &ret_llx, &ret_lly);
-    } else {
-        ret_llx = x;
-        ret_lly = y;
-    }
-}
-
-void matrixrecalculate(scaled urx)
-{
-    matrixtransformrect(last_llx, last_lly, urx, last_ury);
-}
-
-void pdf_out_setmatrix(PDF pdf, halfword p)
-{
-    scaledpos pos = pdf->posstruct->pos;
-    int old_setting;
-    str_number s;
-    old_setting = selector;
-    selector = new_string;
-    show_token_list(token_link(pdf_setmatrix_data(p)), null, -1);
-    pdfsetmatrix((char *) cur_string, pos);
-    tprint(" 0 0 cm");
-    selector = old_setting;
-    s = make_string();
-    pdf_literal(pdf, s, set_origin, false);
-    flush_str(s);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfshipout.w
@@ -0,0 +1,313 @@
+% pdfshipout.w
+%
+% Copyright 2010-2012 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
+scaledpos shipbox_refpos;
+
+@ |ship_out| is used to shipout a box to PDF or DVI mode. If |shipping_mode| is
+set to |SHIPPING_FORM| then the output will be a Form object (only PDF), and if
+it is set to |SHIPPING_PAGE| it will be a Page object.
+
+@c
+void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode)
+{
+    int j, k;              /* indices to first ten count registers */
+    int post_callback_id;
+    int pre_callback_id;
+    posstructure refpoint; /* the origin pos. on the page */
+    int rule_callback_id = 0;
+    scaledpos cur = { 0, 0 };
+    refpoint.pos.h = 0;
+    refpoint.pos.v = 0;
+    ensure_output_state(pdf, ST_HEADER_WRITTEN);
+    fix_o_mode(); /* this is only for complaining if \.{\\outputmode} has changed */
+    init_backend_functionpointers(output_mode_used);
+    pdf->f_cur = null_font;
+    /*
+        Start sheet {\sl Sync\TeX} information record; we assume that |pdf_output| is
+        properly set up.
+    */
+    if (synctex_par) {
+        if (output_mode_used == OMODE_DVI) {
+            synctexsheet(mag_par);
+        } else {
+            synctexsheet(1000);
+        }
+    }
+    pre_callback_id = callback_defined(start_page_number_callback);
+    post_callback_id = callback_defined(stop_page_number_callback);
+    if ((tracing_output_par > 0) && (pre_callback_id == 0)) {
+        tprint_nl("");
+        print_ln();
+        tprint("Completed box being shipped out");
+    }
+    global_shipping_mode = shipping_mode;
+    if (shipping_mode == SHIPPING_PAGE) {
+        if (pre_callback_id > 0)
+            (void) run_callback(pre_callback_id, "->");
+        else if (pre_callback_id == 0) {
+            if (term_offset > max_print_line - 9)
+                print_ln();
+            else if ((term_offset > 0) || (file_offset > 0))
+                print_char(' ');
+            print_char('[');
+            j = 9;
+            while ((count(j) == 0) && (j > 0))
+                j--;
+            for (k = 0; k <= j; k++) {
+                print_int(count(k));
+                if (k < j)
+                    print_char('.');
+            }
+        }
+    }
+    if ((tracing_output_par > 0) && shipping_mode == SHIPPING_PAGE) {
+        print_char(']');
+        update_terminal();
+        begin_diagnostic();
+        show_box(p);
+        end_diagnostic(true);
+    }
+    /* Ship box |p| out */
+    if (shipping_mode == SHIPPING_PAGE && box_dir(p) != page_direction_par)
+        normal_warning("backend","pagedir differs from bodydir, the output may be placed wrongly on the page");
+    /*
+        Update the values of |max_h| and |max_v|; but if the page is too large,
+        |goto done|. Sometimes the user will generate a huge page because other
+        error messages are being ignored. Such pages are not output to the
+        \.{dvi} file, since they may confuse the printing software.
+    */
+    if ((height(p) > max_dimen) || (depth(p) > max_dimen) || (height(p) + depth(p) + v_offset_par > max_dimen) || (width(p) + h_offset_par > max_dimen)) {
+        const char *hlp[] = {
+            "The page just created is more than 18 feet tall or",
+            "more than 18 feet wide, so I suspect something went wrong.",
+            NULL
+        };
+        tex_error("Huge page cannot be shipped out", hlp);
+        if (tracing_output_par <= 0) {
+            begin_diagnostic();
+            tprint_nl("The following box has been deleted:");
+            show_box(p);
+            end_diagnostic(true);
+        }
+        goto DONE;
+    }
+    if (height(p) + depth(p) + v_offset_par > max_v)
+        max_v = height(p) + depth(p) + v_offset_par;
+    if (width(p) + h_offset_par > max_h)
+        max_h = width(p) + h_offset_par;
+    /* Calculate page dimensions and margins */
+    if (global_shipping_mode == SHIPPING_PAGE) {
+        if (page_width_par > 0)
+            pdf->page_size.h = page_width_par;
+        else {
+            switch (page_direction_par) {
+                case dir_TLT:
+                    pdf->page_size.h = width(p) + 2 * page_left_offset_par;
+                    break;
+                case dir_TRT:
+                    pdf->page_size.h = width(p) + 2 * page_right_offset_par;
+                    break;
+                case dir_LTL:
+                    pdf->page_size.h = height(p) + depth(p) + 2 * page_left_offset_par;
+                    break;
+                case dir_RTT:
+                    pdf->page_size.h = height(p) + depth(p) + 2 * page_right_offset_par;
+                    break;
+                default:
+                    pdf->page_size.h = width(p) + 2 * page_left_offset_par;
+                    normal_warning("pdf backend","bad page direction, assuming TLT, case 1");
+            }
+        }
+        if (page_height_par > 0)
+            pdf->page_size.v = page_height_par;
+        else {
+            switch (page_direction_par) {
+                case dir_TLT:
+                case dir_TRT:
+                    pdf->page_size.v = height(p) + depth(p) + 2 * page_top_offset_par;
+                    break;
+                case dir_LTL:
+                case dir_RTT:
+                    pdf->page_size.v = width(p) + 2 * page_top_offset_par;
+                    break;
+                default:
+                    pdf->page_size.v = height(p) + depth(p) + 2 * page_top_offset_par;
+                    normal_warning("pdf backend","bad page direction, assuming TLT, case 2");
+                }
+        }
+        /*
+            Think in upright page/paper coordinates (page origin = lower left edge).
+            First preset |refpoint.pos| to the DVI origin (near upper left page edge).
+        */
+        switch (output_mode_used) {
+            case OMODE_DVI:
+                /* hh: how can we end up here? */
+                refpoint.pos.h = one_true_inch;
+                refpoint.pos.v = pdf->page_size.v - one_true_inch;
+                dvi = refpoint.pos;
+                break;
+            case OMODE_PDF:
+                refpoint.pos.h = pdf_h_origin;
+                refpoint.pos.v = pdf->page_size.v - pdf_v_origin;
+                break;
+            default:
+                normal_error("pdf backend", "unknown output mode");
+        }
+
+        /*
+            Then shift |refpoint.pos| of the DVI origin depending on the
+            |page_direction| within the upright (TLT) page coordinate system
+        */
+        switch (page_direction_par) {
+            case dir_TLT:
+            case dir_LTL:
+                refpoint.pos.h += h_offset_par;
+                refpoint.pos.v -= v_offset_par;
+                break;
+            case dir_TRT:
+            case dir_RTT:
+                refpoint.pos.h += pdf->page_size.h - page_right_offset_par - one_true_inch;
+                refpoint.pos.v -= v_offset_par;
+                break;
+            default:
+                refpoint.pos.h += h_offset_par;
+                refpoint.pos.v -= v_offset_par;
+                normal_warning("pdf backend","bad page direction, assuming TLT, case 3");
+        }
+        /*
+            Then switch to page box coordinate system; do |height(p)| movement,
+            to get the location of the box origin.
+        */
+        pdf->posstruct->dir = page_direction_par;
+        cur.h = 0;
+        cur.v = height(p);
+        synch_pos_with_cur(pdf->posstruct, &refpoint, cur);
+    } else {
+        /* shipping a /Form */
+        pdf->posstruct->dir = box_dir(p);
+        switch (pdf->posstruct->dir) {
+            case dir_TLT:
+            case dir_TRT:
+                pdf->page_size.h = width(p);
+                pdf->page_size.v = height(p) + depth(p);
+                break;
+            case dir_LTL:
+            case dir_RTT:
+                pdf->page_size.h = height(p) + depth(p);
+                pdf->page_size.v = width(p);
+                break;
+            default:
+                pdf->page_size.h = width(p);
+                pdf->page_size.v = height(p) + depth(p);
+                normal_warning("pdf backend","bad page direction, assuming TLT, case 4");
+            }
+        switch (pdf->posstruct->dir) {
+            case dir_TLT:
+                pdf->posstruct->pos.h = 0;
+                pdf->posstruct->pos.v = depth(p);
+                break;
+            case dir_TRT:
+                pdf->posstruct->pos.h = width(p);
+                pdf->posstruct->pos.v = depth(p);
+                break;
+            case dir_LTL:
+                pdf->posstruct->pos.h = height(p);
+                pdf->posstruct->pos.v = width(p);
+                break;
+            case dir_RTT:
+                pdf->posstruct->pos.h = depth(p);
+                pdf->posstruct->pos.v = width(p);
+                break;
+            default:
+                pdf->posstruct->pos.h = 0;
+                pdf->posstruct->pos.v = depth(p);
+                normal_warning("pdf backend","bad page direction, assuming TLT, case 5");
+        }
+    }
+    /* Now we are at the point on the page where the origin of the page box should go. */
+    shipbox_refpos = pdf->posstruct->pos; /* for \.{\\gleaders} */
+    switch (output_mode_used) {
+        case OMODE_DVI:
+            dvi_begin_page(pdf);
+            break;
+        case OMODE_PDF:
+            pdf_begin_page(pdf);
+            break;
+        default:
+            normal_error("pdf backend", "unknown output mode");
+    }
+    rule_callback_id = callback_defined(process_rule_callback);
+    switch (type(p)) {
+        case vlist_node:
+            vlist_out(pdf, p, rule_callback_id);
+            break;
+        case hlist_node:
+            hlist_out(pdf, p, rule_callback_id);
+            break;
+        default:
+            normal_error("pdf backend", "no vlist or hlist in (xform) shipout");
+    }
+    if (shipping_mode == SHIPPING_PAGE)
+        total_pages++;
+    cur_s = -1;
+    /* Finish shipping */
+    switch (output_mode_used) {
+        case OMODE_DVI:
+            dvi_end_page(pdf);
+            break;
+        case OMODE_PDF:
+            pdf_end_page(pdf);
+            break;
+        default:
+            normal_error("pdf backend", "unknown output mode");
+    }
+  DONE:
+    if ((tracing_output_par <= 0) && (post_callback_id == 0) && shipping_mode == SHIPPING_PAGE) {
+        print_char(']');
+        update_terminal();
+    }
+    dead_cycles = 0;
+    /* Flush the box from memory, showing statistics if requested */
+    if ((tracing_stats_par > 1) && (pre_callback_id == 0)) {
+        tprint_nl("Memory usage before: ");
+        print_int(var_used);
+        print_char('&');
+        print_int(dyn_used);
+        print_char(';');
+    }
+    flush_node_list(p);
+    if ((tracing_stats_par > 1) && (post_callback_id == 0)) {
+        tprint(" after: ");
+        print_int(var_used);
+        print_char('&');
+        print_int(dyn_used);
+        print_ln();
+    }
+    if (shipping_mode == SHIPPING_PAGE && (post_callback_id > 0))
+        (void) run_callback(post_callback_id, "->");
+    /* Finish sheet {\sl Sync\TeX} information record */
+    if (synctex_par)
+        synctexteehs();
+    global_shipping_mode = NOT_SHIPPING;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfshipout.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
-
-Copyright 2010-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-scaledpos shipbox_refpos;
-
-/*tex
-
-    |ship_out| is used to shipout a box to PDF or DVI mode. If |shipping_mode| is
-    set to |SHIPPING_FORM| then the output will be a |Form| object (only PDF), and
-    if it is set to |SHIPPING_PAGE| it will be a |Page| object.
-
-*/
-
-void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode)
-{
-    /*tex Indices to first ten count registers: */
-    int j, k;
-    int post_callback_id;
-    int pre_callback_id;
-    /*tex The origin position on the page: */
-    posstructure refpoint;
-    int rule_callback_id = 0;
-    scaledpos cur = { 0, 0 };
-    refpoint.pos.h = 0;
-    refpoint.pos.v = 0;
-    ensure_output_state(pdf, ST_HEADER_WRITTEN);
-    /*tex This is only for complaining if \.{\\outputmode} has changed: */
-    fix_o_mode();
-    pdf->f_cur = null_font;
-    /*tex
-        Start sheet {\sl Sync\TeX} information record. We assume that
-        |pdf_output| is properly set up.
-    */
-    if (synctex_par) {
-        if (output_mode_used == OMODE_DVI) {
-            synctexsheet(mag_par);
-        } else {
-            synctexsheet(1000);
-        }
-    }
-    pre_callback_id = callback_defined(start_page_number_callback);
-    post_callback_id = callback_defined(stop_page_number_callback);
-    if ((tracing_output_par > 0) && (pre_callback_id == 0)) {
-        tprint_nl("");
-        print_ln();
-        tprint("Completed box being shipped out");
-    }
-    global_shipping_mode = shipping_mode;
-    if (shipping_mode == SHIPPING_PAGE) {
-        if (pre_callback_id > 0)
-            (void) run_callback(pre_callback_id, "->");
-        else if (pre_callback_id == 0) {
-            if (term_offset > max_print_line - 9)
-                print_ln();
-            else if ((term_offset > 0) || (file_offset > 0))
-                print_char(' ');
-            print_char('[');
-            j = 9;
-            while ((count(j) == 0) && (j > 0))
-                j--;
-            for (k = 0; k <= j; k++) {
-                print_int(count(k));
-                if (k < j)
-                    print_char('.');
-            }
-        }
-    }
-    if ((tracing_output_par > 0) && shipping_mode == SHIPPING_PAGE) {
-        if (pre_callback_id == 0) {
-            print_char(']');
-        }
-        update_terminal();
-        begin_diagnostic();
-        show_box(p);
-        end_diagnostic(true);
-    }
-    /*tex Ship out box |p|:*/
-    if (shipping_mode == SHIPPING_PAGE && box_dir(p) != page_direction_par)
-        normal_warning("backend","pagedir differs from bodydir, the output may be placed wrongly on the page");
-    /*tex
-        Update the values of |max_h| and |max_v|; but if the page is too large,
-        |goto done|. Sometimes the user will generate a huge page because other
-        error messages are being ignored. Such pages are not output to the
-        \.{dvi} file, since they may confuse the printing software.
-    */
-    if ((height(p) > max_dimen) || (depth(p) > max_dimen) || (height(p) + depth(p) + v_offset_par > max_dimen) || (width(p) + h_offset_par > max_dimen)) {
-        const char *hlp[] = {
-            "The page just created is more than 18 feet tall or",
-            "more than 18 feet wide, so I suspect something went wrong.",
-            NULL
-        };
-        tex_error("Huge page cannot be shipped out", hlp);
-        if (tracing_output_par <= 0) {
-            begin_diagnostic();
-            tprint_nl("The following box has been deleted:");
-            show_box(p);
-            end_diagnostic(true);
-        }
-        goto DONE;
-    }
-    if (height(p) + depth(p) + v_offset_par > max_v)
-        max_v = height(p) + depth(p) + v_offset_par;
-    if (width(p) + h_offset_par > max_h)
-        max_h = width(p) + h_offset_par;
-    /*tex Calculate page dimensions and margins. */
-    if (global_shipping_mode == SHIPPING_PAGE) {
-        if (page_width_par > 0)
-            pdf->page_size.h = page_width_par;
-        else {
-            switch (page_direction_par) {
-                case dir_TLT:
-                    pdf->page_size.h = width(p) + 2 * page_left_offset_par;
-                    break;
-                case dir_TRT:
-                    pdf->page_size.h = width(p) + 2 * page_right_offset_par;
-                    break;
-                case dir_LTL:
-                    pdf->page_size.h = height(p) + depth(p) + 2 * page_left_offset_par;
-                    break;
-                case dir_RTT:
-                    pdf->page_size.h = height(p) + depth(p) + 2 * page_right_offset_par;
-                    break;
-                default:
-                    pdf->page_size.h = width(p) + 2 * page_left_offset_par;
-                    normal_warning("pdf backend","bad page direction, assuming TLT, case 1");
-            }
-        }
-        if (page_height_par > 0)
-            pdf->page_size.v = page_height_par;
-        else {
-            switch (page_direction_par) {
-                case dir_TLT:
-                case dir_TRT:
-                    pdf->page_size.v = height(p) + depth(p) + 2 * page_top_offset_par;
-                    break;
-                case dir_LTL:
-                case dir_RTT:
-                    pdf->page_size.v = width(p) + 2 * page_top_offset_par;
-                    break;
-                default:
-                    pdf->page_size.v = height(p) + depth(p) + 2 * page_top_offset_par;
-                    normal_warning("pdf backend","bad page direction, assuming TLT, case 2");
-                }
-        }
-        /*tex
-            Think in upright page/paper coordinates (page origin = lower left edge).
-            First preset |refpoint.pos| to the DVI origin (near upper left page edge).
-        */
-        backend_out_control[backend_control_set_reference_point](pdf,&refpoint);
-        /*tex
-            Then shift |refpoint.pos| of the DVI origin depending on the
-            |page_direction| within the upright (TLT) page coordinate system.
-        */
-        switch (page_direction_par) {
-            case dir_TLT:
-            case dir_LTL:
-                refpoint.pos.h += h_offset_par;
-                refpoint.pos.v -= v_offset_par;
-                break;
-            case dir_TRT:
-            case dir_RTT:
-                refpoint.pos.h += pdf->page_size.h - page_right_offset_par - one_true_inch;
-                refpoint.pos.v -= v_offset_par;
-                break;
-            default:
-                refpoint.pos.h += h_offset_par;
-                refpoint.pos.v -= v_offset_par;
-                normal_warning("pdf backend","bad page direction, assuming TLT, case 3");
-        }
-        /*tex
-            Then switch to page box coordinate system; do |height(p)| movement,
-            to get the location of the box origin.
-        */
-        pdf->posstruct->dir = page_direction_par;
-        cur.h = 0;
-        cur.v = height(p);
-        synch_pos_with_cur(pdf->posstruct, &refpoint, cur);
-    } else {
-        /*tex We're shipping out a |/Form|. */
-        pdf->posstruct->dir = box_dir(p);
-        switch (pdf->posstruct->dir) {
-            case dir_TLT:
-            case dir_TRT:
-                pdf->page_size.h = width(p);
-                pdf->page_size.v = height(p) + depth(p);
-                break;
-            case dir_LTL:
-            case dir_RTT:
-                pdf->page_size.h = height(p) + depth(p);
-                pdf->page_size.v = width(p);
-                break;
-            default:
-                pdf->page_size.h = width(p);
-                pdf->page_size.v = height(p) + depth(p);
-                normal_warning("pdf backend","bad page direction, assuming TLT, case 4");
-            }
-        switch (pdf->posstruct->dir) {
-            case dir_TLT:
-                pdf->posstruct->pos.h = 0;
-                pdf->posstruct->pos.v = depth(p);
-                break;
-            case dir_TRT:
-                pdf->posstruct->pos.h = width(p);
-                pdf->posstruct->pos.v = depth(p);
-                break;
-            case dir_LTL:
-                pdf->posstruct->pos.h = height(p);
-                pdf->posstruct->pos.v = width(p);
-                break;
-            case dir_RTT:
-                pdf->posstruct->pos.h = depth(p);
-                pdf->posstruct->pos.v = width(p);
-                break;
-            default:
-                pdf->posstruct->pos.h = 0;
-                pdf->posstruct->pos.v = depth(p);
-                normal_warning("pdf backend","bad page direction, assuming TLT, case 5");
-        }
-    }
-    /*tex
-        Now we are at the point on the page where the origin of the page box
-        should go. First we register the poisition for \.{\\gleaders}.
-    */
-    shipbox_refpos = pdf->posstruct->pos;
-    backend_out_control[backend_control_begin_page](pdf);
-    rule_callback_id = callback_defined(process_rule_callback);
-    switch (type(p)) {
-        case vlist_node:
-            vlist_out(pdf, p, rule_callback_id);
-            break;
-        case hlist_node:
-            hlist_out(pdf, p, rule_callback_id);
-            break;
-        default:
-            normal_error("pdf backend", "no vlist or hlist in (xform) shipout");
-    }
-    if (shipping_mode == SHIPPING_PAGE)
-        total_pages++;
-    cur_s = -1;
-    /*tex Finish shipping */
-    backend_out_control[backend_control_end_page](pdf);
-  DONE:
-    if ((tracing_output_par <= 0) && (post_callback_id == 0) && shipping_mode == SHIPPING_PAGE) {
-        print_char(']');
-        update_terminal();
-    }
-    dead_cycles = 0;
-    /*tex Flush the box from memory, showing statistics if requested. */
-    if ((tracing_stats_par > 1) && (pre_callback_id == 0)) {
-        tprint_nl("Memory usage before: ");
-        print_int(var_used);
-        print_char('&');
-        print_int(dyn_used);
-        print_char(';');
-    }
-    flush_node_list(p);
-    if ((tracing_stats_par > 1) && (post_callback_id == 0)) {
-        tprint(" after: ");
-        print_int(var_used);
-        print_char('&');
-        print_int(dyn_used);
-        print_ln();
-    }
-    if (shipping_mode == SHIPPING_PAGE && (post_callback_id > 0))
-        (void) run_callback(post_callback_id, "->");
-    /*tex Finish sheet {\sl Sync\TeX} information record. */
-    if (synctex_par)
-        synctexteehs();
-    global_shipping_mode = NOT_SHIPPING;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdftables.w
@@ -0,0 +1,278 @@
+% pdftables.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"
+
+@ @c
+const char *pdf_obj_typenames[PDF_OBJ_TYPE_MAX + 1] = {
+    "font", "outline", "dest", "obj", "xform", "ximage", "thread",
+    "pagestream", "page", "pages", "catalog", "info", "link", "annot", "annots",
+    "bead", "beads", "objstm", "others"
+};
+
+int pdf_last_annot;
+int pdf_last_link;
+int pdf_last_obj;
+int pdf_retval;                 /* global multi-purpose return value */
+int pdf_cur_form;               /* the form being output */
+
+@ AVL sort entry into |avl_table[]|
+@c
+static int compare_info(const void *pa, const void *pb, void *param)
+{
+    const oentry *a = (const oentry *) pa;
+    const oentry *b = (const oentry *) pb;
+    (void) param;
+    if (a->u_type == b->u_type) {
+        if (a->u_type == union_type_int)
+            return ((a->u.int0 < b->u.int0 ? -1 : (a->u.int0 > b->u.int0 ? 1 : 0)));
+        else /* string type */
+            return strcmp(a->u.str0, b->u.str0);
+    } else if (a->u_type == union_type_int) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+static void avl_put_obj(PDF pdf, int t, oentry * oe)
+{
+    void **pp;
+    if (pdf->obj_tree[t] == NULL) {
+        pdf->obj_tree[t] = avl_create(compare_info, NULL, &avl_xallocator);
+        if (pdf->obj_tree[t] == NULL)
+            formatted_error("pdf backend","avl_create() pdf->obj_tree failed");
+    }
+    pp = avl_probe(pdf->obj_tree[t], oe);
+    if (pp == NULL)
+        formatted_error("pdf backend","avl_probe() out of memory in insertion");
+}
+
+static void avl_put_int_obj(PDF pdf, int int0, int objptr, int t)
+{
+    oentry *oe = xtalloc(1, oentry);
+    oe->u.int0 = int0;
+    oe->u_type = union_type_int;
+    oe->objptr = objptr;
+    avl_put_obj(pdf, t, oe);
+}
+
+static void avl_put_str_obj(PDF pdf, char *str0, int objptr, int t)
+{
+    oentry *oe = xtalloc(1, oentry);
+    oe->u.str0 = str0;          /* no xstrdup() here */
+    oe->u_type = union_type_cstring;
+    oe->objptr = objptr;
+    avl_put_obj(pdf, t, oe);
+}
+
+static int avl_find_int_obj(PDF pdf, int t, int i)
+{
+    oentry *p;
+    oentry tmp;
+    tmp.u.int0 = i;
+    tmp.u_type = union_type_int;
+    if (pdf->obj_tree[t] == NULL)
+        return 0;
+    p = (oentry *) avl_find(pdf->obj_tree[t], &tmp);
+    if (p == NULL)
+        return 0;
+    return p->objptr;
+}
+
+static int avl_find_str_obj(PDF pdf, int t, char *s)
+{
+    oentry *p;
+    oentry tmp;
+    tmp.u.str0 = s;
+    tmp.u_type = union_type_cstring;
+    if (pdf->obj_tree[t] == NULL)
+        return 0;
+    p = (oentry *) avl_find(pdf->obj_tree[t], &tmp);
+    if (p == NULL)
+        return 0;
+    return p->objptr;
+}
+
+@ Create an object with type |t| and identifier |i|
+
+@c
+int pdf_create_obj(PDF pdf, int t, int i)
+{
+    int a;
+    char *ss = NULL;
+    if (pdf->obj_ptr == sup_obj_tab_size)
+        overflow("indirect objects table size", (unsigned) pdf->obj_tab_size);
+    if (pdf->obj_ptr == pdf->obj_tab_size) {
+        a = pdf->obj_tab_size / 5;
+        if (pdf->obj_tab_size < sup_obj_tab_size - a)
+            pdf->obj_tab_size = pdf->obj_tab_size + a;
+        else
+            pdf->obj_tab_size = sup_obj_tab_size;
+        pdf->obj_tab = xreallocarray(pdf->obj_tab, obj_entry, (unsigned) pdf->obj_tab_size);
+    }
+    pdf->obj_ptr++;
+    obj_info(pdf, pdf->obj_ptr) = i;
+    obj_type(pdf, pdf->obj_ptr) = t;
+    set_obj_fresh(pdf, pdf->obj_ptr);
+    obj_aux(pdf, pdf->obj_ptr) = 0;
+    if (i < 0) {
+        ss = makecstring(-i);
+        avl_put_str_obj(pdf, ss, pdf->obj_ptr, t);
+    } else if (i > 0)
+        avl_put_int_obj(pdf, i, pdf->obj_ptr, t);
+    if (t <= HEAD_TAB_MAX) {
+        obj_link(pdf, pdf->obj_ptr) = pdf->head_tab[t];
+        pdf->head_tab[t] = pdf->obj_ptr;
+        if ((t == obj_type_dest) && (i < 0))
+            append_dest_name(pdf, makecstring(-obj_info(pdf, pdf->obj_ptr)), pdf->obj_ptr); /* why not just -i */
+    }
+    return pdf->obj_ptr;
+}
+
+@ @c
+int find_obj(PDF pdf, int t, int i, boolean byname)
+{
+    char *ss = NULL;
+    int ret;
+    if (byname) {
+        ss = makecstring(i);
+        ret = avl_find_str_obj(pdf, t, ss);
+        free(ss);
+    } else {
+        ret = avl_find_int_obj(pdf, t, i);
+    }
+    return ret;
+}
+
+@ The following function finds an object with identifier |i| and type |t|.
+Identifier |i| is either an integer or a token list index. If no such object
+exists then it will be created. This function is used mainly to find destination
+for link annotations and outlines; however it is also used in |ship_out| (to
+check whether a Page object already exists) so we need to declare it together
+with subroutines needed in |hlist_out| and |vlist_out|.
+
+@c
+int pdf_get_obj(PDF pdf, int t, int i, boolean byname)
+{
+    int r;
+    str_number s;
+    if (byname > 0) {
+        s = tokens_to_string(i);
+        r = find_obj(pdf, t, s, true);
+    } else {
+        s = 0;
+        r = find_obj(pdf, t, i, false);
+    }
+    if (r == 0) {
+        if (byname > 0) {
+            r = pdf_create_obj(pdf, t, -s);
+            s = 0;
+        } else {
+            r = pdf_create_obj(pdf, t, i);
+        }
+        if (t == obj_type_dest)
+            set_obj_dest_ptr(pdf, r, null);
+    }
+    if (s != 0)
+        flush_str(s);
+    return r;
+}
+
+@ object checking
+@c
+void check_obj_exists(PDF pdf, int objnum)
+{
+    if (objnum < 0 || objnum > pdf->obj_ptr)
+        normal_error("pdf backend", "cannot find referenced object");
+}
+
+void check_obj_type(PDF pdf, int t, int objnum)
+{
+    int u;
+    check_obj_exists(pdf, objnum);
+    u = obj_type(pdf, objnum);
+    if (t != u) {
+        formatted_error("pdf backend", "referenced object has wrong type %s; should be %s",
+            pdf_obj_typenames[u], pdf_obj_typenames[t]);
+    }
+}
+
+@ @c
+void set_rect_dimens(PDF pdf, halfword p, halfword parent_box, scaledpos cur, scaled_whd alt_rule, scaled margin)
+{
+    scaledpos ll, ur; /* positions relative to cur */
+    scaledpos pos_ll, pos_ur, tmp;
+    posstructure localpos;
+    localpos.dir = pdf->posstruct->dir;
+    ll.h = 0; /* pdf contains current point on page */
+    if (is_running(alt_rule.dp))
+        ll.v = depth(parent_box) - cur.v;
+    else
+        ll.v = alt_rule.dp;
+    if (is_running(alt_rule.wd))
+        ur.h = width(parent_box) - cur.h;
+    else
+        ur.h = alt_rule.wd;
+    if (is_running(alt_rule.ht))
+        ur.v = -height(parent_box) - cur.v;
+    else
+        ur.v = -alt_rule.ht;
+    synch_pos_with_cur(&localpos, pdf->posstruct, ll);
+    pos_ll = localpos.pos;
+    synch_pos_with_cur(&localpos, pdf->posstruct, ur);
+    pos_ur = localpos.pos;
+    if (pos_ll.h > pos_ur.h) {
+        tmp.h = pos_ll.h;
+        pos_ll.h = pos_ur.h;
+        pos_ur.h = tmp.h;
+    }
+    if (pos_ll.v > pos_ur.v) {
+        tmp.v = pos_ll.v;
+        pos_ll.v = pos_ur.v;
+        pos_ur.v = tmp.v;
+    }
+    if (global_shipping_mode == SHIPPING_PAGE && matrixused()) {
+        matrixtransformrect(pos_ll.h, pos_ll.v, pos_ur.h, pos_ur.v);
+        pos_ll.h = getllx();
+        pos_ll.v = getlly();
+        pos_ur.h = geturx();
+        pos_ur.v = getury();
+    }
+    pdf_ann_left(p) = pos_ll.h - margin;
+    pdf_ann_bottom(p) = pos_ll.v - margin;
+    pdf_ann_right(p) = pos_ur.h + margin;
+    pdf_ann_top(p) = pos_ur.v + margin;
+}
+
+@ @c
+void libpdffinish(PDF pdf)
+{
+    strbuf_free(pdf->fb);
+    xfree(pdf->job_id_string);
+    fm_free();
+    t1_free();
+    enc_free();
+    epdf_free();
+    ttf_free();
+    glyph_unicode_free();
+    zip_free(pdf);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdftables.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-const char *pdf_obj_typenames[PDF_OBJ_TYPE_MAX + 1] = {
-    "font", "outline", "dest", "obj", "xform", "ximage", "thread",
-    "pagestream", "page", "pages", "catalog", "info", "link", "annot", "annots",
-    "bead", "beads", "objstm", "others"
-};
-
-int pdf_last_annot;
-int pdf_last_link;
-int pdf_last_obj;
-int pdf_retval;
-int pdf_cur_form;
-
-/*tex
-
-    AVL sort entry into |avl_table[]|.
-*/
-
-static int compare_info(const void *pa, const void *pb, void *param)
-{
-    const oentry *a = (const oentry *) pa;
-    const oentry *b = (const oentry *) pb;
-    (void) param;
-    if (a->u_type == b->u_type) {
-        if (a->u_type == union_type_int) {
-            return ((a->u.int0 < b->u.int0 ? -1 : (a->u.int0 > b->u.int0 ? 1 : 0)));
-        } else {
-            /*tex String type: */
-            return strcmp(a->u.str0, b->u.str0);
-        }
-    } else if (a->u_type == union_type_int) {
-        return -1;
-    } else {
-        return 1;
-    }
-}
-
-static void avl_put_obj(PDF pdf, int t, oentry * oe)
-{
-    void **pp;
-    if (pdf->obj_tree[t] == NULL) {
-        pdf->obj_tree[t] = avl_create(compare_info, NULL, &avl_xallocator);
-        if (pdf->obj_tree[t] == NULL)
-            formatted_error("pdf backend","avl_create() pdf->obj_tree failed");
-    }
-    pp = avl_probe(pdf->obj_tree[t], oe);
-    if (pp == NULL)
-        formatted_error("pdf backend","avl_probe() out of memory in insertion");
-}
-
-static void avl_put_int_obj(PDF pdf, int int0, int objptr, int t)
-{
-    oentry *oe = xtalloc(1, oentry);
-    oe->u.int0 = int0;
-    oe->u_type = union_type_int;
-    oe->objptr = objptr;
-    avl_put_obj(pdf, t, oe);
-}
-
-static void avl_put_str_obj(PDF pdf, char *str0, int objptr, int t)
-{
-    oentry *oe = xtalloc(1, oentry);
-    /*tex No |xstrdup| here! */
-    oe->u.str0 = str0;
-    oe->u_type = union_type_cstring;
-    oe->objptr = objptr;
-    avl_put_obj(pdf, t, oe);
-}
-
-static int avl_find_int_obj(PDF pdf, int t, int i)
-{
-    oentry *p;
-    oentry tmp;
-    tmp.u.int0 = i;
-    tmp.u_type = union_type_int;
-    if (pdf->obj_tree[t] == NULL)
-        return 0;
-    p = (oentry *) avl_find(pdf->obj_tree[t], &tmp);
-    if (p == NULL)
-        return 0;
-    return p->objptr;
-}
-
-static int avl_find_str_obj(PDF pdf, int t, char *s)
-{
-    oentry *p;
-    oentry tmp;
-    tmp.u.str0 = s;
-    tmp.u_type = union_type_cstring;
-    if (pdf->obj_tree[t] == NULL)
-        return 0;
-    p = (oentry *) avl_find(pdf->obj_tree[t], &tmp);
-    if (p == NULL)
-        return 0;
-    return p->objptr;
-}
-
-/*tex Create an object with type |t| and identifier |i|: */
-
-int pdf_create_obj(PDF pdf, int t, int i)
-{
-    int a;
-    char *ss = NULL;
-    if (pdf->obj_ptr == sup_obj_tab_size)
-        overflow("indirect objects table size", (unsigned) pdf->obj_tab_size);
-    if (pdf->obj_ptr == pdf->obj_tab_size) {
-        a = pdf->obj_tab_size / 5;
-        if (pdf->obj_tab_size < sup_obj_tab_size - a)
-            pdf->obj_tab_size = pdf->obj_tab_size + a;
-        else
-            pdf->obj_tab_size = sup_obj_tab_size;
-        pdf->obj_tab = xreallocarray(pdf->obj_tab, obj_entry, (unsigned) pdf->obj_tab_size);
-    }
-    pdf->obj_ptr++;
-    obj_info(pdf, pdf->obj_ptr) = i;
-    obj_type(pdf, pdf->obj_ptr) = t;
-    set_obj_fresh(pdf, pdf->obj_ptr);
-    obj_aux(pdf, pdf->obj_ptr) = 0;
-    if (i < 0) {
-        ss = makecstring(-i);
-        avl_put_str_obj(pdf, ss, pdf->obj_ptr, t);
-    } else if (i > 0)
-        avl_put_int_obj(pdf, i, pdf->obj_ptr, t);
-    if (t <= HEAD_TAB_MAX) {
-        obj_link(pdf, pdf->obj_ptr) = pdf->head_tab[t];
-        pdf->head_tab[t] = pdf->obj_ptr;
-        if ((t == obj_type_dest) && (i < 0))
-            append_dest_name(pdf, makecstring(-obj_info(pdf, pdf->obj_ptr)), pdf->obj_ptr); /* why not just -i */
-    }
-    return pdf->obj_ptr;
-}
-
-int find_obj(PDF pdf, int t, int i, boolean byname)
-{
-    char *ss = NULL;
-    int ret;
-    if (byname) {
-        ss = makecstring(i);
-        ret = avl_find_str_obj(pdf, t, ss);
-        free(ss);
-    } else {
-        ret = avl_find_int_obj(pdf, t, i);
-    }
-    return ret;
-}
-
-/*tex
-
-    The following function finds an object with identifier |i| and type |t|.
-    Identifier |i| is either an integer or a token list index. If no such object
-    exists then it will be created. This function is used mainly to find
-    destination for link annotations and outlines; however it is also used in
-    |ship_out| (to check whether a Page object already exists) so we need to
-    declare it together with subroutines needed in |hlist_out| and |vlist_out|.
-
-*/
-
-int pdf_get_obj(PDF pdf, int t, int i, boolean byname)
-{
-    int r;
-    str_number s;
-    if (byname > 0) {
-        s = tokens_to_string(i);
-        r = find_obj(pdf, t, s, true);
-    } else {
-        s = 0;
-        r = find_obj(pdf, t, i, false);
-    }
-    if (r == 0) {
-        if (byname > 0) {
-            r = pdf_create_obj(pdf, t, -s);
-            s = 0;
-        } else {
-            r = pdf_create_obj(pdf, t, i);
-        }
-        if (t == obj_type_dest)
-            set_obj_dest_ptr(pdf, r, null);
-    }
-    if (s != 0)
-        flush_str(s);
-    return r;
-}
-
-/*tex Some object checking: */
-
-void check_obj_exists(PDF pdf, int objnum)
-{
-    if (objnum < 0 || objnum > pdf->obj_ptr)
-        normal_error("pdf backend", "cannot find referenced object");
-}
-
-void check_obj_type(PDF pdf, int t, int objnum)
-{
-    int u;
-    check_obj_exists(pdf, objnum);
-    u = obj_type(pdf, objnum);
-    if (t != u) {
-        formatted_error("pdf backend", "referenced object has wrong type %s; should be %s",
-            pdf_obj_typenames[u], pdf_obj_typenames[t]);
-    }
-}
-
-void set_rect_dimens(PDF pdf, halfword p, halfword parent_box, scaledpos cur, scaled_whd alt_rule, scaled margin)
-{
-    /*tex The positions relative to cur: */
-    scaledpos ll, ur;
-    scaledpos pos_ll, pos_ur, tmp;
-    posstructure localpos;
-    localpos.dir = pdf->posstruct->dir;
-    /*tex |pdf| contains current point on page: */
-    ll.h = 0;
-    if (is_running(alt_rule.dp))
-        ll.v = depth(parent_box) - cur.v;
-    else
-        ll.v = alt_rule.dp;
-    if (is_running(alt_rule.wd))
-        ur.h = width(parent_box) - cur.h;
-    else
-        ur.h = alt_rule.wd;
-    if (is_running(alt_rule.ht))
-        ur.v = -height(parent_box) - cur.v;
-    else
-        ur.v = -alt_rule.ht;
-    synch_pos_with_cur(&localpos, pdf->posstruct, ll);
-    pos_ll = localpos.pos;
-    synch_pos_with_cur(&localpos, pdf->posstruct, ur);
-    pos_ur = localpos.pos;
-    if (pos_ll.h > pos_ur.h) {
-        tmp.h = pos_ll.h;
-        pos_ll.h = pos_ur.h;
-        pos_ur.h = tmp.h;
-    }
-    if (pos_ll.v > pos_ur.v) {
-        tmp.v = pos_ll.v;
-        pos_ll.v = pos_ur.v;
-        pos_ur.v = tmp.v;
-    }
-    if (global_shipping_mode == SHIPPING_PAGE && matrixused()) {
-        matrixtransformrect(pos_ll.h, pos_ll.v, pos_ur.h, pos_ur.v);
-        pos_ll.h = getllx();
-        pos_ll.v = getlly();
-        pos_ur.h = geturx();
-        pos_ur.v = getury();
-    }
-    pdf_ann_left(p) = pos_ll.h - margin;
-    pdf_ann_bottom(p) = pos_ll.v - margin;
-    pdf_ann_right(p) = pos_ur.h + margin;
-    pdf_ann_top(p) = pos_ur.v + margin;
-}
-
-void libpdffinish(PDF pdf)
-{
-    strbuf_free(pdf->fb);
-    xfree(pdf->job_id_string);
-    fm_free();
-    t1_free();
-    enc_free();
-    epdf_free();
-    ttf_free();
-    glyph_unicode_free();
-    zip_free(pdf);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfthread.w
@@ -0,0 +1,275 @@
+% pdfthread.w
+%
+% Copyright 2009-2012 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"
+
+@ Threads are handled in similar way as link annotations
+@c
+void append_bead(PDF pdf, halfword p)
+{
+    int a, b, c, t;
+    if (global_shipping_mode == SHIPPING_FORM)
+        normal_error("pdf backend", "threads cannot be inside an xform");
+    t = pdf_get_obj(pdf, obj_type_thread, pdf_thread_id(p), pdf_thread_named_id(p));
+    b = pdf_create_obj(pdf, obj_type_others, 0);
+    obj_bead_ptr(pdf, b) = pdf_get_mem(pdf, pdfmem_bead_size);
+    set_obj_bead_page(pdf, b, pdf->last_page);
+    set_obj_bead_data(pdf, b, p);
+    if (pdf_thread_attr(p) != null)
+        set_obj_bead_attr(pdf, b, tokens_to_string(pdf_thread_attr(p)));
+    else
+        set_obj_bead_attr(pdf, b, 0);
+    if (obj_thread_first(pdf, t) == 0) {
+        obj_thread_first(pdf, t) = b;
+        set_obj_bead_next(pdf, b, b);
+        set_obj_bead_prev(pdf, b, b);
+    } else {
+        a = obj_thread_first(pdf, t);
+        c = obj_bead_prev(pdf, a);
+        set_obj_bead_prev(pdf, b, c);
+        set_obj_bead_next(pdf, b, a);
+        set_obj_bead_prev(pdf, a, b);
+        set_obj_bead_next(pdf, c, b);
+    }
+    addto_page_resources(pdf, obj_type_bead, b);
+}
+
+@ @c
+void do_thread(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
+{
+    scaled_whd alt_rule;
+    if ((type(p) == hlist_node) && (subtype(p) == pdf_start_thread_node))
+        normal_error("pdf backend", "'startthread' ended up in hlist");
+    if (doing_leaders)
+        return;
+    if (subtype(p) == pdf_start_thread_node) {
+        pdf->thread.wd = width(p);
+        pdf->thread.ht = height(p);
+        pdf->thread.dp = depth(p);
+        pdf->last_thread_id = pdf_thread_id(p);
+        pdf->last_thread_named_id = (pdf_thread_named_id(p) > 0);
+        if (pdf->last_thread_named_id)
+            add_token_ref(pdf_thread_id(p));
+        pdf->thread_level = cur_s;
+    }
+    alt_rule.wd = width(p);
+    alt_rule.ht = height(p);
+    alt_rule.dp = depth(p);
+    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_thread_margin);
+    append_bead(pdf, p);
+    pdf->last_thread = p;
+}
+
+@ @c
+void append_thread(PDF pdf, halfword parent_box, scaledpos cur)
+{
+    scaled_whd alt_rule;
+    halfword p = new_node(whatsit_node, pdf_thread_data_node);
+    width(p) = pdf->thread.wd;
+    height(p) = pdf->thread.ht;
+    depth(p) = pdf->thread.dp;
+    pdf_thread_attr(p) = null;
+    pdf_thread_id(p) = pdf->last_thread_id;
+    if (pdf->last_thread_named_id) {
+        add_token_ref(pdf_thread_id(p));
+        pdf_thread_named_id(p) = 1;
+    } else {
+        pdf_thread_named_id(p) = 0;
+    }
+    alt_rule.wd = width(p);
+    alt_rule.ht = height(p);
+    alt_rule.dp = depth(p);
+    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_thread_margin);
+    append_bead(pdf, p);
+    pdf->last_thread = p;
+}
+
+@ @c
+void end_thread(PDF pdf, halfword p)
+{
+    scaledpos pos = pdf->posstruct->pos;
+    if (type(p) == hlist_node)
+        normal_error("pdf backend", "'endthread' ended up in hlist");
+    if (pdf->thread_level != cur_s)
+        normal_error("pdf backend", "'endthread' ended up in different nesting level than 'startthread'");
+    if (is_running(pdf->thread.dp) && (pdf->last_thread != null)) {
+        switch (pdf->posstruct->dir) {
+            case dir_TLT:
+            case dir_TRT:
+                pdf_ann_bottom(pdf->last_thread) = pos.v - pdf_thread_margin;
+                break;
+            case dir_LTL:
+                pdf_ann_right(pdf->last_thread) = pos.h + pdf_thread_margin;
+                break;
+            case dir_RTT:
+                pdf_ann_left(pdf->last_thread) = pos.h - pdf_thread_margin;
+                break;
+            default:
+                formatted_warning("pdf backend","forcing bad dir %i to TLT in end tread",pdf->posstruct->dir);
+        }
+    }
+    if (pdf->last_thread_named_id)
+        delete_token_ref(pdf->last_thread_id);
+    pdf->last_thread = null;
+}
+
+@ The following function are needed for outputing article thread.
+@c
+void thread_title(PDF pdf, int t)
+{
+    pdf_add_name(pdf, "Title");
+    pdf_out(pdf, '(');
+    if (obj_info(pdf, t) < 0)
+        pdf_print(pdf, -obj_info(pdf, t));
+    else
+        pdf_print_int(pdf, obj_info(pdf, t));
+    pdf_out(pdf, ')');
+}
+
+void pdf_fix_thread(PDF pdf, int t)
+{
+    halfword a;
+    if (obj_info(pdf, t) < 0) {
+        char *ss = makecstring(-obj_info(pdf, t));
+        formatted_warning("pdf backend", "unknown thread destination name '%s'",ss);
+    } else {
+        formatted_warning("pdf backend", "unknown thread destination num '%d'",obj_info(pdf, t));
+    }
+    a = pdf_create_obj(pdf, obj_type_others, 0);
+    pdf_begin_obj(pdf, a, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_dict_add_ref(pdf, "T", t);
+    pdf_dict_add_ref(pdf, "V", a);
+    pdf_dict_add_ref(pdf, "N", a);
+    pdf_dict_add_ref(pdf, "P", pdf->last_page);
+    pdf_add_name(pdf, "R");
+    pdf_begin_array(pdf);
+    pdf_add_int(pdf, 0);
+    pdf_add_int(pdf, 0);
+    pdf_add_bp(pdf, page_width_par);
+    pdf_add_bp(pdf, page_height_par);
+    pdf_end_array(pdf);
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+    pdf_begin_obj(pdf, t, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    pdf_add_name(pdf, "I");
+    pdf_begin_dict(pdf);
+    thread_title(pdf, t);
+    pdf_end_dict(pdf);
+    pdf_dict_add_ref(pdf, "F", a);
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+}
+
+void out_thread(PDF pdf, int t)
+{
+    halfword a, b;
+    int last_attr;
+    if (obj_thread_first(pdf, t) == 0) {
+        pdf_fix_thread(pdf, t);
+        return;
+    }
+    pdf_begin_obj(pdf, t, OBJSTM_ALWAYS);
+    pdf_begin_dict(pdf);
+    a = obj_thread_first(pdf, t);
+    b = a;
+    last_attr = 0;
+    do {
+        if (obj_bead_attr(pdf, a) != 0)
+            last_attr = obj_bead_attr(pdf, a);
+        a = obj_bead_next(pdf, a);
+    } while (a != b);
+    if (last_attr != 0) {
+        pdf_print_ln(pdf, last_attr);
+    } else {
+        pdf_add_name(pdf, "I");
+        pdf_begin_dict(pdf);
+        thread_title(pdf, t);
+        pdf_end_dict(pdf);
+    }
+    pdf_dict_add_ref(pdf, "F", a);
+    pdf_end_dict(pdf);
+    pdf_end_obj(pdf);
+    do {
+        pdf_begin_obj(pdf, a, OBJSTM_ALWAYS);
+        pdf_begin_dict(pdf);
+        if (a == b)
+            pdf_dict_add_ref(pdf, "T", t);
+        pdf_dict_add_ref(pdf, "V", obj_bead_prev(pdf, a));
+        pdf_dict_add_ref(pdf, "N", obj_bead_next(pdf, a));
+        pdf_dict_add_ref(pdf, "P", obj_bead_page(pdf, a));
+        pdf_dict_add_ref(pdf, "R", obj_bead_rect(pdf, a));
+        pdf_end_dict(pdf);
+        pdf_end_obj(pdf);
+        a = obj_bead_next(pdf, a);
+    } while (a != b);
+}
+
+@ @c
+void scan_thread_id(void)
+{
+    if (scan_keyword("num")) {
+        scan_int();
+        if (cur_val <= 0)
+            normal_error("pdf backend", "num identifier must be positive");
+        if (cur_val > max_halfword)
+            normal_error("pdf backend", "number too big");
+        set_pdf_thread_id(cur_list.tail_field, cur_val);
+        set_pdf_thread_named_id(cur_list.tail_field, 0);
+    } else if (scan_keyword("name")) {
+        scan_toks(false, true);
+        set_pdf_thread_id(cur_list.tail_field, def_ref);
+        set_pdf_thread_named_id(cur_list.tail_field, 1);
+    } else {
+        normal_error("pdf backend", "identifier type missing");
+    }
+}
+
+void check_running_thread(PDF pdf, halfword this_box, scaledpos cur)
+{
+    if ((pdf->last_thread != null) && is_running(pdf->thread.dp)
+        && (pdf->thread_level == cur_s))
+        append_thread(pdf, this_box, cur);
+}
+
+@ @c
+void print_bead_rectangles(PDF pdf)
+{
+    halfword i;
+    pdf_object_list *k;
+    int l;
+    if ((k = get_page_resources_list(pdf, obj_type_bead)) != NULL) {
+        while (k != NULL) {
+            l = pdf_create_obj(pdf, obj_type_others, 0);
+            pdf_begin_obj(pdf, l, OBJSTM_ALWAYS);
+            pdf_begin_array(pdf);
+            i = obj_bead_data(pdf, k->info); /* pointer to a whatsit or whatsit-like node */
+            pdf_add_rect_spec(pdf, i);
+            if (subtype(i) == pdf_thread_data_node)
+                flush_node(i);
+            pdf_end_array(pdf);
+            pdf_end_obj(pdf);
+            set_obj_bead_rect(pdf, k->info, l); /* rewrite |obj_bead_data| */
+            k = k->link;
+        }
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfthread.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
-
-Copyright 2009-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    Threads are handled in similar way as link annotations.
-
-*/
-
-void append_bead(PDF pdf, halfword p)
-{
-    int a, b, c, t;
-    if (global_shipping_mode == SHIPPING_FORM)
-        normal_error("pdf backend", "threads cannot be inside an xform");
-    t = pdf_get_obj(pdf, obj_type_thread, pdf_thread_id(p), pdf_thread_named_id(p));
-    b = pdf_create_obj(pdf, obj_type_others, 0);
-    obj_bead_ptr(pdf, b) = pdf_get_mem(pdf, pdfmem_bead_size);
-    set_obj_bead_page(pdf, b, pdf->last_page);
-    set_obj_bead_data(pdf, b, p);
-    if (pdf_thread_attr(p) != null)
-        set_obj_bead_attr(pdf, b, tokens_to_string(pdf_thread_attr(p)));
-    else
-        set_obj_bead_attr(pdf, b, 0);
-    if (obj_thread_first(pdf, t) == 0) {
-        obj_thread_first(pdf, t) = b;
-        set_obj_bead_next(pdf, b, b);
-        set_obj_bead_prev(pdf, b, b);
-    } else {
-        a = obj_thread_first(pdf, t);
-        c = obj_bead_prev(pdf, a);
-        set_obj_bead_prev(pdf, b, c);
-        set_obj_bead_next(pdf, b, a);
-        set_obj_bead_prev(pdf, a, b);
-        set_obj_bead_next(pdf, c, b);
-    }
-    addto_page_resources(pdf, obj_type_bead, b);
-}
-
-void do_thread(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
-{
-    scaled_whd alt_rule;
-    if ((type(p) == hlist_node) && (subtype(p) == pdf_start_thread_node))
-        normal_error("pdf backend", "'startthread' ended up in hlist");
-    if (doing_leaders)
-        return;
-    if (subtype(p) == pdf_start_thread_node) {
-        pdf->thread.wd = width(p);
-        pdf->thread.ht = height(p);
-        pdf->thread.dp = depth(p);
-        pdf->last_thread_id = pdf_thread_id(p);
-        pdf->last_thread_named_id = (pdf_thread_named_id(p) > 0);
-        if (pdf->last_thread_named_id)
-            add_token_ref(pdf_thread_id(p));
-        pdf->thread_level = cur_s;
-    }
-    alt_rule.wd = width(p);
-    alt_rule.ht = height(p);
-    alt_rule.dp = depth(p);
-    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_thread_margin);
-    append_bead(pdf, p);
-    pdf->last_thread = p;
-}
-
-void append_thread(PDF pdf, halfword parent_box, scaledpos cur)
-{
-    scaled_whd alt_rule;
-    halfword p = new_node(whatsit_node, pdf_thread_data_node);
-    width(p) = pdf->thread.wd;
-    height(p) = pdf->thread.ht;
-    depth(p) = pdf->thread.dp;
-    pdf_thread_attr(p) = null;
-    pdf_thread_id(p) = pdf->last_thread_id;
-    if (pdf->last_thread_named_id) {
-        add_token_ref(pdf_thread_id(p));
-        pdf_thread_named_id(p) = 1;
-    } else {
-        pdf_thread_named_id(p) = 0;
-    }
-    alt_rule.wd = width(p);
-    alt_rule.ht = height(p);
-    alt_rule.dp = depth(p);
-    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_thread_margin);
-    append_bead(pdf, p);
-    pdf->last_thread = p;
-}
-
-void end_thread(PDF pdf, halfword p)
-{
-    scaledpos pos = pdf->posstruct->pos;
-    if (type(p) == hlist_node)
-        normal_error("pdf backend", "'endthread' ended up in hlist");
-    if (pdf->thread_level != cur_s)
-        normal_error("pdf backend", "'endthread' ended up in different nesting level than 'startthread'");
-    if (is_running(pdf->thread.dp) && (pdf->last_thread != null)) {
-        switch (pdf->posstruct->dir) {
-            case dir_TLT:
-            case dir_TRT:
-                pdf_ann_bottom(pdf->last_thread) = pos.v - pdf_thread_margin;
-                break;
-            case dir_LTL:
-                pdf_ann_right(pdf->last_thread) = pos.h + pdf_thread_margin;
-                break;
-            case dir_RTT:
-                pdf_ann_left(pdf->last_thread) = pos.h - pdf_thread_margin;
-                break;
-            default:
-                formatted_warning("pdf backend","forcing bad dir %i to TLT in end tread",pdf->posstruct->dir);
-        }
-    }
-    if (pdf->last_thread_named_id)
-        delete_token_ref(pdf->last_thread_id);
-    pdf->last_thread = null;
-}
-
-/*tex The following function are needed for outputing article thread. */
-
-void thread_title(PDF pdf, int t)
-{
-    pdf_add_name(pdf, "Title");
-    pdf_out(pdf, '(');
-    if (obj_info(pdf, t) < 0)
-        pdf_print(pdf, -obj_info(pdf, t));
-    else
-        pdf_print_int(pdf, obj_info(pdf, t));
-    pdf_out(pdf, ')');
-}
-
-void pdf_fix_thread(PDF pdf, int t)
-{
-    halfword a;
-    if (obj_info(pdf, t) < 0) {
-        char *ss = makecstring(-obj_info(pdf, t));
-        formatted_warning("pdf backend", "unknown thread destination name '%s'",ss);
-    } else {
-        formatted_warning("pdf backend", "unknown thread destination num '%d'",obj_info(pdf, t));
-    }
-    a = pdf_create_obj(pdf, obj_type_others, 0);
-    pdf_begin_obj(pdf, a, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_dict_add_ref(pdf, "T", t);
-    pdf_dict_add_ref(pdf, "V", a);
-    pdf_dict_add_ref(pdf, "N", a);
-    pdf_dict_add_ref(pdf, "P", pdf->last_page);
-    pdf_add_name(pdf, "R");
-    pdf_begin_array(pdf);
-    pdf_add_int(pdf, 0);
-    pdf_add_int(pdf, 0);
-    pdf_add_bp(pdf, page_width_par);
-    pdf_add_bp(pdf, page_height_par);
-    pdf_end_array(pdf);
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-    pdf_begin_obj(pdf, t, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    pdf_add_name(pdf, "I");
-    pdf_begin_dict(pdf);
-    thread_title(pdf, t);
-    pdf_end_dict(pdf);
-    pdf_dict_add_ref(pdf, "F", a);
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-}
-
-void out_thread(PDF pdf, int t)
-{
-    halfword a, b;
-    int last_attr;
-    if (obj_thread_first(pdf, t) == 0) {
-        pdf_fix_thread(pdf, t);
-        return;
-    }
-    pdf_begin_obj(pdf, t, OBJSTM_ALWAYS);
-    pdf_begin_dict(pdf);
-    a = obj_thread_first(pdf, t);
-    b = a;
-    last_attr = 0;
-    do {
-        if (obj_bead_attr(pdf, a) != 0)
-            last_attr = obj_bead_attr(pdf, a);
-        a = obj_bead_next(pdf, a);
-    } while (a != b);
-    if (last_attr != 0) {
-        pdf_print_ln(pdf, last_attr);
-    } else {
-        pdf_add_name(pdf, "I");
-        pdf_begin_dict(pdf);
-        thread_title(pdf, t);
-        pdf_end_dict(pdf);
-    }
-    pdf_dict_add_ref(pdf, "F", a);
-    pdf_end_dict(pdf);
-    pdf_end_obj(pdf);
-    do {
-        pdf_begin_obj(pdf, a, OBJSTM_ALWAYS);
-        pdf_begin_dict(pdf);
-        if (a == b)
-            pdf_dict_add_ref(pdf, "T", t);
-        pdf_dict_add_ref(pdf, "V", obj_bead_prev(pdf, a));
-        pdf_dict_add_ref(pdf, "N", obj_bead_next(pdf, a));
-        pdf_dict_add_ref(pdf, "P", obj_bead_page(pdf, a));
-        pdf_dict_add_ref(pdf, "R", obj_bead_rect(pdf, a));
-        pdf_end_dict(pdf);
-        pdf_end_obj(pdf);
-        a = obj_bead_next(pdf, a);
-    } while (a != b);
-}
-
-void scan_thread_id(void)
-{
-    if (scan_keyword("num")) {
-        scan_int();
-        if (cur_val <= 0)
-            normal_error("pdf backend", "num identifier must be positive");
-        if (cur_val > max_halfword)
-            normal_error("pdf backend", "number too big");
-        set_pdf_thread_id(cur_list.tail_field, cur_val);
-        set_pdf_thread_named_id(cur_list.tail_field, 0);
-    } else if (scan_keyword("name")) {
-        scan_toks(false, true);
-        set_pdf_thread_id(cur_list.tail_field, def_ref);
-        set_pdf_thread_named_id(cur_list.tail_field, 1);
-    } else {
-        normal_error("pdf backend", "identifier type missing");
-    }
-}
-
-void check_running_thread(PDF pdf, halfword this_box, scaledpos cur)
-{
-    if ((pdf->last_thread != null) && is_running(pdf->thread.dp)
-        && (pdf->thread_level == cur_s))
-        append_thread(pdf, this_box, cur);
-}
-
-void print_bead_rectangles(PDF pdf)
-{
-    halfword i;
-    pdf_object_list *k;
-    int l;
-    if ((k = get_page_resources_list(pdf, obj_type_bead)) != NULL) {
-        while (k != NULL) {
-            l = pdf_create_obj(pdf, obj_type_others, 0);
-            pdf_begin_obj(pdf, l, OBJSTM_ALWAYS);
-            pdf_begin_array(pdf);
-            /*tex A pointer to a whatsit or whatsit-like node: */
-            i = obj_bead_data(pdf, k->info);
-            pdf_add_rect_spec(pdf, i);
-            if (subtype(i) == pdf_thread_data_node)
-                flush_node(i);
-            pdf_end_array(pdf);
-            pdf_end_obj(pdf);
-            /*tex Rewrite |obj_bead_data|: */
-            set_obj_bead_rect(pdf, k->info, l);
-            k = k->link;
-        }
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/pdf/pdfxform.w
@@ -0,0 +1,131 @@
+% pdfxform.w
+%
+% Copyright 2009-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 "pdf/pdfpage.h"
+
+@ @c
+int pdf_cur_form;               /* the form being output */
+
+void pdf_place_form(PDF pdf, halfword p)
+{
+    scaled_whd nat, tex;
+    scaled x, y;
+    pdffloat cm[6];
+    pdfstructure *q = pdf->pstruct;
+    int r = 6;
+    int objnum = rule_index(p);
+    nat.wd = obj_xform_width(pdf, objnum);
+    nat.ht = obj_xform_height(pdf, objnum);
+    nat.dp = obj_xform_depth(pdf, objnum);
+    /* no transform yet */
+    tex.wd = width(p);
+    tex.ht = height(p);
+    tex.dp = depth(p);
+    if (nat.wd != tex.wd || nat.ht != tex.ht || nat.dp != tex.dp) {
+        x = ext_xn_over_d(ten_pow[r], tex.wd, nat.wd);
+        y = ext_xn_over_d(ten_pow[r], tex.dp + tex.ht, nat.dp + nat.ht);
+    } else
+        x = y = ten_pow[r];
+    setpdffloat(cm[0], x, r);
+    setpdffloat(cm[1], 0, r);
+    setpdffloat(cm[2], 0, r);
+    setpdffloat(cm[3], y, r);
+    pdf_goto_pagemode(pdf);
+    (void) calc_pdfpos(q, pdf->posstruct->pos);
+    cm[4] = q->cm[4];
+    cm[5] = q->cm[5];
+    pdf_puts(pdf, "q\n");
+    pdf_print_cm(pdf, cm);
+    pdf_printf(pdf, "/Fm%d", (int) obj_info(pdf, objnum));
+    pdf_print_resname_prefix(pdf);
+    pdf_puts(pdf, " Do\nQ\n");
+    addto_page_resources(pdf, obj_type_xform, objnum);
+}
+
+/* we will store token lists as strings too */
+
+@ @c
+void scan_pdfxform(PDF pdf)
+{
+    int k;
+    halfword p;
+    pdf->xform_count++;
+    k = pdf_create_obj(pdf, obj_type_xform, pdf->xform_count);
+    set_obj_data_ptr(pdf, k, pdf_get_mem(pdf, pdfmem_xform_size));
+    if (scan_keyword("type")) {
+        scan_int();
+        set_obj_xform_type(pdf, k, cur_val);
+    } else {
+        set_obj_xform_type(pdf, k, 0);
+    }
+    if (scan_keyword("attr")) {
+        scan_toks(false, true);
+        set_obj_xform_attr(pdf, k, def_ref);
+    } else {
+        set_obj_xform_attr(pdf, k, null);
+    }
+    set_obj_xform_attr_str(pdf, k, null);
+    if (scan_keyword("resources")) {
+        scan_toks(false, true);
+        set_obj_xform_resources(pdf, k, def_ref);
+    } else {
+        set_obj_xform_resources(pdf, k, null);
+    }
+    if (scan_keyword("margin")) {
+        scan_int();
+        set_obj_xform_margin(pdf, k, cur_val);
+    } else {
+        set_obj_xform_margin(pdf, k, pdf_xform_margin);
+    }
+    set_obj_xform_resources_str(pdf, k, null);
+    scan_int();
+    p = box(cur_val);
+    if (p == null)
+        normal_error("pdf backend", "xforms cannot be used with a void box");
+    set_obj_xform_box(pdf, k, p);       /* save pointer to the box */
+    set_obj_xform_width(pdf, k, width(p));
+    set_obj_xform_height(pdf, k, height(p));
+    set_obj_xform_depth(pdf, k, depth(p));
+    box(cur_val) = null;
+    last_saved_box_index = k;
+}
+
+@ @c
+void scan_pdfrefxform(PDF pdf)
+{
+    scaled_whd alt_rule, dim, nat;
+    alt_rule = scan_alt_rule(); /* scans |<rule spec>| to |alt_rule| */
+    scan_int();
+    check_obj_type(pdf, obj_type_xform, cur_val);
+    tail_append(new_rule(box_rule));
+    nat.wd = obj_xform_width(pdf, cur_val);
+    nat.ht = obj_xform_height(pdf, cur_val);
+    nat.dp = obj_xform_depth(pdf, cur_val);
+    if (alt_rule.wd != null_flag || alt_rule.ht != null_flag || alt_rule.dp != null_flag) {
+        dim = tex_scale(nat, alt_rule);
+    } else {
+        dim = nat;
+    }
+    width(tail_par) = dim.wd;
+    height(tail_par) = dim.ht;
+    depth(tail_par) = dim.dp;
+    rule_index(tail_par) = cur_val;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/pdf/pdfxform.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "pdf/pdfpage.h"
-
-/*tex The form being output: */
-
-int pdf_cur_form;
-
-void pdf_place_form(PDF pdf, halfword p)
-{
-    scaled_whd nat, tex;
-    scaled x, y;
-    pdffloat cm[6];
-    pdfstructure *q = pdf->pstruct;
-    int r = 6;
-    int objnum = rule_index(p);
-    nat.wd = obj_xform_width(pdf, objnum);
-    nat.ht = obj_xform_height(pdf, objnum);
-    nat.dp = obj_xform_depth(pdf, objnum);
-    /*tex No transform yet: */
-    tex.wd = width(p);
-    tex.ht = height(p);
-    tex.dp = depth(p);
-    if (nat.wd != tex.wd || nat.ht != tex.ht || nat.dp != tex.dp) {
-        x = ext_xn_over_d(ten_pow[r], tex.wd, nat.wd);
-        y = ext_xn_over_d(ten_pow[r], tex.dp + tex.ht, nat.dp + nat.ht);
-    } else
-        x = y = ten_pow[r];
-    setpdffloat(cm[0], x, r);
-    setpdffloat(cm[1], 0, r);
-    setpdffloat(cm[2], 0, r);
-    setpdffloat(cm[3], y, r);
-    pdf_goto_pagemode(pdf);
-    (void) calc_pdfpos(q, pdf->posstruct->pos);
-    cm[4] = q->cm[4];
-    cm[5] = q->cm[5];
-    pdf_puts(pdf, "q\n");
-    pdf_print_cm(pdf, cm);
-    pdf_printf(pdf, "/Fm%d", (int) obj_info(pdf, objnum));
-    pdf_print_resname_prefix(pdf);
-    pdf_puts(pdf, " Do\nQ\n");
-    addto_page_resources(pdf, obj_type_xform, objnum);
-}
-
-/*tex We will store token lists as strings too. */
-
-void scan_pdfxform(PDF pdf)
-{
-    int k;
-    halfword p;
-    pdf->xform_count++;
-    k = pdf_create_obj(pdf, obj_type_xform, pdf->xform_count);
-    set_obj_data_ptr(pdf, k, pdf_get_mem(pdf, pdfmem_xform_size));
-    if (scan_keyword("type")) {
-        scan_int();
-        set_obj_xform_type(pdf, k, cur_val);
-    } else {
-        set_obj_xform_type(pdf, k, 0);
-    }
-    if (scan_keyword("attr")) {
-        scan_toks(false, true);
-        set_obj_xform_attr(pdf, k, def_ref);
-    } else {
-        set_obj_xform_attr(pdf, k, null);
-    }
-    set_obj_xform_attr_str(pdf, k, null);
-    if (scan_keyword("resources")) {
-        scan_toks(false, true);
-        set_obj_xform_resources(pdf, k, def_ref);
-    } else {
-        set_obj_xform_resources(pdf, k, null);
-    }
-    if (scan_keyword("margin")) {
-        scan_int();
-        set_obj_xform_margin(pdf, k, cur_val);
-    } else {
-        set_obj_xform_margin(pdf, k, pdf_xform_margin);
-    }
-    set_obj_xform_resources_str(pdf, k, null);
-    scan_int();
-    p = box(cur_val);
-    if (p == null)
-        normal_error("pdf backend", "xforms cannot be used with a void box");
-    /*tex Save the pointer to the box: */
-    set_obj_xform_box(pdf, k, p);
-    set_obj_xform_width(pdf, k, width(p));
-    set_obj_xform_height(pdf, k, height(p));
-    set_obj_xform_depth(pdf, k, depth(p));
-    box(cur_val) = null;
-    last_saved_box_index = k;
-}
-
-void scan_pdfrefxform(PDF pdf)
-{
-    scaled_whd alt_rule, dim, nat;
-    alt_rule = scan_alt_rule();
-    scan_int();
-    check_obj_type(pdf, obj_type_xform, cur_val);
-    tail_append(new_rule(box_rule));
-    nat.wd = obj_xform_width(pdf, cur_val);
-    nat.ht = obj_xform_height(pdf, cur_val);
-    nat.dp = obj_xform_depth(pdf, cur_val);
-    if (alt_rule.wd != null_flag || alt_rule.ht != null_flag || alt_rule.dp != null_flag) {
-        dim = tex_scale(nat, alt_rule);
-    } else {
-        dim = nat;
-    }
-    width(tail_par) = dim.wd;
-    height(tail_par) = dim.ht;
-    depth(tail_par) = dim.dp;
-    rule_index(tail_par) = cur_val;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/align.c
+++ /dev/null
@@ -1,1297 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-void fin_align(void);
-void init_row(void);
-void init_col(void);
-
-#define noDEBUG
-
-/*tex
-
-    It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because
-    they cut across so many of the control structures of \TeX. Therefore the
-    present page is probably not the best place for a beginner to start reading
-    this program; it is better to master everything else first.
-
-    Let us focus our thoughts on an example of what the input might be, in order
-    to get some idea about how the alignment miracle happens. The example doesn't
-    do anything useful, but it is sufficiently general to indicate all of the
-    special cases that must be dealt with; please do not be disturbed by its
-    apparent complexity and meaninglessness.
-
-    \starttyping
-    \tabskip 2pt plus 3pt
-    \halign to 300pt{u1#v1&
-    \hskip 50pt \tabskip 1pt plus 1fil u2#v2&
-    \hskip 50pt u3#v3\cr
-    \hskip 25pt a1&\omit a2&\vrule\cr
-    \hskip 25pt \noalign\{\vskip 3pt}
-    \hskip 25pt b1\span b2\cr
-    \hskip 25pt \omit&c2\span\omit\cr}
-    \stoptyping
-
-    Here's what happens:
-
-    \startitemize
-
-        \startitem
-            When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine
-            places the 300pt dimension onto the |save_stack|, and an
-            |align_group| code is placed above it. This will make it possible to
-            complete the alignment when the matching `\.\}' is found.
-        \stopitem
-
-        \startitem
-            The preamble is scanned next. Macros in the preamble are not
-            expanded, except as part of a tabskip specification. For example, if
-            \.{u2} had been a macro in the preamble above, it would have been
-            expanded, since \TeX\ must look for `\.{minus...}' as part of the
-            tabskip glue. A ``preamble list'' is constructed based on the user's
-            preamble; in our case it contains the following seven items:
-
-            \starttabulate
-            \NC \type{\glue 2pt plus 3pt}              \NC the tabskip preceding column 1      \NC \NR
-            \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 1          \NC \NR
-            \NC \type{\glue 2pt plus 3pt}              \NC the tabskip between columns 1 and 2 \NC \NR
-            \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 2          \NC \NR
-            \NC \type{\glue 1pt plus 1fil}             \NC the tabskip between columns 2 and 3 \NC \NR
-            \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 3          \NC \NR
-            \NC \type{\glue 1pt plus 1fil}             \NC the tabskip following column 3      \NC \NR
-            \stoptabulate
-
-            These ``alignrecord'' entries have the same size as an |unset_node|,
-            since they will later be converted into such nodes. These alignrecord
-            nodes have no |depth| field; this is split into |u_part| and
-            |v_part|, and they point to token lists for the templates of the
-            alignment. For example, the |u_part| field in the first alignrecord
-            points to the token list `\.{u1}', i.e., the template preceding the
-            `\.\#' for column~1. Furthermore, They have a |span_ptr| instead of a
-            |node_attr| field, and these |span_ptr| fields are initially set to
-            the value |end_span|, for reasons explained below.
-        \stopitem
-
-        \startitem
-            \TeX\ now looks at what follows the \.{\\cr} that ended the preamble.
-            It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back
-            to be read again, and the template `\.{u1}' is fed to the scanner.
-            Just before reading `\.{u1}', \TeX\ goes into restricted horizontal
-            mode. Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then
-            (when the {\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans
-            an |endv| token, indicating the end of a column. At this point an
-            |unset_node| is created, containing the contents of the current hlist
-            (i.e., `\.{u1a1v1}'). The natural width of this unset node replaces
-            the |width| field of the alignrecord for column~1; in general, the
-            alignrecords will record the maximum natural width that has occurred
-            so far in a given column.
-        \stopitem
-
-        \startitem
-            Since `\.{\\omit}' follows the `\.\&', the templates for column~2 are
-            now bypassed. Again \TeX\ goes into restricted horizontal mode and
-            makes an |unset_node| from the resulting hlist; but this time the
-            hlist contains simply `\.{a2}'. The natural width of the new unset
-            box is remembered in the |width| field of the alignrecord for
-            column~2.
-        \stopitem
-
-        \startitem
-            A third |unset_node| is created for column 3, using essentially the
-            mechanism that worked for column~1; this unset box contains
-            `\.{u3\\vrule v3}'. The vertical rule in this case has running
-            dimensions that will later extend to the height and depth of the
-            whole first row, since each |unset_node| in a row will eventually
-            inherit the height and depth of its enclosing box.
-        \stopitem
-
-        \startitem
-            The first row has now ended; it is made into a single unset box
-            comprising the following seven items:
-
-            \starttyping
-            \glue 2pt plus 3pt
-            \unsetbox for 1 column: u1a1v1
-            \glue 2pt plus 3pt
-            \unsetbox for 1 column: a2
-            \glue 1pt plus 1fil
-            \unsetbox for 1 column: u3\vrule v3
-            \glue 1pt plus 1fil
-            \stoptyping
-
-            The width of this unset row is unimportant, but it has the correct
-            height and depth, so the correct baselineskip glue will be computed
-            as the row is inserted into a vertical list.
-        \stopitem
-
-        \startitem
-            Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends
-            additional material (in this case \.{\\vskip 3pt}) to the vertical
-            list. While processing this material, \TeX\ will be in internal
-            vertical mode, and |no_align_group| will be on |save_stack|.
-        \stopitem
-
-        \startitem
-            The next row produces an unset box that looks like this:
-
-            \starttyping
-            \glue 2pt plus 3pt
-            \unsetbox for 2 columns: u1b1v1u2b2v2
-            \glue 1pt plus 1fil
-            \unsetbox for 1 column: {(empty)}
-            \glue 1pt plus 1fil
-            \stoptyping
-
-            The natural width of the unset box that spans columns 1~and~2 is
-            stored in a ``span node,'' which we will explain later; the
-            |span_ptr| field of the alignrecord for column~1 now points to the
-            new span node, and the |span_ptr| of the span node points to
-            |end_span|.
-        \stopitem
-
-        \startitem
-
-            The final row produces the unset box
-
-            \starttyping
-            \glue 2pt plus 3pt\cr
-            \unsetbox for 1 column: {(empty)}
-            \glue 2pt plus 3pt\cr
-            \unsetbox for 2 columns: u2c2v2
-            \glue 1pt plus 1fil
-            \stoptyping
-
-            A new span node is attached to the alignrecord for column 2.
-        \stopitem
-
-        \startitem
-            The last step is to compute the true column widths and to change all
-            the unset boxes to hboxes, appending the whole works to the vertical
-            list that encloses the \.{\\halign}. The rules for deciding on the
-            final widths of each unset column box will be explained below.
-        \stopitem
-
-    \stopitemize
-
-    Note that as \.{\\halign} is being processed, we fearlessly give up control
-    to the rest of \TeX. At critical junctures, an alignment routine is called
-    upon to step in and do some little action, but most of the time these
-    routines just lurk in the background. It's something like post-hypnotic
-    suggestion.
-
-    We have mentioned that alignrecords contain no |height| or |depth| fields.
-    Their |glue_sign| and |glue_order| are pre-empted as well, since it is
-    necessary to store information about what to do when a template ends. This
-    information is called the |extra_info| field.
-
-*/
-
-/*tex The pointer to \<u_j> token list: */
-
-#define u_part(A)     vlink((A)+depth_offset)
-
-/*tex The pointer to \<v_j> token list */
-
-#define v_part(A)     vinfo((A)+depth_offset)
-
-/*tex A column spanning list */
-
-#define span_ptr(A)   vinfo((A)+1)
-
-/*tex Info to remember during template */
-
-#define extra_info(A) vinfo((A)+list_offset)
-
-/*tex
-
-    Alignments can occur within alignments, so a small stack is used to access
-    the alignrecord information. At each level we have a |preamble| pointer,
-    indicating the beginning of the preamble list; a |cur_align| pointer,
-    indicating the current position in the preamble list; a |cur_span| pointer,
-    indicating the value of |cur_align| at the beginning of a sequence of spanned
-    columns; a |cur_loop| pointer, indicating the tabskip glue before an
-    alignrecord that should be copied next if the current list is extended; and
-    the |align_state| variable, which indicates the nesting of braces so that
-    \.{\\cr} and \.{\\span} and tab marks are properly intercepted. There also
-    are pointers |cur_head| and |cur_tail| to the head and tail of a list of
-    adjustments being moved out from horizontal mode to vertical~mode, and alike
-    |cur_pre_head| and |cur_pre_tail| for pre-adjust lists.
-
-    The current values of these nine quantities appear in global variables; when
-    they have to be pushed down, they are stored in 6-word nodes, and |align_ptr|
-    points to the topmost such node.
-
-*/
-
-/*tex This could be in |texnodes.h| but it's documented here. */
-
-/*tex The current preamble list: */
-
-#define preamble vlink(align_head)
-
-/*tex The current position in the preamble list: */
-
-pointer cur_align = null;
-
-/*tex The start of the currently spanned columns in the preamble list: */
-
-pointer cur_span = null;
-
-/*tex A place to copy when extending a periodic preamble: */
-
-pointer cur_loop = null;
-
-/*tex The most recently pushed-down alignment stack node: */
-
-pointer align_ptr = null;
-
-/*tex Adjustment list pointers: */
-
-pointer cur_head = null, cur_tail = null;
-
-/*tex Pre-adjustment list pointers: */
-
-pointer cur_pre_head = null, cur_pre_tail = null;
-
-/*tex
-
-    The |align_state| and |preamble| variables are initialized elsewhere.
-
-    Alignment stack maintenance is handled by a pair of trivial routines called
-    |push_alignment| and |pop_alignment|.
-
-    (HH:) It makes not much sense to add support for an \.{attr} keyword to
-    \.{\\halign} and \.{\\valign} because then we need to decide if we tag rows
-    or cells or both or come up with \.{cellattr} and \.{rowattr} and such. But
-    then it even makes sense to have explicit commands (in addition to the
-    seperator) to tags individual cells. Too muss hassle for now and the
-    advantages are not that large.
-
-*/
-
-static void push_alignment(void)
-{
-    /*tex The new alignment stack node: */
-    pointer p;
-    p = new_node(align_stack_node, 0);
-    vinfo(p + 1) = align_ptr;
-    vlink(p + 1) = cur_align;
-    vinfo(p + 2) = preamble;
-    vlink(p + 2) = cur_span;
-    vinfo(p + 3) = cur_loop;
-    vlink(p + 3) = align_state;
-    vinfo(p + 4) = cur_head;
-    vlink(p + 4) = cur_tail;
-    vinfo(p + 5) = cur_pre_head;
-    vlink(p + 5) = cur_pre_tail;
-    align_ptr = p;
-    cur_head = new_node(temp_node, 0);
-    cur_pre_head = new_node(temp_node, 0);
-}
-
-static void pop_alignment(void)
-{
-    /*tex The top alignment stack node: */
-    pointer p;
-    flush_node(cur_head);
-    flush_node(cur_pre_head);
-    p = align_ptr;
-    cur_pre_tail = vlink(p + 5);
-    cur_pre_head = vinfo(p + 5);
-    cur_tail = vlink(p + 4);
-    cur_head = vinfo(p + 4);
-    align_state = vlink(p + 3);
-    cur_loop = vinfo(p + 3);
-    cur_span = vlink(p + 2);
-    preamble = vinfo(p + 2);
-    cur_align = vlink(p + 1);
-    align_ptr = vinfo(p + 1);
-    flush_node(p);
-}
-
-/*tex
-
-    \TeX\ has eight procedures that govern alignments: |init_align| and
-    |fin_align| are used at the very beginning and the very end; |init_row| and
-    |fin_row| are used at the beginning and end of individual rows; |init_span|
-    is used at the beginning of a sequence of spanned columns (possibly involving
-    only one column); |init_col| and |fin_col| are used at the beginning and end
-    of individual columns; and |align_peek| is used after \.{\\cr} to see whether
-    the next item is \.{\\noalign}.
-
-    We shall consider these routines in the order they are first used during the
-    course of a complete \.{\\halign}, namely |init_align|, |align_peek|,
-    |init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|.
-
-    The preamble is copied directly, except that \.{\\tabskip} causes a change to
-    the tabskip glue, thereby possibly expanding macros that immediately follow
-    it. An appearance of \.{\\span} also causes such an expansion.
-
-    Note that if the preamble contains `\.{\\global\\tabskip}', the
-    `\.{\\global}' token survives in the preamble and the `\.{\\tabskip}' defines
-    new tabskip glue (locally).
-
-*/
-
-static void get_preamble_token(void)
-{
-  RESTART:
-    get_token();
-    while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) {
-        /*tex This token will be expanded once. */
-        get_token();
-        if (cur_cmd > max_command_cmd) {
-            expand();
-            get_token();
-        }
-    }
-    if (cur_cmd == endv_cmd)
-        fatal_error("(interwoven alignment preambles are not allowed)");
-    if ((cur_cmd == assign_glue_cmd)
-        && (cur_chr == glue_base + tab_skip_code)) {
-        scan_optional_equals();
-        scan_glue(glue_val_level);
-        if (global_defs_par > 0)
-            geq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val);
-        else
-            eq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val);
-        goto RESTART;
-    }
-}
-
-/*tex
-
-    When \.{\\halign} or \.{\\valign} has been scanned in an appropriate mode,
-    \TeX\ calls |init_align|, whose task is to get everything off to a good
-    start. This mostly involves scanning the preamble and putting its information
-    into the preamble list.
-
-*/
-
-void init_align(void)
-{
-    /*tex |warning_index| value for error messages */
-    pointer save_cs_ptr;
-    /*tex for short-term temporary use */
-    pointer p, r;
-    /*tex \.{\\halign} or \.{\\valign}, usually */
-    save_cs_ptr = cur_cs;
-    push_alignment();
-    /*tex enter a new alignment level */
-    align_state = -1000000;
-    /*tex
-
-        When \.{\\halign} is used as a displayed formula, there should be no
-        other pieces of mlists present.
-
-    */
-    if ((cur_list.mode_field == mmode) && ((cur_list.tail_field != cur_list.head_field) || (incompleat_noad_par != null))) {
-        const char *hlp[] = {
-            "Displays can use special alignments (like \\eqalignno)",
-            "only if nothing but the alignment itself is between $$'s.",
-            "So I've deleted the formulas that preceded this alignment.",
-            NULL
-        };
-        tex_error("Improper \\halign inside $$'s", hlp);
-        flush_math();
-    }
-    /*tex Enter a new semantic level. */
-    push_nest();
-    /*tex
-
-        In vertical modes, |prev_depth| already has the correct value. But if we
-        are in |mmode| (displayed formula mode), we reach out to the enclosing
-        vertical mode for the |prev_depth| value that produces the correct
-        baseline calculations.
-    */
-    if (cur_list.mode_field == mmode) {
-        cur_list.mode_field = -vmode;
-        prev_depth_par = nest[nest_ptr - 2].prev_depth_field;
-    } else if (cur_list.mode_field > 0) {
-        cur_list.mode_field = -(cur_list.mode_field);
-    }
-    scan_spec(align_group);
-    /*tex Scan the preamble. */
-    preamble = null;
-    cur_align = align_head;
-    cur_loop = null;
-    scanner_status = aligning;
-    warning_index = save_cs_ptr;
-    align_state = -1000000;
-    /*tex At this point, |cur_cmd=left_brace|. */
-    while (true) {
-        /*tex Append the current tabskip glue to the preamble list. */
-        r = new_param_glue(tab_skip_code);
-        vlink(cur_align) = r;
-        cur_align = vlink(cur_align);
-        if (cur_cmd == car_ret_cmd) {
-            /*tex \.{\\cr} ends the preamble. */
-            break;
-        }
-        /*tex
-
-            Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| and
-            then scan the template \<u_j>, putting the resulting token list in
-            |hold_token_head|. Spaces are eliminated from the beginning of a
-            template.
-
-        */
-        p = hold_token_head;
-        token_link(p) = null;
-        while (1) {
-            get_preamble_token();
-            if (cur_cmd == mac_param_cmd)
-                break;
-            if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd)
-                && (align_state == -1000000)) {
-                if ((p == hold_token_head) && (cur_loop == null) && (cur_cmd == tab_mark_cmd)) {
-                    cur_loop = cur_align;
-                } else {
-                    const char *hlp[] = {
-                        "There should be exactly one # between &'s, when an",
-                        "\\halign or \\valign is being set up. In this case you had",
-                        "none, so I've put one in; maybe that will work.",
-                        NULL
-                    };
-                    back_input();
-                    tex_error("Missing # inserted in alignment preamble", hlp);
-                    break;
-                }
-            } else if ((cur_cmd != spacer_cmd) || (p != hold_token_head)) {
-                r = get_avail();
-                token_link(p) = r;
-                p = token_link(p);
-                token_info(p) = cur_tok;
-            }
-        }
-        r = new_node(align_record_node, 0);
-        vlink(cur_align) = r;
-        /*tex A new align record: */
-        cur_align = vlink(cur_align);
-        span_ptr(cur_align) = end_span;
-        width(cur_align) = null_flag;
-        u_part(cur_align) = token_link(hold_token_head);
-        /*tex
-
-            Scan the template \<v_j>, putting the resulting token list in
-            |hold_token_head|.
-
-        */
-        p = hold_token_head;
-        token_link(p) = null;
-        while (1) {
-          CONTINUE:
-            get_preamble_token();
-            if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) && (align_state == -1000000))
-                break;
-            if (cur_cmd == mac_param_cmd) {
-                const char *hlp[] = {
-                    "There should be exactly one # between &'s, when an",
-                    "\\halign or \\valign is being set up. In this case you had",
-                    "more than one, so I'm ignoring all but the first.",
-                    NULL
-                };
-                tex_error("Only one # is allowed per tab", hlp);
-                goto CONTINUE;
-            }
-            r = get_avail();
-            token_link(p) = r;
-            p = token_link(p);
-            token_info(p) = cur_tok;
-        }
-        r = get_avail();
-        token_link(p) = r;
-        p = token_link(p);
-        /*tex Put \.{\\endtemplate} at the end: */
-        token_info(p) = end_template_token;
-        v_part(cur_align) = token_link(hold_token_head);
-    }
-    scanner_status = normal;
-    new_save_level(align_group);
-    if (every_cr_par != null)
-        begin_token_list(every_cr_par, every_cr_text);
-    /*tex Look for \.{\\noalign} or \.{\\omit}. */
-    align_peek();
-}
-
-/*tex
-
-    The tricky part about alignments is getting the templates into the scanner at
-    the right time, and recovering control when a row or column is finished.
-
-    We usually begin a row after each \.{\\cr} has been sensed, unless that
-    \.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates
-    the alignment. The |align_peek| routine is used to look ahead and do the
-    right thing; it either gets a new row started, or gets a \.{\\noalign}
-    started, or finishes off the alignment.
-
-*/
-
-void align_peek(void)
-{
-  RESTART:
-    align_state = 1000000;
-    do {
-        get_x_or_protected();
-    } while (cur_cmd == spacer_cmd);
-    if (cur_cmd == no_align_cmd) {
-        scan_left_brace();
-        new_save_level(no_align_group);
-        if (cur_list.mode_field == -vmode)
-            normal_paragraph();
-    } else if (cur_cmd == right_brace_cmd) {
-        fin_align();
-    } else if ((cur_cmd == car_ret_cmd) && (cur_chr == cr_cr_code)) {
-        /*tex Ignore \.{\\crcr}. */
-        goto RESTART;
-    } else {
-        /*tex Start a new row. */
-        init_row();
-        /*tex Start a new column and replace what we peeked at. */
-        init_col();
-    }
-}
-
-
-/*tex
-
-    The parameter to |init_span| is a pointer to the alignrecord where the next
-    column or group of columns will begin. A new semantic level is entered, so
-    that the columns will generate a list for subsequent packaging.
-
-*/
-
-static void init_span(pointer p)
-{
-    push_nest();
-    if (cur_list.mode_field == -hmode) {
-        space_factor_par = 1000;
-    } else {
-        prev_depth_par = ignore_depth;
-        normal_paragraph();
-    }
-    cur_span = p;
-}
-
-/*tex
-
-    To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'),
-    we enter a new semantic level, copy the first tabskip glue, and change from
-    internal vertical mode to restricted horizontal mode or vice versa. The
-    |space_factor| and |prev_depth| are not used on this semantic level, but we
-    clear them to zero just to be tidy.
-
-*/
-
-void init_row(void)
-{
-    push_nest();
-    cur_list.mode_field = (-hmode - vmode) - cur_list.mode_field;
-    if (cur_list.mode_field == -hmode)
-        space_factor_par = 0;
-    else
-        prev_depth_par = 0;
-    tail_append(new_glue(preamble));
-    subtype(cur_list.tail_field) = tab_skip_code + 1;
-    cur_align = vlink(preamble);
-    cur_tail = cur_head;
-    cur_pre_tail = cur_pre_head;
-    init_span(cur_align);
-}
-
-/*tex
-
-    When a column begins, we assume that |cur_cmd| is either |omit| or else the
-    current token should be put back into the input until the \<u_j> template has
-    been scanned. (Note that |cur_cmd| might be |tab_mark| or |car_ret|.) We also
-    assume that |align_state| is approximately 1000000 at this time. We remain in
-    the same mode, and start the template if it is called for.
-
-*/
-
-void init_col(void)
-{
-    extra_info(cur_align) = cur_cmd;
-    if (cur_cmd == omit_cmd)
-        align_state = 0;
-    else {
-        back_input();
-        begin_token_list(u_part(cur_align), u_template);
-    }
-    /*tex now |align_state=1000000| */
-}
-
-
-/*tex
-
-    The scanner sets |align_state| to zero when the \<u_j> template ends. When a
-    subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|,
-    the scanner activates the following code, which fires up the \<v_j> template.
-    We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|,
-    |span_code|, or a character code, depending on how the column text has ended.
-
-    This part of the program had better not be activated when the preamble to
-    another alignment is being scanned, or when no alignment preamble is active.
-
-*/
-
-void insert_vj_template(void)
-{
-    if ((scanner_status == aligning) || (cur_align == null))
-        fatal_error("(interwoven alignment preambles are not allowed)");
-    cur_cmd = extra_info(cur_align);
-    extra_info(cur_align) = cur_chr;
-    if (cur_cmd == omit_cmd)
-        begin_token_list(omit_template, v_template);
-    else
-        begin_token_list(v_part(cur_align), v_template);
-    align_state = 1000000;
-}
-
-/*tex Determine the stretch order */
-
-#define determine_stretch_order() do {             \
-    if      (total_stretch[filll]!= 0)  o = filll; \
-    else if (total_stretch[fill] != 0)  o = fill;  \
-    else if (total_stretch[fil]  != 0)  o = fil;   \
-    else if (total_stretch[sfi]  != 0)  o = sfi;   \
-    else o=normal;                                 \
-} while (0)
-
-/*tex Determine the shrink order */
-
-#define determine_shrink_order() do {             \
-    if      (total_shrink[filll] != 0) o = filll; \
-    else if (total_shrink[fill]  != 0) o = fill;  \
-    else if (total_shrink[fil]   != 0) o = fil;   \
-    else if (total_shrink[sfi]   != 0) o = sfi;   \
-    else o=normal;                                \
-} while (0)
-
-/*tex
-
-    When the |endv| command at the end of a \<v_j> template comes through the
-    scanner, things really start to happen; and it is the |fin_col| routine that
-    makes them happen. This routine returns |true| if a row as well as a column
-    has been finished.
-
-*/
-
-boolean fin_col(void)
-{
-    /*tex the alignrecord after the current one */
-    pointer p;
-    /*tex temporary pointers for list manipulation */
-    pointer q, r;
-    /*tex a new span node */
-    pointer s;
-    /*tex a new unset box */
-    pointer u;
-    /*tex natural width */
-    scaled w;
-    /*tex order of infinity */
-    unsigned char o;
-    /*tex span counter */
-    halfword n;
-    if (cur_align == null)
-        confusion("endv");
-    q = vlink(cur_align);
-    if (q == null)
-        confusion("endv");
-    if (align_state < 500000)
-        fatal_error("(interwoven alignment preambles are not allowed)");
-    p = vlink(q);
-    /*tex If the preamble list has been traversed, check that the row has ended. */
-    if ((p == null) && (extra_info(cur_align) < cr_code)) {
-        if (cur_loop != null) {
-            /*tex Lengthen the preamble periodically: */
-            r = new_node(align_record_node, 0);
-            vlink(q) = r;
-            /*tex A new align record: */
-            p = vlink(q);
-            span_ptr(p) = end_span;
-            width(p) = null_flag;
-            cur_loop = vlink(cur_loop);
-            /*tex Copy the templates from node |cur_loop| into node |p|. */
-            q = hold_token_head;
-            r = u_part(cur_loop);
-            while (r != null) {
-                s = get_avail();
-                token_link(q) = s;
-                q = token_link(q);
-                token_info(q) = token_info(r);
-                r = token_link(r);
-            }
-            token_link(q) = null;
-            u_part(p) = token_link(hold_token_head);
-            q = hold_token_head;
-            r = v_part(cur_loop);
-            while (r != null) {
-                s = get_avail();
-                token_link(q) = s;
-                q = token_link(q);
-                token_info(q) = token_info(r);
-                r = token_link(r);
-            }
-            token_link(q) = null;
-            v_part(p) = token_link(hold_token_head);
-            cur_loop = vlink(cur_loop);
-            r = new_glue(cur_loop);
-            vlink(p) = r;
-        } else {
-            const char *hlp[] = {
-                "You have given more \\span or & marks than there were",
-                "in the preamble to the \\halign or \\valign now in progress.",
-                "So I'll assume that you meant to type \\cr instead.",
-                NULL
-            };
-            extra_info(cur_align) = cr_code;
-            tex_error("Extra alignment tab has been changed to \\cr", hlp);
-        }
-    }
-    if (extra_info(cur_align) != span_code) {
-        unsave();
-        new_save_level(align_group);
-        /*tex Package an unset box for the current column and record its width. */
-        if (cur_list.mode_field == -hmode) {
-            adjust_tail = cur_tail;
-            pre_adjust_tail = cur_pre_tail;
-            u = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, additional, align_set_group, -1, 0, 0);
-            w = width(u);
-            cur_tail = adjust_tail;
-            adjust_tail = null;
-            cur_pre_tail = pre_adjust_tail;
-            pre_adjust_tail = null;
-        } else {
-            u = filtered_vpackage(vlink(cur_list.head_field), 0, additional, 0, align_set_group, -1, 0, 0);
-            w = height(u);
-        }
-        /*tex This represents a span count of 1: */
-        n = min_quarterword;
-        if (cur_span != cur_align) {
-            /*tex Update width entry for spanned columns. */
-            q = cur_span;
-            do {
-                incr(n);
-                q = vlink(vlink(q));
-            } while (q != cur_align);
-            if (n > max_quarterword) {
-                /*tex This can happen, but won't. */
-                confusion("too many spans");
-            }
-            q = cur_span;
-            while (span_span(span_ptr(q)) < n) {
-                q = span_ptr(q);
-            }
-            if (span_span(span_ptr(q)) > n) {
-                s = new_span_node(span_ptr(q), n, w);
-                span_ptr(q) = s;
-            } else if (width(span_ptr(q)) < w) {
-                width(span_ptr(q)) = w;
-            }
-        } else if (w > width(cur_align)) {
-            width(cur_align) = w;
-        }
-        type(u) = unset_node;
-        span_count(u) = (quarterword) n;
-        determine_stretch_order();
-        glue_order(u) = o;
-        glue_stretch(u) = total_stretch[o];
-        determine_shrink_order();
-        glue_sign(u) = o;
-        glue_shrink(u) = total_shrink[o];
-        pop_nest();
-        vlink(cur_list.tail_field) = u;
-        cur_list.tail_field = u;
-        /*tex Copy the tabskip glue between columns. */
-        tail_append(new_glue(vlink(cur_align)));
-        subtype(cur_list.tail_field) = tab_skip_code + 1;
-        if (extra_info(cur_align) >= cr_code) {
-            return true;
-        }
-        init_span(p);
-    }
-    align_state = 1000000;
-    do {
-        get_x_or_protected();
-    } while (cur_cmd == spacer_cmd);
-    cur_align = p;
-    init_col();
-    return false;
-}
-
-/*tex
-
-    A span node is a 3-word record containing |width|, |span_span|, and
-    |span_ptr| fields. The |span_span| field indicates the number of spanned
-    columns; the |span_ptr| field points to a span node for the same starting
-    column, having a greater extent of spanning, or to |end_span|, which has the
-    largest possible |span_span| field; the |width| field holds the largest
-    natural width corresponding to a particular set of spanned columns.
-
-    A list of the maximum widths so far, for spanned columns starting at a given
-    column, begins with the |span_ptr| field of the alignrecord for that column.
-    The code has to make sure that there is room for |span_ptr| in both the
-    alignrecord and the span nodes, which is why |span_ptr| replaces |node_attr|.
-
-    The |new_span_node| function is defined in |texnodes.c|.
-
-*/
-
-/*tex This is normally |alink|: */
-
-#ifndef span_span
-#  define span_span(A) vlink((A)+1)
-#endif
-
-/*tex
-
-    At the end of a row, we append an unset box to the current vlist (for
-    \.{\\halign}) or the current hlist (for \.{\\valign}). This unset box
-    contains the unset boxes for the columns, separated by the tabskip glue.
-    Everything will be set later.
-
-*/
-
-void fin_row(void)
-{
-    /*tex The new unset box: */
-    pointer p;
-    if (cur_list.mode_field == -hmode) {
-        p = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0,
-                           additional, fin_row_group, -1, 0, 0);
-        pop_nest();
-        if (cur_pre_head != cur_pre_tail)
-            append_list(cur_pre_head, cur_pre_tail);
-        append_to_vlist(p,lua_key_index(alignment));
-        if (cur_head != cur_tail)
-            append_list(cur_head, cur_tail);
-    } else {
-        p = filtered_vpackage(vlink(cur_list.head_field),
-            0, additional, max_depth_par, fin_row_group, -1, 0, 0);
-        pop_nest();
-        vlink(cur_list.tail_field) = p;
-        cur_list.tail_field = p;
-        space_factor_par = 1000;
-    }
-    type(p) = unset_node;
-    glue_stretch(p) = 0;
-    if (every_cr_par != null)
-        begin_token_list(every_cr_par, every_cr_text);
-    align_peek();
-    /*tex Note that |glue_shrink(p)=0| since |glue_shrink==shift_amount|. */
-}
-
-/*tex
-
-    Finally, we will reach the end of the alignment, and we can breathe a sigh of
-    relief that memory hasn't overflowed. All the unset boxes will now be set so
-    that the columns line up, taking due account of spanned columns.
-
-*/
-
-void fin_align(void)
-{
-    /*tex registers for the list operations */
-    pointer p, q, r, s, u, rr;
-    /*tex width of column */
-    scaled t, w;
-    /*tex shift offset for unset boxes */
-    scaled o;
-    /*tex matching span amount */
-    halfword n;
-    /*tex temporary storage for |overfull_rule| */
-    scaled rule_save;
-    /*tex temporary storage for |prev_depth| */
-    halfword pd;
-    /*tex temporary storage for |new_glue| */
-    halfword ng;
-    /*tex The |align_group| was for individual entries: */
-    if (cur_group != align_group)
-        confusion("align1");
-    unsave();
-    /*tex The |align_group| was for the whole alignment: */
-    if (cur_group != align_group)
-        confusion("align0");
-    unsave();
-    if (nest[nest_ptr - 1].mode_field == mmode) {
-        o = display_indent_par;
-    } else {
-        o = 0;
-    }
-    /*tex
-
-        Go through the preamble list, determining the column widths and
-        changing the alignrecords to dummy unset boxes.
-
-        It's time now to dismantle the preamble list and to compute the
-        column widths. Let $w_{ij}$ be the maximum of the natural widths of
-        all entries that span columns $i$ through $j$, inclusive. The
-        alignrecord for column~$i$ contains $w_{ii}$ in its |width| field,
-        and there is also a linked list of the nonzero $w_{ij}$ for
-        increasing $j$, accessible via the |info| field; these span nodes
-        contain the value $j-i+|min_quarterword|$ in their |link| fields. The
-        values of $w_{ii}$ were initialized to |null_flag|, which we regard
-        as $-\infty$.
-
-        The final column widths are defined by the formula $$w_j=\max_{1\L
-        i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$ where $t_k$ is
-        the natural width of the tabskip glue between columns $k$ and~$k+1$.
-        However, if $w_{ij}=-\infty$ for all |i| in the range |1<=i<=j|
-        (i.e., if every entry that involved column~|j| also involved
-        column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after
-        column~|j|.
-
-        \TeX\ computes these values by using the following scheme: First
-        $w_1=w_{11}$. Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$,
-        for all $j>1$. Then $w_2=w_{22}$. Then replace $w_{3j}$ by
-        $\max(w_{3j},w_{2j}-t_2-w_2)$ for all $j>2$; and so on. If any $w_j$
-        turns out to be $-\infty$, its value is changed to zero and so is the
-        next tabskip.
-
-    */
-    q = vlink(preamble);
-    do {
-        flush_list(u_part(q));
-        flush_list(v_part(q));
-        p = vlink(vlink(q));
-        if (width(q) == null_flag) {
-            /*tex Nullify |width(q)| and the tabskip glue following this column. */
-            width(q) = 0;
-            r = vlink(q);
-            reset_glue_to_zero(r);
-        }
-        if (span_ptr(q) != end_span) {
-            /*tex
-
-                Merge the widths in the span nodes of |q| with those of |p|,
-                destroying the span nodes of |q|.
-
-                Merging of two span-node lists is a typical exercise in the
-                manipulation of linearly linked data structures. The essential
-                invariant in the following |repeat| loop is that we want to
-                dispense with node |r|, in |q|'s list, and |u| is its successor;
-                all nodes of |p|'s list up to and including |s| have been
-                processed, and the successor of |s| matches |r| or precedes |r|
-                or follows |r|, according as |link(r)=n| or |link(r)>n| or
-                |link(r)<n|.
-
-            */
-            t = width(q) + width(vlink(q));
-            r = span_ptr(q);
-            s = end_span;
-            span_ptr(s) = p;
-            n = min_quarterword + 1;
-            do {
-                width(r) = width(r) - t;
-                u = span_ptr(r);
-                while (span_span(r) > n) {
-                    s = span_ptr(s);
-                    n = span_span(span_ptr(s)) + 1;
-                }
-                if (span_span(r) < n) {
-                    span_ptr(r) = span_ptr(s);
-                    span_ptr(s) = r;
-                    decr(span_span(r));
-                    s = r;
-                } else {
-                    if (width(r) > width(span_ptr(s)))
-                        width(span_ptr(s)) = width(r);
-                    flush_node(r);
-                }
-                r = u;
-            } while (r != end_span);
-        }
-        type(q) = unset_node;
-        span_count(q) = min_quarterword;
-        height(q) = 0;
-        depth(q) = 0;
-        glue_order(q) = normal;
-        glue_sign(q) = normal;
-        glue_stretch(q) = 0;
-        glue_shrink(q) = 0;
-        q = p;
-    } while (q != null);
-    /*tex
-
-        Package the preamble list, to determine the actual tabskip glue amounts,
-        and let |p| point to this prototype box.
-
-        Now the preamble list has been converted to a list of alternating unset
-        boxes and tabskip glue, where the box widths are equal to the final
-        column sizes. In case of \.{\\valign}, we change the widths to heights,
-        so that a correct error message will be produced if the alignment is
-        overfull or underfull.
-
-    */
-    decr(save_ptr);
-    pack_begin_line = -cur_list.ml_field;
-    if (cur_list.mode_field == -vmode) {
-        rule_save = overfull_rule_par;
-        /*tex Prevent the rule from being packaged. */
-        overfull_rule_par = 0;
-        p = hpack(preamble, saved_value(0), saved_level(0), -1);
-        overfull_rule_par = rule_save;
-    } else {
-        q = vlink(preamble);
-        do {
-            height(q) = width(q);
-            width(q) = 0;
-            q = vlink(vlink(q));
-        } while (q != null);
-        p = filtered_vpackage(preamble,
-            saved_value(0), saved_level(0), max_depth_par, preamble_group, -1, 0, 0);
-        q = vlink(preamble);
-        do {
-            width(q) = height(q);
-            height(q) = 0;
-            q = vlink(vlink(q));
-        } while (q != null);
-    }
-    pack_begin_line = 0;
-    /*tex Set the glue in all the unset boxes of the current list. */
-    q = vlink(cur_list.head_field);
-    s = cur_list.head_field;
-    while (q != null) {
-        if (!is_char_node(q)) {
-            if (type(q) == unset_node) {
-                /*tex
-
-                    We set the unset box |q| and the unset boxes in it. The unset
-                    box |q| represents a row that contains one or more unset
-                    boxes, depending on how soon \.{\\cr} occurred in that row.
-
-                */
-                if (cur_list.mode_field == -vmode) {
-                    type(q) = hlist_node;
-                    subtype(q) = align_row_list;
-                    width(q) = width(p);
-                } else {
-                    type(q) = vlist_node;
-                    subtype(q) = align_row_list;
-                    height(q) = height(p);
-                }
-                glue_order(q) = glue_order(p);
-                glue_sign(q) = glue_sign(p);
-                glue_set(q) = glue_set(p);
-                shift_amount(q) = o;
-                r = vlink(list_ptr(q));
-                assert (type(r) == unset_node);
-                s = vlink(list_ptr(p));
-                do {
-                    /*tex
-
-                        We set the glue in node |r| and change it from an unset
-                        node. A box made from spanned columns will be followed by
-                        tabskip glue nodes and by empty boxes as if there were no
-                        spanning. This permits perfect alignment of subsequent
-                        entries, and it prevents values that depend on floating
-                        point arithmetic from entering into the dimensions of any
-                        boxes.
-
-                    */
-                    n = span_count(r);
-                    t = width(s);
-                    w = t;
-                    u = hold_head;
-                    while (n > min_quarterword) {
-                        decr(n);
-                        /*tex
-
-                            Append tabskip glue and an empty box to list |u|, and
-                            update |s| and |t| as the prototype nodes are passed.
-
-                        */
-                        s = vlink(s);
-                        ng = new_glue(s);
-                        vlink(u) = ng;
-                        u = vlink(u);
-                        subtype(u) = tab_skip_code + 1;
-                        t = t + width(s);
-                        if (glue_sign(p) == stretching) {
-                            if (stretch_order(s) == glue_order(p))
-                                t = t + round(float_cast(glue_set(p)) * float_cast(stretch(s)));
-                        } else if (glue_sign(p) == shrinking) {
-                            if (shrink_order(s) == glue_order(p))
-                                t = t - round(float_cast(glue_set(p)) * float_cast(shrink(s)));
-                        }
-                        s = vlink(s);
-                        rr = new_null_box();
-                        vlink(u) = rr;
-                        u = vlink(u);
-                        t = t + width(s);
-                        subtype(u) = align_cell_list;
-                        if (cur_list.mode_field == -vmode) {
-                            width(u) = width(s);
-                        } else {
-                            type(u) = vlist_node;
-                            height(u) = width(s);
-                        }
-                    }
-                    if (cur_list.mode_field == -vmode) {
-                        /*tex
-
-                            Make the unset node |r| into an |hlist_node| of width
-                            |w|, setting the glue as if the width were |t|.
-
-                        */
-                        height(r) = height(q);
-                        depth(r) = depth(q);
-                        if (t == width(r)) {
-                            glue_sign(r) = normal;
-                            glue_order(r) = normal;
-                            set_glue_ratio_zero(glue_set(r));
-                        } else if (t > width(r)) {
-                            glue_sign(r) = stretching;
-                            if (glue_stretch(r) == 0)
-                                set_glue_ratio_zero(glue_set(r));
-                            else
-                                glue_set(r) = unfloat((double) (t - width(r)) / glue_stretch(r));
-                        } else {
-                            glue_order(r) = glue_sign(r);
-                            glue_sign(r) = shrinking;
-                            if (glue_shrink(r) == 0)
-                                set_glue_ratio_zero(glue_set(r));
-                            else if ((glue_order(r) == normal) && (width(r) - t > glue_shrink(r)))
-                                set_glue_ratio_one(glue_set(r));
-                            else
-                                glue_set(r) = unfloat((double) (width(r) - t) / glue_shrink(r));
-                        }
-                        width(r) = w;
-                        type(r) = hlist_node;
-                        subtype(r) = align_cell_list;
-
-                    } else {
-                        /*tex
-
-                            Make the unset node |r| into a |vlist_node| of height
-                            |w|, setting the glue as if the height were |t|.
-
-                        */
-                        width(r) = width(q);
-                        if (t == height(r)) {
-                            glue_sign(r) = normal;
-                            glue_order(r) = normal;
-                            set_glue_ratio_zero(glue_set(r));
-                        } else if (t > height(r)) {
-                            glue_sign(r) = stretching;
-                            if (glue_stretch(r) == 0)
-                                set_glue_ratio_zero(glue_set(r));
-                            else
-                                glue_set(r) = unfloat((t - height(r)) / glue_stretch(r));
-                        } else {
-                            glue_order(r) = glue_sign(r);
-                            glue_sign(r) = shrinking;
-                            if (glue_shrink(r) == 0)
-                                set_glue_ratio_zero(glue_set(r));
-                            else if ((glue_order(r) == normal) && (height(r) - t > glue_shrink(r)))
-                                set_glue_ratio_one(glue_set(r));
-                            else
-                                glue_set(r) = unfloat((height(r) - t) / glue_shrink(r));
-                        }
-                        height(r) = w;
-                        type(r) = vlist_node;
-                        subtype(r) = align_cell_list;
-                    }
-                    shift_amount(r) = 0;
-                    if (u != hold_head) {
-                        /*tex Append blank boxes to account for spanned nodes. */
-                        vlink(u) = vlink(r);
-                        vlink(r) = vlink(hold_head);
-                        r = u;
-                    }
-
-                    r = vlink(vlink(r));
-                    s = vlink(vlink(s));
-                } while (r != null);
-
-            } else if (type(q) == rule_node) {
-                /*tex
-
-                    Make the running dimensions in rule |q| extend to the
-                    boundaries of the alignment.
-
-                */
-                if (is_running(width(q)))
-                    width(q) = width(p);
-                if (is_running(height(q)))
-                    height(q) = height(p);
-                if (is_running(depth(q)))
-                    depth(q) = depth(p);
-                if (o != 0) {
-                    r = vlink(q);
-                    vlink(q) = null;
-                    q = hpack(q, 0, additional, -1);
-                    shift_amount(q) = o;
-                    subtype(q) = align_cell_list;
-                    vlink(q) = r;
-                    vlink(s) = q;
-                }
-            }
-        }
-        s = q;
-        q = vlink(q);
-    }
-    flush_node_list(p);
-    pop_alignment();
-    /*tex
-
-        We now have a completed alignment, in the list that starts at
-        |cur_list.head_field| and ends at |cur_list.tail_field|. This list will
-        be merged with the one that encloses it. (In case the enclosing mode is
-        |mmode|, for displayed formulas, we will need to insert glue before and
-        after the display; that part of the program will be deferred until we're
-        more familiar with such operations.)
-
-    */
-    pd = prev_depth_par;
-    p = vlink(cur_list.head_field);
-    q = cur_list.tail_field;
-    pop_nest();
-    if (cur_list.mode_field == mmode) {
-        finish_display_alignment(p, q, pd);
-    } else {
-        prev_depth_par = pd;
-        vlink(cur_list.tail_field) = p;
-        if (p != null)
-            cur_list.tail_field = q;
-        if (cur_list.mode_field == vmode) {
-            if (!output_active)
-                lua_node_filter_s(buildpage_filter_callback,lua_key_index(alignment));
-            build_page();
-        }
-    }
-}
-
-/*tex
-
-    The token list |omit_template| just referred to is a constant token list that
-    contains the special control sequence \.{\\endtemplate} only.
-
-*/
-
-void initialize_alignments(void)
-{
-    token_info(omit_template) = end_template_token;
-    span_span(end_span) = max_quarterword + 1;
-    span_ptr(end_span) = null;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/align.w
@@ -0,0 +1,1144 @@
+% align.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\<#1>{$#1$}
+
+@ @c
+
+
+#include "ptexlib.h"
+
+@ @c
+void fin_align(void);
+void init_row(void);
+void init_col(void);
+
+#define noDEBUG
+
+@ It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because
+they cut across so many of the control structures of \TeX.
+
+Therefore the present page is probably not the best place for a beginner to
+start reading this program; it is better to master everything else first.
+
+Let us focus our thoughts on an example of what the input might be, in order
+to get some idea about how the alignment miracle happens. The example doesn't
+do anything useful, but it is sufficiently general to indicate all of the
+special cases that must be dealt with; please do not be disturbed by its
+apparent complexity and meaninglessness.
+$$\vbox{\halign{\.{#}\hfil\cr
+{}\\tabskip 2pt plus 3pt\cr
+{}\\halign to 300pt\{u1\#v1\&\cr
+\hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr
+\hskip 50pt u3\#v3\\cr\cr
+\hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr
+\hskip 25pt \\noalign\{\\vskip 3pt\}\cr
+\hskip 25pt b1\\span b2\\cr\cr
+\hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$
+Here's what happens:
+
+\yskip
+(0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine
+places the 300pt dimension onto the |save_stack|, and an |align_group|
+code is placed above it. This will make it possible to complete the alignment
+when the matching `\.\}' is found.
+
+(1) The preamble is scanned next. Macros in the preamble are not expanded,
+@^preamble@>
+except as part of a tabskip specification. For example, if \.{u2} had been
+a macro in the preamble above, it would have been expanded, since \TeX\
+must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list''
+is constructed based on the user's preamble; in our case it contains the
+following seven items:
+$$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr
+{}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 1\cr
+{}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 2\cr
+{}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 3\cr
+{}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$
+These ``alignrecord'' entries have the same size as an |unset_node|,
+since they will later be converted into such nodes. These alignrecord
+nodes have no |depth| field; this is split into |u_part| and |v_part|,
+and they point to token lists for the templates of the alignment. For
+example, the |u_part| field in the first alignrecord points to the
+token list `\.{u1}', i.e., the template preceding the `\.\#' for
+column~1.  Furthermore, They have a |span_ptr| instead of a |node_attr|
+field, and these |span_ptr| fields are initially set to the value
+|end_span|, for reasons explained below.
+
+(2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble.
+It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to
+be read again, and the template `\.{u1}' is fed to the scanner. Just
+before reading `\.{u1}', \TeX\ goes into restricted horizontal mode.
+Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the
+{\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv|
+token, indicating the end of a column. At this point an |unset_node| is
+created, containing the contents of the current hlist (i.e., `\.{u1a1v1}').
+The natural width of this unset node replaces the |width| field of the
+alignrecord for column~1; in general, the alignrecords will record the
+maximum natural width that has occurred so far in a given column.
+
+(3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2
+are now bypassed. Again \TeX\ goes into restricted horizontal mode and
+makes an |unset_node| from the resulting hlist; but this time the
+hlist contains simply `\.{a2}'. The natural width of the new unset box
+is remembered in the |width| field of the alignrecord for column~2.
+
+(4) A third |unset_node| is created for column 3, using essentially the
+mechanism that worked for column~1; this unset box contains `\.{u3\\vrule
+v3}'. The vertical rule in this case has running dimensions that will later
+extend to the height and depth of the whole first row, since each |unset_node|
+in a row will eventually inherit the height and depth of its enclosing box.
+
+(5) The first row has now ended; it is made into a single unset box
+comprising the following seven items:
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: u1a1v1\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: a2\cr
+{}\\glue 1pt plus 1fil\cr
+{}\\unsetbox for 1 column: u3\\vrule v3\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+The width of this unset row is unimportant, but it has the correct height
+and depth, so the correct baselineskip glue will be computed as the row
+is inserted into a vertical list.
+
+(6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends
+additional material (in this case \.{\\vskip 3pt}) to the vertical list.
+While processing this material, \TeX\ will be in internal vertical
+mode, and |no_align_group| will be on |save_stack|.
+
+(7) The next row produces an unset box that looks like this:
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr
+{}\\glue 1pt plus 1fil\cr
+{}\\unsetbox for 1 column: {\rm(empty)}\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+The natural width of the unset box that spans columns 1~and~2 is stored
+in a ``span node,'' which we will explain later; the |span_ptr| field of the
+alignrecord for column~1 now points to the new span node, and the |span_ptr|
+of the span node points to |end_span|.
+
+(8) The final row produces the unset box
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: {\rm(empty)}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 2 columns: u2c2v2\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+A new span node is attached to the alignrecord for column 2.
+
+(9) The last step is to compute the true column widths and to change all the
+unset boxes to hboxes, appending the whole works to the vertical list that
+encloses the \.{\\halign}. The rules for deciding on the final widths of
+each unset column box will be explained below.
+
+\yskip\noindent
+Note that as \.{\\halign} is being processed, we fearlessly give up control
+to the rest of \TeX. At critical junctures, an alignment routine is
+called upon to step in and do some little action, but most of the time
+these routines just lurk in the background. It's something like
+post-hypnotic suggestion.
+
+@ We have mentioned that alignrecords contain no |height| or |depth| fields.
+Their |glue_sign| and |glue_order| are pre-empted as well, since it
+is necessary to store information about what to do when a template ends.
+This information is called the |extra_info| field.
+
+@c
+/* could be in texnodes.h, but documented here*/
+
+#define u_part(A)     vlink((A)+depth_offset) /* pointer to \<u_j> token list */
+#define v_part(A)     vinfo((A)+depth_offset) /* pointer to \<v_j> token list */
+#define span_ptr(A)   vinfo((A)+1)            /* column spanning list */
+#define extra_info(A) vinfo((A)+list_offset)  /* info to remember during template */
+
+@ Alignments can occur within alignments, so a small stack is used to access
+the alignrecord information. At each level we have a |preamble| pointer,
+indicating the beginning of the preamble list; a |cur_align| pointer,
+indicating the current position in the preamble list; a |cur_span| pointer,
+indicating the value of |cur_align| at the beginning of a sequence of
+spanned columns; a |cur_loop| pointer, indicating the tabskip glue before
+an alignrecord that should be copied next if the current list is extended;
+and the |align_state| variable, which indicates the nesting of braces so
+that \.{\\cr} and \.{\\span} and tab marks are properly intercepted.
+There also are pointers |cur_head| and |cur_tail| to the head and tail
+of a list of adjustments being moved out from horizontal mode to
+vertical~mode, and alike |cur_pre_head| and |cur_pre_tail| for pre-adjust
+lists.
+
+The current values of these nine quantities appear in global variables;
+when they have to be pushed down, they are stored in 6-word nodes, and
+|align_ptr| points to the topmost such node.
+
+@c
+/* could be in texnodes.h but documented here*/
+
+#define preamble vlink(align_head)      /* the current preamble list */
+
+pointer cur_align = null;       /* current position in preamble list */
+pointer cur_span = null;        /* start of currently spanned columns in preamble list */
+pointer cur_loop = null;        /* place to copy when extending a periodic preamble */
+pointer align_ptr = null;       /* most recently pushed-down alignment stack node */
+pointer cur_head = null, cur_tail = null;       /* adjustment list pointers */
+pointer cur_pre_head = null, cur_pre_tail = null;       /* pre-adjustment list pointers */
+
+/* The |align_state| and |preamble| variables are initialized elsewhere. */
+
+@ Alignment stack maintenance is handled by a pair of trivial routines
+called |push_alignment| and |pop_alignment|.
+
+(HH:) It makes not much sense to add support for an \.{attr} keyword to
+\.{\\halign} and \.{\\valign} because then we need to decide if we tag
+rows or cells or both or come up with \.{cellattr} and \.{rowattr} and
+such. But then it even makes sense to have explicit commands (in addition
+to the seperator) to tags individual cells. Too muss hassle for now and the
+advantages are not that large.
+
+@c
+static void push_alignment(void)
+{
+    pointer p;                  /* the new alignment stack node */
+    p = new_node(align_stack_node, 0);
+    vinfo(p + 1) = align_ptr;
+    vlink(p + 1) = cur_align;
+    vinfo(p + 2) = preamble;
+    vlink(p + 2) = cur_span;
+    vinfo(p + 3) = cur_loop;
+    vlink(p + 3) = align_state;
+    vinfo(p + 4) = cur_head;
+    vlink(p + 4) = cur_tail;
+    vinfo(p + 5) = cur_pre_head;
+    vlink(p + 5) = cur_pre_tail;
+    align_ptr = p;
+    cur_head = new_node(temp_node, 0);
+    cur_pre_head = new_node(temp_node, 0);
+}
+
+static void pop_alignment(void)
+{
+    pointer p;                  /* the top alignment stack node */
+    flush_node(cur_head);
+    flush_node(cur_pre_head);
+    p = align_ptr;
+    cur_pre_tail = vlink(p + 5);
+    cur_pre_head = vinfo(p + 5);
+    cur_tail = vlink(p + 4);
+    cur_head = vinfo(p + 4);
+    align_state = vlink(p + 3);
+    cur_loop = vinfo(p + 3);
+    cur_span = vlink(p + 2);
+    preamble = vinfo(p + 2);
+    cur_align = vlink(p + 1);
+    align_ptr = vinfo(p + 1);
+    flush_node(p);
+}
+
+
+@ \TeX\ has eight procedures that govern alignments: |init_align| and
+|fin_align| are used at the very beginning and the very end; |init_row| and
+|fin_row| are used at the beginning and end of individual rows; |init_span|
+is used at the beginning of a sequence of spanned columns (possibly involving
+only one column); |init_col| and |fin_col| are used at the beginning and
+end of individual columns; and |align_peek| is used after \.{\\cr} to see
+whether the next item is \.{\\noalign}.
+
+We shall consider these routines in the order they are first used during
+the course of a complete \.{\\halign}, namely |init_align|, |align_peek|,
+|init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|.
+
+
+@ The preamble is copied directly, except that \.{\\tabskip} causes a change
+to the tabskip glue, thereby possibly expanding macros that immediately
+follow it. An appearance of \.{\\span} also causes such an expansion.
+
+Note that if the preamble contains `\.{\\global\\tabskip}', the `\.{\\global}'
+token survives in the preamble and the `\.{\\tabskip}' defines new
+tabskip glue (locally).
+
+@c
+static void get_preamble_token(void)
+{
+  RESTART:
+    get_token();
+    while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) {
+        get_token();            /* this token will be expanded once */
+        if (cur_cmd > max_command_cmd) {
+            expand();
+            get_token();
+        }
+    }
+    if (cur_cmd == endv_cmd)
+        fatal_error("(interwoven alignment preambles are not allowed)");
+    if ((cur_cmd == assign_glue_cmd)
+        && (cur_chr == glue_base + tab_skip_code)) {
+        scan_optional_equals();
+        scan_glue(glue_val_level);
+        if (global_defs_par > 0)
+            geq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val);
+        else
+            eq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val);
+        goto RESTART;
+    }
+}
+
+
+
+@ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate
+mode, \TeX\ calls |init_align|, whose task is to get everything off to a
+good start. This mostly involves scanning the preamble and putting its
+information into the preamble list.
+@^preamble@>
+
+@c
+void init_align(void)
+{
+    /* label done, done1, done2, continue; */
+    pointer save_cs_ptr;        /* |warning_index| value for error messages */
+    pointer p, r;               /* for short-term temporary use */
+    save_cs_ptr = cur_cs;       /* \.{\\halign} or \.{\\valign}, usually */
+    push_alignment();
+    align_state = -1000000;     /* enter a new alignment level */
+
+    /* When \.{\\halign} is used as a displayed formula, there should be
+       no other pieces of mlists present. */
+
+    if ((cur_list.mode_field == mmode)
+        && ((cur_list.tail_field != cur_list.head_field)
+            || (incompleat_noad_par != null))) {
+        const char *hlp[] =
+            { "Displays can use special alignments (like \\eqalignno)",
+            "only if nothing but the alignment itself is between $$'s.",
+            "So I've deleted the formulas that preceded this alignment.",
+            NULL
+        };
+        tex_error("Improper \\halign inside $$'s", hlp);
+        flush_math();
+    }
+    push_nest();                /* enter a new semantic level */
+    /* In vertical modes, |prev_depth| already has the correct value. But
+       if we are in |mmode| (displayed formula mode), we reach out to the
+       enclosing vertical mode for the |prev_depth| value that produces the
+       correct baseline calculations. */
+    if (cur_list.mode_field == mmode) {
+        cur_list.mode_field = -vmode;
+        prev_depth_par = nest[nest_ptr - 2].prev_depth_field;
+    } else if (cur_list.mode_field > 0) {
+        cur_list.mode_field = -(cur_list.mode_field);
+    }
+    scan_spec(align_group);
+    /* Scan the preamble */
+    preamble = null;
+    cur_align = align_head;
+    cur_loop = null;
+    scanner_status = aligning;
+    warning_index = save_cs_ptr;
+    align_state = -1000000;
+    /* at this point, |cur_cmd=left_brace| */
+    while (true) {
+        /* Append the current tabskip glue to the preamble list */
+        r = new_param_glue(tab_skip_code);
+        vlink(cur_align) = r;
+        cur_align = vlink(cur_align);
+
+        if (cur_cmd == car_ret_cmd)
+            break;              /* \.{\\cr} ends the preamble */
+
+        /* Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| */
+        /* Scan the template \<u_j>, putting the resulting token list in |hold_token_head| */
+        /* Spaces are eliminated from the beginning of a template. */
+
+        p = hold_token_head;
+        token_link(p) = null;
+        while (1) {
+            get_preamble_token();
+            if (cur_cmd == mac_param_cmd)
+                break;
+            if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd)
+                && (align_state == -1000000)) {
+                if ((p == hold_token_head) && (cur_loop == null)
+                    && (cur_cmd == tab_mark_cmd)) {
+                    cur_loop = cur_align;
+                } else {
+                    const char *hlp[] =
+                        { "There should be exactly one # between &'s, when an",
+                        "\\halign or \\valign is being set up. In this case you had",
+                        "none, so I've put one in; maybe that will work.",
+                        NULL
+                    };
+                    back_input();
+                    tex_error("Missing # inserted in alignment preamble", hlp);
+                    break;
+                }
+            } else if ((cur_cmd != spacer_cmd) || (p != hold_token_head)) {
+                r = get_avail();
+                token_link(p) = r;
+                p = token_link(p);
+                token_info(p) = cur_tok;
+            }
+        }
+        r = new_node(align_record_node, 0);
+        vlink(cur_align) = r;
+        cur_align = vlink(cur_align);   /* a new alignrecord */
+        span_ptr(cur_align) = end_span;
+        width(cur_align) = null_flag;
+        u_part(cur_align) = token_link(hold_token_head);
+        /* Scan the template \<v_j>, putting the resulting token list in |hold_token_head| */
+
+        p = hold_token_head;
+        token_link(p) = null;
+        while (1) {
+          CONTINUE:
+            get_preamble_token();
+            if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd)
+                && (align_state == -1000000))
+                break;
+            if (cur_cmd == mac_param_cmd) {
+                const char *hlp[] =
+                    { "There should be exactly one # between &'s, when an",
+                    "\\halign or \\valign is being set up. In this case you had",
+                    "more than one, so I'm ignoring all but the first.",
+                    NULL
+                };
+                tex_error("Only one # is allowed per tab", hlp);
+                goto CONTINUE;
+            }
+            r = get_avail();
+            token_link(p) = r;
+            p = token_link(p);
+            token_info(p) = cur_tok;
+        }
+        r = get_avail();
+        token_link(p) = r;
+        p = token_link(p);
+        token_info(p) = end_template_token;     /* put \.{\\endtemplate} at the end */
+
+        v_part(cur_align) = token_link(hold_token_head);
+    }
+    scanner_status = normal;
+
+    new_save_level(align_group);
+    if (every_cr_par != null)
+        begin_token_list(every_cr_par, every_cr_text);
+    align_peek();               /* look for \.{\\noalign} or \.{\\omit} */
+}
+
+
+@ The tricky part about alignments is getting the templates into the
+scanner at the right time, and recovering control when a row or column
+is finished.
+
+We usually begin a row after each \.{\\cr} has been sensed, unless that
+\.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates
+the alignment. The |align_peek| routine is used to look ahead and do
+the right thing; it either gets a new row started, or gets a \.{\\noalign}
+started, or finishes off the alignment.
+
+@c
+void align_peek(void)
+{
+  RESTART:
+    align_state = 1000000;
+    do {
+        get_x_or_protected();
+    } while (cur_cmd == spacer_cmd);
+    if (cur_cmd == no_align_cmd) {
+        scan_left_brace();
+        new_save_level(no_align_group);
+        if (cur_list.mode_field == -vmode)
+            normal_paragraph();
+    } else if (cur_cmd == right_brace_cmd) {
+        fin_align();
+    } else if ((cur_cmd == car_ret_cmd) && (cur_chr == cr_cr_code)) {
+        goto RESTART;           /* ignore \.{\\crcr} */
+    } else {
+        init_row();             /* start a new row */
+        init_col();             /* start a new column and replace what we peeked at */
+    }
+}
+
+
+@ The parameter to |init_span| is a pointer to the alignrecord where the
+next column or group of columns will begin. A new semantic level is
+entered, so that the columns will generate a list for subsequent packaging.
+
+@c
+static void init_span(pointer p)
+{
+    push_nest();
+    if (cur_list.mode_field == -hmode) {
+        space_factor_par = 1000;
+    } else {
+        prev_depth_par = ignore_depth;
+        normal_paragraph();
+    }
+    cur_span = p;
+}
+
+
+@ To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'),
+we enter a new semantic level, copy the first tabskip glue, and change
+from internal vertical mode to restricted horizontal mode or vice versa.
+The |space_factor| and |prev_depth| are not used on this semantic level,
+but we clear them to zero just to be tidy.
+
+@c
+void init_row(void)
+{
+    push_nest();
+    cur_list.mode_field = (-hmode - vmode) - cur_list.mode_field;
+    if (cur_list.mode_field == -hmode)
+        space_factor_par = 0;
+    else
+        prev_depth_par = 0;
+    tail_append(new_glue(preamble));
+    subtype(cur_list.tail_field) = tab_skip_code + 1;
+    cur_align = vlink(preamble);
+    cur_tail = cur_head;
+    cur_pre_tail = cur_pre_head;
+    init_span(cur_align);
+}
+
+
+@ When a column begins, we assume that |cur_cmd| is either |omit| or else
+the current token should be put back into the input until the \<u_j>
+template has been scanned.  (Note that |cur_cmd| might be |tab_mark| or
+|car_ret|.)  We also assume that |align_state| is approximately 1000000 at
+this time.  We remain in the same mode, and start the template if it is
+called for.
+
+@c
+void init_col(void)
+{
+    extra_info(cur_align) = cur_cmd;
+    if (cur_cmd == omit_cmd)
+        align_state = 0;
+    else {
+        back_input();
+        begin_token_list(u_part(cur_align), u_template);
+    }                           /* now |align_state=1000000| */
+}
+
+
+@ The scanner sets |align_state| to zero when the \<u_j> template ends. When
+a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|,
+the scanner activates the following code, which fires up the \<v_j> template.
+We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|,
+|span_code|, or a character code, depending on how the column text has ended.
+
+This part of the program had better not be activated when the preamble
+to another alignment is being scanned, or when no alignment preamble is active.
+
+@c
+void insert_vj_template(void)
+{
+    if ((scanner_status == aligning) || (cur_align == null))
+        fatal_error("(interwoven alignment preambles are not allowed)");
+    cur_cmd = extra_info(cur_align);
+    extra_info(cur_align) = cur_chr;
+    if (cur_cmd == omit_cmd)
+        begin_token_list(omit_template, v_template);
+    else
+        begin_token_list(v_part(cur_align), v_template);
+    align_state = 1000000;
+}
+
+/* Determine the stretch order */
+#define determine_stretch_order() do {              \
+        if (total_stretch[filll]!=0)  o=filll;      \
+        else if (total_stretch[fill]!=0)  o=fill;   \
+        else if (total_stretch[fil]!=0)  o=fil;     \
+        else if (total_stretch[sfi]!=0)  o=sfi;     \
+        else o=normal;                              \
+    } while (0)
+
+
+/* Determine the shrink order */
+#define determine_shrink_order() do {              \
+        if (total_shrink[filll]!=0)  o=filll;      \
+        else if (total_shrink[fill]!=0)  o=fill;   \
+        else if (total_shrink[fil]!=0)  o=fil;     \
+        else if (total_shrink[sfi]!=0)  o=sfi;     \
+        else o=normal;                              \
+    } while (0)
+
+
+
+@ When the |endv| command at the end of a \<v_j> template comes through the
+scanner, things really start to happen; and it is the |fin_col| routine
+that makes them happen. This routine returns |true| if a row as well as a
+column has been finished.
+
+@c
+boolean fin_col(void)
+{
+    pointer p;                  /* the alignrecord after the current one */
+    pointer q, r;               /* temporary pointers for list manipulation */
+    pointer s;                  /* a new span node */
+    pointer u;                  /* a new unset box */
+    scaled w;                   /* natural width */
+    unsigned char o;            /* order of infinity */
+    halfword n;                 /* span counter */
+    if (cur_align == null)
+        confusion("endv");
+    q = vlink(cur_align);
+    if (q == null)
+        confusion("endv");
+    if (align_state < 500000)
+        fatal_error("(interwoven alignment preambles are not allowed)");
+    p = vlink(q);
+    /* If the preamble list has been traversed, check that the row has ended */
+    if ((p == null) && (extra_info(cur_align) < cr_code)) {
+        if (cur_loop != null) {
+            /* Lengthen the preamble periodically */
+            r = new_node(align_record_node, 0);
+            vlink(q) = r;
+            p = vlink(q);       /* a new alignrecord */
+            span_ptr(p) = end_span;
+            width(p) = null_flag;
+            cur_loop = vlink(cur_loop);
+
+            /* Copy the templates from node |cur_loop| into node |p| */
+            q = hold_token_head;
+            r = u_part(cur_loop);
+            while (r != null) {
+                s = get_avail();
+                token_link(q) = s;
+                q = token_link(q);
+                token_info(q) = token_info(r);
+                r = token_link(r);
+            }
+            token_link(q) = null;
+            u_part(p) = token_link(hold_token_head);
+            q = hold_token_head;
+            r = v_part(cur_loop);
+            while (r != null) {
+                s = get_avail();
+                token_link(q) = s;
+                q = token_link(q);
+                token_info(q) = token_info(r);
+                r = token_link(r);
+            }
+            token_link(q) = null;
+            v_part(p) = token_link(hold_token_head);
+
+            cur_loop = vlink(cur_loop);
+            r = new_glue(cur_loop);
+            vlink(p) = r;
+        } else {
+            const char *hlp[] =
+                { "You have given more \\span or & marks than there were",
+                "in the preamble to the \\halign or \\valign now in progress.",
+                "So I'll assume that you meant to type \\cr instead.",
+                NULL
+            };
+            extra_info(cur_align) = cr_code;
+            tex_error("Extra alignment tab has been changed to \\cr", hlp);
+        }
+    }
+    if (extra_info(cur_align) != span_code) {
+        unsave();
+        new_save_level(align_group);
+        /* Package an unset box for the current column and record its width */
+        if (cur_list.mode_field == -hmode) {
+            adjust_tail = cur_tail;
+            pre_adjust_tail = cur_pre_tail;
+            u = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0,
+                               additional, align_set_group, -1, 0, 0);
+            w = width(u);
+            cur_tail = adjust_tail;
+            adjust_tail = null;
+            cur_pre_tail = pre_adjust_tail;
+            pre_adjust_tail = null;
+        } else {
+            u = filtered_vpackage(vlink(cur_list.head_field),
+                0, additional, 0, align_set_group, -1, 0, 0);
+            w = height(u);
+        }
+        n = min_quarterword;    /* this represents a span count of 1 */
+        if (cur_span != cur_align) {
+            /* Update width entry for spanned columns */
+            q = cur_span;
+            do {
+                incr(n);
+                q = vlink(vlink(q));
+            } while (q != cur_align);
+            if (n > max_quarterword)
+                confusion("too many spans");    /* this can happen, but won't */
+            q = cur_span;
+            while (span_span(span_ptr(q)) < n) {
+                q = span_ptr(q);
+            }
+            if (span_span(span_ptr(q)) > n) {
+                s = new_span_node(span_ptr(q), n, w);
+                span_ptr(q) = s;
+            } else if (width(span_ptr(q)) < w) {
+                width(span_ptr(q)) = w;
+            }
+
+        } else if (w > width(cur_align)) {
+            width(cur_align) = w;
+        }
+        type(u) = unset_node;
+        span_count(u) = (quarterword) n;
+        determine_stretch_order();
+        glue_order(u) = o;
+        glue_stretch(u) = total_stretch[o];
+        determine_shrink_order();
+        glue_sign(u) = o;
+        glue_shrink(u) = total_shrink[o];
+        pop_nest();
+        vlink(cur_list.tail_field) = u;
+        cur_list.tail_field = u;
+
+        /* Copy the tabskip glue between columns */
+        tail_append(new_glue(vlink(cur_align)));
+        subtype(cur_list.tail_field) = tab_skip_code + 1;
+
+        if (extra_info(cur_align) >= cr_code) {
+            return true;
+        }
+        init_span(p);
+    }
+    align_state = 1000000;
+    do {
+        get_x_or_protected();
+    } while (cur_cmd == spacer_cmd);
+    cur_align = p;
+    init_col();
+    return false;
+}
+
+
+
+@ A span node is a 3-word record containing |width|, |span_span|, and
+|span_ptr| fields. The |span_span| field indicates the number of
+spanned columns; the |span_ptr| field points to a span node for the same
+starting column, having a greater extent of spanning, or to
+|end_span|, which has the largest possible |span_span| field; the |width|
+field holds the largest natural width corresponding to a particular
+set of spanned columns.
+
+A list of the maximum widths so far, for spanned columns starting at a
+given column, begins with the |span_ptr| field of the alignrecord for
+that column. The code has to make sure that there is room for
+|span_ptr| in both the alignrecord and the span nodes, which is why
+|span_ptr| replaces |node_attr|.
+@^data structure assumptions@>
+
+The |new_span_node| function is defined in |texnodes.c|.
+
+@c
+#ifndef span_span
+#  define span_span(A) vlink((A)+1)     /* that is normally |alink| */
+#endif
+
+
+@ At the end of a row, we append an unset box to the current vlist (for
+\.{\\halign}) or the current hlist (for \.{\\valign}). This unset box
+contains the unset boxes for the columns, separated by the tabskip glue.
+Everything will be set later.
+
+@c
+void fin_row(void)
+{
+    pointer p;                  /* the new unset box */
+    if (cur_list.mode_field == -hmode) {
+        p = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0,
+                           additional, fin_row_group, -1, 0, 0);
+        pop_nest();
+        if (cur_pre_head != cur_pre_tail)
+            append_list(cur_pre_head, cur_pre_tail);
+        append_to_vlist(p,lua_key_index(alignment));
+        if (cur_head != cur_tail)
+            append_list(cur_head, cur_tail);
+    } else {
+        p = filtered_vpackage(vlink(cur_list.head_field),
+            0, additional, max_depth_par, fin_row_group, -1, 0, 0);
+        pop_nest();
+        vlink(cur_list.tail_field) = p;
+        cur_list.tail_field = p;
+        space_factor_par = 1000;
+    }
+    type(p) = unset_node;
+    glue_stretch(p) = 0;
+    if (every_cr_par != null)
+        begin_token_list(every_cr_par, every_cr_text);
+    align_peek();
+    /* note that |glue_shrink(p)=0| since |glue_shrink==shift_amount| */
+}
+
+
+@ Finally, we will reach the end of the alignment, and we can breathe a
+sigh of relief that memory hasn't overflowed. All the unset boxes will now be
+set so that the columns line up, taking due account of spanned columns.
+
+@c
+void fin_align(void)
+{
+    pointer p, q, r, s, u, rr;  /* registers for the list operations */
+    scaled t, w;                /* width of column */
+    scaled o;                   /* shift offset for unset boxes */
+    halfword n;                 /* matching span amount */
+    scaled rule_save;           /* temporary storage for |overfull_rule| */
+    halfword pd;                /* temporary storage for |prev_depth| */
+    halfword ng;                /*  temporary storage for |new_glue| */
+    if (cur_group != align_group)
+        confusion("align1");
+    unsave();                   /* that |align_group| was for individual entries */
+    if (cur_group != align_group)
+        confusion("align0");
+    unsave();                   /* that |align_group| was for the whole alignment */
+    if (nest[nest_ptr - 1].mode_field == mmode)
+        o = display_indent_par;
+    else
+        o = 0;
+    /* Go through the preamble list, determining the column widths and
+     * changing the alignrecords to dummy unset boxes
+     */
+
+/* It's time now to dismantle the preamble list and to compute the column
+widths. Let $w_{ij}$ be the maximum of the natural widths of all entries
+that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$
+contains $w_{ii}$ in its |width| field, and there is also a linked list of
+the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field;
+these span nodes contain the value $j-i+|min_quarterword|$ in their
+|link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which
+we regard as $-\infty$.
+
+The final column widths are defined by the formula
+$$w_j=\max_{1\L i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$
+where $t_k$ is the natural width of the tabskip glue between columns
+$k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range
+|1<=i<=j| (i.e., if every entry that involved column~|j| also involved
+column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after
+column~|j|.
+
+\TeX\ computes these values by using the following scheme: First $w_1=w_{11}$.
+Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$.
+Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$
+for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its
+value is changed to zero and so is the next tabskip.
+*/
+    q = vlink(preamble);
+    do {
+        flush_list(u_part(q));
+        flush_list(v_part(q));
+        p = vlink(vlink(q));
+        if (width(q) == null_flag) {
+            /* Nullify |width(q)| and the tabskip glue following this column */
+            width(q) = 0;
+            r = vlink(q);
+            reset_glue_to_zero(r); /* is a lready copy */
+        }
+        if (span_ptr(q) != end_span) {
+            /* Merge the widths in the span nodes of |q| with those of |p|,
+               destroying the span nodes of |q| */
+            /*
+               Merging of two span-node lists is a typical exercise in the manipulation of
+               linearly linked data structures. The essential invariant in the following
+               |repeat| loop is that we want to dispense with node |r|, in |q|'s list,
+               and |u| is its successor; all nodes of |p|'s list up to and including |s|
+               have been processed, and the successor of |s| matches |r| or precedes |r|
+               or follows |r|, according as |link(r)=n| or |link(r)>n| or |link(r)<n|.
+             */
+            t = width(q) + width(vlink(q));
+            r = span_ptr(q);
+            s = end_span;
+            span_ptr(s) = p;
+            n = min_quarterword + 1;
+            do {
+                width(r) = width(r) - t;
+                u = span_ptr(r);
+                while (span_span(r) > n) {
+                    s = span_ptr(s);
+                    n = span_span(span_ptr(s)) + 1;
+                }
+                if (span_span(r) < n) {
+                    span_ptr(r) = span_ptr(s);
+                    span_ptr(s) = r;
+                    decr(span_span(r));
+                    s = r;
+                } else {
+                    if (width(r) > width(span_ptr(s)))
+                        width(span_ptr(s)) = width(r);
+                    flush_node(r);
+                }
+                r = u;
+            } while (r != end_span);
+        }
+        type(q) = unset_node;
+        span_count(q) = min_quarterword;
+        height(q) = 0;
+        depth(q) = 0;
+        glue_order(q) = normal;
+        glue_sign(q) = normal;
+        glue_stretch(q) = 0;
+        glue_shrink(q) = 0;
+        q = p;
+    } while (q != null);
+
+    /* Package the preamble list, to determine the actual tabskip glue amounts,
+       and let |p| point to this prototype box */
+    /* Now the preamble list has been converted to a list of alternating unset
+       boxes and tabskip glue, where the box widths are equal to the final
+       column sizes. In case of \.{\\valign}, we change the widths to heights,
+       so that a correct error message will be produced if the alignment is
+       overfull or underfull.
+     */
+
+    decr(save_ptr);
+    pack_begin_line = -cur_list.ml_field;
+    if (cur_list.mode_field == -vmode) {
+        rule_save = overfull_rule_par;
+        overfull_rule_par = 0;      /* prevent rule from being packaged */
+        p = hpack(preamble, saved_value(0), saved_level(0), -1);
+        overfull_rule_par = rule_save;
+    } else {
+        q = vlink(preamble);
+        do {
+            height(q) = width(q);
+            width(q) = 0;
+            q = vlink(vlink(q));
+        } while (q != null);
+        p = filtered_vpackage(preamble,
+            saved_value(0), saved_level(0), max_depth_par, preamble_group, -1, 0, 0);
+        q = vlink(preamble);
+        do {
+            width(q) = height(q);
+            height(q) = 0;
+            q = vlink(vlink(q));
+        } while (q != null);
+    }
+    pack_begin_line = 0;
+
+    /* Set the glue in all the unset boxes of the current list */
+    q = vlink(cur_list.head_field);
+    s = cur_list.head_field;
+    while (q != null) {
+        if (!is_char_node(q)) {
+            if (type(q) == unset_node) {
+                /* Set the unset box |q| and the unset boxes in it */
+                /* The unset box |q| represents a row that contains one or more unset boxes,
+                   depending on how soon \.{\\cr} occurred in that row. */
+
+                if (cur_list.mode_field == -vmode) {
+                    type(q) = hlist_node;
+                    subtype(q) = align_row_list;
+                    width(q) = width(p);
+                } else {
+                    type(q) = vlist_node;
+                    subtype(q) = align_row_list;
+                    height(q) = height(p);
+                }
+                glue_order(q) = glue_order(p);
+                glue_sign(q) = glue_sign(p);
+                glue_set(q) = glue_set(p);
+                shift_amount(q) = o;
+                r = vlink(list_ptr(q));
+                assert (type(r) == unset_node);
+                s = vlink(list_ptr(p));
+                do {
+                    /* Set the glue in node |r| and change it from an unset node */
+                    /* A box made from spanned columns will be followed by tabskip glue nodes and
+                       by empty boxes as if there were no spanning. This permits perfect alignment
+                       of subsequent entries, and it prevents values that depend on floating point
+                       arithmetic from entering into the dimensions of any boxes.
+                     */
+                    n = span_count(r);
+                    t = width(s);
+                    w = t;
+                    u = hold_head;
+                    while (n > min_quarterword) {
+                        decr(n);
+                        /* Append tabskip glue and an empty box to list |u|,
+                           and update |s| and |t| as the prototype nodes are passed */
+
+                        s = vlink(s);
+                        ng = new_glue(s);
+                        vlink(u) = ng;
+                        u = vlink(u);
+                        subtype(u) = tab_skip_code + 1;
+                        t = t + width(s);
+                        if (glue_sign(p) == stretching) {
+                            if (stretch_order(s) == glue_order(p))
+                                t = t + round(float_cast(glue_set(p)) * float_cast(stretch(s)));
+                        } else if (glue_sign(p) == shrinking) {
+                            if (shrink_order(s) == glue_order(p))
+                                t = t - round(float_cast(glue_set(p)) * float_cast(shrink(s)));
+                        }
+                        s = vlink(s);
+                        rr = new_null_box();
+                        vlink(u) = rr;
+                        u = vlink(u);
+                        t = t + width(s);
+                        subtype(u) = align_cell_list;
+                        if (cur_list.mode_field == -vmode) {
+                            width(u) = width(s);
+                        } else {
+                            type(u) = vlist_node;
+                            height(u) = width(s);
+                        }
+
+                    }
+                    if (cur_list.mode_field == -vmode) {
+                        /* Make the unset node |r| into an |hlist_node| of width |w|,
+                           setting the glue as if the width were |t| */
+
+                        height(r) = height(q);
+                        depth(r) = depth(q);
+                        if (t == width(r)) {
+                            glue_sign(r) = normal;
+                            glue_order(r) = normal;
+                            set_glue_ratio_zero(glue_set(r));
+                        } else if (t > width(r)) {
+                            glue_sign(r) = stretching;
+                            if (glue_stretch(r) == 0)
+                                set_glue_ratio_zero(glue_set(r));
+                            else
+                                glue_set(r) =
+                                    unfloat((double) (t - width(r)) /
+                                            glue_stretch(r));
+                        } else {
+                            glue_order(r) = glue_sign(r);
+                            glue_sign(r) = shrinking;
+                            if (glue_shrink(r) == 0)
+                                set_glue_ratio_zero(glue_set(r));
+                            else if ((glue_order(r) == normal)
+                                     && (width(r) - t > glue_shrink(r)))
+                                set_glue_ratio_one(glue_set(r));
+                            else
+                                glue_set(r) =
+                                    unfloat((double) (width(r) - t) /
+                                            glue_shrink(r));
+                        }
+                        width(r) = w;
+                        type(r) = hlist_node;
+                        subtype(r) = align_cell_list;
+
+                    } else {
+                        /* Make the unset node |r| into a |vlist_node| of height |w|,
+                           setting the glue as if the height were |t| */
+
+                        width(r) = width(q);
+                        if (t == height(r)) {
+                            glue_sign(r) = normal;
+                            glue_order(r) = normal;
+                            set_glue_ratio_zero(glue_set(r));
+                        } else if (t > height(r)) {
+                            glue_sign(r) = stretching;
+                            if (glue_stretch(r) == 0)
+                                set_glue_ratio_zero(glue_set(r));
+                            else
+                                glue_set(r) =
+                                    unfloat((t - height(r)) / glue_stretch(r));
+                        } else {
+                            glue_order(r) = glue_sign(r);
+                            glue_sign(r) = shrinking;
+                            if (glue_shrink(r) == 0)
+                                set_glue_ratio_zero(glue_set(r));
+                            else if ((glue_order(r) == normal)
+                                     && (height(r) - t > glue_shrink(r)))
+                                set_glue_ratio_one(glue_set(r));
+                            else
+                                glue_set(r) =
+                                    unfloat((height(r) - t) / glue_shrink(r));
+                        }
+                        height(r) = w;
+                        type(r) = vlist_node;
+                        subtype(r) = align_cell_list;
+
+                    }
+                    /* subtype(r) = 0; */
+                    shift_amount(r) = 0;
+                    if (u != hold_head) {       /* append blank boxes to account for spanned nodes */
+                        vlink(u) = vlink(r);
+                        vlink(r) = vlink(hold_head);
+                        r = u;
+                    }
+
+                    r = vlink(vlink(r));
+                    s = vlink(vlink(s));
+                } while (r != null);
+
+            } else if (type(q) == rule_node) {
+                /* Make the running dimensions in rule |q| extend to the
+                   boundaries of the alignment */
+                if (is_running(width(q)))
+                    width(q) = width(p);
+                if (is_running(height(q)))
+                    height(q) = height(p);
+                if (is_running(depth(q)))
+                    depth(q) = depth(p);
+                if (o != 0) {
+                    r = vlink(q);
+                    vlink(q) = null;
+                    q = hpack(q, 0, additional, -1);
+                    shift_amount(q) = o;
+                    subtype(q) = align_cell_list;
+                    vlink(q) = r;
+                    vlink(s) = q;
+                }
+            }
+        }
+        s = q;
+        q = vlink(q);
+    }
+    flush_node_list(p);
+    pop_alignment();
+    /* Insert the current list into its environment */
+    /* We now have a completed alignment, in the list that starts at |cur_list.head_field|
+       and ends at |cur_list.tail_field|. This list will be merged with the one that encloses
+       it. (In case the enclosing mode is |mmode|, for displayed formulas,
+       we will need to insert glue before and after the display; that part of the
+       program will be deferred until we're more familiar with such operations.)
+     */
+    pd = prev_depth_par;
+    p = vlink(cur_list.head_field);
+    q = cur_list.tail_field;
+    pop_nest();
+    if (cur_list.mode_field == mmode) {
+        finish_display_alignment(p, q, pd);
+    } else {
+        prev_depth_par = pd; /* aux:=aux_save; */
+        vlink(cur_list.tail_field) = p;
+        if (p != null)
+            cur_list.tail_field = q;
+        if (cur_list.mode_field == vmode) {
+            if (!output_active)
+                lua_node_filter_s(buildpage_filter_callback,lua_key_index(alignment));
+            build_page();
+        }
+    }
+}
+
+@ The token list |omit_template| just referred to is a constant token
+list that contains the special control sequence \.{\\endtemplate} only.
+
+@c
+void initialize_alignments(void)
+{
+    token_info(omit_template) = end_template_token;     /* |link(omit_template)=null| */
+    span_span(end_span) = max_quarterword + 1;
+    span_ptr(end_span) = null;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/arithmetic.c
+++ /dev/null
@@ -1,815 +0,0 @@
-/*
-
-arithmetic.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-The principal computations performed by \TeX\ are done entirely in terms of
-integers less than $2^{31}$ in magnitude; and divisions are done only when both
-dividend and divisor are nonnegative. Thus, the arithmetic specified in this
-program can be carried out in exactly the same way on a wide variety of
-computers, including some small ones. Why? Because the arithmetic calculations
-need to be spelled out precisely in order to guarantee that \TeX\ will produce
-identical output on different machines. If some quantities were rounded
-differently in different implementations, we would find that line breaks and even
-page breaks might occur in different places. Hence the arithmetic of \TeX\ has
-been designed with care, and systems that claim to be implementations of \TeX82
-should follow precisely the @:TeX82}{\TeX82@> calculations as they appear in the
-present program.
-
-Actually there are three places where \TeX\ uses |div| with a possibly negative
-numerator. These are harmless; see |div| in the index. Also if the user sets the
-\.{\\time} or the \.{\\year} to a negative value, some diagnostic information
-will involve negative-numerator division. The same remarks apply for |mod| as
-well as for |div|.
-
-Here is a routine that calculates half of an integer, using an unambiguous
-convention with respect to signed odd numbers.
-
-*/
-
-int half(int x)
-{
-    if (odd(x))
-        return ((x + 1) / 2);
-    else
-        return (x / 2);
-}
-
-/*tex
-
-The following function is used to create a scaled integer from a given decimal
-fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is
-given in |dig[i]|, and the calculation produces a correctly rounded result.
-
-*/
-
-scaled round_decimals(int k)
-{
-    int a = 0;
-    while (k-- > 0) {
-        a = (a + dig[k] * two) / 10;
-    }
-    return ((a + 1) / 2);
-}
-
-/*tex
-
-Conversely, here is a procedure analogous to |print_int|. If the output of this
-procedure is subsequently read by \TeX\ and converted by the |round_decimals|
-routine above, it turns out that the original value will be reproduced exactly;
-the ``simplest'' such decimal number is output, but there is always at least one
-digit following the decimal point.
-
-The invariant relation in the \&{repeat} loop is that a sequence of decimal
-digits yet to be printed will yield the original number if and only if they form
-a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$. We can stop if and only
-if $f=0$ satisfies this condition; the loop will terminate before $s$ can
-possibly become zero.
-
-The next one prints a scaled real, rounded to five digits.
-
-*/
-
-void print_scaled(scaled s)
-{
-    /*tex The amount of allowable inaccuracy: */
-    scaled delta;
-    char buffer[20];
-    int i = 0;
-    if (s < 0) {
-        /*tex Print the sign, if negative. */
-        print_char('-');
-        negate(s);
-    }
-    /*tex Print the integer part. */
-    print_int(s / unity);
-    buffer[i++] = '.';
-    s = 10 * (s % unity) + 5;
-    delta = 10;
-    do {
-        if (delta > unity) {
-            /*tex Round the last digit. */
-            s = s + 0100000 - 50000;
-        }
-        buffer[i++] = '0' + (s / unity);
-        s = 10 * (s % unity);
-        delta = delta * 10;
-    } while (s > delta);
-    buffer[i++] = '\0';
-    tprint(buffer);
-}
-
-/*tex
-
-Physical sizes that a \TeX\ user specifies for portions of documents are
-represented internally as scaled points. Thus, if we define an `sp' (scaled
-@^sp@> point) as a unit equal to $2^{-16}$ printer's points, every dimension
-inside of \TeX\ is an integer number of sp. There are exactly 4,736,286.72 sp per
-inch. Users are not allowed to specify dimensions larger than $2^{30}-1$ sp,
-which is a distance of about 18.892 feet (5.7583 meters); two such quantities can
-be added without overflow on a 32-bit computer.
-
-The present implementation of \TeX\ does not check for overflow when @^overflow
-in arithmetic@> dimensions are added or subtracted. This could be done by
-inserting a few dozen tests of the form `\ignorespaces|if x>=010000000000 then
-@t\\{report\_overflow}@>|', but the chance of overflow is so remote that such
-tests do not seem worthwhile.
-
-\TeX\ needs to do only a few arithmetic operations on scaled quantities, other
-than addition and subtraction, and the following subroutines do most of the work.
-A single computation might use several subroutine calls, and it is desirable to
-avoid producing multiple error messages in case of arithmetic overflow; so the
-routines set the global variable |arith_error| to |true| instead of reporting
-errors directly to the user. Another global variable, |tex_remainder|, holds the
-remainder after a division.
-
-*/
-
-/*tex Has arithmetic overflow occurred recently? */
-
-boolean arith_error;
-
-/*tex The amount subtracted to get an exact division. */
-
-scaled tex_remainder;
-
-/*tex
-
- The first arithmetical subroutine we need computes $nx+y$, where |x|
-and~|y| are |scaled| and |n| is an integer. We will also use it to
-multiply integers.
-
-*/
-
-scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer)
-{
-    if (n == 0)
-        return y;
-    if (n < 0) {
-        negate(x);
-        negate(n);
-    }
-    if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) {
-        return (n * x + y);
-    } else {
-        arith_error = true;
-        return 0;
-    }
-}
-
-/*tex
-
-We also need to divide scaled dimensions by integers.
-
-*/
-
-scaled x_over_n(scaled x, int n)
-{
-    /*tex Should |tex_remainder| be negated? */
-    boolean negative = false;
-    if (n == 0) {
-        arith_error = true;
-        tex_remainder = x;
-        return 0;
-    } else {
-        if (n < 0) {
-            negate(x);
-            negate(n);
-            negative = true;
-        }
-        if (x >= 0) {
-            tex_remainder = x % n;
-            if (negative)
-                negate(tex_remainder);
-            return (x / n);
-        } else {
-            tex_remainder = -((-x) % n);
-            if (negative)
-                negate(tex_remainder);
-            return (-((-x) / n));
-        }
-    }
-}
-
-/*tex
-
-Then comes the multiplication of a scaled number by a fraction |n/d|, where |n|
-and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is positive. It would
-be too dangerous to multiply by~|n| and then divide by~|d|, in separate
-operations, since overflow might well occur; and it would be too inaccurate to
-divide by |d| and then multiply by |n|. Hence this subroutine simulates
-1.5-precision arithmetic.
-
-*/
-
-scaled xn_over_d(scaled x, int n, int d)
-{
-    nonnegative_integer t, u, v, xx, dd;
-    boolean positive = true;
-    if (x < 0) {
-        negate(x);
-        positive = false;
-    }
-    xx = (nonnegative_integer) x;
-    dd = (nonnegative_integer) d;
-    t = ((xx % 0100000) * (nonnegative_integer) n);
-    u = ((xx / 0100000) * (nonnegative_integer) n + (t / 0100000));
-    v = (u % dd) * 0100000 + (t % 0100000);
-    if (u / dd >= 0100000)
-        arith_error = true;
-    else
-        u = 0100000 * (u / dd) + (v / dd);
-    if (positive) {
-        tex_remainder = (int) (v % dd);
-        return (scaled) u;
-    } else {
-        /*tex The casts are for ms cl. */
-        tex_remainder = -(int) (v % dd);
-        return -(scaled) (u);
-    }
-}
-
-/*tex
-
-The next subroutine is used to compute the ``badness'' of glue, when a total~|t|
-is supposed to be made from amounts that sum to~|s|. According to {\sl The \TeX
-book}, the badness of this situation is $100(t/s)^3$; however, badness is simply
-a heuristic, so we need not squeeze out the last drop of accuracy when computing
-it. All we really want is an approximation that has similar properties.
-@:TeXbook}{\sl The \TeX book@>
-
-The actual method used to compute the badness is easier to read from the program
-than to describe in words. It produces an integer value that is a reasonably
-close approximation to $100(t/s)^3$, and all implementations of \TeX\ should use
-precisely this method. Any badness of $2^{13}$ or more is treated as infinitely
-bad, and represented by 10000.
-
-It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s)
->= badness(t,s+1)|}.$$ The badness function defined here is capable of computing
-at most 1095 distinct values, but that is plenty.
-
-*/
-
-halfword badness(scaled t, scaled s)
-{
-    /*tex Approximation to $\alpha t/s$, where $\alpha^3\approx 100\cdot2^{18}$ */
-    int r;
-    if (t == 0) {
-        return 0;
-    } else if (s <= 0) {
-        return inf_bad;
-    } else {
-        /*tex $297^3=99.94\times2^{18}$ */
-        if (t <= 7230584) {
-            r = (t * 297) / s;
-        } else if (s >= 1663497) {
-            r = t / (s / 297);
-        } else {
-            r = t;
-        }
-        if (r > 1290) {
-            /*tex $1290^3<2^{31}<1291^3$ */
-            return inf_bad;
-        } else {
-            /*tex This is $r^3/2^{18}$, rounded to the nearest integer. */
-            return ((r * r * r + 0400000) / 01000000);
-        }
-    }
-}
-
-/*tex
-
-When \TeX\ ``packages'' a list into a box, it needs to calculate the
-proportionality ratio by which the glue inside the box should stretch or shrink.
-This calculation does not affect \TeX's decision making, so the precise details
-of rounding, etc., in the glue calculation are not of critical importance for the
-consistency of results on different computers.
-
-We shall use the type |glue_ratio| for such proportionality ratios. A glue ratio
-should take the same amount of memory as an |integer| (usually 32 bits) if it is
-to blend smoothly with \TeX's other data structures. Thus |glue_ratio| should be
-equivalent to |short_real| in some implementations of PASCAL. Alternatively, it
-is possible to deal with glue ratios using nothing but fixed-point arithmetic;
-see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the routines cited there must
-be modified to allow negative glue ratios.) @^system dependencies@>
-
-*/
-
-/*
-
-This section is (almost) straight from MetaPost. I (Taco) had to change the types
-(use |integer| instead of |fraction|), but that should not have any influence on
-the actual calculations (the original comments refer to quantities like
-|fraction_four| ($2^{30}$), and that is the same as the numeric representation of
-|max_dimen|).
-
-I've copied the low-level variables and routines that are needed, but only those
-(e.g. |m_log|), not the accompanying ones like |m_exp|. Most of the following
-low-level numeric routines are only needed within the calculation of |norm_rand|.
-I've been forced to rename |make_fraction| to |make_frac| because TeX already has
-a routine by that name with a wholly different function (it creates a
-|fraction_noad| for math typesetting)
-
-And now let's complete our collection of numeric utility routines by considering
-random number generation. \MP{} generates pseudo-random numbers with the additive
-scheme recommended in Section 3.6 of {\sl The Art of Computer Programming};
-however, the results are random fractions between 0 and |fraction_one-1|,
-inclusive.
-
-There's an auxiliary array |randoms| that contains 55 pseudo-random fractions.
-Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$, we generate batches
-of 55 new $x_n$'s at a time by calling |new_randoms|. The global variable
-|j_random| tells which element has most recently been consumed.
-
-*/
-
-/*tex The last 55 random values generated: */
-
-static int randoms[55];
-
-/*tex The number of unused |randoms|: */
-
-static int j_random;
-
-/*tex The default random seed: */
-
-scaled random_seed;
-
-/*tex A small bit of \METAPOST\ is needed. */
-
-#define fraction_half  01000000000  /* $2^{27}  $, represents 0.50000000 */
-#define fraction_one   02000000000  /* $2^{28}  $, represents 1.00000000 */
-#define fraction_four 010000000000  /* $2^{30}  $, represents 4.00000000 */
-#define el_gordo      017777777777  /* $2^{31}-1$, the largest value that \MP\ likes */
-
-/*tex
-
-The |make_frac| routine produces the |fraction| equivalent of |p/q|, given
-integers |p| and~|q|; it computes the integer
-$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are positive. If |p| and
-|q| are both of the same scaled type |t|, the ``type relation''
-|make_frac(t,t)=fraction| is valid; and it's also possible to use the subroutine
-``backwards,'' using the relation |make_frac(t,fraction)=t| between scaled types.
-
-If the result would have magnitude $2^{31}$ or more, |make_frac| sets
-|arith_error:=true|. Most of \MP's internal computations have been designed to
-avoid this sort of error.
-
-If this subroutine were programmed in assembly language on a typical machine, we
-could simply compute |(@t$2^{28}$@>*p)div q|, since a double-precision product
-can often be input to a fixed-point division instruction. But when we are
-restricted to PASCAL arithmetic it is necessary either to resort to
-multiple-precision maneuvering or to use a simple but slow iteration. The
-multiple-precision technique would be about three times faster than the code
-adopted here, but it would be comparatively long and tricky, involving about
-sixteen additional multiplications and divisions.
-
-This operation is part of \MP's ``inner loop''; indeed, it will consume nearly
-10\%! of the running time (exclusive of input and output) if the code below is
-left unchanged. A machine-dependent recoding will therefore make \MP\ run faster.
-The present implementation is highly portable, but slow; it avoids multiplication
-and division except in the initial stage. System wizards should be careful to
-replace it with a routine that is guaranteed to produce identical results in all
-cases. @^system dependencies@>
-
-As noted below, a few more routines should also be replaced by machine-dependent
-code, for efficiency. But when a procedure is not part of the ``inner loop,''
-such changes aren't advisable; simplicity and robustness are preferable to
-trickery, unless the cost is too high.
-
-*/
-
-static int make_frac(int p, int q)
-{
-    /*tex The fraction bits, with a leading 1 bit: */
-    int f;
-    /*tex The integer part of $\vert p/q\vert$: */
-    int n;
-    /*tex Disables certain compiler optimizations: */
-    register int be_careful;
-    /*tex Should the result be negated? */
-    boolean negative = false;
-    if (p < 0) {
-        negate(p);
-        negative = true;
-    }
-    if (q <= 0) {
-        negate(q);
-        negative = !negative;
-    }
-    n = p / q;
-    p = p % q;
-    if (n >= 8) {
-        arith_error = true;
-        if (negative)
-            return (-el_gordo);
-        else
-            return el_gordo;
-    } else {
-        n = (n - 1) * fraction_one;
-        /*tex_remainder
-
-            Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$. The |repeat| loop
-            here preserves the following invariant relations between |f|, |p|,
-            and~|q|: (i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an
-            integer and $p_0$ is the original value of~$p$.
-
-            Notice that the computation specifies |(p-q)+p| instead of |(p+p)-q|,
-            because the latter could overflow. Let us hope that optimizing
-            compilers do not miss this point; a special variable |be_careful| is
-            used to emphasize the necessary order of computation. Optimizing
-            compilers should keep |be_careful| in a register, not store it in
-            memory.
-
-        */
-        f = 1;
-        do {
-            be_careful = p - q;
-            p = be_careful + p;
-            if (p >= 0)
-                f = f + f + 1;
-            else {
-                f += f;
-                p = p + q;
-            }
-        } while (f < fraction_one);
-        be_careful = p - q;
-        if (be_careful + p >= 0)
-            incr(f);
-
-        if (negative)
-            return (-(f + n));
-        else
-            return (f + n);
-    }
-}
-
-static int take_frac(int q, int f)
-{
-    /*tex The fraction so far: */
-    int p;
-    /*tex Additional multiple of $q$: */
-    int n;
-    /*tex Disables certain compiler optimizations. */
-    register int be_careful;
-    /*tex Should the result be negated? */
-    boolean negative = false;
-    /*tex Reduce to the case that |f>=0| and |q>0|. */
-    if (f < 0) {
-        negate(f);
-        negative = true;
-    }
-    if (q < 0) {
-        negate(q);
-        negative = !negative;
-    }
-    if (f < fraction_one) {
-        n = 0;
-    } else {
-        n = f / fraction_one;
-        f = f % fraction_one;
-        if (q <= el_gordo / n) {
-            n = n * q;
-        } else {
-            arith_error = true;
-            n = el_gordo;
-        }
-    }
-    f = f + fraction_one;
-    /*tex
-
-        Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$. The invariant relations
-        in this case are (i)~$\lfloor(qf+p)/2^k\rfloor =\lfloor
-        qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and $f_0$ is the
-        original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
-
-        Here |p| becomes $2^{27}$; the invariants hold now with $k=28$:
-
-    */
-    p = fraction_half;
-    if (q < fraction_four) {
-        do {
-            if (odd(f))
-                p = halfp(p + q);
-            else
-                p = halfp(p);
-            f = halfp(f);
-        } while (f != 1);
-    } else {
-        do {
-            if (odd(f))
-                p = p + halfp(q - p);
-            else
-                p = halfp(p);
-            f = halfp(f);
-        } while (f != 1);
-    }
-    be_careful = n - el_gordo;
-    if (be_careful + p > 0) {
-        arith_error = true;
-        n = el_gordo - p;
-    }
-    if (negative)
-        return (-(n + p));
-    else
-        return (n + p);
-}
-
-/*tex
-
-The subroutines for logarithm and exponential involve two tables. The first is
-simple: |two_to_the[k]| equals $2^k$. The second involves a bit more calculation,
-which the author claims to have done correctly: |spec_log[k]| is $2^{27}$ times
-$\ln\bigl(1/(1-2^{-k})\bigr)= 2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$,
-rounded to the nearest integer.
-
-*/
-
-/*tex The powers of two: */
-
-static int two_to_the[31];
-
-/*tex Special logarithms: */
-
-static int spec_log[29];
-
-void initialize_arithmetic(void)
-{
-    int k;
-    two_to_the[0] = 1;
-    for (k = 1; k <= 30; k++) {
-        two_to_the[k] = 2 * two_to_the[k - 1];
-    }
-    spec_log [1] = 93032640;
-    spec_log [2] = 38612034;
-    spec_log [3] = 17922280;
-    spec_log [4] =  8662214;
-    spec_log [5] =  4261238;
-    spec_log [6] =  2113709;
-    spec_log [7] =  1052693;
-    spec_log [8] =   525315;
-    spec_log [9] =   262400;
-    spec_log[10] =   131136;
-    spec_log[11] =    65552;
-    spec_log[12] =    32772;
-    spec_log[13] =    16385;
-    for (k = 14; k <= 27; k++) {
-        spec_log[k] = two_to_the[27 - k];
-    }
-    spec_log[28] = 1;
-}
-
-static int m_log(int x)
-{
-    /*tex Auxiliary registers: */
-    int y, z;
-    /*tex Iteration counter: */
-    int k;
-    if (x <= 0) {
-        /*tex Handle non-positive logarithm. */
-        print_err("Logarithm of ");
-        print_scaled(x);
-        tprint(" has been replaced by 0");
-        help2(
-            "Since I don't take logs of non-positive numbers,",
-            "I'm zeroing this one. Proceed, with fingers crossed."
-        );
-        error();
-        return 0;
-    } else {
-        /*tex $14\times2^{27}\ln2\approx1302456956.421063$ */
-        y = 1302456956 + 4 - 100;
-        /*tex $2^{16}\times .421063\approx 27595$ */
-        z = 27595 + 6553600;
-        while (x < fraction_four) {
-            x += x;
-            /*tex $2^{27}\ln2\approx 93032639.74436163$ */
-            y = y - 93032639;
-            /*tex $2^{16}\times.74436163\approx 48782$ */
-            z = z - 48782;
-        }
-
-        y = y + (z / unity);
-        k = 2;
-        while (x > fraction_four + 4) {
-            /*tex
-                Increase |k| until |x| can be multiplied by a factor of $2^{-k}$,
-                and adjust $y$ accordingly. Here $z=\lceil x/2^k\rceil$.
-            */
-            z = ((x - 1) / two_to_the[k]) + 1;
-            while (x < fraction_four + z) {
-                z = halfp(z + 1);
-                k = k + 1;
-            }
-            y = y + spec_log[k];
-            x = x - z;
-        }
-        return (y / 8);
-    }
-}
-
-/*tex
-
-The following somewhat different subroutine tests rigorously if $ab$ is greater
-than, equal to, or less than~$cd$, given integers $(a,b,c,d)$. In most cases a
-quick decision is reached. The result is $+1$, 0, or~$-1$ in the three respective
-cases.
-
-*/
-
-static int ab_vs_cd(int a, int b, int c, int d)
-{
-    int q, r;
-    /*tex Reduce to the case that |a,c>=0| and |b,d>0|. */
-    if (a < 0) {
-        negate(a);
-        negate(b);
-    }
-    if (c < 0) {
-        negate(c);
-        negate(d);
-    }
-    if (d <= 0) {
-        if (b >= 0)
-            return (((a == 0 || b == 0) && (c == 0 || d == 0)) ? 0 : 1);
-        if (d == 0)
-            return (a == 0 ? 0 : -1);
-        q = a;
-        a = c;
-        c = q;
-        q = -b;
-        b = -d;
-        d = q;
-    } else if (b <= 0) {
-        if (b < 0 && a > 0)
-            return -1;
-        return (c == 0 ? 0 : -1);
-    }
-    while (1) {
-        q = a / d;
-        r = c / b;
-        if (q != r)
-            return (q > r ? 1 : -1);
-        q = a % d;
-        r = c % b;
-        if (r == 0)
-            return (q == 0 ? 0 : 1);
-        if (q == 0)
-            return -1;
-        a = b;
-        b = q;
-        c = d;
-        d = r;
-        /*tex Now |a>d>0| and |c>b>0|. */
-    }
-}
-
-/*tex
-
-To consume a random integer, the program below will say `|next_random|' and then
-it will fetch |randoms[j_random]|.
-
-*/
-
-#define next_random() do { \
-    if (j_random==0) \
-        new_randoms(); \
-    else \
-        decr(j_random); \
-} while (0)
-
-static void new_randoms(void)
-{
-    /*tex The index into |randoms|. */
-    int k;
-    /*tex The accumulator. */
-    int x;
-    for (k = 0; k <= 23; k++) {
-        x = randoms[k] - randoms[k + 31];
-        if (x < 0)
-            x = x + fraction_one;
-        randoms[k] = x;
-    }
-    for (k = 24; k <= 54; k++) {
-        x = randoms[k] - randoms[k - 24];
-        if (x < 0)
-            x = x + fraction_one;
-        randoms[k] = x;
-    }
-    j_random = 54;
-}
-
-/*tex
-
-To initialize the |randoms| table, we call the following routine.
-
-*/
-
-void init_randoms(int seed)
-{
-    /*tex Three more or less random integers. */
-    int j, jj, k;
-    /*tex The index into |randoms|. */
-    int i;
-    j = abs(seed);
-    while (j >= fraction_one)
-        j = halfp(j);
-    k = 1;
-    for (i = 0; i <= 54; i++) {
-        jj = k;
-        k = j - k;
-        j = jj;
-        if (k < 0)
-            k = k + fraction_one;
-        randoms[(i * 21) % 55] = j;
-    }
-    /*tex We ``warm up'' the array. */
-    new_randoms();
-    new_randoms();
-    new_randoms();
-}
-
-/*tex
-
-To produce a uniform random number in the range |0<=u<x| or |0>=u>x| or |0=u=x|,
-given a |scaled| value~|x|, we proceed as shown here.
-
-Note that the call of |take_frac| will produce the values 0 and~|x| with about
-half the probability that it will produce any other particular values between 0
-and~|x|, because it rounds its answers.
-
-*/
-
-int unif_rand(int x)
-{
-    int y;
-    next_random();
-    y = take_frac(abs(x), randoms[j_random]);
-    if (y == abs(x))
-        return 0;
-    else if (x > 0)
-        return y;
-    else
-        return -y;
-}
-
-/*tex
-
-Finally, a normal deviate with mean zero and unit standard deviation can readily
-be obtained with the ratio method (Algorithm 3.4.1R in {\sl The Art of Computer
-Programming\/}.
-
-*/
-
-int norm_rand(void)
-{
-    /*tex What the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$. */
-    int x, u, l;
-    do {
-        do {
-            next_random();
-            x = take_frac(112429, randoms[j_random] - fraction_half);
-            /*tex Which is $2^{16}\sqrt{8/e}\approx 112428.82793$. */
-            next_random();
-            u = randoms[j_random];
-        } while (abs(x) >= u);
-        x = make_frac(x, u);
-        /*tex More fuzzyness: $2^{24}\cdot12\ln2\approx139548959.6165$. */
-        l = 139548960 - m_log(u);
-    } while (ab_vs_cd(1024, l, x, x) < 0);
-    return x;
-}
-
-/*tex
-
-This function could also be expressed as a macro, but it is a useful breakpoint
-for debugging.
-
-*/
-
-int fix_int(int val, int min, int max)
-{
-    return (val < min ? min : (val > max ? max : val));
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/arithmetic.w
@@ -0,0 +1,735 @@
+% arithmetic.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\MP{MetaPost}
+
+@ @c
+
+
+#include "ptexlib.h"
+
+@ The principal computations performed by \TeX\ are done entirely in terms of
+integers less than $2^{31}$ in magnitude; and divisions are done only when both
+dividend and divisor are nonnegative. Thus, the arithmetic specified in this
+program can be carried out in exactly the same way on a wide variety of
+computers, including some small ones. Why? Because the arithmetic
+calculations need to be spelled out precisely in order to guarantee that
+\TeX\ will produce identical output on different machines. If some
+quantities were rounded differently in different implementations, we would
+find that line breaks and even page breaks might occur in different places.
+Hence the arithmetic of \TeX\ has been designed with care, and systems that
+claim to be implementations of \TeX82 should follow precisely the
+@:TeX82}{\TeX82@>
+calculations as they appear in the present program.
+
+(Actually there are three places where \TeX\ uses |div| with a possibly negative
+numerator. These are harmless; see |div| in the index. Also if the user
+sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic
+information will involve negative-numerator division. The same remarks
+apply for |mod| as well as for |div|.)
+
+Here is a routine that calculates half of an integer, using an
+unambiguous convention with respect to signed odd numbers.
+
+@c
+int half(int x)
+{
+    if (odd(x))
+        return ((x + 1) / 2);
+    else
+        return (x / 2);
+}
+
+
+@ The following function is used to create a scaled integer from a given decimal
+fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is
+given in |dig[i]|, and the calculation produces a correctly rounded result.
+
+@c
+scaled round_decimals(int k)
+{                               /* converts a decimal fraction */
+    int a;                      /* the accumulator */
+    a = 0;
+    while (k-- > 0) {
+        a = (a + dig[k] * two) / 10;
+    }
+    return ((a + 1) / 2);
+}
+
+
+@ Conversely, here is a procedure analogous to |print_int|. If the output
+of this procedure is subsequently read by \TeX\ and converted by the
+|round_decimals| routine above, it turns out that the original value will
+be reproduced exactly; the ``simplest'' such decimal number is output,
+but there is always at least one digit following the decimal point.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@c
+void print_scaled(scaled s)
+{                               /* prints scaled real, rounded to five digits */
+    scaled delta;               /* amount of allowable inaccuracy */
+    char buffer[20];
+    int i = 0;
+    if (s < 0) {
+        print_char('-');
+        negate(s);              /* print the sign, if negative */
+    }
+    print_int(s / unity);       /* print the integer part */
+    buffer[i++] = '.';
+    s = 10 * (s % unity) + 5;
+    delta = 10;
+    do {
+        if (delta > unity)
+            s = s + 0100000 - 50000;    /* round the last digit */
+        buffer[i++] = '0' + (s / unity);
+        s = 10 * (s % unity);
+        delta = delta * 10;
+    } while (s > delta);
+    buffer[i++] = '\0';
+    tprint(buffer);
+}
+
+@ Physical sizes that a \TeX\ user specifies for portions of documents are
+represented internally as scaled points. Thus, if we define an `sp' (scaled
+@^sp@>
+point) as a unit equal to $2^{-16}$ printer's points, every dimension
+inside of \TeX\ is an integer number of sp. There are exactly
+4,736,286.72 sp per inch.  Users are not allowed to specify dimensions
+larger than $2^{30}-1$ sp, which is a distance of about 18.892 feet (5.7583
+meters); two such quantities can be added without overflow on a 32-bit
+computer.
+
+The present implementation of \TeX\ does not check for overflow when
+@^overflow in arithmetic@>
+dimensions are added or subtracted. This could be done by inserting a
+few dozen tests of the form `\ignorespaces|if x>=010000000000 then
+@t\\{report\_overflow}@>|', but the chance of overflow is so remote that
+such tests do not seem worthwhile.
+
+\TeX\ needs to do only a few arithmetic operations on scaled quantities,
+other than addition and subtraction, and the following subroutines do most of
+the work. A single computation might use several subroutine calls, and it is
+desirable to avoid producing multiple error messages in case of arithmetic
+overflow; so the routines set the global variable |arith_error| to |true|
+instead of reporting errors directly to the user. Another global variable,
+|tex_remainder|, holds the remainder after a division.
+
+@c
+boolean arith_error;            /* has arithmetic overflow occurred recently? */
+scaled tex_remainder;           /* amount subtracted to get an exact division */
+
+
+@ The first arithmetical subroutine we need computes $nx+y$, where |x|
+and~|y| are |scaled| and |n| is an integer. We will also use it to
+multiply integers.
+
+@c
+scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer)
+{
+    if (n == 0)
+        return y;
+    if (n < 0) {
+        negate(x);
+        negate(n);
+    }
+    if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) {
+        return (n * x + y);
+    } else {
+        arith_error = true;
+        return 0;
+    }
+}
+
+@ We also need to divide scaled dimensions by integers. 
+@c
+scaled x_over_n(scaled x, int n)
+{
+    boolean negative;           /* should |tex_remainder| be negated? */
+    negative = false;
+    if (n == 0) {
+        arith_error = true;
+        tex_remainder = x;
+        return 0;
+    } else {
+        if (n < 0) {
+            negate(x);
+            negate(n);
+            negative = true;
+        }
+        if (x >= 0) {
+            tex_remainder = x % n;
+            if (negative)
+                negate(tex_remainder);
+            return (x / n);
+        } else {
+            tex_remainder = -((-x) % n);
+            if (negative)
+                negate(tex_remainder);
+            return (-((-x) / n));
+        }
+    }
+}
+
+
+@ Then comes the multiplication of a scaled number by a fraction |n/d|,
+where |n| and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is
+positive. It would be too dangerous to multiply by~|n| and then divide
+by~|d|, in separate operations, since overflow might well occur; and it
+would be too inaccurate to divide by |d| and then multiply by |n|. Hence
+this subroutine simulates 1.5-precision arithmetic.
+
+@c
+scaled xn_over_d(scaled x, int n, int d)
+{
+    nonnegative_integer t, u, v, xx, dd;        /* intermediate quantities */
+    boolean positive = true;    /* was |x>=0|? */
+    if (x < 0) {
+        negate(x);
+        positive = false;
+    }
+    xx = (nonnegative_integer) x;
+    dd = (nonnegative_integer) d;
+    t = ((xx % 0100000) * (nonnegative_integer) n);
+    u = ((xx / 0100000) * (nonnegative_integer) n + (t / 0100000));
+    v = (u % dd) * 0100000 + (t % 0100000);
+    if (u / dd >= 0100000)
+        arith_error = true;
+    else
+        u = 0100000 * (u / dd) + (v / dd);
+    if (positive) {
+        tex_remainder = (int) (v % dd);
+        return (scaled) u;
+    } else {
+        /* casts are for ms cl */
+        tex_remainder = -(int) (v % dd);
+        return -(scaled) (u);
+    }
+}
+
+
+@ The next subroutine is used to compute the ``badness'' of glue, when a
+total~|t| is supposed to be made from amounts that sum to~|s|.  According
+to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$;
+however, badness is simply a heuristic, so we need not squeeze out the
+last drop of accuracy when computing it. All we really want is an
+approximation that has similar properties.
+@:TeXbook}{\sl The \TeX book@>
+
+The actual method used to compute the badness is easier to read from the
+program than to describe in words. It produces an integer value that is a
+reasonably close approximation to $100(t/s)^3$, and all implementations
+of \TeX\ should use precisely this method. Any badness of $2^{13}$ or more is
+treated as infinitely bad, and represented by 10000.
+
+It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s)
+>=badness(t,s+1)|}.$$ The badness function defined here is capable of
+computing at most 1095 distinct values, but that is plenty.
+
+@c
+halfword badness(scaled t, scaled s)
+{                               /* compute badness, given |t>=0| */
+    int r;                      /* approximation to $\alpha t/s$, where $\alpha^3\approx
+                                   100\cdot2^{18}$ */
+    if (t == 0) {
+        return 0;
+    } else if (s <= 0) {
+        return inf_bad;
+    } else {
+        if (t <= 7230584)
+            r = (t * 297) / s;  /* $297^3=99.94\times2^{18}$ */
+        else if (s >= 1663497)
+            r = t / (s / 297);
+        else
+            r = t;
+        if (r > 1290)
+            return inf_bad;     /* $1290^3<2^{31}<1291^3$ */
+        else
+            return ((r * r * r + 0400000) / 01000000);
+        /* that was $r^3/2^{18}$, rounded to the nearest integer */
+    }
+}
+
+
+@ When \TeX\ ``packages'' a list into a box, it needs to calculate the
+proportionality ratio by which the glue inside the box should stretch
+or shrink. This calculation does not affect \TeX's decision making,
+so the precise details of rounding, etc., in the glue calculation are not
+of critical importance for the consistency of results on different computers.
+
+We shall use the type |glue_ratio| for such proportionality ratios.
+A glue ratio should take the same amount of memory as an
+|integer| (usually 32 bits) if it is to blend smoothly with \TeX's
+other data structures. Thus |glue_ratio| should be equivalent to
+|short_real| in some implementations of PASCAL. Alternatively,
+it is possible to deal with glue ratios using nothing but fixed-point
+arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the
+routines cited there must be modified to allow negative glue ratios.)
+@^system dependencies@>
+
+
+@ This section is (almost) straight from MetaPost. I had to change
+the types (use |integer| instead of |fraction|), but that should
+not have any influence on the actual calculations (the original
+comments refer to quantities like |fraction_four| ($2^{30}$), and
+that is the same as the numeric representation of |max_dimen|).
+
+I've copied the low-level variables and routines that are needed, but
+only those (e.g. |m_log|), not the accompanying ones like |m_exp|. Most
+of the following low-level numeric routines are only needed within the
+calculation of |norm_rand|. I've been forced to rename |make_fraction|
+to |make_frac| because TeX already has a routine by that name with
+a wholly different function (it creates a |fraction_noad| for math
+typesetting) -- Taco
+
+And now let's complete our collection of numeric utility routines
+by considering random number generation.
+\MP{} generates pseudo-random numbers with the additive scheme recommended
+in Section 3.6 of {\sl The Art of Computer Programming}; however, the
+results are random fractions between 0 and |fraction_one-1|, inclusive.
+
+There's an auxiliary array |randoms| that contains 55 pseudo-random
+fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$,
+we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|.
+The global variable |j_random| tells which element has most recently
+been consumed.
+
+@c
+static int randoms[55];         /* the last 55 random values generated */
+static int j_random;            /* the number of unused |randoms| */
+scaled random_seed;             /* the default random seed */
+
+@ A small bit of metafont is needed. 
+
+@c
+#define fraction_half 01000000000       /* $2^{27}$, represents 0.50000000 */
+#define fraction_one 02000000000        /* $2^{28}$, represents 1.00000000 */
+#define fraction_four 010000000000      /* $2^{30}$, represents 4.00000000 */
+#define el_gordo 017777777777   /* $2^{31}-1$, the largest value that \MP\ likes */
+
+@ The |make_frac| routine produces the |fraction| equivalent of
+|p/q|, given integers |p| and~|q|; it computes the integer
+$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are
+positive. If |p| and |q| are both of the same scaled type |t|,
+the ``type relation'' |make_frac(t,t)=fraction| is valid;
+and it's also possible to use the subroutine ``backwards,'' using
+the relation |make_frac(t,fraction)=t| between scaled types.
+
+If the result would have magnitude $2^{31}$ or more, |make_frac|
+sets |arith_error:=true|. Most of \MP's internal computations have
+been designed to avoid this sort of error.
+
+If this subroutine were programmed in assembly language on a typical
+machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a
+double-precision product can often be input to a fixed-point division
+instruction. But when we are restricted to PASCAL arithmetic it
+is necessary either to resort to multiple-precision maneuvering
+or to use a simple but slow iteration. The multiple-precision technique
+would be about three times faster than the code adopted here, but it
+would be comparatively long and tricky, involving about sixteen
+additional multiplications and divisions.
+
+This operation is part of \MP's ``inner loop''; indeed, it will
+consume nearly 10\%! of the running time (exclusive of input and output)
+if the code below is left unchanged. A machine-dependent recoding
+will therefore make \MP\ run faster. The present implementation
+is highly portable, but slow; it avoids multiplication and division
+except in the initial stage. System wizards should be careful to
+replace it with a routine that is guaranteed to produce identical
+results in all cases.
+@^system dependencies@>
+
+As noted below, a few more routines should also be replaced by machine-dependent
+code, for efficiency. But when a procedure is not part of the ``inner loop,''
+such changes aren't advisable; simplicity and robustness are
+preferable to trickery, unless the cost is too high.
+
+@c
+static int make_frac(int p, int q)
+{
+    int f;                      /* the fraction bits, with a leading 1 bit */
+    int n;                      /* the integer part of $\vert p/q\vert$ */
+    register int be_careful;    /* disables certain compiler optimizations */
+    boolean negative = false;   /* should the result be negated? */
+    if (p < 0) {
+        negate(p);
+        negative = true;
+    }
+    if (q <= 0) {
+#ifdef DEBUG
+        if (q == 0)
+            confusion("/");
+#endif
+        negate(q);
+        negative = !negative;
+    }
+    n = p / q;
+    p = p % q;
+    if (n >= 8) {
+        arith_error = true;
+        if (negative)
+            return (-el_gordo);
+        else
+            return el_gordo;
+    } else {
+        n = (n - 1) * fraction_one;
+        /* Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$ */
+        /* The |repeat| loop here preserves the following invariant relations
+           between |f|, |p|, and~|q|:
+           (i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
+           $p_0$ is the original value of~$p$.
+
+           Notice that the computation specifies
+           |(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
+           Let us hope that optimizing compilers do not miss this point; a
+           special variable |be_careful| is used to emphasize the necessary
+           order of computation. Optimizing compilers should keep |be_careful|
+           in a register, not store it in memory.
+         */
+        f = 1;
+        do {
+            be_careful = p - q;
+            p = be_careful + p;
+            if (p >= 0)
+                f = f + f + 1;
+            else {
+                f += f;
+                p = p + q;
+            }
+        } while (f < fraction_one);
+        be_careful = p - q;
+        if (be_careful + p >= 0)
+            incr(f);
+
+        if (negative)
+            return (-(f + n));
+        else
+            return (f + n);
+    }
+}
+
+@ @c
+static int take_frac(int q, int f)
+{
+    int p;                      /* the fraction so far */
+    int n;                      /* additional multiple of $q$ */
+    register int be_careful;    /* disables certain compiler optimizations */
+    boolean negative = false;   /* should the result be negated? */
+    /* Reduce to the case that |f>=0| and |q>0| */
+    if (f < 0) {
+        negate(f);
+        negative = true;
+    }
+    if (q < 0) {
+        negate(q);
+        negative = !negative;
+    }
+
+    if (f < fraction_one) {
+        n = 0;
+    } else {
+        n = f / fraction_one;
+        f = f % fraction_one;
+        if (q <= el_gordo / n) {
+            n = n * q;
+        } else {
+            arith_error = true;
+            n = el_gordo;
+        }
+    }
+    f = f + fraction_one;
+    /* Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$ */
+    /* The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
+       =\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
+       $f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$. 
+     */
+    p = fraction_half;          /* that's $2^{27}$; the invariants hold now with $k=28$ */
+    if (q < fraction_four) {
+        do {
+            if (odd(f))
+                p = halfp(p + q);
+            else
+                p = halfp(p);
+            f = halfp(f);
+        } while (f != 1);
+    } else {
+        do {
+            if (odd(f))
+                p = p + halfp(q - p);
+            else
+                p = halfp(p);
+            f = halfp(f);
+        } while (f != 1);
+    }
+
+    be_careful = n - el_gordo;
+    if (be_careful + p > 0) {
+        arith_error = true;
+        n = el_gordo - p;
+    }
+    if (negative)
+        return (-(n + p));
+    else
+        return (n + p);
+}
+
+
+
+@ The subroutines for logarithm and exponential involve two tables.
+The first is simple: |two_to_the[k]| equals $2^k$. The second involves
+a bit more calculation, which the author claims to have done correctly:
+|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)=
+2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the
+nearest integer.
+
+@c
+static int two_to_the[31];      /* powers of two */
+static int spec_log[29];        /* special logarithms */
+
+@ @c
+void initialize_arithmetic(void)
+{
+    int k;
+    two_to_the[0] = 1;
+    for (k = 1; k <= 30; k++)
+        two_to_the[k] = 2 * two_to_the[k - 1];
+    spec_log[1] = 93032640;
+    spec_log[2] = 38612034;
+    spec_log[3] = 17922280;
+    spec_log[4] = 8662214;
+    spec_log[5] = 4261238;
+    spec_log[6] = 2113709;
+    spec_log[7] = 1052693;
+    spec_log[8] = 525315;
+    spec_log[9] = 262400;
+    spec_log[10] = 131136;
+    spec_log[11] = 65552;
+    spec_log[12] = 32772;
+    spec_log[13] = 16385;
+    for (k = 14; k <= 27; k++)
+        spec_log[k] = two_to_the[27 - k];
+    spec_log[28] = 1;
+}
+
+@ @c
+static int m_log(int x)
+{
+    int y, z;                   /* auxiliary registers */
+    int k;                      /* iteration counter */
+    if (x <= 0) {
+        /* Handle non-positive logarithm */
+        print_err("Logarithm of ");
+        print_scaled(x);
+        tprint(" has been replaced by 0");
+        help2("Since I don't take logs of non-positive numbers,",
+              "I'm zeroing this one. Proceed, with fingers crossed.");
+        error();
+        return 0;
+    } else {
+        y = 1302456956 + 4 - 100;       /* $14\times2^{27}\ln2\approx1302456956.421063$ */
+        z = 27595 + 6553600;    /* and $2^{16}\times .421063\approx 27595$ */
+        while (x < fraction_four) {
+            x += x;
+            y = y - 93032639;
+            z = z - 48782;
+        }                       /* $2^{27}\ln2\approx 93032639.74436163$
+                                   and $2^{16}\times.74436163\approx 48782$ */
+        y = y + (z / unity);
+        k = 2;
+        while (x > fraction_four + 4) {
+            /* Increase |k| until |x| can be multiplied by a
+               factor of $2^{-k}$, and adjust $y$ accordingly */
+            z = ((x - 1) / two_to_the[k]) + 1;  /* $z=\lceil x/2^k\rceil$ */
+            while (x < fraction_four + z) {
+                z = halfp(z + 1);
+                k = k + 1;
+            }
+            y = y + spec_log[k];
+            x = x - z;
+        }
+        return (y / 8);
+    }
+}
+
+
+
+@ The following somewhat different subroutine tests rigorously if $ab$ is
+greater than, equal to, or less than~$cd$,
+given integers $(a,b,c,d)$. In most cases a quick decision is reached.
+The result is $+1$, 0, or~$-1$ in the three respective cases.
+
+@c
+static int ab_vs_cd(int a, int b, int c, int d)
+{
+    int q, r;                   /* temporary registers */
+    /* Reduce to the case that |a,c>=0|, |b,d>0| */
+    if (a < 0) {
+        negate(a);
+        negate(b);
+    }
+    if (c < 0) {
+        negate(c);
+        negate(d);
+    }
+    if (d <= 0) {
+        if (b >= 0)
+            return (((a == 0 || b == 0) && (c == 0 || d == 0)) ? 0 : 1);
+        if (d == 0)
+            return (a == 0 ? 0 : -1);
+        q = a;
+        a = c;
+        c = q;
+        q = -b;
+        b = -d;
+        d = q;
+    } else if (b <= 0) {
+        if (b < 0 && a > 0)
+            return -1;
+        return (c == 0 ? 0 : -1);
+    }
+
+    while (1) {
+        q = a / d;
+        r = c / b;
+        if (q != r)
+            return (q > r ? 1 : -1);
+        q = a % d;
+        r = c % b;
+        if (r == 0)
+            return (q == 0 ? 0 : 1);
+        if (q == 0)
+            return -1;
+        a = b;
+        b = q;
+        c = d;
+        d = r;                  /* now |a>d>0| and |c>b>0| */
+    }
+}
+
+
+
+@ To consume a random integer, the program below will say `|next_random|'
+and then it will fetch |randoms[j_random]|.
+
+@c
+#define next_random() do {					\
+	if (j_random==0) new_randoms(); else decr(j_random);	\
+    } while (0)
+
+static void new_randoms(void)
+{
+    int k;                      /* index into |randoms| */
+    int x;                      /* accumulator */
+    for (k = 0; k <= 23; k++) {
+        x = randoms[k] - randoms[k + 31];
+        if (x < 0)
+            x = x + fraction_one;
+        randoms[k] = x;
+    }
+    for (k = 24; k <= 54; k++) {
+        x = randoms[k] - randoms[k - 24];
+        if (x < 0)
+            x = x + fraction_one;
+        randoms[k] = x;
+    }
+    j_random = 54;
+}
+
+
+@ To initialize the |randoms| table, we call the following routine. 
+
+@c
+void init_randoms(int seed)
+{
+    int j, jj, k;               /* more or less random integers */
+    int i;                      /* index into |randoms| */
+    j = abs(seed);
+    while (j >= fraction_one)
+        j = halfp(j);
+    k = 1;
+    for (i = 0; i <= 54; i++) {
+        jj = k;
+        k = j - k;
+        j = jj;
+        if (k < 0)
+            k = k + fraction_one;
+        randoms[(i * 21) % 55] = j;
+    }
+    new_randoms();
+    new_randoms();
+    new_randoms();              /* ``warm up'' the array */
+}
+
+
+@ To produce a uniform random number in the range |0<=u<x| or |0>=u>x|
+or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here.
+
+Note that the call of |take_frac| will produce the values 0 and~|x|
+with about half the probability that it will produce any other particular
+values between 0 and~|x|, because it rounds its answers.
+
+@c
+int unif_rand(int x)
+{
+    int y;                      /* trial value */
+    next_random();
+    y = take_frac(abs(x), randoms[j_random]);
+    if (y == abs(x))
+        return 0;
+    else if (x > 0)
+        return y;
+    else
+        return -y;
+}
+
+
+@ Finally, a normal deviate with mean zero and unit standard deviation
+can readily be obtained with the ratio method (Algorithm 3.4.1R in
+{\sl The Art of Computer Programming\/}).
+
+@c
+int norm_rand(void)
+{
+    int x, u, l;                /* what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$ */
+    do {
+        do {
+            next_random();
+            x = take_frac(112429, randoms[j_random] - fraction_half);
+            /* $2^{16}\sqrt{8/e}\approx 112428.82793$ */
+            next_random();
+            u = randoms[j_random];
+        } while (abs(x) >= u);
+        x = make_frac(x, u);
+        l = 139548960 - m_log(u);       /* $2^{24}\cdot12\ln2\approx139548959.6165$ */
+    } while (ab_vs_cd(1024, l, x, x) < 0);
+    return x;
+}
+
+@ This function could also be expressed as a macro, but it is a useful
+   breakpoint for debugging.
+
+@c
+int fix_int(int val, int min, int max)
+{
+    return (val < min ? min : (val > max ? max : val));
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/backend.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/* to fill */
-
-#include "ptexlib.h"
-
-scaled max_v = 0;               /* maximum height-plus-depth of pages shipped so far */
-scaled max_h = 0;               /* maximum width of pages shipped so far */
-boolean doing_leaders = false;  /* are we inside a leader box? */
-int cur_s = -1;                 /* current depth of output box nesting, initially $-1$ */
-
-static backend_struct *backend = NULL;
-backend_function *backend_out, *backend_out_whatsit, *backend_out_control;
-
-static void missing_backend_function(PDF pdf, halfword p)
-{
-    const char *n = get_node_name(type(p), subtype(p));
-    if (type(p) == whatsit_node)
-        formatted_error("pdf backend","no output function for whatsit %s",n);
-    else
-        formatted_error("pdf backend","no output function for node %s",n);
-}
-
-static void init_none_backend_functions(void)
-{
-    backend_struct *p = &backend[OMODE_NONE];
-    p->name = strdup("NONE");
-}
-
-static void init_pdf_backend_functions(void)
-{
-    backend_struct *p = &backend[OMODE_PDF];
-    p->name = strdup("PDF");
-    p->node_fu[rule_node] = &pdf_place_rule;
-    p->node_fu[glyph_node] = &pdf_place_glyph;
-    p->whatsit_fu[special_node] = &pdf_special;
-    p->whatsit_fu[pdf_literal_node] = &pdf_out_literal;
-    p->whatsit_fu[pdf_refobj_node] = &pdf_ref_obj;
-    p->whatsit_fu[pdf_annot_node] = &do_annot;
-    p->whatsit_fu[pdf_start_link_node] = &do_link;
-    p->whatsit_fu[pdf_end_link_node] = &end_link;
-    p->whatsit_fu[pdf_dest_node] = &do_dest;
-    p->whatsit_fu[pdf_thread_node] = &do_thread;
-    p->whatsit_fu[pdf_end_thread_node] = &end_thread;
-    p->whatsit_fu[late_lua_node] = &late_lua;
-    p->whatsit_fu[pdf_colorstack_node] = &pdf_out_colorstack;
-    p->whatsit_fu[pdf_setmatrix_node] = &pdf_out_setmatrix;
-    p->whatsit_fu[pdf_save_node] = &pdf_out_save;
-    p->whatsit_fu[pdf_restore_node] = &pdf_out_restore;
-    p->control_fu[backend_control_push_list] = &pdf_push_list;
-    p->control_fu[backend_control_pop_list] = &pdf_pop_list;
-    p->control_fu[backend_control_begin_page] = &pdf_begin_page;
-    p->control_fu[backend_control_end_page] = &pdf_end_page;
-    p->control_fu[backend_control_open_file] = &pdf_open_file;
-    p->control_fu[backend_control_write_header] = &pdf_write_header;
-    p->control_fu[backend_control_finish_file] = &pdf_finish_file;
-    p->control_fu[backend_control_set_reference_point] = &pdf_set_reference_point;
-}
-
-static void init_dvi_backend_functions(void)
-{
-    backend_struct *p = &backend[OMODE_DVI];
-    p->name = strdup("DVI");
-    p->node_fu[rule_node] = &dvi_place_rule;
-    p->node_fu[glyph_node] = &dvi_place_glyph;
-    p->whatsit_fu[special_node] = &dvi_special;
-    p->whatsit_fu[late_lua_node] = &late_lua;
-    p->control_fu[backend_control_push_list] = &dvi_push_list;
-    p->control_fu[backend_control_pop_list] = &dvi_pop_list;
-    p->control_fu[backend_control_begin_page] = &dvi_begin_page;
-    p->control_fu[backend_control_end_page] = &dvi_end_page;
-    p->control_fu[backend_control_open_file] = &dvi_open_file;
-    p->control_fu[backend_control_write_header] = &dvi_write_header;
-    p->control_fu[backend_control_finish_file] = &dvi_finish_file;
-    p->control_fu[backend_control_set_reference_point] = &dvi_set_reference_point;
-}
-
-
-void init_backend_functionpointers(output_mode o_mode)
-{
-    int i, j;
-    if (backend == NULL) {
-        backend = xmalloc((MAX_OMODE + 1) * sizeof(backend_struct));
-        for (i = 0; i <= MAX_OMODE; i++) {
-            backend[i].node_fu = xmalloc((MAX_NODE_TYPE + 1) * sizeof(backend_function));
-            backend[i].whatsit_fu = xmalloc((MAX_WHATSIT_TYPE + 1) * sizeof(backend_function));
-            backend[i].control_fu = xmalloc((MAX_CONTROL_TYPE + 1) * sizeof(backend_function));
-            for (j = 0; j < MAX_NODE_TYPE + 1; j++)
-                backend[i].node_fu[j] = &missing_backend_function;
-            for (j = 0; j < MAX_WHATSIT_TYPE + 1; j++)
-                backend[i].whatsit_fu[j] = &missing_backend_function;
-            for (j = 0; j < MAX_CONTROL_TYPE + 1; j++)
-                backend[i].control_fu[j] = &missing_backend_function;
-        }
-        init_none_backend_functions();
-        init_dvi_backend_functions();
-        init_pdf_backend_functions();
-    }
-    backend_out = backend[o_mode].node_fu;
-    backend_out_whatsit = backend[o_mode].whatsit_fu;
-    backend_out_control = backend[o_mode].control_fu;
-}
-
-output_mode get_o_mode(void)
-{
-    output_mode o_mode;
-    if (output_mode_par > 0) {
-        o_mode = OMODE_PDF;
-    } else
-        o_mode = OMODE_DVI;
-    return o_mode;
-}
-
-void fix_o_mode(void)
-{
-    output_mode o_mode = get_o_mode();
-    if (output_mode_used == OMODE_NONE) {
-        output_mode_used = o_mode;
-        /*tex Used by synctex, we need to use output_mode_used there: */
-        static_pdf->o_mode = output_mode_used;
-    } else if (output_mode_used != o_mode) {
-        normal_error("pdf backend", "\\outputmode can only be changed before anything is written to the output");
-    }
-    init_backend_functionpointers(output_mode_used);
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/backend.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* to fill */
-
-#ifndef BACKEND_H
-#  define BACKEND_H
-
-#include "ptexlib.h"
-
-extern scaled max_v;
-extern scaled max_h;
-extern boolean doing_leaders;
-extern int cur_s;
-
-# define MAX_CONTROL_TYPE 7
-
-typedef enum {
-    backend_control_push_list = 0,
-    backend_control_pop_list,
-    backend_control_begin_page,
-    backend_control_end_page,
-    backend_control_open_file,
-    backend_control_write_header,
-    backend_control_finish_file,
-    backend_control_set_reference_point
-} backend_control_types ;
-
-typedef void (*backend_function) (); /* variadic arguments  */
-
-typedef struct {
-    char *name;                    /* name of the backend */
-    backend_function *node_fu;     /* array of node output functions */
-    backend_function *whatsit_fu;  /* array of whatsit output functions */
-    backend_function *control_fu;  /* array of whatsit output functions */
-} backend_struct;
-
-/* extern pos_info_structure pos_info; */
-
-extern backend_function *backend_out;
-extern backend_function *backend_out_whatsit;
-extern backend_function *backend_out_control;
-
-/* get_o_mode translates from output_mode to output_mode_used */
-/* fix_o_mode freezes output_mode as soon as anything goes through the backend */
-
-/*
-    extern void check_o_mode(PDF pdf, const char *s, int o_mode, boolean errorflag);
-    extern void ensure_output_file_open(PDF pdf, const char *ext);
-*/
-
-extern void fix_o_mode(void);
-extern output_mode get_o_mode(void);
-
-extern void init_backend_functionpointers(output_mode o_mode);
-
-#endif
--- texlive-bin.orig/texk/web2c/luatexdir/tex/buildpage.c
+++ /dev/null
@@ -1,1164 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-
-#define mode mode_par
-#define head head_par
-#define tail tail_par
-
-/*tex
-
-    When \TeX\ appends new material to its main vlist in vertical mode, it uses a
-    method something like |vsplit| to decide where a page ends, except that the
-    calculations are done ``on line'' as new items come in. The main complication
-    in this process is that insertions must be put into their boxes and removed
-    from the vlist, in a more-or-less optimum manner.
-
-    We shall use the term ``current page'' for that part of the main vlist that
-    is being considered as a candidate for being broken off and sent to the
-    user's output routine. The current page starts at |vlink(page_head)|, and it
-    ends at |page_tail|. We have |page_head=page_tail| if this list is empty.
-
-    Utter chaos would reign if the user kept changing page specifications while a
-    page is being constructed, so the page builder keeps the pertinent
-    specifications frozen as soon as the page receives its first box or
-    insertion. The global variable |page_contents| is |empty| when the current
-    page contains only mark nodes and content-less whatsit nodes; it is
-    |inserts_only| if the page contains only insertion nodes in addition to marks
-    and whatsits. Glue nodes, kern nodes, and penalty nodes are discarded until a
-    box or rule node appears, at which time |page_contents| changes to
-    |box_there|. As soon as |page_contents| becomes non-|empty|, the current
-    |vsize| and |max_depth| are squirreled away into |page_goal| and
-    |page_max_depth|; the latter values will be used until the page has been
-    forwarded to the user's output routine. The \.{\\topskip} adjustment is made
-    when |page_contents| changes to |box_there|.
-
-    Although |page_goal| starts out equal to |vsize|, it is decreased by the
-    scaled natural height-plus-depth of the insertions considered so far, and by
-    the \.{\\skip} corrections for those insertions. Therefore it represents the
-    size into which the non-inserted material should fit, assuming that all
-    insertions in the current page have been made.
-
-    The global variables |best_page_break| and |least_page_cost| correspond
-    respectively to the local variables |best_place| and |least_cost| in the
-    |vert_break| routine that we have already studied; i.e., they record the
-    location and value of the best place currently known for breaking the current
-    page. The value of |page_goal| at the time of the best break is stored in
-    |best_size|.
-
-*/
-
-/*tex the final node on the current page */
-
-halfword page_tail;
-
-/*tex what is on the current page so far? */
-
-int page_contents;
-
-/*tex maximum box depth on page being built */
-
-scaled page_max_depth;
-
-/*tex break here to get the best page known so far */
-
-halfword best_page_break;
-
-/*tex the score for this currently best page */
-
-int least_page_cost;
-
-/*tex its |page_goal| */
-
-scaled best_size;
-
-/*tex
-
-    The page builder has another data structure to keep track of insertions. This
-    is a list of four-word nodes, starting and ending at |page_ins_head|. That
-    is, the first element of the list is node |t$_1$=vlink(page_ins_head)|;
-    node $r_j$ is followed by |t$_{j+1}$=vlink(t$_j$)|; and if there are
-    |n| items we have |$_{n+1}$>=page_ins_head|. The |subtype| field of each
-    node in this list refers to an insertion number; for example, `\.{\\insert
-    250}' would correspond to a node whose |subtype| is |qi(250)| (the same as
-    the |subtype| field of the relevant |ins_node|). These |subtype| fields are
-    in increasing order, and |subtype(page_ins_head)=65535|, so |page_ins_head|
-    serves as a convenient sentinel at the end of the list. A record is present
-    for each insertion number that appears in the current page.
-
-    The |type| field in these nodes distinguishes two possibilities that might
-    occur as we look ahead before deciding on the optimum page break. If
-    |type(r)=inserting_node|, then |height(r)| contains the total of the
-    height-plus-depth dimensions of the box and all its inserts seen so far.
-    |type(r)=split_up_node|, then no more insertions will be made into this box,
-    because at least one previous insertion was too big to fit on the current
-    page; |broken_ptr(r)| points to the node where that insertion will be split,
-    if \TeX\ decides to split it, |broken_ins(r)| points to the insertion node
-    that was tentatively split, and |height(r)| includes also the natural height
-    plus depth of the part that would be split off.
-
-    In both cases, |last_ins_ptr(r)| points to the last |ins_node| encountered
-    for box |qo(subtype(r))| that would be at least partially inserted on the
-    next page; and |best_ins_ptr(r)| points to the last such |ins_node| that
-    should actually be inserted, to get the page with minimum badness among all
-    page breaks considered so far. We have |best_ins_ptr(r)=null| if and only if
-    no insertion for this box should be made to produce this optimum page.
-
-    Pages are built by appending nodes to the current list in \TeX's vertical
-    mode, which is at the outermost level of the semantic nest. This vlist is
-    split into two parts; the ``current page'' that we have been talking so much
-    about already, and the ``contribution list'' that receives new nodes as they
-    are created. The current page contains everything that the page builder has
-    accounted for in its data structures, as described above, while the
-    contribution list contains other things that have been generated by other
-    parts of \TeX\ but have not yet been seen by the page builder. The
-    contribution list starts at |vlink(contrib_head)|, and it ends at the current
-    node in \TeX's vertical mode.
-
-    When \TeX\ has appended new material in vertical mode, it calls the procedure
-    |build_page|, which tries to catch up by moving nodes from the contribution
-    list to the current page. This procedure will succeed in its goal of emptying
-    the contribution list, unless a page break is discovered, i.e., unless the
-    current page has grown to the point where the optimum next page break has
-    been determined. In the latter case, the nodes after the optimum break will
-    go back onto the contribution list, and control will effectively pass to the
-    user's output routine.
-
-    We make |type(page_head)=glue_node|, so that an initial glue node on the
-    current page will not be considered a valid breakpoint.
-
-*/
-
-void initialize_buildpage(void)
-{
-    subtype(page_ins_head) = 65535;
-    type(page_ins_head) = split_up_node;
-    vlink(page_ins_head) = page_ins_head;
-
-    type(page_head) = glue_node;
-    subtype(page_head) = normal;
-}
-
-/*tex
-
-    An array |page_so_far| records the heights and depths of everything on the
-    current page. This array contains six |scaled| numbers, like the similar
-    arrays already considered in |line_break| and |vert_break|; and it also
-    contains |page_goal| and |page_depth|, since these values are all accessible
-    to the user via |set_page_dimen| commands. The value of |page_so_far[1]| is
-    also called |page_total|. The stretch and shrink components of the \.{\\skip}
-    corrections for each insertion are included in |page_so_far|, but the natural
-    space components of these corrections are not, since they have been
-    subtracted from |page_goal|.
-
-    The variable |page_depth| records the depth of the current page; it has been
-    adjusted so that it is at most |page_max_depth|. The variable |last_glue|
-    points to the glue specification of the most recent node contributed from the
-    contribution list, if this was a glue node; otherwise
-    |last_glue=max_halfword|. (If the contribution list is nonempty, however, the
-    value of |last_glue| is not necessarily accurate.) The variables
-    |last_penalty|, |last_kern|, and |last_node_type| are similar. And finally,
-    |insert_penalties| holds the sum of the penalties associated with all split
-    and floating insertions.
-
-*/
-
-/*tex height and glue of the current page */
-
-scaled page_so_far[8];
-
-/*tex used to implement \.{\\lastskip} */
-
-halfword last_glue;
-
-/*tex used to implement \.{\\lastpenalty} */
-
-int last_penalty;
-
-/*tex used to implement \.{\\lastkern} */
-
-scaled last_kern;
-
-/*tex used to implement \.{\\lastnodetype} */
-
-int last_node_type;
-
-/*tex sum of the penalties for held-over insertions */
-
-int insert_penalties;
-
-#define print_plus(A,B) do { \
-    if (page_so_far[(A)]!=0) { \
-        tprint(" plus "); \
-        print_scaled(page_so_far[(A)]); \
-        tprint((B)); \
-    } \
-} while (0)
-
-void print_totals(void)
-{
-    print_scaled(page_total);
-    print_plus(2, "");
-    print_plus(3, "fil");
-    print_plus(4, "fill");
-    print_plus(5, "filll");
-    if (page_shrink != 0) {
-        tprint(" minus ");
-        print_scaled(page_shrink);
-    }
-}
-
-/*tex
-
-    Here is a procedure that is called when the |page_contents| is changing from
-    |empty| to |inserts_only| or |box_there|.
-
-*/
-
-#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
-#define set_page_so_far_zero(A) page_so_far[(A)]=0
-
-void freeze_page_specs(int s)
-{
-    page_contents = s;
-    page_goal = vsize_par;
-    page_max_depth = max_depth_par;
-    page_depth = 0;
-    do_all_six(set_page_so_far_zero);
-    least_page_cost = awful_bad;
-    if (tracing_pages_par > 0) {
-        begin_diagnostic();
-        tprint_nl("%% goal height=");
-        print_scaled(page_goal);
-        tprint(", max depth=");
-        print_scaled(page_max_depth);
-        end_diagnostic(false);
-    }
-}
-
-/*tex
-
-    The global variable |output_active| is true during the time the user's output
-    routine is driving \TeX.
-
-*/
-
-/*tex Are we in the midst of an output routine? */
-
-boolean output_active;
-
-/*tex
-
-    The page builder is ready to start a fresh page if we initialize the
-    following state variables. (However, the page insertion list is initialized
-    elsewhere.)
-
-*/
-
-void start_new_page(void)
-{
-    page_contents = empty;
-    page_tail = page_head;
-    vlink(page_head) = null;
-    last_glue = max_halfword;
-    last_penalty = 0;
-    last_kern = 0;
-    last_node_type = -1;
-    page_depth = 0;
-    page_max_depth = 0;
-}
-
-/*tex
-
-    At certain times box \.{\\outputbox} is supposed to be void (i.e., |null|),
-    or an insertion box is supposed to be ready to accept a vertical list. If
-    not, an error message is printed, and the following subroutine flushes the
-    unwanted contents, reporting them to the user.
-
-*/
-
-static void box_error(int n)
-{
-    error();
-    begin_diagnostic();
-    tprint_nl("The following box has been deleted:");
-    show_box(box(n));
-    end_diagnostic(true);
-    flush_node_list(box(n));
-    box(n) = null;
-}
-
-/*tex
-
-    The following procedure guarantees that a given box register does not contain
-    an \.{\\hbox}.
-
-*/
-
-static void ensure_vbox(int n)
-{
-    halfword p = box(n);
-    if (p != null && type(p) == hlist_node) {
-        print_err("Insertions can only be added to a vbox");
-        help3(
-            "Tut tut: You're trying to \\insert into a",
-            "\\box register that now contains an \\hbox.",
-            "Proceed, and I'll discard its present contents."
-        );
-        box_error(n);
-    }
-}
-
-/*tex
-
-    \TeX\ is not always in vertical mode at the time |build_page| is called; the
-    current mode reflects what \TeX\ should return to, after the contribution
-    list has been emptied. A call on |build_page| should be immediately followed
-    by `|goto big_switch|', which is \TeX's central control point.
-
-*/
-
-/*tex Append contributions to the current page. */
-
-void build_page(void)
-{
-    /*tex the node being appended */
-    halfword p;
-    /*tex nodes being examined */
-    halfword q, r;
-    /*tex badness and cost of current page */
-    int b, c;
-    /*tex penalty to be added to the badness */
-    int pi = 0;
-    /*tex insertion box number */
-    int n;
-    /*tex sizes used for insertion calculations */
-    scaled delta, h, w;
-    int id, sk, i;
-    if ((vlink(contrib_head) == null) || output_active)
-        return;
-    do {
-      CONTINUE:
-        p = vlink(contrib_head);
-        /*tex Update the values of |last_glue|, |last_penalty|, and |last_kern|. */
-        if (last_glue != max_halfword) {
-            flush_node(last_glue);
-            last_glue = max_halfword;
-        }
-        last_penalty = 0;
-        last_kern = 0;
-        last_node_type = type(p) + 1;
-        if (type(p) == glue_node) {
-            last_glue = new_glue(p);
-        } else if (type(p) == penalty_node) {
-            last_penalty = penalty(p);
-        } else if (type(p) == kern_node) {
-            last_kern = width(p);
-        }
-        /*tex
-
-            Move node |p| to the current page; if it is time for a page break,
-            put the nodes following the break back onto the contribution list,
-            and |return| to the users output routine if there is one.
-
-            The code here is an example of a many-way switch into routines that
-            merge together in different places. Some people call this
-            unstructured programming, but the author doesn't see much wrong with
-            it, as long as the various labels have a well-understood meaning.
-
-            If the current page is empty and node |p| is to be deleted, |goto
-            done1|; otherwise use node |p| to update the state of the current
-            page; if this node is an insertion, |goto contribute|; otherwise if
-            this node is not a legal breakpoint, |goto contribute| or
-            |update_heights|; otherwise set |pi| to the penalty associated with
-            this breakpoint.
-
-            The title of this section is already so long, it seems best to avoid
-            making it more accurate but still longer, by mentioning the fact that
-            a kern node at the end of the contribution list will not be
-            contributed until we know its successor.
-
-        */
-        switch (type(p)) {
-            case hlist_node:
-            case vlist_node:
-            case rule_node:
-                if (page_contents < box_there) {
-                    /*tex
-
-                        Initialize the current page, insert the \.{\\topskip}
-                        glue ahead of |p|, and |goto continue|.
-
-                    */
-                    if (page_contents == empty)
-                        freeze_page_specs(box_there);
-                    else
-                        page_contents = box_there;
-                    q = new_skip_param(top_skip_code);
-                    if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) {
-                        if (width(q) > depth(p))
-                            width(q) = width(q) - depth(p);
-                        else
-                            width(q) = 0;
-                    } else {
-                        if (width(q) > height(p))
-                            width(q) = width(q) - height(p);
-                        else
-                            width(q) = 0;
-                    }
-                    couple_nodes(q, p);
-                    couple_nodes(contrib_head, q);
-                    goto CONTINUE;
-                } else {
-                    /*tex
-
-                        Prepare to move a box or rule node to the current page,
-                        then |goto contribute|.
-
-                    */
-                    if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) {
-                        page_total = page_total + page_depth + depth(p);
-                        page_depth = height(p);
-                    } else {
-                        page_total = page_total + page_depth + height(p);
-                        page_depth = depth(p);
-                    }
-                    goto CONTRIBUTE;
-
-                }
-                break;
-            case boundary_node:
-            case whatsit_node:
-                goto CONTRIBUTE;
-                break;
-            case glue_node:
-                if (page_contents < box_there)
-                    goto DONE1;
-                else if (precedes_break(page_tail))
-                    pi = 0;
-                else
-                    goto UPDATE_HEIGHTS;
-                break;
-            case kern_node:
-                if (page_contents < box_there)
-                    goto DONE1;
-                else if (vlink(p) == null)
-                    goto EXIT;
-                else if (type(vlink(p)) == glue_node)
-                    pi = 0;
-                else
-                    goto UPDATE_HEIGHTS;
-                break;
-            case penalty_node:
-                if (page_contents < box_there)
-                    goto DONE1;
-                else
-                    pi = penalty(p);
-                break;
-            case mark_node:
-                goto CONTRIBUTE;
-                break;
-            case ins_node:
-                /*tex Append an insertion to the current page and |goto contribute|. */
-                if (page_contents == empty)
-                    freeze_page_specs(inserts_only);
-                n = subtype(p);
-                r = page_ins_head;
-                i = 1 ;
-                while (n >= subtype(vlink(r))) {
-                    r = vlink(r);
-                    i = i + 1 ;
-                }
-                if (subtype(r) != n) {
-                    /*tex
-
-                        Create a page insertion node with |subtype(r)=qi(n)|, and
-                        include the glue correction for box |n| in the current
-                        page state.
-
-                        We take note of the value of \.{\\skip} |n| and the
-                        height plus depth of \.{\\box}~|n| only when the first
-                        \.{\\insert}~|n| node is encountered for a new page. A
-                        user who changes the contents of \.{\\box}~|n| after that
-                        first \.{\\insert}~|n| had better be either extremely
-                        careful or extremely lucky, or both.
-
-                    */
-                    id = callback_defined(build_page_insert_callback);
-                    if (id != 0) {
-                        run_callback(id, "dd->d",n,i,&sk);
-                    } else {
-                        sk = n;
-                    }
-                    q = new_node(inserting_node, n);
-                    try_couple_nodes(q, vlink(r));
-                    couple_nodes(r, q);
-                    r = q;
-                    ensure_vbox(n);
-                    if (box(n) == null)
-                        height(r) = 0;
-                    else
-                        height(r) = height(box(n)) + depth(box(n));
-                    best_ins_ptr(r) = null;
-                    q = skip(sk);
-                    if (count(n) == 1000)
-                        h = height(r);
-                    else
-                        h = x_over_n(height(r), 1000) * count(n);
-                    page_goal = page_goal - h - width(q);
-                    if (stretch_order(q) > 1)
-                        page_so_far[1 + stretch_order(q)] = page_so_far[1 + stretch_order(q)] + stretch(q);
-                    else
-                        page_so_far[2 + stretch_order(q)] = page_so_far[2 + stretch_order(q)] + stretch(q);
-                    page_shrink = page_shrink + shrink(q);
-                    if ((shrink_order(q) != normal) && (shrink(q) != 0)) {
-                        print_err("Infinite glue shrinkage inserted from \\skip");
-                        print_int(n);
-                        help3(
-                            "The correction glue for page breaking with insertions",
-                            "must have finite shrinkability. But you may proceed,",
-                            "since the offensive shrinkability has been made finite."
-                        );
-                        error();
-                    }
-
-                }
-                if (type(r) == split_up_node) {
-                    insert_penalties = insert_penalties + float_cost(p);
-                } else {
-                    last_ins_ptr(r) = p;
-                    delta = page_goal - page_total - page_depth + page_shrink;
-                    /*tex This much room is left if we shrink the maximum. */
-                    if (count(n) == 1000) {
-                        h = height(p);
-                    } else {
-                        /*tex This much room is needed. */
-                        h = x_over_n(height(p), 1000) * count(n);
-                    }
-                    if (((h <= 0) || (h <= delta))
-                        && (height(p) + height(r) <= dimen(n))) {
-                        page_goal = page_goal - h;
-                        height(r) = height(r) + height(p);
-                    } else {
-                        /*tex
-
-                            Find the best way to split the insertion, and change
-                            |type(r)| to |split_up_node|.
-
-                            Here is the code that will split a long footnote
-                            between pages, in an emergency. The current situation
-                            deserves to be recapitulated: Node |p| is an
-                            insertion into box |n|; the insertion will not fit,
-                            in its entirety, either because it would make the
-                            total contents of box |n| greater than \.{\\dimen}
-                            |n|, or because it would make the incremental amount
-                            of growth |h| greater than the available space
-                            |delta|, or both. (This amount |h| has been weighted
-                            by the insertion scaling factor, i.e., by \.{\\count}
-                            |n| over 1000.) Now we will choose the best way to
-                            break the vlist of the insertion, using the same
-                            criteria as in the \.{\\vsplit} operation.
-
-                        */
-                        if (count(n) <= 0) {
-                            w = max_dimen;
-                        } else {
-                            w = page_goal - page_total - page_depth;
-                            if (count(n) != 1000)
-                                w = x_over_n(w, count(n)) * 1000;
-                        }
-                        if (w > dimen(n) - height(r))
-                            w = dimen(n) - height(r);
-                        q = vert_break(ins_ptr(p), w, depth(p));
-                        height(r) = height(r) + best_height_plus_depth;
-                        if (tracing_pages_par > 0) {
-                            /*tex Display the insertion split cost. */
-                            begin_diagnostic();
-                            tprint_nl("% split");
-                            print_int(n);
-                            tprint(" to ");
-                            print_scaled(w);
-                            print_char(',');
-                            print_scaled(best_height_plus_depth);
-                            tprint(" p=");
-                            if (q == null)
-                                print_int(eject_penalty);
-                            else if (type(q) == penalty_node)
-                                print_int(penalty(q));
-                            else
-                                print_char('0');
-                            end_diagnostic(false);
-                        }
-                        if (count(n) != 1000)
-                            best_height_plus_depth = x_over_n(best_height_plus_depth, 1000) * count(n);
-                        page_goal = page_goal - best_height_plus_depth;
-                        type(r) = split_up_node;
-                        broken_ptr(r) = q;
-                        broken_ins(r) = p;
-                        if (q == null)
-                            insert_penalties = insert_penalties + eject_penalty;
-                        else if (type(q) == penalty_node)
-                            insert_penalties = insert_penalties + penalty(q);
-                    }
-                }
-                goto CONTRIBUTE;
-
-                break;
-            default:
-                formatted_error("pagebuilder","invalid node of type %d in vertical mode", type(p));
-                break;
-        }
-        /*tex
-
-            Check if node |p| is a new champion breakpoint; then if it is time
-            for a page break, prepare for output, and either fire up the users
-            output routine and |return| or ship out the page and |goto done|.
-
-        */
-        if (pi < inf_penalty) {
-            /*tex
-
-                Compute the badness, |b|, of the current page, using |awful_bad|
-                if the box is too full.
-
-            */
-            if (page_total < page_goal) {
-                if ((page_so_far[3] != 0) || (page_so_far[4] != 0) ||
-                    (page_so_far[5] != 0))
-                    b = 0;
-                else
-                    b = badness(page_goal - page_total, page_so_far[2]);
-            } else if (page_total - page_goal > page_shrink) {
-                b = awful_bad;
-            } else {
-                b = badness(page_total - page_goal, page_shrink);
-            }
-            if (b < awful_bad) {
-                if (pi <= eject_penalty)
-                    c = pi;
-                else if (b < inf_bad)
-                    c = b + pi + insert_penalties;
-                else
-                    c = deplorable;
-            } else {
-                c = b;
-            }
-            if (insert_penalties >= 10000)
-                c = awful_bad;
-            if (tracing_pages_par > 0) {
-                /*tex Display the page break cost. */
-                begin_diagnostic();
-                tprint_nl("%");
-                tprint(" t=");
-                print_totals();
-                tprint(" g=");
-                print_scaled(page_goal);
-                tprint(" b=");
-                if (b == awful_bad)
-                    print_char('*');
-                else
-                    print_int(b);
-                tprint(" p=");
-                print_int(pi);
-                tprint(" c=");
-                if (c == awful_bad)
-                    print_char('*');
-                else
-                    print_int(c);
-                if (c <= least_page_cost)
-                    print_char('#');
-                end_diagnostic(false);
-            }
-            if (c <= least_page_cost) {
-                best_page_break = p;
-                best_size = page_goal;
-                least_page_cost = c;
-                r = vlink(page_ins_head);
-                while (r != page_ins_head) {
-                    best_ins_ptr(r) = last_ins_ptr(r);
-                    r = vlink(r);
-                }
-            }
-            if ((c == awful_bad) || (pi <= eject_penalty)) {
-                /*tex Output the current page at the best place. */
-                fire_up(p);
-                if (output_active) {
-                    /*tex User's output routine will act. */
-                    goto EXIT;
-                }
-                /*tex The page has been shipped out by default output routine. */
-                goto DONE;
-            }
-        }
-        if ((type(p) < glue_node) || (type(p) > kern_node))
-            goto CONTRIBUTE;
-      UPDATE_HEIGHTS:
-        /*tex
-
-            Go here to record glue in the |active_height| table. Update the
-            current page measurements with respect to the glue or kern specified
-            by node~|p|.
-
-        */
-        if (type(p) != kern_node) {
-            if (stretch_order(p) > 1)
-                page_so_far[1 + stretch_order(p)] = page_so_far[1 + stretch_order(p)] + stretch(p);
-            else
-                page_so_far[2 + stretch_order(p)] = page_so_far[2 + stretch_order(p)] + stretch(p);
-            page_shrink = page_shrink + shrink(p);
-            if ((shrink_order(p) != normal) && (shrink(p) != 0)) {
-                print_err("Infinite glue shrinkage found on current page");
-                help4(
-                    "The page about to be output contains some infinitely",
-                    "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
-                    "Such glue doesn't belong there; but you can safely proceed,",
-                    "since the offensive shrinkability has been made finite."
-                );
-                error();
-                reset_glue_to_zero(p);
-                shrink_order(p) = normal;
-            }
-        }
-        page_total = page_total + page_depth + width(p);
-        page_depth = 0;
-      CONTRIBUTE:
-        /*tex
-
-            Go here to link a node into the current page. Make sure that
-            |page_max_depth| is not exceeded.
-
-        */
-        if (page_depth > page_max_depth) {
-            page_total = page_total + page_depth - page_max_depth;
-            page_depth = page_max_depth;
-        }
-        /*tex Link node |p| into the current page and |goto done|. */
-        couple_nodes(page_tail, p);
-        page_tail = p;
-        try_couple_nodes(contrib_head,vlink(p));
-        vlink(p) = null;
-        goto DONE;
-      DONE1:
-        /*tex Recycle node |p|. */
-        try_couple_nodes(contrib_head,vlink(p));
-        vlink(p) = null;
-        if (saving_vdiscards_par > 0) {
-            if (page_disc == null) {
-                page_disc = p;
-            } else {
-                couple_nodes(tail_page_disc, p);
-            }
-            tail_page_disc = p;
-        } else {
-            flush_node_list(p);
-        }
-      DONE:
-        ;
-    } while (vlink(contrib_head) != null);
-    /*tex Make the contribution list empty by setting its tail to |contrib_head|. */
-    contrib_tail = contrib_head;
-  EXIT:
-    ;
-}
-
-/*tex
-
-    When the page builder has looked at as much material as could appear before
-    the next page break, it makes its decision. The break that gave minimum
-    badness will be used to put a completed ``page'' into box \.{\\outputbox},
-    with insertions appended to their other boxes.
-
-    We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The
-    program uses the fact that |bot_mark(x)<>null| implies |first_mark(x)<>null|;
-    it also knows that |bot_mark(x)=null| implies
-    |top_mark(x)=first_mark(x)=null|.
-
-    The |fire_up| subroutine prepares to output the current page at the best
-    place; then it fires up the user's output routine, if there is one, or it
-    simply ships out the page. There is one parameter, |c|, which represents the
-    node that was being contributed to the page when the decision to force an
-    output was made.
-
-*/
-
-void fire_up(halfword c)
-{
-    /*tex nodes being examined and/or changed */
-    halfword p, q, r, s;
-    /*tex predecessor of |p| */
-    halfword prev_p;
-    /*tex insertion box number */
-    int n;
-    /*tex should the present insertion be held over? */
-    boolean wait;
-    /*tex saved value of |vbadness| */
-    int save_vbadness;
-    /*tex saved value of |vfuzz| */
-    scaled save_vfuzz;
-    /*tex saved value of |split_top_skip| */
-    halfword save_split_top_skip;
-    /*tex for looping through the marks */
-    halfword i;
-    /*tex Set the value of |output_penalty|. */
-    if (type(best_page_break) == penalty_node) {
-        geq_word_define(int_base + output_penalty_code,
-                        penalty(best_page_break));
-        penalty(best_page_break) = inf_penalty;
-    } else {
-        geq_word_define(int_base + output_penalty_code, inf_penalty);
-    }
-
-    for (i = 0; i <= biggest_used_mark; i++) {
-        if (bot_mark(i) != null) {
-            if (top_mark(i) != null)
-                delete_token_ref(top_mark(i));
-            set_top_mark(i, bot_mark(i));
-            add_token_ref(top_mark(i));
-            delete_first_mark(i);
-        }
-    }
-    /*tex
-
-        Put the optimal current page into box |output_box|, update |first_mark|
-        and |bot_mark|, append insertions to their boxes, and put the remaining
-        nodes back on the contribution list.
-
-        As the page is finally being prepared for output, pointer |p| runs
-        through the vlist, with |prev_p| trailing behind; pointer |q| is the tail
-        of a list of insertions that are being held over for a subsequent page.
-
-    */
-    if (c == best_page_break) {
-        /*tex |c| not yet linked in */
-        best_page_break = null;
-    }
-    /*tex Ensure that box |output_box| is empty before output. */
-    if (box(output_box_par) != null) {
-        print_err("\\box");
-        print_int(output_box_par);
-        tprint(" is not void");
-        help2(
-            "You shouldn't use \\box\\outputbox except in \\output routines.",
-            "Proceed, and I'll discard its present contents."
-        );
-        box_error(output_box_par);
-    }
-    /*tex This will count the number of insertions held over. */
-    insert_penalties = 0;
-    save_split_top_skip = split_top_skip_par;
-    if (holding_inserts_par <= 0) {
-        /*tex
-
-            Prepare all the boxes involved in insertions to act as queues. If
-            many insertions are supposed to go into the same box, we want to know
-            the position of the last node in that box, so that we don't need to
-            waste time when linking further information into it. The
-            |last_ins_ptr| fields of the page insertion nodes are therefore used
-            for this purpose during the packaging phase.
-
-        */
-        r = vlink(page_ins_head);
-        while (r != page_ins_head) {
-            if (best_ins_ptr(r) != null) {
-                n = subtype(r);
-                ensure_vbox(n);
-                if (box(n) == null)
-                    box(n) = new_null_box();
-                p = box(n) + list_offset;
-                while (vlink(p) != null)
-                    p = vlink(p);
-                last_ins_ptr(r) = p;
-            }
-            r = vlink(r);
-        }
-
-    }
-    q = hold_head;
-    vlink(q) = null;
-    prev_p = page_head;
-    p = vlink(prev_p);
-    while (p != best_page_break) {
-        if (type(p) == ins_node) {
-            if (holding_inserts_par <= 0) {
-                /*tex
-
-                    Either insert the material specified by node |p| into the
-                    appropriate box, or hold it for the next page; also delete
-                    node |p| from the current page.
-
-                    We will set |best_ins_ptr:=null| and package the box
-                    corresponding to insertion node~|r|, just after making the
-                    final insertion into that box. If this final insertion is
-                    `|split_up_node|', the remainder after splitting and pruning
-                    (if any) will be carried over to the next page.
-
-                */
-                r = vlink(page_ins_head);
-                while (subtype(r) != subtype(p))
-                    r = vlink(r);
-                if (best_ins_ptr(r) == null) {
-                    wait = true;
-                } else {
-                    wait = false;
-                    s = last_ins_ptr(r);
-                    vlink(s) = ins_ptr(p);
-                    if (best_ins_ptr(r) == p) {
-                        halfword t;
-                        /*tex
-
-                            Wrap up the box specified by node |r|, splitting node
-                            |p| if called for; set |wait:=true| if node |p| holds
-                            a remainder after splitting.
-
-                        */
-                        if (type(r) == split_up_node) {
-                            if ((broken_ins(r) == p) && (broken_ptr(r) != null)) {
-                                while (vlink(s) != broken_ptr(r))
-                                    s = vlink(s);
-                                vlink(s) = null;
-                                split_top_skip_par = split_top_ptr(p);
-                                ins_ptr(p) =
-                                    prune_page_top(broken_ptr(r), false);
-                                if (ins_ptr(p) != null) {
-                                    t = vpack(ins_ptr(p), 0, additional, -1);
-                                    height(p) = height(t) + depth(t);
-                                    list_ptr(t) = null;
-                                    flush_node(t);
-                                    wait = true;
-                                }
-                            }
-                        }
-                        best_ins_ptr(r) = null;
-                        n = subtype(r);
-                        t = list_ptr(box(n));
-                        list_ptr(box(n)) = null;
-                        flush_node(box(n));
-                        box(n) = vpack(t, 0, additional, body_direction_par);
-
-                    } else {
-                        while (vlink(s) != null)
-                            s = vlink(s);
-                        last_ins_ptr(r) = s;
-                    }
-                }
-                /*tex
-
-                    Either append the insertion node |p| after node |q|, and
-                    remove it from the current page, or delete |node(p)|.
-
-                */
-                try_couple_nodes(prev_p, vlink(p));
-                vlink(p) = null;
-                if (wait) {
-                    couple_nodes(q, p);
-                    q = p;
-                    incr(insert_penalties);
-                } else {
-                    ins_ptr(p) = null;
-                    flush_node(p);
-                }
-                p = prev_p;
-
-            }
-        } else if (type(p) == mark_node) {
-            /*tex Update the values of |first_mark| and |bot_mark|. */
-            if (first_mark(mark_class(p)) == null) {
-                set_first_mark(mark_class(p), mark_ptr(p));
-                add_token_ref(first_mark(mark_class(p)));
-            }
-            if (bot_mark(mark_class(p)) != null)
-                delete_token_ref(bot_mark(mark_class(p)));
-            set_bot_mark(mark_class(p), mark_ptr(p));
-            add_token_ref(bot_mark(mark_class(p)));
-
-        }
-        prev_p = p;
-        p = vlink(prev_p);
-    }
-    split_top_skip_par = save_split_top_skip;
-    /*tex
-
-        Break the current page at node |p|, put it in box~|output_box|, and put
-        the remaining nodes on the contribution list.
-
-        When the following code is executed, the current page runs from node
-        |vlink(page_head)| to node |prev_p|, and the nodes from |p| to
-        |page_tail| are to be placed back at the front of the contribution list.
-        Furthermore the heldover insertions appear in a list from
-        |vlink(hold_head)| to |q|; we will put them into the current page list
-        for safekeeping while the user's output routine is active. We might have
-        |q=hold_head|; and |p=null| if and only if |prev_p=page_tail|. Error
-        messages are suppressed within |vpackage|, since the box might appear to
-        be overfull or underfull simply because the stretch and shrink from the
-        \.{\\skip} registers for inserts are not actually present in the box.
-
-    */
-    if (p != null) {
-        if (vlink(contrib_head) == null) {
-            contrib_tail = page_tail;
-        }
-        couple_nodes(page_tail,vlink(contrib_head));
-        couple_nodes(contrib_head, p);
-        vlink(prev_p) = null;
-    }
-    save_vbadness = vbadness_par;
-    vbadness_par = inf_bad;
-    save_vfuzz = vfuzz_par;
-    /*tex Inhibit error messages. */
-    vfuzz_par = max_dimen;
-    box(output_box_par) = filtered_vpackage(vlink(page_head),
-        best_size, exactly, page_max_depth, output_group, body_direction_par, 0, 0);
-    vbadness_par = save_vbadness;
-    vfuzz_par = save_vfuzz;
-    if (last_glue != max_halfword)
-        flush_node(last_glue);
-    /*tex Start a new current page. This sets |last_glue:=max_halfword|. */
-    start_new_page();
-    if (q != hold_head) {
-        vlink(page_head) = vlink(hold_head);
-        page_tail = q;
-    }
-    /*tex Delete the page-insertion nodes. */
-    r = vlink(page_ins_head);
-    while (r != page_ins_head) {
-	    /*tex Todo: couple. */
-        q = vlink(r);
-        flush_node(r);
-        r = q;
-    }
-    vlink(page_ins_head) = page_ins_head;
-    for (i = 0; i <= biggest_used_mark; i++) {
-        if ((top_mark(i) != null) && (first_mark(i) == null)) {
-            set_first_mark(i, top_mark(i));
-            add_token_ref(top_mark(i));
-        }
-    }
-    if (output_routine_par != null) {
-        if (dead_cycles >= max_dead_cycles_par) {
-            /*tex Explain that too many dead cycles have occurred in a row. */
-            print_err("Output loop---");
-            print_int(dead_cycles);
-            tprint(" consecutive dead cycles");
-            help3(
-                "I've concluded that your \\output is awry; it never does a",
-                "\\shipout, so I'm shipping \\box\\outputbox out myself. Next time",
-                "increase \\maxdeadcycles if you want me to be more patient!"
-            );
-            error();
-        } else {
-            /*tex Fire up the users output routine and |return|. */
-            output_active = true;
-            incr(dead_cycles);
-            push_nest();
-            mode = -vmode;
-            prev_depth_par = ignore_depth;
-            mode_line_par = -line;
-            begin_token_list(output_routine_par, output_text);
-            new_save_level(output_group);
-            normal_paragraph();
-            scan_left_brace();
-            return;
-        }
-    }
-    /*tex
-
-        Perform the default output routine. The list of heldover insertions,
-        running from |vlink(page_head)| to |page_tail|, must be moved to the
-        contribution list when the user has specified no output routine.
-
-    */
-    if (vlink(page_head) != null) {
-        if (vlink(contrib_head) == null) {
-            contrib_tail = page_tail;
-        } else {
-            vlink(page_tail) = vlink(contrib_head);
-        }
-        vlink(contrib_head) = vlink(page_head);
-        vlink(page_head) = null;
-        page_tail = page_head;
-    }
-    flush_node_list(page_disc);
-    page_disc = null;
-    ship_out(static_pdf, box(output_box_par), SHIPPING_PAGE);
-    box(output_box_par) = null;
-}
-
-/*tex
-
-    When the user's output routine finishes, it has constructed a vlist in
-    internal vertical mode, and \TeX\ will do the following:
-
-*/
-
-void resume_after_output(void)
-{
-    if ((iloc != null) || ((token_type != output_text) && (token_type != backed_up))) {
-        /*tex Recover from an unbalanced output routine */
-        print_err("Unbalanced output routine");
-        help2(
-            "Your sneaky output routine has problematic {'s and/or }'s.",
-            "I can't handle that very well; good luck."
-        );
-        error();
-        /*tex Loops forever if reading from a file, since |null=min_halfword<=0|. */
-        do {
-            get_token();
-        } while (iloc != null);
-    }
-    /*tex Conserve stack space in case more outputs are triggered. */
-    end_token_list();
-    end_graf(bottom_level);
-    unsave();
-    output_active = false;
-    insert_penalties = 0;
-    /*tex Ensure that box |output_box| is empty after output. */
-    if (box(output_box_par) != null) {
-        print_err("Output routine didn't use all of \\box");
-        print_int(output_box_par);
-        help3(
-            "Your \\output commands should empty \\box\\outputbox,",
-            "e.g., by saying `\\shipout\\box\\outputbox'.",
-            "Proceed; I'll discard its present contents."
-        );
-        box_error(output_box_par);
-    }
-    if (tail != head) {
-        /*tex Current list goes after heldover insertions. */
-        try_couple_nodes(page_tail, vlink(head));
-        page_tail = tail;
-    }
-    if (vlink(page_head) != null) {
-        /* Both go before heldover contributions. */
-        if (vlink(contrib_head) == null)
-            contrib_tail = page_tail;
-        try_couple_nodes(page_tail, vlink(contrib_head));
-        try_couple_nodes(contrib_head, vlink(page_head));
-        vlink(page_head) = null;
-        page_tail = page_head;
-    }
-    flush_node_list(page_disc);
-    page_disc = null;
-    pop_nest();
-    normal_page_filter(after_output);
-    build_page();
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/buildpage.w
@@ -0,0 +1,1013 @@
+% buildpage.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"
+
+@ @c
+#define mode mode_par
+#define head head_par
+#define tail tail_par
+
+@ When \TeX\ appends new material to its main vlist in vertical mode, it uses
+a method something like |vsplit| to decide where a page ends, except that
+the calculations are done ``on line'' as new items come in.
+The main complication in this process is that insertions must be put
+into their boxes and removed from the vlist, in a more-or-less optimum manner.
+
+We shall use the term ``current page'' for that part of the main vlist that
+is being considered as a candidate for being broken off and sent to the
+user's output routine. The current page starts at |vlink(page_head)|, and
+it ends at |page_tail|.  We have |page_head=page_tail| if this list is empty.
+@^current page@>
+
+Utter chaos would reign if the user kept changing page specifications
+while a page is being constructed, so the page builder keeps the pertinent
+specifications frozen as soon as the page receives its first box or
+insertion.  The global variable |page_contents| is |empty| when the
+current page contains only mark nodes and content-less whatsit nodes; it
+is |inserts_only| if the page contains only insertion nodes in addition to
+marks and whatsits.  Glue nodes, kern nodes, and penalty nodes are
+discarded until a box or rule node appears, at which time |page_contents|
+changes to |box_there|.  As soon as |page_contents| becomes non-|empty|,
+the current |vsize| and |max_depth| are squirreled away into |page_goal|
+and |page_max_depth|; the latter values will be used until the page has
+been forwarded to the user's output routine. The \.{\\topskip} adjustment
+is made when |page_contents| changes to |box_there|.
+
+Although |page_goal| starts out equal to |vsize|, it is decreased by the
+scaled natural height-plus-depth of the insertions considered so far, and by
+the \.{\\skip} corrections for those insertions. Therefore it represents
+the size into which the non-inserted material should fit, assuming that
+all insertions in the current page have been made.
+
+The global variables |best_page_break| and |least_page_cost| correspond
+respectively to the local variables |best_place| and |least_cost| in the
+|vert_break| routine that we have already studied; i.e., they record the
+location and value of the best place currently known for breaking the
+current page. The value of |page_goal| at the time of the best break is
+stored in |best_size|.
+
+@c
+halfword page_tail;             /* the final node on the current page */
+int page_contents;              /* what is on the current page so far? */
+scaled page_max_depth;          /* maximum box depth on page being built */
+halfword best_page_break;       /* break here to get the best page known so far */
+int least_page_cost;            /* the score for this currently best page */
+scaled best_size;               /* its |page_goal| */
+
+@ The page builder has another data structure to keep track of insertions.
+This is a list of four-word nodes, starting and ending at |page_ins_head|.
+That is, the first element of the list is node |r@t$_1$@>=vlink(page_ins_head)|;
+node $r_j$ is followed by |r@t$_{j+1}$@>=vlink(r@t$_j$@>)|; and if there are
+|n| items we have |r@t$_{n+1}$@>=page_ins_head|. The |subtype| field of
+each node in this list refers to an insertion number; for example, `\.{\\insert
+250}' would correspond to a node whose |subtype| is |qi(250)|
+(the same as the |subtype| field of the relevant |ins_node|). These |subtype|
+fields are in increasing order, and |subtype(page_ins_head)=65535|, so
+|page_ins_head| serves as a convenient sentinel
+at the end of the list. A record is present for each insertion number that
+appears in the current page.
+
+The |type| field in these nodes distinguishes two possibilities that
+might occur as we look ahead before deciding on the optimum page break.
+If |type(r)=inserting_node|, then |height(r)| contains the total of the
+height-plus-depth dimensions of the box and all its inserts seen so far.
+ |type(r)=split_up_node|, then no more insertions will be made into this box,
+because at least one previous insertion was too big to fit on the current
+page; |broken_ptr(r)| points to the node where that insertion will be
+split, if \TeX\ decides to split it, |broken_ins(r)| points to the
+insertion node that was tentatively split, and |height(r)| includes also the
+natural height plus depth of the part that would be split off.
+
+In both cases, |last_ins_ptr(r)| points to the last |ins_node|
+encountered for box |qo(subtype(r))| that would be at least partially
+inserted on the next page; and |best_ins_ptr(r)| points to the last
+such |ins_node| that should actually be inserted, to get the page with
+minimum badness among all page breaks considered so far. We have
+|best_ins_ptr(r)=null| if and only if no insertion for this box should
+be made to produce this optimum page.
+
+@ Pages are built by appending nodes to the current list in \TeX's
+vertical mode, which is at the outermost level of the semantic nest. This
+vlist is split into two parts; the ``current page'' that we have been
+talking so much about already, and the ``contribution list'' that receives
+new nodes as they are created.  The current page contains everything that
+the page builder has accounted for in its data structures, as described
+above, while the contribution list contains other things that have been
+generated by other parts of \TeX\ but have not yet been
+seen by the page builder.
+The contribution list starts at |vlink(contrib_head)|, and it ends at the
+current node in \TeX's vertical mode.
+
+When \TeX\ has appended new material in vertical mode, it calls the procedure
+|build_page|, which tries to catch up by moving nodes from the contribution
+list to the current page. This procedure will succeed in its goal of
+emptying the contribution list, unless a page break is discovered, i.e.,
+unless the current page has grown to the point where the optimum next
+page break has been determined. In the latter case, the nodes after the
+optimum break will go back onto the contribution list, and control will
+effectively pass to the user's output routine.
+
+We make |type(page_head)=glue_node|, so that an initial glue node on
+the current page will not be considered a valid breakpoint.
+
+@c
+void initialize_buildpage(void)
+{
+    subtype(page_ins_head) = 65535;
+    type(page_ins_head) = split_up_node;
+    vlink(page_ins_head) = page_ins_head;
+
+    type(page_head) = glue_node;
+    subtype(page_head) = normal;
+}
+
+
+@ An array |page_so_far| records the heights and depths of everything
+on the current page. This array contains six |scaled| numbers, like the
+similar arrays already considered in |line_break| and |vert_break|; and it
+also contains |page_goal| and |page_depth|, since these values are
+all accessible to the user via |set_page_dimen| commands. The
+value of |page_so_far[1]| is also called |page_total|.  The stretch
+and shrink components of the \.{\\skip} corrections for each insertion are
+included in |page_so_far|, but the natural space components of these
+corrections are not, since they have been subtracted from |page_goal|.
+
+The variable |page_depth| records the depth of the current page; it has been
+adjusted so that it is at most |page_max_depth|. The variable
+|last_glue| points to the glue specification of the most recent node
+contributed from the contribution list, if this was a glue node; otherwise
+|last_glue=max_halfword|. (If the contribution list is nonempty,
+however, the value of |last_glue| is not necessarily accurate.)
+The variables |last_penalty|, |last_kern|, and |last_node_type|
+are similar.  And
+finally, |insert_penalties| holds the sum of the penalties associated with
+all split and floating insertions.
+
+@c
+scaled page_so_far[8];          /* height and glue of the current page */
+halfword last_glue;             /* used to implement \.{\\lastskip} */
+int last_penalty;               /* used to implement \.{\\lastpenalty} */
+scaled last_kern;               /* used to implement \.{\\lastkern} */
+int last_node_type;             /* used to implement \.{\\lastnodetype} */
+int insert_penalties;           /* sum of the penalties for held-over insertions */
+
+#define print_plus(A,B) do { \
+    if (page_so_far[(A)]!=0) { \
+        tprint(" plus "); \
+        print_scaled(page_so_far[(A)]); \
+        tprint((B)); \
+    } \
+} while (0)
+
+void print_totals(void)
+{
+    print_scaled(page_total);
+    print_plus(2, "");
+    print_plus(3, "fil");
+    print_plus(4, "fill");
+    print_plus(5, "filll");
+    if (page_shrink != 0) {
+        tprint(" minus ");
+        print_scaled(page_shrink);
+    }
+}
+
+@ Here is a procedure that is called when the |page_contents| is changing
+from |empty| to |inserts_only| or |box_there|.
+
+@c
+#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
+#define set_page_so_far_zero(A) page_so_far[(A)]=0
+
+void freeze_page_specs(int s)
+{
+    page_contents = s;
+    page_goal = vsize_par;
+    page_max_depth = max_depth_par;
+    page_depth = 0;
+    do_all_six(set_page_so_far_zero);
+    least_page_cost = awful_bad;
+    if (tracing_pages_par > 0) {
+        begin_diagnostic();
+        tprint_nl("%% goal height=");
+        print_scaled(page_goal);
+        tprint(", max depth=");
+        print_scaled(page_max_depth);
+        end_diagnostic(false);
+    }
+}
+
+@ The global variable |output_active| is true during the time the
+user's output routine is driving \TeX.
+
+@c
+boolean output_active;          /* are we in the midst of an output routine? */
+
+@ The page builder is ready to start a fresh page if we initialize
+the following state variables. (However, the page insertion list is initialized
+elsewhere.)
+
+@c
+void start_new_page(void)
+{
+    page_contents = empty;
+    page_tail = page_head;
+    vlink(page_head) = null;
+    last_glue = max_halfword;
+    last_penalty = 0;
+    last_kern = 0;
+    last_node_type = -1;
+    page_depth = 0;
+    page_max_depth = 0;
+}
+
+@ At certain times box \.{\\outputbox} is supposed to be void (i.e., |null|),
+or an insertion box is supposed to be ready to accept a vertical list.
+If not, an error message is printed, and the following subroutine
+flushes the unwanted contents, reporting them to the user.
+
+@c
+static void box_error(int n)
+{
+    error();
+    begin_diagnostic();
+    tprint_nl("The following box has been deleted:");
+    show_box(box(n));
+    end_diagnostic(true);
+    flush_node_list(box(n));
+    box(n) = null;
+}
+
+@ The following procedure guarantees that a given box register
+does not contain an \.{\\hbox}.
+
+@c
+static void ensure_vbox(int n)
+{
+    halfword p;                 /* the box register contents */
+    p = box(n);
+    if (p != null && type(p) == hlist_node) {
+        print_err("Insertions can only be added to a vbox");
+        help3("Tut tut: You're trying to \\insert into a",
+              "\\box register that now contains an \\hbox.",
+              "Proceed, and I'll discard its present contents.");
+        box_error(n);
+    }
+}
+
+@ \TeX\ is not always in vertical mode at the time |build_page|
+is called; the current mode reflects what \TeX\ should return to, after
+the contribution list has been emptied. A call on |build_page| should
+be immediately followed by `|goto big_switch|', which is \TeX's central
+control point.
+
+@c
+void build_page(void)
+{                               /* append contributions to the current page */
+    halfword p;                 /* the node being appended */
+    halfword q, r;              /* nodes being examined */
+    int b, c;                   /* badness and cost of current page */
+    int pi = 0;                 /* penalty to be added to the badness */
+    int n;                      /* insertion box number */
+    scaled delta, h, w;         /* sizes used for insertion calculations */
+    int id, sk, i;
+    if ((vlink(contrib_head) == null) || output_active)
+        return;
+    do {
+      CONTINUE:
+        p = vlink(contrib_head);
+        /* Update the values of |last_glue|, |last_penalty|, and |last_kern| */
+        if (last_glue != max_halfword) {
+            flush_node(last_glue);
+            last_glue = max_halfword;
+        }
+        last_penalty = 0;
+        last_kern = 0;
+        last_node_type = type(p) + 1;
+        if (type(p) == glue_node) {
+            last_glue = new_glue(p);
+        } else if (type(p) == penalty_node) {
+            last_penalty = penalty(p);
+        } else if (type(p) == kern_node) {
+            last_kern = width(p);
+        }
+
+        /* Move node |p| to the current page; if it is time for a page break,
+           put the nodes following the break back onto the contribution list,
+           and |return| to the users output routine if there is one */
+
+        /* The code here is an example of a many-way switch into routines that
+           merge together in different places. Some people call this unstructured
+           programming, but the author doesn't see much wrong with it, as long as
+           the various labels have a well-understood meaning.
+         */
+        /* If the current page is empty and node |p| is to be deleted, |goto done1|;
+           otherwise use node |p| to update the state of the current page;
+           if this node is an insertion, |goto contribute|; otherwise if this node
+           is not a legal breakpoint, |goto contribute| or |update_heights|;
+           otherwise set |pi| to the penalty associated with this breakpoint */
+        /* The title of this section is already so long, it seems best to avoid
+           making it more accurate but still longer, by mentioning the fact that a
+           kern node at the end of the contribution list will not be contributed until
+           we know its successor. */
+        switch (type(p)) {
+        case hlist_node:
+        case vlist_node:
+        case rule_node:
+            if (page_contents < box_there) {
+                /* Initialize the current page, insert the \.{\\topskip} glue
+                   ahead of |p|, and |goto continue| */
+                if (page_contents == empty)
+                    freeze_page_specs(box_there);
+                else
+                    page_contents = box_there;
+                q = new_skip_param(top_skip_code);
+                if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) {
+                    if (width(q) > depth(p))
+                        width(q) = width(q) - depth(p);
+                    else
+                        width(q) = 0;
+                } else {
+                    if (width(q) > height(p))
+                        width(q) = width(q) - height(p);
+                    else
+                        width(q) = 0;
+                }
+                couple_nodes(q, p);
+                couple_nodes(contrib_head, q);
+                goto CONTINUE;
+
+            } else {
+                /* Prepare to move a box or rule node to the current page,
+                   then |goto contribute| */
+                if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) {
+                    page_total = page_total + page_depth + depth(p);
+                    page_depth = height(p);
+                } else {
+                    page_total = page_total + page_depth + height(p);
+                    page_depth = depth(p);
+                }
+                goto CONTRIBUTE;
+
+            }
+            break;
+        case boundary_node:
+        case whatsit_node:
+            goto CONTRIBUTE;
+            break;
+        case glue_node:
+            if (page_contents < box_there)
+                goto DONE1;
+            else if (precedes_break(page_tail))
+                pi = 0;
+            else
+                goto UPDATE_HEIGHTS;
+            break;
+        case kern_node:
+            if (page_contents < box_there)
+                goto DONE1;
+            else if (vlink(p) == null)
+                goto EXIT;
+            else if (type(vlink(p)) == glue_node)
+                pi = 0;
+            else
+                goto UPDATE_HEIGHTS;
+            break;
+        case penalty_node:
+            if (page_contents < box_there)
+                goto DONE1;
+            else
+                pi = penalty(p);
+            break;
+        case mark_node:
+            goto CONTRIBUTE;
+            break;
+        case ins_node:
+            /* Append an insertion to the current page and |goto contribute| */
+            if (page_contents == empty)
+                freeze_page_specs(inserts_only);
+            n = subtype(p);
+            r = page_ins_head;
+            i = 1 ;
+            while (n >= subtype(vlink(r))) {
+                r = vlink(r);
+                i = i + 1 ;
+            }
+            if (subtype(r) != n) {
+                /* Create a page insertion node with |subtype(r)=qi(n)|, and
+                   include the glue correction for box |n| in the
+                   current page state */
+                /* We take note of the value of \.{\\skip} |n| and the height plus depth
+                   of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is
+                   encountered for a new page. A user who changes the contents of \.{\\box}~|n|
+                   after that first \.{\\insert}~|n| had better be either extremely careful
+                   or extremely lucky, or both. */
+id = callback_defined(build_page_insert_callback);
+if (id != 0) {
+    run_callback(id, "dd->d",n,i,&sk);
+} else {
+    sk = n;
+}
+                q = new_node(inserting_node, n);
+                try_couple_nodes(q, vlink(r));
+                couple_nodes(r, q);
+                r = q;
+                ensure_vbox(n);
+                if (box(n) == null)
+                    height(r) = 0;
+                else
+                    height(r) = height(box(n)) + depth(box(n));
+                best_ins_ptr(r) = null;
+            /*  q = skip(n); */
+q = skip(sk);
+                if (count(n) == 1000)
+                    h = height(r);
+                else
+                    h = x_over_n(height(r), 1000) * count(n);
+                page_goal = page_goal - h - width(q);
+                if (stretch_order(q) > 1)
+                    page_so_far[1 + stretch_order(q)] = page_so_far[1 + stretch_order(q)] + stretch(q);
+                else
+                    page_so_far[2 + stretch_order(q)] = page_so_far[2 + stretch_order(q)] + stretch(q);
+                page_shrink = page_shrink + shrink(q);
+                if ((shrink_order(q) != normal) && (shrink(q) != 0)) {
+                    print_err("Infinite glue shrinkage inserted from \\skip");
+                    print_int(n);
+                    help3
+                        ("The correction glue for page breaking with insertions",
+                         "must have finite shrinkability. But you may proceed,",
+                         "since the offensive shrinkability has been made finite.");
+                    error();
+                }
+
+            }
+            if (type(r) == split_up_node) {
+                insert_penalties = insert_penalties + float_cost(p);
+            } else {
+                last_ins_ptr(r) = p;
+                delta = page_goal - page_total - page_depth + page_shrink;
+                /* this much room is left if we shrink the maximum */
+                if (count(n) == 1000)
+                    h = height(p);
+                else
+                    h = x_over_n(height(p), 1000) * count(n);   /* this much room is needed */
+                if (((h <= 0) || (h <= delta))
+                    && (height(p) + height(r) <= dimen(n))) {
+                    page_goal = page_goal - h;
+                    height(r) = height(r) + height(p);
+                } else {
+                    /* Find the best way to split the insertion, and change
+                       |type(r)| to |split_up_node| */
+                    /* Here is the code that will split a long footnote between pages, in an
+                       emergency. The current situation deserves to be recapitulated: Node |p|
+                       is an insertion into box |n|; the insertion will not fit, in its entirety,
+                       either because it would make the total contents of box |n| greater than
+                       \.{\\dimen} |n|, or because it would make the incremental amount of growth
+                       |h| greater than the available space |delta|, or both. (This amount |h| has
+                       been weighted by the insertion scaling factor, i.e., by \.{\\count} |n|
+                       over 1000.) Now we will choose the best way to break the vlist of the
+                       insertion, using the same criteria as in the \.{\\vsplit} operation.
+                     */
+                    if (count(n) <= 0) {
+                        w = max_dimen;
+                    } else {
+                        w = page_goal - page_total - page_depth;
+                        if (count(n) != 1000)
+                            w = x_over_n(w, count(n)) * 1000;
+                    }
+                    if (w > dimen(n) - height(r))
+                        w = dimen(n) - height(r);
+                    q = vert_break(ins_ptr(p), w, depth(p));
+                    height(r) = height(r) + best_height_plus_depth;
+                    if (tracing_pages_par > 0) {
+                        /* Display the insertion split cost */
+                        begin_diagnostic();
+                        tprint_nl("% split");
+                        print_int(n);
+                        tprint(" to ");
+                        print_scaled(w);
+                        print_char(',');
+                        print_scaled(best_height_plus_depth);
+                        tprint(" p=");
+                        if (q == null)
+                            print_int(eject_penalty);
+                        else if (type(q) == penalty_node)
+                            print_int(penalty(q));
+                        else
+                            print_char('0');
+                        end_diagnostic(false);
+
+                    }
+                    if (count(n) != 1000)
+                        best_height_plus_depth =
+                            x_over_n(best_height_plus_depth, 1000) * count(n);
+                    page_goal = page_goal - best_height_plus_depth;
+                    type(r) = split_up_node;
+                    broken_ptr(r) = q;
+                    broken_ins(r) = p;
+                    if (q == null)
+                        insert_penalties = insert_penalties + eject_penalty;
+                    else if (type(q) == penalty_node)
+                        insert_penalties = insert_penalties + penalty(q);
+                }
+            }
+            goto CONTRIBUTE;
+
+            break;
+        default:
+            fprintf(stderr, "type(p)=%d\n", type(p));
+            confusion("page");
+            break;
+        }
+
+        /* Check if node |p| is a new champion breakpoint; then if it is time for
+           a page break, prepare for output, and either fire up the users
+           output routine and |return| or ship out the page and |goto done| */
+
+        if (pi < inf_penalty) {
+            /* Compute the badness, |b|, of the current page,
+               using |awful_bad| if the box is too full */
+            if (page_total < page_goal) {
+                if ((page_so_far[3] != 0) || (page_so_far[4] != 0) ||
+                    (page_so_far[5] != 0))
+                    b = 0;
+                else
+                    b = badness(page_goal - page_total, page_so_far[2]);
+            } else if (page_total - page_goal > page_shrink) {
+                b = awful_bad;
+            } else {
+                b = badness(page_total - page_goal, page_shrink);
+            }
+
+            if (b < awful_bad) {
+                if (pi <= eject_penalty)
+                    c = pi;
+                else if (b < inf_bad)
+                    c = b + pi + insert_penalties;
+                else
+                    c = deplorable;
+            } else {
+                c = b;
+            }
+            if (insert_penalties >= 10000)
+                c = awful_bad;
+            if (tracing_pages_par > 0) {
+                /* Display the page break cost */
+                begin_diagnostic();
+                tprint_nl("%");
+                tprint(" t=");
+                print_totals();
+                tprint(" g=");
+                print_scaled(page_goal);
+                tprint(" b=");
+                if (b == awful_bad)
+                    print_char('*');
+                else
+                    print_int(b);
+                tprint(" p=");
+                print_int(pi);
+                tprint(" c=");
+                if (c == awful_bad)
+                    print_char('*');
+                else
+                    print_int(c);
+                if (c <= least_page_cost)
+                    print_char('#');
+                end_diagnostic(false);
+
+            }
+            if (c <= least_page_cost) {
+                best_page_break = p;
+                best_size = page_goal;
+                least_page_cost = c;
+                r = vlink(page_ins_head);
+                while (r != page_ins_head) {
+                    best_ins_ptr(r) = last_ins_ptr(r);
+                    r = vlink(r);
+                }
+            }
+            if ((c == awful_bad) || (pi <= eject_penalty)) {
+                fire_up(p);     /* output the current page at the best place */
+                if (output_active)
+                    goto EXIT;  /* user's output routine will act */
+                goto DONE;      /* the page has been shipped out by default output routine */
+            }
+        }
+
+        if ((type(p) < glue_node) || (type(p) > kern_node))
+            goto CONTRIBUTE;
+
+      UPDATE_HEIGHTS:          /* go here to record glue in the |active_height| table */
+
+        /* Update the current page measurements with respect to the
+           glue or kern specified by node~|p| */
+        if (type(p) != kern_node) {
+            if (stretch_order(p) > 1)
+                page_so_far[1 + stretch_order(p)] = page_so_far[1 + stretch_order(p)] + stretch(p);
+            else
+                page_so_far[2 + stretch_order(p)] = page_so_far[2 + stretch_order(p)] + stretch(p);
+            page_shrink = page_shrink + shrink(p);
+            if ((shrink_order(p) != normal) && (shrink(p) != 0)) {
+                print_err("Infinite glue shrinkage found on current page");
+                help4("The page about to be output contains some infinitely",
+                      "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
+                      "Such glue doesn't belong there; but you can safely proceed,",
+                      "since the offensive shrinkability has been made finite.");
+                error();
+                reset_glue_to_zero(p);
+                shrink_order(p) = normal;
+            }
+        }
+        page_total = page_total + page_depth + width(p);
+        page_depth = 0;
+
+      CONTRIBUTE:              /* go here to link a node into the current page */
+
+        /* Make sure that |page_max_depth| is not exceeded */
+        if (page_depth > page_max_depth) {
+            page_total = page_total + page_depth - page_max_depth;
+            page_depth = page_max_depth;
+        }
+
+        /* Link node |p| into the current page and |goto done| */
+        couple_nodes(page_tail, p);
+        page_tail = p;
+        try_couple_nodes(contrib_head,vlink(p));
+        vlink(p) = null;
+        goto DONE;
+      DONE1:
+        /* Recycle node |p| */
+        try_couple_nodes(contrib_head,vlink(p));
+        vlink(p) = null;
+        if (saving_vdiscards_par > 0) {
+            if (page_disc == null) {
+                page_disc = p;
+            } else {
+                couple_nodes(tail_page_disc, p);
+            }
+            tail_page_disc = p;
+        } else {
+            flush_node_list(p);
+        }
+      DONE:
+        ;
+    } while (vlink(contrib_head) != null);
+    /* Make the contribution list empty by setting its tail to |contrib_head| */
+    contrib_tail = contrib_head;
+  EXIT:
+    ;
+}
+
+@ When the page builder has looked at as much material as could appear before
+the next page break, it makes its decision. The break that gave minimum
+badness will be used to put a completed ``page'' into box \.{\\outputbox}, with insertions
+appended to their other boxes.
+
+We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The
+program uses the fact that |bot_mark(x)<>null| implies |first_mark(x)<>null|;
+it also knows that |bot_mark(x)=null| implies |top_mark(x)=first_mark(x)=null|.
+
+The |fire_up| subroutine prepares to output the current page at the best
+place; then it fires up the user's output routine, if there is one,
+or it simply ships out the page. There is one parameter, |c|, which represents
+the node that was being contributed to the page when the decision to
+force an output was made.
+
+@c
+void fire_up(halfword c)
+{
+    halfword p, q, r, s;        /* nodes being examined and/or changed */
+    halfword prev_p;            /* predecessor of |p| */
+    int n;                      /* insertion box number */
+    boolean wait;               /* should the present insertion be held over? */
+    int save_vbadness;          /* saved value of |vbadness| */
+    scaled save_vfuzz;          /* saved value of |vfuzz| */
+    halfword save_split_top_skip;       /* saved value of |split_top_skip| */
+    halfword i;                 /* for looping through the marks */
+
+    /* Set the value of |output_penalty| */
+    if (type(best_page_break) == penalty_node) {
+        geq_word_define(int_base + output_penalty_code,
+                        penalty(best_page_break));
+        penalty(best_page_break) = inf_penalty;
+    } else {
+        geq_word_define(int_base + output_penalty_code, inf_penalty);
+    }
+
+    for (i = 0; i <= biggest_used_mark; i++) {
+        if (bot_mark(i) != null) {
+            if (top_mark(i) != null)
+                delete_token_ref(top_mark(i));
+            set_top_mark(i, bot_mark(i));
+            add_token_ref(top_mark(i));
+            delete_first_mark(i);
+        }
+    }
+    /* Put the optimal current page into box |output_box|, update |first_mark| and
+       |bot_mark|, append insertions to their boxes, and put the
+       remaining nodes back on the contribution list; */
+
+    /* As the page is finally being prepared for output,
+       pointer |p| runs through the vlist, with |prev_p| trailing behind;
+       pointer |q| is the tail of a list of insertions that
+       are being held over for a subsequent page. */
+
+    if (c == best_page_break)
+        best_page_break = null; /* |c| not yet linked in */
+    /* Ensure that box |output_box| is empty before output */
+    if (box(output_box_par) != null) {
+        print_err("\\box");
+        print_int(output_box_par);
+        tprint(" is not void");
+        help2("You shouldn't use \\box\\outputbox except in \\output routines.",
+              "Proceed, and I'll discard its present contents.");
+        box_error(output_box_par);
+    }
+
+    insert_penalties = 0;       /* this will count the number of insertions held over */
+    save_split_top_skip = split_top_skip_par;
+    if (holding_inserts_par <= 0) {
+        /* Prepare all the boxes involved in insertions to act as queues */
+        /* If many insertions are supposed to go into the same box, we want to know
+           the position of the last node in that box, so that we don't need to waste time
+           when linking further information into it. The |last_ins_ptr| fields of the
+           page insertion nodes are therefore used for this purpose during the
+           packaging phase. */
+
+        r = vlink(page_ins_head);
+        while (r != page_ins_head) {
+            if (best_ins_ptr(r) != null) {
+                n = subtype(r);
+                ensure_vbox(n);
+                if (box(n) == null)
+                    box(n) = new_null_box();
+                p = box(n) + list_offset;
+                while (vlink(p) != null)
+                    p = vlink(p);
+                last_ins_ptr(r) = p;
+            }
+            r = vlink(r);
+        }
+
+    }
+    q = hold_head;
+    vlink(q) = null;
+    prev_p = page_head;
+    p = vlink(prev_p);
+    while (p != best_page_break) {
+        if (type(p) == ins_node) {
+            if (holding_inserts_par <= 0) {
+                /* Either insert the material specified by node |p| into the
+                   appropriate box, or hold it for the next page;
+                   also delete node |p| from the current page */
+                /* We will set |best_ins_ptr:=null| and package the box corresponding to
+                   insertion node~|r|, just after making the final insertion into that box.
+                   If this final insertion is `|split_up_node|', the remainder after splitting
+                   and pruning (if any) will be carried over to the next page. */
+                r = vlink(page_ins_head);
+                while (subtype(r) != subtype(p))
+                    r = vlink(r);
+                if (best_ins_ptr(r) == null) {
+                    wait = true;
+                } else {
+                    wait = false;
+                    s = last_ins_ptr(r);
+                    vlink(s) = ins_ptr(p);
+                    if (best_ins_ptr(r) == p) {
+                        halfword t; /* was a global temp_ptr */
+                        /* Wrap up the box specified by node |r|, splitting node |p| if
+                           called for; set |wait:=true| if node |p| holds a remainder after
+                           splitting */
+                        if (type(r) == split_up_node) {
+                            if ((broken_ins(r) == p) && (broken_ptr(r) != null)) {
+                                while (vlink(s) != broken_ptr(r))
+                                    s = vlink(s);
+                                vlink(s) = null;
+                                split_top_skip_par = split_top_ptr(p);
+                                ins_ptr(p) =
+                                    prune_page_top(broken_ptr(r), false);
+                                if (ins_ptr(p) != null) {
+                                    t = vpack(ins_ptr(p), 0, additional, -1);
+                                    height(p) = height(t) + depth(t);
+                                    list_ptr(t) = null;
+                                    flush_node(t);
+                                    wait = true;
+                                }
+                            }
+                        }
+                        best_ins_ptr(r) = null;
+                        n = subtype(r);
+                        t = list_ptr(box(n));
+                        list_ptr(box(n)) = null;
+                        flush_node(box(n));
+                        box(n) = vpack(t, 0, additional, body_direction_par);
+
+                    } else {
+                        while (vlink(s) != null)
+                            s = vlink(s);
+                        last_ins_ptr(r) = s;
+                    }
+                }
+                /* Either append the insertion node |p| after node |q|, and remove it
+                   from the current page, or delete |node(p)| */
+                try_couple_nodes(prev_p, vlink(p));
+                vlink(p) = null;
+                if (wait) {
+                    couple_nodes(q, p);
+                    q = p;
+                    incr(insert_penalties);
+                } else {
+                    ins_ptr(p) = null;
+                    flush_node(p);
+                }
+                p = prev_p;
+
+            }
+        } else if (type(p) == mark_node) {
+            /* Update the values of |first_mark| and |bot_mark| */
+            if (first_mark(mark_class(p)) == null) {
+                set_first_mark(mark_class(p), mark_ptr(p));
+                add_token_ref(first_mark(mark_class(p)));
+            }
+            if (bot_mark(mark_class(p)) != null)
+                delete_token_ref(bot_mark(mark_class(p)));
+            set_bot_mark(mark_class(p), mark_ptr(p));
+            add_token_ref(bot_mark(mark_class(p)));
+
+        }
+        prev_p = p;
+        p = vlink(prev_p);
+    }
+    split_top_skip_par = save_split_top_skip;
+    /* Break the current page at node |p|, put it in box~|output_box|,
+       and put the remaining nodes on the contribution list */
+    /* When the following code is executed, the current page runs from node
+       |vlink(page_head)| to node |prev_p|, and the nodes from |p| to |page_tail|
+       are to be placed back at the front of the contribution list. Furthermore
+       the heldover insertions appear in a list from |vlink(hold_head)| to |q|; we
+       will put them into the current page list for safekeeping while the user's
+       output routine is active.  We might have |q=hold_head|; and |p=null| if
+       and only if |prev_p=page_tail|. Error messages are suppressed within
+       |vpackage|, since the box might appear to be overfull or underfull simply
+       because the stretch and shrink from the \.{\\skip} registers for inserts
+       are not actually present in the box. */
+
+    if (p != null) {
+        if (vlink(contrib_head) == null) {
+            contrib_tail = page_tail;
+        }
+        couple_nodes(page_tail,vlink(contrib_head));
+        couple_nodes(contrib_head, p);
+        vlink(prev_p) = null;
+    }
+    save_vbadness = vbadness_par;
+    vbadness_par = inf_bad;
+    save_vfuzz = vfuzz_par;
+    vfuzz_par = max_dimen;          /* inhibit error messages */
+    box(output_box_par) = filtered_vpackage(vlink(page_head),
+        best_size, exactly, page_max_depth, output_group, body_direction_par, 0, 0);
+    vbadness_par = save_vbadness;
+    vfuzz_par = save_vfuzz;
+    if (last_glue != max_halfword)
+        flush_node(last_glue);
+    /* Start a new current page */
+    start_new_page();           /* this sets |last_glue:=max_halfword| */
+    if (q != hold_head) {
+        vlink(page_head) = vlink(hold_head);
+        page_tail = q;
+    }
+
+    /* Delete the page-insertion nodes */
+    r = vlink(page_ins_head);
+    while (r != page_ins_head) {
+	    /* todo: couple */
+        q = vlink(r);
+        flush_node(r);
+        r = q;
+    }
+    vlink(page_ins_head) = page_ins_head;
+
+    for (i = 0; i <= biggest_used_mark; i++) {
+        if ((top_mark(i) != null) && (first_mark(i) == null)) {
+            set_first_mark(i, top_mark(i));
+            add_token_ref(top_mark(i));
+        }
+    }
+    if (output_routine_par != null) {
+        if (dead_cycles >= max_dead_cycles_par) {
+            /* Explain that too many dead cycles have occurred in a row */
+            print_err("Output loop---");
+            print_int(dead_cycles);
+            tprint(" consecutive dead cycles");
+            help3("I've concluded that your \\output is awry; it never does a",
+                  "\\shipout, so I'm shipping \\box\\outputbox out myself. Next time",
+                  "increase \\maxdeadcycles if you want me to be more patient!");
+            error();
+
+        } else {
+            /* Fire up the users output routine and |return| */
+            output_active = true;
+            incr(dead_cycles);
+            push_nest();
+            mode = -vmode;
+            prev_depth_par = ignore_depth;
+            mode_line_par = -line;
+            begin_token_list(output_routine_par, output_text);
+            new_save_level(output_group);
+            normal_paragraph();
+            scan_left_brace();
+            return;
+
+        }
+    }
+    /* Perform the default output routine */
+    /* The list of heldover insertions, running from |vlink(page_head)| to
+       |page_tail|, must be moved to the contribution list when the user has
+       specified no output routine. */
+    if (vlink(page_head) != null) {
+        if (vlink(contrib_head) == null) {
+            contrib_tail = page_tail;
+        } else {
+            vlink(page_tail) = vlink(contrib_head);
+        }
+        vlink(contrib_head) = vlink(page_head);
+        vlink(page_head) = null;
+        page_tail = page_head;
+    }
+    flush_node_list(page_disc);
+    page_disc = null;
+    ship_out(static_pdf, box(output_box_par), SHIPPING_PAGE);
+    box(output_box_par) = null;
+}
+
+@ When the user's output routine finishes, it has constructed a vlist
+in internal vertical mode, and \TeX\ will do the following:
+
+@c
+void resume_after_output(void)
+{
+    if ((iloc != null)
+        || ((token_type != output_text) && (token_type != backed_up))) {
+        /* Recover from an unbalanced output routine */
+        print_err("Unbalanced output routine");
+        help2("Your sneaky output routine has problematic {'s and/or }'s.",
+              "I can't handle that very well; good luck.");
+        error();
+        do {
+            get_token();
+        } while (iloc != null);
+        /* loops forever if reading from a file, since |null=min_halfword<=0| */
+
+    }
+    end_token_list();           /* conserve stack space in case more outputs are triggered */
+    end_graf(bottom_level);
+    unsave();
+    output_active = false;
+    insert_penalties = 0;
+    /* Ensure that box |output_box| is empty after output */
+    if (box(output_box_par) != null) {
+        print_err("Output routine didn't use all of \\box");
+        print_int(output_box_par);
+        help3("Your \\output commands should empty \\box\\outputbox,",
+              "e.g., by saying `\\shipout\\box\\outputbox'.",
+              "Proceed; I'll discard its present contents.");
+        box_error(output_box_par);
+    }
+
+    if (tail != head) {         /* current list goes after heldover insertions */
+        try_couple_nodes(page_tail, vlink(head));
+        page_tail = tail;
+    }
+    if (vlink(page_head) != null) {     /* and both go before heldover contributions */
+        if (vlink(contrib_head) == null)
+            contrib_tail = page_tail;
+        try_couple_nodes(page_tail, vlink(contrib_head));
+        try_couple_nodes(contrib_head, vlink(page_head));
+        vlink(page_head) = null;
+        page_tail = page_head;
+    }
+    flush_node_list(page_disc);
+    page_disc = null;
+    pop_nest();
+    normal_page_filter(after_output);
+    build_page();
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/commands.w
@@ -0,0 +1,894 @@
+% 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_luatex("mathscriptboxmode", assign_int_cmd, int_base + math_script_box_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_luatex("prerelpenalty", assign_int_cmd, int_base + pre_rel_penalty_code, int_base);
+    primitive_luatex("prebinoppenalty", assign_int_cmd, int_base + pre_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_luatex("mathpenaltiesmode", assign_int_cmd, int_base + math_penalties_mode_code, int_base);
+    primitive_luatex("mathdelimitersmode", assign_int_cmd, int_base + math_delimiters_mode_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);
+    primitive_luatex("automatichyphenmode", assign_int_cmd, int_base + automatic_hyphen_mode_code, int_base);
+    primitive_luatex("breakafterdirmode", assign_int_cmd, int_base + break_after_dir_mode_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("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); /* good old tex */
+    primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0);
+    primitive_luatex("explicitdiscretionary", discretionary_cmd, explicit_disc, 0);
+    primitive_luatex("automaticdiscretionary", discretionary_cmd, automatic_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_luatex("Unosuperscript", no_super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd);
+    primitive_luatex("Unosubscript", no_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("suppressprimitiveerror", assign_int_cmd, int_base + suppress_primitive_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);
+
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/commands.c
+++ /dev/null
@@ -1,932 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-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.
-
-*/
-
-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_luatex("mathscriptboxmode", assign_int_cmd, int_base + math_script_box_mode_code, int_base);
-    primitive_luatex("mathscriptcharmode", assign_int_cmd, int_base + math_script_char_mode_code, int_base);
-    primitive_luatex("mathrulethicknessmode", assign_int_cmd, int_base + math_rule_thickness_mode_code, int_base);
-    primitive_luatex("mathflattenmode", assign_int_cmd, int_base + math_flatten_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);
-
-    /*tex 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_luatex("prerelpenalty", assign_int_cmd, int_base + pre_rel_penalty_code, int_base);
-    primitive_luatex("prebinoppenalty", assign_int_cmd, int_base + pre_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_luatex("mathpenaltiesmode", assign_int_cmd, int_base + math_penalties_mode_code, int_base);
-    primitive_luatex("mathdelimitersmode", assign_int_cmd, int_base + math_delimiters_mode_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);
-    primitive_luatex("automatichyphenmode", assign_int_cmd, int_base + automatic_hyphen_mode_code, int_base);
-    primitive_luatex("compoundhyphenmode", assign_int_cmd, int_base + compound_hyphen_mode_code, int_base);
-    primitive_luatex("breakafterdirmode", assign_int_cmd, int_base + break_after_dir_mode_code, int_base);
-    primitive_luatex("exceptionpenalty", assign_int_cmd, int_base + exception_penalty_code, int_base);
-    primitive_luatex("fixupboxesmode", assign_int_cmd, int_base + fixup_boxes_code, int_base);
-
-    /*tex
-
-    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("etoksapp", combine_toks_cmd, 1, 0);
-    primitive_luatex("tokspre", combine_toks_cmd, 2, 0);
-    primitive_luatex("etokspre", combine_toks_cmd, 3, 0);
-    primitive_luatex("gtoksapp", combine_toks_cmd, 4, 0);
-    primitive_luatex("xtoksapp", combine_toks_cmd, 5, 0);
-    primitive_luatex("gtokspre", combine_toks_cmd, 6, 0);
-    primitive_luatex("xtokspre", combine_toks_cmd, 7, 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_luatex("luafunctioncall", lua_function_call_cmd, 0, 0);
-    primitive_luatex("luabytecodecall", lua_bytecode_call_cmd, 0, 0);
-    primitive_luatex("luadef", def_lua_call_cmd, 0, 0);
-    primitive_tex("par", par_end_cmd, too_big_char, too_big_char);
-    par_loc = cur_val;
-    par_token = cs_token_flag + par_loc;
-
-    /*tex
-        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_luatex("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("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("luabytecode", convert_cmd, lua_bytecode_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_luatex("immediateassignment", convert_cmd, immediate_assignment_code, 0);
-    primitive_luatex("immediateassigned", convert_cmd, immediate_assigned_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, if_x_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_luatex("ifcondition", if_test_cmd, if_condition_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 - 2, 0);        /* |ship_out_flag=leader_flag-2| */
-    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_luatex("boxdirection", assign_box_direction_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); /* good old tex */
-    primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0);
-    primitive_luatex("explicitdiscretionary", discretionary_cmd, explicit_disc, 0);
-    primitive_luatex("automaticdiscretionary", discretionary_cmd, automatic_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_luatex("Unosuperscript", no_super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd);
-    primitive_luatex("Unosubscript", no_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("glet", let_cmd, 0, 0);
-    primitive_tex("let", let_cmd, 1, 0);
-    primitive_tex("futurelet", let_cmd, 2, 0);
-    primitive_luatex("letcharcode", let_cmd, 3, 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);
-
-    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);
-
-    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_luatex("endlocalcontrol", extension_cmd, end_local_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("lateluafunction", normal_cmd, late_lua_call_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);
-
-    primitive_luatex("luacopyinputnodes", assign_int_cmd, int_base + copy_lua_input_nodes_code, int_base);
-
-    primitive_luatex("pagedirection", assign_direction_cmd, int_base + page_direction_code, dir_base);
-    primitive_luatex("bodydirection", assign_direction_cmd, int_base + body_direction_code, dir_base);
-    primitive_luatex("pardirection",  assign_direction_cmd, int_base + par_direction_code,  dir_base);
-    primitive_luatex("textdirection", assign_direction_cmd, int_base + text_direction_code, dir_base);
-    primitive_luatex("mathdirection", assign_direction_cmd, int_base + math_direction_code, dir_base);
-    primitive_luatex("linedirection", assign_direction_cmd, int_base + line_direction_code, dir_base);
-
-    /*
-        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);
-
-}
-
-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);
-
-    /*tex
-
-        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("suppressprimitiveerror", assign_int_cmd, int_base + suppress_primitive_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);
-
-    /*tex
-
-        The \.{\\showtokens} command displays a token list.
-
-    */
-
-    primitive_etex("showtokens", xray_cmd, show_tokens, 0);
-
-    /*tex
-
-        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);
-
-    /*tex
-
-        The \.{\\showifs} command displays all currently active conditionals.
-
-    */
-
-    primitive_etex("showifs", xray_cmd, show_ifs, 0);
-
-    /*tex
-
-        The \.{\\interactionmode} primitive allows to query and set the interaction mode.
-
-    */
-
-    primitive_etex("interactionmode", set_page_int_cmd, 2, 0);
-
-    /*tex
-
-        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);
-
-    /*tex
-
-        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);
-
-    /*tex
-
-        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);
-
-    /*tex
-
-        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);
-
-    /*tex
-
-        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);
-
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/conditional.w
@@ -0,0 +1,552 @@
+% conditional.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 consider now the way \TeX\ handles various kinds of \.{\\if} commands.
+
+@ Conditions can be inside conditions, and this nesting has a stack
+that is independent of the |save_stack|.
+
+Four global variables represent the top of the condition stack:
+|cond_ptr| points to pushed-down entries, if any; |if_limit| specifies
+the largest code of a |fi_or_else| command that is syntactically legal;
+|cur_if| is the name of the current type of conditional; and |if_line|
+is the line number at which it began.
+
+If no conditions are currently in progress, the condition stack has the
+special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
+Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and
+|link| fields of the first word contain |if_limit|, |cur_if|, and
+|cond_ptr| at the next level, and the second word contains the
+corresponding |if_line|.
+
+@c
+halfword cond_ptr;              /* top of the condition stack */
+int if_limit;                   /* upper bound on |fi_or_else| codes */
+int cur_if;                     /* type of conditional being worked on */
+int if_line;                    /* line where that conditional began */
+
+@ When we skip conditional text, we keep track of the line number
+where skipping began, for use in error messages.
+
+@c
+int skip_line;                  /* skipping began here */
+
+@ Here is a procedure that ignores text until coming to an \.{\\or},
+\.{\\else}, or \.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$
+nesting. After it has acted, |cur_chr| will indicate the token that
+was found, but |cur_tok| will not be set (because this makes the
+procedure run faster).
+
+@c
+void pass_text(void)
+{
+    int l = 0;                                /* level of $\.{\\if}\ldots\.{\\fi}$ nesting */
+    int save_scanner_status = scanner_status; /* |scanner_status| upon entry */
+    scanner_status = skipping;
+    skip_line = line;
+    while (1) {
+        get_next();
+        if (cur_cmd == fi_or_else_cmd) {
+            if (l == 0)
+                break;
+            if (cur_chr == fi_code)
+                decr(l);
+        } else if (cur_cmd == if_test_cmd) {
+            incr(l);
+        }
+    }
+    scanner_status = save_scanner_status;
+    if (tracing_ifs_par > 0)
+        show_cur_cmd_chr();
+}
+
+@ When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then
+if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if}
+condition has been evaluated, \.{\\relax} will be inserted.
+For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}'
+would otherwise require something after the `\.1'.
+
+@c
+void push_condition_stack(void)
+{
+    halfword p = new_node(if_node, 0);
+    vlink(p) = cond_ptr;
+    if_limit_type(p) = (quarterword) if_limit;
+    if_limit_subtype(p) = (quarterword) cur_if;
+    if_line_field(p) = if_line;
+    cond_ptr = p;
+    cur_if = cur_chr;
+    if_limit = if_code;
+    if_line = line;
+}
+
+void pop_condition_stack(void)
+{
+    halfword p;
+    if (if_stack[in_open] == cond_ptr)
+        if_warning();
+    /* conditionals possibly not properly nested with files */
+    p = cond_ptr;
+    if_line = if_line_field(p);
+    cur_if = if_limit_subtype(p);
+    if_limit = if_limit_type(p);
+    cond_ptr = vlink(p);
+    flush_node(p);
+}
+
+@ Here's a procedure that changes the |if_limit| code corresponding to
+a given value of |cond_ptr|.
+
+@c
+void change_if_limit(int l, halfword p)
+{
+    if (p == cond_ptr) {
+        /* that's the easy case */
+        if_limit = l;
+    } else {
+        halfword q = cond_ptr;
+        while (1) {
+            if (q == null)
+                confusion("if");
+            if (vlink(q) == p) {
+                if_limit_type(q) = (quarterword) l;
+                return;
+            }
+            q = vlink(q);
+        }
+    }
+}
+
+@ The conditional \.{\\ifcsname} is equivalent to \.{\\expandafter}
+\.{\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new
+control sequence will be entered into the hash table (once all tokens
+preceding the mandatory \.{\\endcsname} have been expanded).
+
+@c
+static halfword last_tested_cs ;
+
+static boolean test_for_cs(void)
+{
+    boolean b = false;          /*is the condition true? */
+    int m, s;                   /*to be tested against the second operand */
+    halfword q;                 /*for traversing token lists in \.{\\ifx} tests */
+    halfword n = get_avail();
+    halfword p = n;             /*head of the list of characters */
+is_in_csname += 1;
+    while (1) {
+        get_x_token();
+        if (cur_cs != 0)
+            break;
+        store_new_token(cur_tok);
+    }
+    if (cur_cmd != end_cs_name_cmd) {
+        last_tested_cs = null_cs;
+        if (suppress_ifcsname_error_par) {
+            do {
+                get_x_token();
+            } while (cur_cmd != end_cs_name_cmd);
+            flush_list(n);
+is_in_csname -= 1;
+            return b;
+        } else {
+            complain_missing_csname();
+        }
+    }
+    /* Look up the characters of list |n| in the hash table, and set |cur_cs| */
+    m = first;
+    p = token_link(n);
+    while (p != null) {
+        if (m >= max_buf_stack) {
+            max_buf_stack = m + 4;
+            if (max_buf_stack >= buf_size)
+                check_buffer_overflow(max_buf_stack);
+        }
+        s = token_chr(token_info(p));
+        if (s <= 0x7F) {
+            buffer[m++] = (packed_ASCII_code) s;
+        } else if (s <= 0x7FF) {
+            buffer[m++] = (packed_ASCII_code) (0xC0 + s / 0x40);
+            buffer[m++] = (packed_ASCII_code) (0x80 + s % 0x40);
+        } else if (s <= 0xFFFF) {
+            buffer[m++] = (packed_ASCII_code) (0xE0 + s / 0x1000);
+            buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) / 0x40);
+            buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) % 0x40);
+        } else {
+            buffer[m++] = (packed_ASCII_code) (0xF0 + s / 0x40000);
+            buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x40000) / 0x1000);
+            buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) / 0x40);
+            buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) % 0x40);
+        }
+        p = token_link(p);
+    }
+    if (m > first) {
+        cur_cs = id_lookup(first, m - first);  /* |no_new_control_sequence| is |true| */
+    } else if (m == first) {
+        cur_cs = null_cs;                      /* the list is empty */
+    }
+    b = (eq_type(cur_cs) != undefined_cs_cmd);
+    flush_list(n);
+    last_cs_name = cur_cs;
+is_in_csname -= 1;
+    return b;
+}
+
+@ An active character will be treated as category 13 following
+\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}.
+
+@c
+#define get_x_token_or_active_char() do { \
+    get_x_token(); \
+    if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \
+        if (is_active_cs(cs_text(cur_cs))) { \
+            cur_cmd=active_char_cmd; \
+            cur_chr=active_cs_value(cs_text(cur_tok-cs_token_flag)); \
+        } \
+    } \
+} while (0)
+
+@ A condition is started when the |expand| procedure encounters
+an |if_test| command; in that case |expand| reduces to |conditional|,
+which is a recursive procedure.
+@^recursion@>
+
+@c
+void conditional(void)
+{
+    boolean b = false;          /*is the condition true? */
+    int r;                      /*relation to be evaluated */
+    int m, n;                   /*to be tested against the second operand */
+    halfword p, q;              /*for traversing token lists in \.{\\ifx} tests */
+    int save_scanner_status;    /*|scanner_status| upon entry */
+    halfword save_cond_ptr;     /*|cond_ptr| corresponding to this conditional */
+    int this_if;                /*type of this conditional */
+    boolean is_unless;          /*was this if preceded by `\.{\\unless}' ? */
+    if ((tracing_ifs_par > 0) && (tracing_commands_par <= 1))
+        show_cur_cmd_chr();
+    push_condition_stack();
+    save_cond_ptr = cond_ptr;
+    is_unless = (cur_chr >= unless_code);
+    this_if = cur_chr % unless_code;
+    /* Either process \.{\\ifcase} or set |b| to the value of a boolean condition */
+    switch (this_if) {
+        case if_char_code:
+        case if_cat_code:
+            /* Test if two characters match */
+            get_x_token_or_active_char();
+            if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
+                /*not a character */
+                m = relax_cmd;
+                n = too_big_char;
+            } else {
+                m = cur_cmd;
+                n = cur_chr;
+            }
+            get_x_token_or_active_char();
+            if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
+                cur_cmd = relax_cmd;
+                cur_chr = too_big_char;
+            }
+            if (this_if == if_char_code)
+                b = (n == cur_chr);
+            else
+                b = (m == cur_cmd);
+            break;
+        case if_int_code:
+        case if_dim_code:
+        case if_abs_dim_code:
+        case if_abs_num_code:
+            /* Test relation between integers or dimensions */
+            /* Here we use the fact that |"<"|, |"="|, and |">"| are consecutive ASCII codes. */
+            if (this_if == if_int_code || this_if == if_abs_num_code)
+                scan_int();
+            else
+                scan_normal_dimen();
+            n = cur_val;
+            if ((n < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
+                negate(n);
+            /* Get the next non-blank non-call... */
+            do {
+                get_x_token();
+            } while (cur_cmd == spacer_cmd);
+            /*
+            if ((cur_tok >= other_token + '<') && (cur_tok <= other_token + '>')) {
+               r = cur_tok - other_token;
+            } else {
+            */
+            r = cur_tok - other_token;
+            if ((r < '<') || (r > '>')) {
+                print_err("Missing = inserted for ");
+                print_cmd_chr(if_test_cmd, this_if);
+                help1("I was expecting to see `<', `=', or `>'. Didn't.");
+                back_error();
+                r = '=';
+            }
+            if (this_if == if_int_code || this_if == if_abs_num_code)
+                scan_int();
+            else
+                scan_normal_dimen();
+            if ((cur_val < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
+                negate(cur_val);
+            switch (r) {
+                case '<':
+                    b = (n < cur_val);
+                    break;
+                case '=':
+                    b = (n == cur_val);
+                    break;
+                case '>':
+                    b = (n > cur_val);
+                    break;
+                default:
+                    /* can't happen */
+                    b = false;
+                    break;
+            }
+            break;
+        case if_odd_code:
+            /* Test if an integer is odd */
+            scan_int();
+            b = odd(cur_val);
+            break;
+        case if_vmode_code:
+            b = (abs(cur_list.mode_field) == vmode);
+            break;
+        case if_hmode_code:
+            b = (abs(cur_list.mode_field) == hmode);
+            break;
+        case if_mmode_code:
+            b = (abs(cur_list.mode_field) == mmode);
+            break;
+        case if_inner_code:
+            b = (cur_list.mode_field < 0);
+            break;
+        /*
+        case if_void_code:
+        case if_hbox_code:
+        case if_vbox_code:
+            scan_register_num();
+            p = box(cur_val);
+            if (this_if == if_void_code)
+                b = (p == null);
+            else if (p == null)
+                b = false;
+            else if (this_if == if_hbox_code)
+                b = (type(p) == hlist_node);
+            else
+                b = (type(p) == vlist_node);
+            break;
+        */
+        case if_void_code:
+            scan_register_num();
+            p = box(cur_val);
+            b = (p == null);
+            break;
+        case if_hbox_code:
+            scan_register_num();
+            p = box(cur_val);
+            b = (p != null) && (type(p) == hlist_node);
+            break;
+        case if_vbox_code:
+            scan_register_num();
+            p = box(cur_val);
+            b = (p != null) && (type(p) == vlist_node);
+            break;
+        case ifx_code:
+            /*
+                Test if two tokens match
+
+                Note that `\.{\\ifx}' will declare two macros different if one is \\{long}
+                or \\{outer} and the other isn't, even though the texts of the macros are
+                the same.
+
+                We need to reset |scanner_status|, since \.{\\outer} control sequences
+                are allowed, but we might be scanning a macro definition or preamble.
+             */
+            save_scanner_status = scanner_status;
+            scanner_status = normal;
+            get_next();
+            n = cur_cs;
+            p = cur_cmd;
+            q = cur_chr;
+            get_next();
+            if (cur_cmd != p) {
+                b = false;
+            } else if (cur_cmd < call_cmd) {
+                b = (cur_chr == q);
+            } else {
+                /*
+                    Test if two macro texts match
+
+                    Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are
+                    different in examples like this:
+
+                    $$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr
+                    {}\\def\\a\{\\c\}&
+                    {}\\def\\c\{\}\cr
+                    {}\\def\\b\{\\d\}&
+                    {}\\def\\d\{\}\cr}}$$
+                */
+                p = token_link(cur_chr);
+                /*omit reference counts */
+                q = token_link(equiv(n));
+                if (p == q) {
+                    b = true;
+                } else {
+                    while ((p != null) && (q != null)) {
+                        if (token_info(p) != token_info(q)) {
+                            p = null;
+                            break;
+                        } else {
+                            p = token_link(p);
+                            q = token_link(q);
+                        }
+                    }
+                    b = ((p == null) && (q == null));
+                }
+            }
+            scanner_status = save_scanner_status;
+            break;
+        case if_eof_code:
+            scan_four_bit_int();
+            b = (read_open[cur_val] == closed);
+            break;
+        case if_true_code:
+            b = true;
+            break;
+        case if_false_code:
+            b = false;
+            break;
+        case if_case_code:
+            /* Select the appropriate case and |return| or |goto common_ending| */
+            scan_int();
+            /* |n| is the number of cases to pass */
+            n = cur_val;
+            if (tracing_commands_par > 1) {
+                begin_diagnostic();
+                tprint("{case ");
+                print_int(n);
+                print_char('}');
+                end_diagnostic(false);
+            }
+            while (n != 0) {
+                pass_text();
+                if (cond_ptr == save_cond_ptr) {
+                    if (cur_chr == or_code)
+                        decr(n);
+                    else
+                        goto COMMON_ENDING;
+                } else if (cur_chr == fi_code) {
+                    pop_condition_stack();
+                }
+            }
+            change_if_limit(or_code, save_cond_ptr);
+            /*wait for \.{\\or}, \.{\\else}, or \.{\\fi} */
+            return;
+            break;
+        case if_primitive_code:
+            save_scanner_status = scanner_status;
+            scanner_status = normal;
+            get_next();
+            scanner_status = save_scanner_status;
+            m = prim_lookup(cs_text(cur_cs));
+            b = ((cur_cmd != undefined_cs_cmd) &&
+                 (m != undefined_primitive) &&
+                 (cur_cmd == get_prim_eq_type(m)) &&
+                 (cur_chr == get_prim_equiv(m)));
+            break;
+        case if_def_code:
+            /*
+                The conditional \.{\\ifdefined} tests if a control sequence is defined.
+                We need to reset |scanner_status|, since \.{\\outer} control sequences
+                are allowed, but we might be scanning a macro definition or preamble.
+            */
+            save_scanner_status = scanner_status;
+            scanner_status = normal;
+            get_next();
+            b = (cur_cmd != undefined_cs_cmd);
+            scanner_status = save_scanner_status;
+            break;
+        case if_cs_code:
+            b = test_for_cs();
+            break;
+        case if_in_csname_code:
+            b = is_in_csname;
+            break;
+        case if_font_char_code:
+            /*
+                The conditional \.{\\iffontchar} tests the existence of a character in
+                a font.
+            */
+            scan_font_ident();
+            n = cur_val;
+            scan_char_num();
+            b = char_exists(n, cur_val);
+            break;
+        default:
+            /* there are no other cases, but for -Wall: */
+            b = false;
+    }
+    if (is_unless)
+        b = !b;
+    if (tracing_commands_par > 1) {
+        /* Display the value of |b| */
+        begin_diagnostic();
+        if (b)
+            tprint("{true}");
+        else
+            tprint("{false}");
+        end_diagnostic(false);
+    }
+    if (b) {
+        change_if_limit(else_code, save_cond_ptr);
+        /*wait for \.{\\else} or \.{\\fi} */
+        return;
+    }
+    /*
+        Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|
+
+        In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first
+        \.{\\else} that we come to after learning that the \.{\\if} is false is
+        not the \.{\\else} we're looking for. Hence the following curious
+        logic is needed.
+     */
+    while (1) {
+        pass_text();
+        if (cond_ptr == save_cond_ptr) {
+            if (cur_chr != or_code)
+                goto COMMON_ENDING;
+            print_err("Extra \\or");
+            help1("I'm ignoring this; it doesn't match any \\if.");
+            error();
+        } else if (cur_chr == fi_code) {
+            pop_condition_stack();
+        }
+    }
+  COMMON_ENDING:
+    if (cur_chr == fi_code) {
+        pop_condition_stack();
+    } else {
+        /*wait for \.{\\fi} */
+        if_limit = fi_code;
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/conditional.c
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
-
-conditional.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-@* We consider now the way \TeX\ handles various kinds of \.{\\if} commands.
-
-Conditions can be inside conditions, and this nesting has a stack that is
-independent of the |save_stack|.
-
-Four global variables represent the top of the condition stack: |cond_ptr| points
-to pushed-down entries, if any; |if_limit| specifies the largest code of a
-|fi_or_else| command that is syntactically legal; |cur_if| is the name of the
-current type of conditional; and |if_line| is the line number at which it began.
-
-If no conditions are currently in progress, the condition stack has the special
-state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|. Otherwise
-|cond_ptr| points to a two-word node; the |type|, |subtype|, and |link| fields of
-the first word contain |if_limit|, |cur_if|, and |cond_ptr| at the next level,
-and the second word contains the corresponding |if_line|.
-
-In |cond_ptr| we keep track of the top of the condition stack while |if_limit|
-holds the upper bound on |fi_or_else| codes. The type of conditional being worked
-on is stored in cur_if and |if_line| keeps track of the line where that
-conditional began. When we skip conditional text, |skip_line| keeps track of the
-line number where skipping began, for use in error messages.
-
-*/
-
-halfword cond_ptr;
-int if_limit, cur_if, if_line, skip_line;
-
-/*tex
-
-Here is a procedure that ignores text until coming to an \.{\\or}, \.{\\else}, or
-\.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$ nesting. After it has acted,
-|cur_chr| will indicate the token that was found, but |cur_tok| will not be set
-(because this makes the procedure run faster).
-
-With |l| we keep track of the level of $\.{\\if}\ldots\.{\\fi}$ nesting and
-|scanner_status| let us return to the entry status.
-
-*/
-
-void pass_text(void)
-{
-    int l = 0;
-    int save_scanner_status = scanner_status;
-    scanner_status = skipping;
-    skip_line = line;
-    while (1) {
-        get_next();
-        if (cur_cmd == fi_or_else_cmd) {
-            if (l == 0)
-                break;
-            if (cur_chr == fi_code)
-                decr(l);
-        } else if (cur_cmd == if_test_cmd) {
-            incr(l);
-        }
-    }
-    scanner_status = save_scanner_status;
-    if (tracing_ifs_par > 0)
-        show_cur_cmd_chr();
-}
-
-/*tex
-
-When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then if\/
-\.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if} condition
-has been evaluated, \.{\\relax} will be inserted. For example, a sequence of
-commands like `\.{\\ifvoid1\\else...\\fi}' would otherwise require something
-after the `\.1'.
-
-*/
-
-void push_condition_stack(void)
-{
-    halfword p = new_node(if_node, 0);
-    vlink(p) = cond_ptr;
-    if_limit_type(p) = (quarterword) if_limit;
-    if_limit_subtype(p) = (quarterword) cur_if;
-    if_line_field(p) = if_line;
-    cond_ptr = p;
-    cur_if = cur_chr;
-    if_limit = if_code;
-    if_line = line;
-}
-
-void pop_condition_stack(void)
-{
-    halfword p;
-    if (if_stack[in_open] == cond_ptr) {
-        /*tex Conditionals are possibly not properly nested with files. */
-        if_warning();
-    }
-    p = cond_ptr;
-    if_line = if_line_field(p);
-    cur_if = if_limit_subtype(p);
-    if_limit = if_limit_type(p);
-    cond_ptr = vlink(p);
-    flush_node(p);
-}
-
-/*tex
-
-Here's a procedure that changes the |if_limit| code corresponding to a given
-value of |cond_ptr|.
-
-*/
-
-void change_if_limit(int l, halfword p)
-{
-    if (p == cond_ptr) {
-        if_limit = l;
-    } else {
-        halfword q = cond_ptr;
-        while (1) {
-            if (q == null)
-                confusion("if");
-            if (vlink(q) == p) {
-                if_limit_type(q) = (quarterword) l;
-                return;
-            }
-            q = vlink(q);
-        }
-    }
-}
-
-/*tex
-
-The conditional \.{\\ifcsname} is equivalent to \.{\\expandafter}
-\.{\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new control
-sequence will be entered into the hash table (once all tokens preceding the
-mandatory \.{\\endcsname} have been expanded).
-
-*/
-
-static halfword last_tested_cs ;
-
-static boolean test_for_cs(void)
-{
-    /*tex Is the condition true? */
-    boolean b = false;
-    /*tex To be tested against the second operand: */
-    int m, s;
-    /*tex For traversing token lists in \.{\\ifx} tests: */
-    halfword q;
-    halfword n = get_avail();
-    /*tex Head of the list of characters: */
-    halfword p = n;
-    is_in_csname += 1;
-    while (1) {
-        get_x_token();
-        if (cur_cs != 0)
-            break;
-        store_new_token(cur_tok);
-    }
-    if (cur_cmd != end_cs_name_cmd) {
-        last_tested_cs = null_cs;
-        if (suppress_ifcsname_error_par) {
-            do {
-                get_x_token();
-            } while (cur_cmd != end_cs_name_cmd);
-            flush_list(n);
-            is_in_csname -= 1;
-            return b;
-        } else {
-            complain_missing_csname();
-        }
-    }
-    /*tex Look up the characters of list |n| in the hash table, and set |cur_cs|. */
-    m = first;
-    p = token_link(n);
-    while (p != null) {
-        if (m >= max_buf_stack) {
-            max_buf_stack = m + 4;
-            if (max_buf_stack >= buf_size)
-                check_buffer_overflow(max_buf_stack);
-        }
-        s = token_chr(token_info(p));
-        if (s <= 0x7F) {
-            buffer[m++] = (packed_ASCII_code) s;
-        } else if (s <= 0x7FF) {
-            buffer[m++] = (packed_ASCII_code) (0xC0 + s / 0x40);
-            buffer[m++] = (packed_ASCII_code) (0x80 + s % 0x40);
-        } else if (s <= 0xFFFF) {
-            buffer[m++] = (packed_ASCII_code) (0xE0 + s / 0x1000);
-            buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) / 0x40);
-            buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) % 0x40);
-        } else {
-            buffer[m++] = (packed_ASCII_code) (0xF0 + s / 0x40000);
-            buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x40000) / 0x1000);
-            buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) / 0x40);
-            buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) % 0x40);
-        }
-        p = token_link(p);
-    }
-    if (m > first) {
-        /*tex |no_new_control_sequence| is |true| */
-        cur_cs = id_lookup(first, m - first);
-    } else if (m == first) {
-        /*tex the list is empty */
-        cur_cs = null_cs;
-    }
-    b = (eq_type(cur_cs) != undefined_cs_cmd);
-    flush_list(n);
-    last_cs_name = cur_cs;
-    is_in_csname -= 1;
-    return b;
-}
-
-/*tex
-
-An active character will be treated as category 13 following \.{\\if\\noexpand}
-or following \.{\\ifcat\\noexpand}.
-
-*/
-
-#define get_x_token_or_active_char() do { \
-    get_x_token(); \
-    if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \
-        if (is_active_cs(cs_text(cur_cs))) { \
-            cur_cmd=active_char_cmd; \
-            cur_chr=active_cs_value(cs_text(cur_tok-cs_token_flag)); \
-        } \
-    } \
-} while (0)
-
-/*tex
-
-A condition is started when the |expand| procedure encounters an |if_test|
-command; in that case |expand| reduces to |conditional|, which is a recursive
-procedure. @^recursion@>
-
-*/
-
-void conditional(void)
-{
-    /*tex Is the condition true? */
-    boolean b = false;
-    /*tex The relation to be evaluated: */
-    int r;
-    /*tex To be tested against the second operand: */
-    int m, n;
-    /*tex For traversing token lists in \.{\\ifx} tests: */
-    halfword p, q;
-    /*tex The |scanner_status| upon entry: */
-    int save_scanner_status;
-    /*tex The |cond_ptr| corresponding to this conditional: */
-    halfword save_cond_ptr;
-    /*tex The type of this conditional: */
-    int this_if;
-    /*tex Was this \.{\\if} preceded by \.{\\unless}? */
-    boolean is_unless;
-    if ((tracing_ifs_par > 0) && (tracing_commands_par <= 1)) {
-        show_cur_cmd_chr();
-    }
-    push_condition_stack();
-    save_cond_ptr = cond_ptr;
-    is_unless = (cur_chr >= unless_code);
-    this_if = cur_chr % unless_code;
-    /*tex Either process \.{\\ifcase} or set |b| to the value of a boolean condition. */
-    switch (this_if) {
-        case if_char_code:
-        case if_cat_code:
-            /*tex Test if two characters match. */
-            get_x_token_or_active_char();
-            if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
-                /*tex It's not a character. */
-                m = relax_cmd;
-                n = too_big_char;
-            } else {
-                m = cur_cmd;
-                n = cur_chr;
-            }
-            get_x_token_or_active_char();
-            if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
-                cur_cmd = relax_cmd;
-                cur_chr = too_big_char;
-            }
-            if (this_if == if_char_code)
-                b = (n == cur_chr);
-            else
-                b = (m == cur_cmd);
-            break;
-        case if_int_code:
-        case if_dim_code:
-        case if_abs_dim_code:
-        case if_abs_num_code:
-            /*tex
-                Test the relation between integers or dimensions. Here we use the fact
-                that |<|, |=|, and |>| are consecutive ASCII codes.
-            */
-            if (this_if == if_int_code || this_if == if_abs_num_code)
-                scan_int();
-            else
-                scan_normal_dimen();
-            n = cur_val;
-            if ((n < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
-                negate(n);
-            /*tex Get the next non-blank non-call... */
-            do {
-                get_x_token();
-            } while (cur_cmd == spacer_cmd);
-            r = cur_tok - other_token;
-            if ((r < '<') || (r > '>')) {
-                print_err("Missing = inserted for ");
-                print_cmd_chr(if_test_cmd, this_if);
-                help1("I was expecting to see `<', `=', or `>'. Didn't.");
-                back_error();
-                r = '=';
-            }
-            if (this_if == if_int_code || this_if == if_abs_num_code)
-                scan_int();
-            else
-                scan_normal_dimen();
-            if ((cur_val < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
-                negate(cur_val);
-            switch (r) {
-                case '<':
-                    b = (n < cur_val);
-                    break;
-                case '=':
-                    b = (n == cur_val);
-                    break;
-                case '>':
-                    b = (n > cur_val);
-                    break;
-                default:
-                    /*tex This can't happen. */
-                    b = false;
-                    break;
-            }
-            break;
-        case if_odd_code:
-            /*tex Test if an integer is odd. */
-            scan_int();
-            b = odd(cur_val);
-            break;
-        case if_vmode_code:
-            b = (abs(cur_list.mode_field) == vmode);
-            break;
-        case if_hmode_code:
-            b = (abs(cur_list.mode_field) == hmode);
-            break;
-        case if_mmode_code:
-            b = (abs(cur_list.mode_field) == mmode);
-            break;
-        case if_inner_code:
-            b = (cur_list.mode_field < 0);
-            break;
-        case if_void_code:
-            scan_register_num();
-            p = box(cur_val);
-            b = (p == null);
-            break;
-        case if_hbox_code:
-            scan_register_num();
-            p = box(cur_val);
-            b = (p != null) && (type(p) == hlist_node);
-            break;
-        case if_vbox_code:
-            scan_register_num();
-            p = box(cur_val);
-            b = (p != null) && (type(p) == vlist_node);
-            break;
-        case if_x_code:
-            /*tex
-                Test if two tokens match. Note that `\.{\\ifx}' will declare two
-                macros different if one is \\{long} or \\{outer} and the other
-                isn't, even though the texts of the macros are the same.
-
-                We need to reset |scanner_status|, since \.{\\outer} control
-                sequences are allowed, but we might be scanning a macro
-                definition or preamble.
-             */
-            save_scanner_status = scanner_status;
-            scanner_status = normal;
-            get_next();
-            n = cur_cs;
-            p = cur_cmd;
-            q = cur_chr;
-            get_next();
-            if (cur_cmd != p) {
-                b = false;
-            } else if (cur_cmd < call_cmd) {
-                b = (cur_chr == q);
-            } else {
-                /*tex
-                    Test if two macro texts match. Note also that `\.{\\ifx}'
-                    decides that macros \.{\\a} and \.{\\b} are different in
-                    examples like this:
-
-                    $$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr
-                    {}\\def\\a\{\\c\}&
-                    {}\\def\\c\{\}\cr
-                    {}\\def\\b\{\\d\}&
-                    {}\\def\\d\{\}\cr}}$$
-                */
-                p = token_link(cur_chr);
-                /*tex Omit reference counts. */
-                q = token_link(equiv(n));
-                if (p == q) {
-                    b = true;
-                } else {
-                    while ((p != null) && (q != null)) {
-                        if (token_info(p) != token_info(q)) {
-                            p = null;
-                            break;
-                        } else {
-                            p = token_link(p);
-                            q = token_link(q);
-                        }
-                    }
-                    b = ((p == null) && (q == null));
-                }
-            }
-            scanner_status = save_scanner_status;
-            break;
-        case if_eof_code:
-            scan_four_bit_int();
-            b = (read_open[cur_val] == closed);
-            break;
-        case if_true_code:
-            b = true;
-            break;
-        case if_false_code:
-            b = false;
-            break;
-        case if_case_code:
-            /*tex Select the appropriate case and |return| or |goto common_ending|. */
-            scan_int();
-            /*tex |n| is the number of cases to pass. */
-            n = cur_val;
-            if (tracing_commands_par > 1) {
-                begin_diagnostic();
-                tprint("{case ");
-                print_int(n);
-                print_char('}');
-                end_diagnostic(false);
-            }
-            while (n != 0) {
-                pass_text();
-                if (cond_ptr == save_cond_ptr) {
-                    if (cur_chr == or_code)
-                        decr(n);
-                    else
-                        goto COMMON_ENDING;
-                } else if (cur_chr == fi_code) {
-                    pop_condition_stack();
-                }
-            }
-            change_if_limit(or_code, save_cond_ptr);
-            /*tex Wait for \.{\\or}, \.{\\else}, or \.{\\fi}. */
-            return;
-            break;
-        case if_primitive_code:
-            save_scanner_status = scanner_status;
-            scanner_status = normal;
-            get_next();
-            scanner_status = save_scanner_status;
-            m = prim_lookup(cs_text(cur_cs));
-            b = ((cur_cmd != undefined_cs_cmd) &&
-                 (m != undefined_primitive) &&
-                 (cur_cmd == get_prim_eq_type(m)) &&
-                 (cur_chr == get_prim_equiv(m)));
-            break;
-        case if_def_code:
-            /*tex
-                The conditional \.{\\ifdefined} tests if a control sequence is
-                defined. We need to reset |scanner_status|, since \.{\\outer}
-                control sequences are allowed, but we might be scanning a macro
-                definition or preamble.
-            */
-            save_scanner_status = scanner_status;
-            scanner_status = normal;
-            get_next();
-            b = (cur_cmd != undefined_cs_cmd);
-            scanner_status = save_scanner_status;
-            break;
-        case if_cs_code:
-            b = test_for_cs();
-            break;
-        case if_in_csname_code:
-            b = is_in_csname;
-            break;
-        case if_font_char_code:
-            /*tex
-                The conditional \.{\\iffontchar} tests the existence of a
-                character in a font.
-            */
-            scan_font_ident();
-            n = cur_val;
-            scan_char_num();
-            b = char_exists(n, cur_val);
-            break;
-        default:
-            /*tex there are no other cases, but we need to please |-Wall|. */
-            b = false;
-    }
-    if (is_unless)
-        b = !b;
-    if (tracing_commands_par > 1) {
-        /*tex Display the value of |b|. */
-        begin_diagnostic();
-        if (b)
-            tprint("{true}");
-        else
-            tprint("{false}");
-        end_diagnostic(false);
-    }
-    if (b) {
-        change_if_limit(else_code, save_cond_ptr);
-        /*tex Wait for \.{\\else} or \.{\\fi}. */
-        return;
-    }
-    /*tex
-        Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|. In a
-        construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first
-        \.{\\else} that we come to after learning that the \.{\\if} is false is
-        not the \.{\\else} we're looking for. Hence the following curious logic
-        is needed.
-     */
-    while (1) {
-        pass_text();
-        if (cond_ptr == save_cond_ptr) {
-            if (cur_chr != or_code)
-                goto COMMON_ENDING;
-            print_err("Extra \\or");
-            help1(
-                "I'm ignoring this; it doesn't match any \\if."
-            );
-            error();
-        } else if (cur_chr == fi_code) {
-            pop_condition_stack();
-        }
-    }
-  COMMON_ENDING:
-    if (cur_chr == fi_code) {
-        pop_condition_stack();
-    } else {
-        /*tex Wait for \.{\\fi}. */
-        if_limit = fi_code;
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/directions.w
@@ -0,0 +1,223 @@
+% directions.w
+%
+% Copyright 2009-2014 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
+void scan_direction(void)
+{
+    int save_cur_cmd = cur_cmd;
+    int save_cur_chr = cur_chr;
+    get_x_token();
+    if (cur_cmd == assign_dir_cmd) {
+        cur_val = eqtb[cur_chr].cint;
+        goto EXIT;
+    } else {
+        back_input();
+    }
+    if (scan_keyword("tlt")) {
+        cur_val = dir_TLT;
+    } else if (scan_keyword("trt")) {
+        cur_val = dir_TRT;
+    } else if (scan_keyword("ltl")) {
+        cur_val = dir_LTL;
+    } else if (scan_keyword("rtt")) {
+        cur_val = dir_RTT;
+    } else {
+        tex_error("Bad direction", NULL);
+        cur_val = 0;
+    }
+    get_x_token();
+    if (cur_cmd != spacer_cmd)
+        back_input();
+  EXIT:
+    cur_cmd = save_cur_cmd;
+    cur_chr = save_cur_chr;
+}
+
+@ the next two are used by postlinebreak.c
+
+@c
+halfword do_push_dir_node(halfword p, halfword a)
+{
+    halfword n = copy_node(a);
+    vlink(n) = p;
+    return n;
+}
+
+halfword do_pop_dir_node(halfword p)
+{
+    halfword n = vlink(p);
+    flush_node(p);
+    return n;
+}
+
+@ @c
+halfword dir_ptr;
+
+halfword text_dir_ptr;
+
+@ There is no need to do anything here at the moment.
+@c
+void initialize_directions(void)
+{
+}
+
+@ @c
+halfword new_dir(int s)
+{
+    halfword p = new_node(dir_node, 0);
+    dir_dir(p) = s;
+    dir_level(p) = cur_level;
+    return p;
+}
+
+@ The global static array variable  |dir_strings| is also used
+by the lua nodelib interface, so it cannot be static. Putting
+it here instead of there avoid the nodelib having to know
+about the actual values of |dir_TRT| etc.
+
+@c
+
+/*
+const char *dir_strings[128] = {
+  "-TLT","???", "???", "???", "-TRT","???", "???", "???",
+  "???", "-LTL","???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "-RTT","???", "???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "+TLT","???", "???", "???", "+TRT","???", "???", "???",
+  "???", "+LTL","???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "+RTT","???", "???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???",
+  "???", "???", "???", "???", "???", "???", "???", "???"
+};
+
+int dir_swap = 64;
+*/
+
+const char *dir_strings[8] = {
+  "-TLT","-TRT","-LTL","-RTT",
+  "+TLT","+TRT","+LTL","+RTT",
+};
+
+int dir_swap = 4;
+
+const char *string_dir(int d)
+{
+    return (dir_strings[d+dir_swap]+1);
+}
+
+@ @c
+void print_dir(int d)
+{
+    tprint(string_dir(d));
+}
+
+@ @c
+scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph)
+{
+    scaled wd = 0;
+    if (isglyph) {
+        if (textdir_parallel(curdir, pdir) == textglyphdir_orthogonal(pdir)) {
+            wd = glyph_width(p);
+            if (ex_glyph(p) != 0) {
+             /* wd = round_xn_over_d(wd, 1000 + ex_glyph(p)/1000, 1000); */
+                wd = ext_xn_over_d(wd, 1000000+ex_glyph(p), 1000000);
+
+            }
+        } else {
+            wd = glyph_depth(p) + glyph_height(p);
+        }
+/* experimental */
+wd += x_advance(p);
+    } else {                    /* hlist, vlist, image, form, rule */
+        if (textdir_parallel(pdir, curdir))
+            wd = width(p);
+        else
+            wd = depth(p) + height(p);
+    }
+    return wd;
+}
+
+@ @c
+scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph)
+{
+    scaled_whd whd = { 0, 0, 0 };
+    whd.wd = pack_width(curdir, pdir, p, isglyph);
+    if (isglyph) {
+        if (is_rotated(curdir)) {
+            if (textdir_parallel(curdir, pdir))
+                whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2;
+            else
+                whd.ht = whd.dp = glyph_width(p) / 2;
+        } else if (is_rotated(pdir)) {
+            if (textdir_parallel(curdir, pdir))
+                whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2;
+            else
+                whd.ht = glyph_width(p);
+        } else {
+            if (glyphdir_eq(curdir, pdir)) {
+                whd.ht = glyph_height(p);
+                whd.dp = glyph_depth(p);
+            } else if (glyphdir_opposite(curdir, pdir)) {
+                whd.ht = glyph_depth(p);
+                whd.dp = glyph_height(p);
+            } else
+                whd.ht = glyph_width(p);
+        }
+    } else {
+        if (is_rotated(curdir)) {
+            if (textdir_parallel(curdir, pdir))
+                whd.ht = whd.dp = (height(p) + depth(p)) / 2;
+            else
+                whd.ht = whd.dp = width(p) / 2;
+        } else if (pardir_eq(curdir, pdir)) {
+            whd.ht = height(p);
+            whd.dp = depth(p);
+        } else if (pardir_opposite(curdir, pdir)) {
+            whd.ht = depth(p);
+            whd.dp = height(p);
+        } else
+            whd.ht = width(p);
+    }
+    return whd;
+}
+
+@ @c
+void update_text_dir_ptr(int val)
+{
+    if (dir_level(text_dir_ptr) == cur_level) {
+        /* update */
+        dir_dir(text_dir_ptr) = val;
+    } else {
+        /* addition */
+        halfword text_dir_tmp = new_dir(val);
+        vlink(text_dir_tmp) = text_dir_ptr;
+        text_dir_ptr = text_dir_tmp;
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/directions.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
-
-directions.w
-
-Copyright 2009-2014 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-void scan_direction(void)
-{
-    int save_cur_cmd = cur_cmd;
-    int save_cur_chr = cur_chr;
-    get_x_token();
-    if (cur_cmd == assign_dir_cmd) {
-        cur_val = eqtb[cur_chr].cint;
-        goto EXIT;
-    } else {
-        back_input();
-    }
-    if (scan_keyword("tlt")) {
-        cur_val = dir_TLT;
-    } else if (scan_keyword("trt")) {
-        cur_val = dir_TRT;
-    } else if (scan_keyword("ltl")) {
-        cur_val = dir_LTL;
-    } else if (scan_keyword("rtt")) {
-        cur_val = dir_RTT;
-    } else {
-        tex_error("Bad direction", NULL);
-        cur_val = 0;
-    }
-    get_x_token();
-    if (cur_cmd != spacer_cmd)
-        back_input();
-  EXIT:
-    cur_cmd = save_cur_cmd;
-    cur_chr = save_cur_chr;
-}
-
-/*tex
-
-    The next two are used by |postlinebreak.c|:
-
-*/
-
-halfword do_push_dir_node(halfword p, halfword a)
-{
-    halfword n = copy_node(a);
-    vlink(n) = p;
-    return n;
-}
-
-halfword do_pop_dir_node(halfword p)
-{
-    halfword n = vlink(p);
-    flush_node(p);
-    return n;
-}
-
-halfword dir_ptr;
-halfword text_dir_ptr;
-
-void initialize_directions(void)
-{
-    /*tex There is no need to do anything here at the moment. */
-}
-
-halfword new_dir(int s)
-{
-    halfword p = new_node(dir_node, 0);
-    dir_dir(p) = s;
-    dir_level(p) = cur_level;
-    return p;
-}
-
-const char *dir_strings_par[4] = { [0] =
-  "TLT","TRT","LTL","RTT",
-};
-
-const char *dir_strings_text_normal[4] = { [0] =
-  "+TLT","+TRT","+LTL","+RTT",
-};
-
-const char *dir_strings_text_cancel[4] = { [0] =
-  "-TLT","-TRT","-LTL","-RTT",
-};
-
-void print_dir_par(int d)
-{
-    tprint(dir_strings_par[d]);
-}
-
-void print_dir_text(halfword d)
-{
-    if (subtype(d) == cancel_dir) {
-        tprint(dir_strings_text_cancel[dir_dir(d)]);
-    } else {
-        tprint(dir_strings_text_normal[dir_dir(d)]);
-    }
-}
-
-scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph)
-{
-    scaled wd = 0;
-    if (isglyph) {
-        if (textdir_parallel(curdir, pdir) == textglyphdir_orthogonal(pdir)) {
-            wd = glyph_width(p);
-            if (ex_glyph(p) != 0) {
-             /* wd = round_xn_over_d(wd, 1000 + ex_glyph(p)/1000, 1000); */
-                wd = ext_xn_over_d(wd, 1000000+ex_glyph(p), 1000000);
-
-            }
-        } else {
-            wd = glyph_depth(p) + glyph_height(p);
-        }
-    } else {
-        if (textdir_parallel(pdir, curdir))
-            wd = width(p);
-        else
-            wd = depth(p) + height(p);
-    }
-    return wd;
-}
-
-scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph)
-{
-    scaled_whd whd = { 0, 0, 0 };
-    whd.wd = pack_width(curdir, pdir, p, isglyph);
-    if (isglyph) {
-        if (is_rotated(curdir)) {
-            if (textdir_parallel(curdir, pdir))
-                whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2;
-            else
-                whd.ht = whd.dp = glyph_width(p) / 2;
-        } else if (is_rotated(pdir)) {
-            if (textdir_parallel(curdir, pdir))
-                whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2;
-            else
-                whd.ht = glyph_width(p);
-        } else {
-            if (glyphdir_eq(curdir, pdir)) {
-                whd.ht = glyph_height(p);
-                whd.dp = glyph_depth(p);
-            } else if (glyphdir_opposite(curdir, pdir)) {
-                whd.ht = glyph_depth(p);
-                whd.dp = glyph_height(p);
-            } else
-                whd.ht = glyph_width(p);
-        }
-    } else {
-        if (is_rotated(curdir)) {
-            if (textdir_parallel(curdir, pdir))
-                whd.ht = whd.dp = (height(p) + depth(p)) / 2;
-            else
-                whd.ht = whd.dp = width(p) / 2;
-        } else if (pardir_eq(curdir, pdir)) {
-            whd.ht = height(p);
-            whd.dp = depth(p);
-        } else if (pardir_opposite(curdir, pdir)) {
-            whd.ht = depth(p);
-            whd.dp = height(p);
-        } else
-            whd.ht = width(p);
-    }
-    return whd;
-}
-
-void update_text_dir_ptr(int val)
-{
-    if (dir_level(text_dir_ptr) == cur_level) {
-        /* update */
-        dir_dir(text_dir_ptr) = val;
-    } else {
-        /* addition */
-        halfword text_dir_tmp = new_dir(val);
-        vlink(text_dir_tmp) = text_dir_ptr;
-        text_dir_ptr = text_dir_tmp;
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/dumpdata.w
@@ -0,0 +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+37)
+#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;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/dumpdata.c
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    We use a magic number to register the version of the format. Normally this
-    number only increments when we add a new primitive of change command codes.
-    We start with 907 which is the sum of the values of the bytes of \quote
-    {don knuth}.
-
-*/
-
-#define FORMAT_ID (907+48)
-#if ((FORMAT_ID>=0) && (FORMAT_ID<=256))
-#error Wrong value for FORMAT_ID.
-#endif
-
-/*tex
-
-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.
-
-*/
-
-str_number format_ident;
-str_number format_name;
-
-
-/*tex
-
-Format files consist of |memory_word| items, and we use the following macros to
-dump words of different types:
-
-*/
-
-FILE *fmt_file;
-
-void store_fmt_file(void)
-{
-    int j, k, l, x;
-    halfword p;
-    char *format_engine;
-    int callback_id;
-    char *fmtname = NULL;
-    /*tex
-        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();
-    }
-    /*tex
-        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);
-    /*tex
-        Dump constants for consistency check. The next few sections of the
-        program should make it clear how we use the dump/undump macros. First
-        comes Web2C \TeX's magic constant: "W2TX"
-    */
-    dump_int(0x57325458);
-    dump_int(FORMAT_ID);
-    /*tex
-        We align |engine_name| 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);
-    dump_int(max_halfword);
-    dump_int(hash_high);
-    dump_int(eqtb_size);
-    dump_int(hash_prime);
-    /*tex Dump the string pool. */
-    k = dump_string_pool();
-    print_ln();
-    print_int(k);
-    tprint(" strings using ");
-    print_int((longinteger) pool_size);
-    tprint(" bytes");
-    /*tex
-        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);
-    /*tex
-        Dump regions 1 to 4 of |eqtb|, the table of equivalents. 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;
-        /*tex |j=int_base-1| */
-        goto DONE1;
-      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);
-    /*tex 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;
-        /*tex |j=eqtb_size| */
-        goto DONE2;
-      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) {
-        /*tex Dump the |hash_extra| part: */
-        dump_things(eqtb[eqtb_size + 1], hash_high);
-    }
-    dump_int(par_loc);
-    dump_int(write_loc);
-    dump_math_codes();
-    dump_text_codes();
-    /*tex
-        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");
-    /*tex Dump the font information. */
-    dump_int(max_font_id());
-    for (k = 0; k <= max_font_id(); k++) {
-        /*tex 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();
-    /*tex Dump the hyphenation tables. */
-    dump_language_data();
-    /*tex Dump a couple more things and the closing check word. */
-    dump_int(interaction);
-    dump_int(format_ident);
-    dump_int(format_name);
-    dump_int(69069);
-    /*tex
-        We have already printed a lot of statistics, so we set |tracing_stats:=0|
-        to prevent them from appearing again.
-    */
-    tracing_stats_par = 0;
-    /*tex Dump the \LUA\ bytecodes. */
-    dump_luac_registers();
-    /*tex Close the format file. */
-    zwclose(fmt_file);
-}
-
-/*tex
-
-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.
-
-*/
-
-#define too_small(A) do { \
-    wake_up_terminal(); \
-    wterm_cr(); \
-    fprintf(term_out,"---! Must increase the %s",(A)); \
-    goto BAD_FMT; \
-} while (0)
-
-/*tex
-
-    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|.
-
-*/
-
-#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)
-
-boolean load_fmt_file(const char *fmtname)
-{
-    int j, k, x;
-    halfword p;
-    char *format_engine;
-    /*tex 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) {
-        /*tex it's not a format file. */
-        goto BAD_FMT;
-    }
-    undump_int(x);
-    format_debug("format id", x);
-    if (x != FORMAT_ID) {
-        /*tex We have a |FORMAT_ID| mismatch. */
-        goto BAD_FMT;
-    }
-    undump_int(x);
-    format_debug("engine name size", x);
-    if ((x < 0) || (x > 256)) {
-       /*tex The format file is corrupt. */
-        goto BAD_FMT;
-    }
-    format_engine = xmalloc((unsigned) x);
-    undump_things(format_engine[0], x);
-    format_engine[x - 1] = 0;
-    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) {
-        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;
-    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;
-    /*tex Undump the string pool */
-    str_ptr = undump_string_pool();
-    /*tex 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);
-    /*tex Undump regions 1 to 6 of the table of equivalents |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) {
-        /*tex 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();
-    /*tex 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);
-    /*tex Undump the font information */
-    undump_int(x);
-    set_max_font_id(x);
-    for (k = 0; k <= max_font_id(); k++) {
-        /*tex Undump the array info for internal font number |k| */
-        undump_font(k);
-    }
-    undump_math_data();
-    /*tex Undump the hyphenation tables */
-    undump_language_data();
-    /*tex 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;
-    /*tex Undump the lua bytecodes. */
-    undump_luac_registers();
-    prev_depth_par = ignore_depth;
-    return true;
-  BAD_FMT:
-    wake_up_terminal();
-    wterm_cr();
-    fprintf(term_out, "(Fatal format file error; I'm stymied)");
-    return false;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/equivalents.w
@@ -0,0 +1,1168 @@
+% equivalents.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"
+
+halfword last_cs_name = null_cs;
+
+/* |eqtb[p]| has just been restored or retained */
+
+static void diagnostic_trace(halfword p, const char *s)
+{
+    begin_diagnostic();
+    print_char('{');
+    tprint(s);
+    print_char(' ');
+    show_eqtb(p);
+    print_char('}');
+    end_diagnostic(false);
+}
+
+@ @c
+void show_eqtb_meaning(halfword n);     /* forward */
+
+@ Now that we have studied the data structures for \TeX's semantic routines,
+we ought to consider the data structures used by its syntactic routines. In
+other words, our next concern will be
+the tables that \TeX\ looks at when it is scanning
+what the user has written.
+
+The biggest and most important such table is called |eqtb|. It holds the
+current ``equivalents'' of things; i.e., it explains what things mean
+or what their current values are, for all quantities that are subject to
+the nesting structure provided by \TeX's grouping mechanism. There are six
+parts to |eqtb|:
+
+\yskip\hang 1) |eqtb[null_cs]| holds the current equivalent of the
+zero-length control sequence.
+
+\yskip\hang 2) |eqtb[hash_base..(glue_base-1)]| holds the current
+equivalents of single- and multiletter control sequences.
+
+\yskip\hang 3) |eqtb[glue_base..(local_base-1)]| holds the current
+equivalents of glue parameters like the current baselineskip.
+
+\yskip\hang 4) |eqtb[local_base..(int_base-1)]| holds the current
+equivalents of local halfword quantities like the current box registers,
+the current ``catcodes,'' the current font, and a pointer to the current
+paragraph shape.
+
+\yskip\hang 5) |eqtb[int_base..(dimen_base-1)]| holds the current
+equivalents of fullword integer parameters like the current hyphenation
+penalty.
+
+\yskip\hang 6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents
+of fullword dimension parameters like the current hsize or amount of
+hanging indentation.
+
+\yskip\noindent Note that, for example, the current amount of
+baselineskip glue is determined by the setting of a particular location
+in region~3 of |eqtb|, while the current meaning of the control sequence
+`\.{\\baselineskip}' (which might have been changed by \.{\\def} or
+\.{\\let}) appears in region~2.
+
+@ The last two regions of |eqtb| have fullword values instead of the
+three fields |eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary,
+but \TeX\ needs to store the |eq_level| information in another array
+called |xeq_level|.
+
+@c
+memory_word *eqtb;
+halfword eqtb_top;              /* maximum of the |eqtb| */
+quarterword xeq_level[(eqtb_size + 1)];
+
+@ @c
+void initialize_equivalents(void)
+{
+    int k;
+    for (k = int_base; k <= eqtb_size; k++)
+        xeq_level[k] = level_one;
+}
+
+@ The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups
+in \TeX\ means that |eqtb| entries valid in outer groups should be saved
+and restored later if they are overridden inside the braces. When a new |eqtb|
+value is being assigned, the program therefore checks to see if the previous
+entry belongs to an outer level. In such a case, the old value is placed
+on the |save_stack| just before the new value enters |eqtb|. At the
+end of a grouping level, i.e., when the right brace is sensed, the
+|save_stack| is used to restore the outer values, and the inner ones are
+destroyed.
+
+Entries on the |save_stack| are of type |save_record|. The top item on
+this stack is |save_stack[p]|, where |p=save_ptr-1|; it contains three
+fields called |save_type|, |save_level|, and |save_value|, and it is
+interpreted in one of four ways:
+
+\yskip\hang 1) If |save_type(p)=restore_old_value|, then
+|save_value(p)| is a location in |eqtb| whose current value should
+be destroyed at the end of the current group and replaced by |save_word(p-1)|
+(|save_type(p-1)==saved_eqtb|).
+Furthermore if |save_value(p)>=int_base|, then |save_level(p)| should
+replace the corresponding entry in |xeq_level| (if |save_value(p)<int_base|,
+then the level is part of |save_word(p-1)|).
+
+\yskip\hang 2) If |save_type(p)=restore_zero|, then |save_value(p)|
+is a location in |eqtb| whose current value should be destroyed at the end
+of the current group, when it should be
+replaced by the current value of |eqtb[undefined_control_sequence]|.
+
+\yskip\hang 3) If |save_type(p)=insert_token|, then |save_value(p)|
+is a token that should be inserted into \TeX's input when the current
+group ends.
+
+\yskip\hang 4) If |save_type(p)=level_boundary|, then |save_level(p)|
+is a code explaining what kind of group we were previously in, and
+|save_value(p)| points to the level boundary word at the bottom of
+the entries for that group. Furthermore, |save_value(p-1)| contains the
+source line number at which the current level of grouping was entered,
+this field has itself a type: |save_type(p-1)==saved_line|.
+
+Besides this `official' use, various subroutines push temporary
+variables on the save stack when it is handy to do so. These all have
+an explicit |save_type|, and they are:
+
+|saved_adjust| signifies an adjustment is beging scanned,
+|saved_insert| an insertion is being scanned,
+|saved_disc| the \.{\\discretionary} sublist we are working on right now,
+|saved_boxtype| whether a \.{\\localbox} is \.{\\left} or \.{\\right},
+|saved_textdir| a text direction to be restored,
+|saved_eqno| diffentiates between \.{\\eqno} and \.{\\leqno},
+|saved_choices| the \.{\\mathchoices} sublist we are working on right now,
+|saved_math| and interrupted math list,
+|saved_boxcontext| the box context value,
+|saved_boxspec| the box \.{to} or \.{spread} specification,
+|saved_boxdir| the box \.{dir} specification,
+|saved_boxattr| the box \.{attr} specification,
+|saved_boxpack| the box \.{pack} specification.
+
+@ The global variable |cur_group| keeps track of what sort of group we are
+currently in. Another global variable, |cur_boundary|, points to the
+topmost |level_boundary| word.  And |cur_level| is the current depth of
+nesting. The routines are designed to preserve the condition that no entry
+in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|.
+
+@c
+save_record *save_stack;
+int save_ptr;                        /* first unused entry on |save_stack| */
+int max_save_stack;                  /* maximum usage of save stack */
+quarterword cur_level = level_one;   /* current nesting level for groups */
+group_code cur_group = bottom_level; /* current group type */
+int cur_boundary;                    /* where the current level begins */
+
+@ At this time it might be a good idea for the reader to review the introduction
+to |eqtb| that was given above just before the long lists of parameter names.
+Recall that the ``outer level'' of the program is |level_one|, since
+undefined control sequences are assumed to be ``defined'' at |level_zero|.
+
+
+
+@ The following macro is used to test if there is room for up to eight more
+entries on |save_stack|. By making a conservative test like this, we can
+get by with testing for overflow in only a few places.
+
+@c
+#define check_full_save_stack() do { \
+    if (save_ptr>max_save_stack) { \
+        max_save_stack=save_ptr; \
+        if (max_save_stack>save_size-8) \
+            overflow("save size",(unsigned)save_size); \
+    } \
+} while (0)
+
+@ Procedure |new_save_level| is called when a group begins. The
+argument is a group identification code like `|hbox_group|'. After
+calling this routine, it is safe to put six more entries on |save_stack|.
+
+In some cases integer-valued items are placed onto the
+|save_stack| just below a |level_boundary| word, because this is a
+convenient place to keep information that is supposed to ``pop up'' just
+when the group has finished.
+For example, when `\.{\\hbox to 100pt}' is being treated, the 100pt
+dimension is stored on |save_stack| just before |new_save_level| is
+called.
+
+@c
+void new_save_level(group_code c)
+{   /* begin a new level of grouping */
+    check_full_save_stack();
+    set_saved_record(0, saved_line, 0, line);
+    incr(save_ptr);
+    save_type(save_ptr) = level_boundary;
+    save_level(save_ptr) = cur_group;
+    save_value(save_ptr) = cur_boundary;
+    if (cur_level == max_quarterword)
+        overflow("grouping levels", max_quarterword - min_quarterword);
+    /* quit if |(cur_level+1)| is too big to be stored in |eqtb| */
+    cur_boundary = save_ptr;
+    cur_group = c;
+    if (tracing_groups_par > 0)
+        group_trace(false);
+    incr(cur_level);
+    incr(save_ptr);
+}
+
+@ @c
+static const char *save_stack_type(int v)
+{
+    const char *s = "";
+    switch (save_type(v)) {
+        case restore_old_value: s = "restore_old_value"; break;
+        case restore_zero:      s = "restore_zero";      break;
+        case insert_token:      s = "insert_token";      break;
+        case level_boundary:    s = "level_boundary";    break;
+        case saved_line:        s = "saved_line";        break;
+        case saved_adjust:      s = "saved_adjust";      break;
+        case saved_insert:      s = "saved_insert";      break;
+        case saved_disc:        s = "saved_disc";        break;
+        case saved_boxtype:     s = "saved_boxtype";     break;
+        case saved_textdir:     s = "saved_textdir";     break;
+        case saved_eqno:        s = "saved_eqno";        break;
+        case saved_choices:     s = "saved_choices";     break;
+        case saved_math:        s = "saved_math";        break;
+        case saved_boxcontext:  s = "saved_boxcontext";  break;
+        case saved_boxspec:     s = "saved_boxspec";     break;
+        case saved_boxdir:      s = "saved_boxdir";      break;
+        case saved_boxattr:     s = "saved_boxattr";     break;
+        case saved_boxpack:     s = "saved_boxpack";     break;
+        case saved_eqtb:        s = "saved_eqtb";        break;
+        default: break;
+    }
+    return s;
+}
+
+@ @c
+void print_save_stack(void)
+{
+    int i;
+    begin_diagnostic();
+    selector = term_and_log;
+    print_ln();
+    for (i = (save_ptr - 1); i >= 0; i--) {
+        tprint("save_stack[");
+        if (i < 100)
+            print_char(' ');
+        if (i < 10)
+            print_char(' ');
+        print_int(i);
+        tprint("]: ");
+        tprint(save_stack_type(i));
+        switch (save_type(i)) {
+            case restore_old_value:
+                tprint(", ");
+                show_eqtb_meaning(save_value(i));
+                tprint("=");
+                if (save_value(i) >= int_base) {
+                    print_int(save_word(i - 1).cint);
+                } else {
+                    print_int(eq_type_field(save_word(i - 1)));
+                    print_char(',');        /* |print_int(eq_level_field(save_word(i-1)));| */
+                    print_int(equiv_field(save_word(i - 1)));
+                }
+                i--;
+                break;
+            case restore_zero:
+                tprint(", ");
+                show_eqtb_meaning(save_value(i));
+                break;
+            case insert_token:
+                tprint(", ");
+                {
+                    halfword p = get_avail();
+                    set_token_info(p, save_value(i));
+                    show_token_list(p, null, 1);
+                    free_avail(p);
+                }
+                break;
+            case level_boundary:
+                tprint(", old group=");
+                print_int(save_level(i));
+                tprint(", boundary = ");
+                print_int(save_value(i));
+                tprint(", line = ");
+                print_int(save_value(i - 1));
+                i--;
+                break;
+            case saved_adjust:
+                tprint(", ");
+                print_int(save_level(i));   /* vadjust vs vadjust pre */
+                break;
+            case saved_insert:
+                tprint(", ");
+                print_int(save_value(i));   /* insert number */
+                break;
+            case saved_boxtype:    /* \.{\\localleftbox} vs \.{\\localrightbox} */
+                tprint(", ");
+                print_int(save_value(i));
+                break;
+            case saved_eqno:       /* \.{\\eqno} vs \.{\\leqno} */
+                tprint(", ");
+                print_int(save_value(i));
+                break;
+            case saved_disc:
+            case saved_choices:
+                tprint(", ");
+                print_int(save_value(i));
+                break;
+            case saved_math:
+                tprint(", listptr=");
+                print_int(save_value(i));
+                break;
+            case saved_boxcontext:
+                tprint(", ");
+                print_int(save_value(i));
+                break;
+            case saved_boxspec:
+                tprint(", spec=");
+                print_int(save_level(i));
+                tprint(", dimen=");
+                print_int(save_value(i));
+                break;
+            case saved_textdir:
+            case saved_boxdir:
+                tprint(", ");
+                print_dir(dir_dir(save_value(i)));
+                break;
+            case saved_boxattr:
+            case saved_boxpack:
+                tprint(", ");
+                print_int(save_value(i));
+                break;
+            case saved_line:
+            case saved_eqtb:
+                break;
+            default:
+                break;
+        }
+        print_ln();
+    }
+    end_diagnostic(true);
+}
+
+@ The \.{\\showgroups} command displays all currently active grouping
+  levels.
+
+@ The modifications of \TeX\ required for the display produced by the
+  |show_save_groups| procedure were first discussed by Donald~E. Knuth in
+  {\sl TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990.
+  @^Knuth, Donald Ervin@>
+
+  In order to understand a group type we also have to know its mode.
+  Since unrestricted horizontal modes are not associated with grouping,
+  they are skipped when traversing the semantic nest.
+
+@c
+void show_save_groups(void)
+{
+    int p = nest_ptr;           /* index into |nest| */
+    int m;                      /* mode */
+    save_pointer v = save_ptr;  /* saved value of |save_ptr| */
+    quarterword l = cur_level;  /* saved value of |cur_level| */
+    group_code c = cur_group;   /* saved value of |cur_group| */
+    int a = 1;                  /* to keep track of alignments */
+    int i;
+    quarterword j;
+    const char *s = NULL;
+    save_ptr = cur_boundary;
+    decr(cur_level);
+    tprint_nl("");
+    print_ln();
+    while (1) {
+        tprint_nl("### ");
+        print_group(true);
+        if (cur_group == bottom_level)
+            goto DONE;
+        do {
+            m = nest[p].mode_field;
+            if (p > 0)
+                decr(p);
+            else
+                m = vmode;
+        } while (m == hmode);
+        tprint(" (");
+        switch (cur_group) {
+            case simple_group:
+                incr(p);
+                goto FOUND2;
+                break;
+            case hbox_group:
+            case adjusted_hbox_group:
+                s = "hbox";
+                break;
+            case vbox_group:
+                s = "vbox";
+                break;
+            case vtop_group:
+                s = "vtop";
+                break;
+            case align_group:
+                if (a == 0) {
+                    if (m == -vmode)
+                        s = "halign";
+                    else
+                        s = "valign";
+                    a = 1;
+                    goto FOUND1;
+                } else {
+                    if (a == 1)
+                        tprint("align entry");
+                    else
+                        tprint_esc("cr");
+                    if (p >= a)
+                        p = p - a;
+                    a = 0;
+                    goto FOUND;
+                }
+                break;
+            case no_align_group:
+                incr(p);
+                a = -1;
+                tprint_esc("noalign");
+                goto FOUND2;
+                break;
+            case output_group:
+                tprint_esc("output");
+                goto FOUND;
+                break;
+            case math_group:
+                goto FOUND2;
+                break;
+            case disc_group:
+                tprint_esc("discretionary");
+                for (i = 1; i < 3; i++)
+                    if (i <= saved_value(-2))
+                        tprint("{}");
+                goto FOUND2;
+                break;
+            case math_choice_group:
+                tprint_esc("mathchoice");
+                for (i = 1; i < 4; i++)
+                    if (i <= saved_value(-3))       /* different offset because |-2==saved_textdir| */
+                        tprint("{}");
+                goto FOUND2;
+                break;
+            case insert_group:
+                if (saved_type(-1) == saved_adjust) {
+                    tprint_esc("vadjust");
+                    if (saved_level(-1) != 0)
+                        tprint(" pre");
+                } else {
+                    tprint_esc("insert");
+                    print_int(saved_value(-1));
+                }
+                goto FOUND2;
+                break;
+            case vcenter_group:
+                s = "vcenter";
+                goto FOUND1;
+                break;
+            case semi_simple_group:
+                incr(p);
+                tprint_esc("begingroup");
+                goto FOUND;
+                break;
+            case math_shift_group:
+                if (m == mmode) {
+                    print_char('$');
+                } else if (nest[p].mode_field == mmode) {
+                    print_cmd_chr(eq_no_cmd, saved_value(-2));
+                    goto FOUND;
+                }
+                print_char('$');
+                goto FOUND;
+                break;
+            case math_left_group:
+                if (subtype(nest[p + 1].eTeX_aux_field) == left_noad_side)
+                    tprint_esc("left");
+                else
+                    tprint_esc("middle");
+                goto FOUND;
+                break;
+            default:
+                confusion("showgroups");
+                break;
+        }
+        /* Show the box context */
+        i = saved_value(-5);
+        if (i != 0) {
+            if (i < box_flag) {
+                if (abs(nest[p].mode_field) == vmode)
+                    j = hmove_cmd;
+                else
+                    j = vmove_cmd;
+                if (i > 0)
+                    print_cmd_chr(j, 0);
+                else
+                    print_cmd_chr(j, 1);
+                print_scaled(abs(i));
+                tprint("pt");
+            } else if (i < ship_out_flag) {
+                if (i >= global_box_flag) {
+                    tprint_esc("global");
+                    i = i - (global_box_flag - box_flag);
+                }
+                tprint_esc("setbox");
+                print_int(i - box_flag);
+                print_char('=');
+            } else {
+                print_cmd_chr(leader_ship_cmd, i - (leader_flag - a_leaders));
+            }
+        }
+      FOUND1:
+        tprint_esc(s);
+        /* Show the box packaging info */
+        {
+            /* offsets may vary */
+            int ii = -1;
+            while (saved_type(ii) != saved_boxspec)
+                ii--;
+            if (saved_value(ii) != 0) {
+                print_char(' ');
+                if (saved_level(ii) == exactly)
+                    tprint("to");
+                else
+                    tprint("spread");
+                print_scaled(saved_value(ii));
+                tprint("pt");
+            }
+        }
+      FOUND2:
+        print_char('{');
+      FOUND:
+        print_char(')');
+        decr(cur_level);
+        cur_group = save_level(save_ptr);
+        save_ptr = save_value(save_ptr);
+    }
+  DONE:
+    save_ptr = v;
+    cur_level = l;
+    cur_group = c;
+}
+
+@ Just before an entry of |eqtb| is changed, the following procedure should
+be called to update the other data structures properly. It is important
+to keep in mind that reference counts in |mem| include references from
+within |save_stack|, so these counts must be handled carefully.
+@^reference counts@>
+
+@c
+/* we don't need to destroy when an assignment has the same node */
+
+void eq_destroy(memory_word w)
+{                               /* gets ready to forget |w| */
+    halfword q;                 /* |equiv| field of |w| */
+    switch (eq_type_field(w)) {
+        case call_cmd:
+        case long_call_cmd:
+        case outer_call_cmd:
+        case long_outer_call_cmd:
+            delete_token_ref(equiv_field(w));
+            break;
+        case glue_ref_cmd:
+            flush_node(equiv_field(w));
+            break;
+        case shape_ref_cmd:
+            q = equiv_field(w);     /* we need to free a \.{\\parshape} block */
+            if (q != null)
+                flush_node(q);
+            break;                  /* such a block is |2n+1| words long, where |n=vinfo(q)| */
+        case box_ref_cmd:
+            flush_node_list(equiv_field(w));
+            break;
+        default:
+            break;
+    }
+}
+
+@ To save a value of |eqtb[p]| that was established at level |l|, we
+can use the following subroutine.
+
+@c
+void eq_save(halfword p, quarterword l)
+{                               /* saves |eqtb[p]| */
+    check_full_save_stack();
+    if (l == level_zero) {
+        save_type(save_ptr) = restore_zero;
+    } else {
+        save_word(save_ptr) = eqtb[p];
+        save_type(save_ptr) = saved_eqtb;
+        incr(save_ptr);
+        save_type(save_ptr) = restore_old_value;
+    }
+    save_level(save_ptr) = l;
+    save_value(save_ptr) = p;
+    incr(save_ptr);
+}
+
+@ The procedure |eq_define| defines an |eqtb| entry having specified
+|eq_type| and |equiv| fields, and saves the former value if appropriate.
+This procedure is used only for entries in the first four regions of |eqtb|,
+i.e., only for entries that have |eq_type| and |equiv| fields.
+After calling this routine, it is safe to put four more entries on
+|save_stack|, provided that there was room for four more entries before
+the call, since |eq_save| makes the necessary test.
+
+@ new data for |eqtb|
+@c
+void eq_define(halfword p, quarterword t, halfword e)
+{
+    boolean trace = tracing_assigns_par > 0;
+    if ((eq_type(p) == t) && (equiv(p) == e)) {
+        if (trace)
+            diagnostic_trace(p, "reassigning");
+        eq_destroy(eqtb[p]);
+        return;
+    }
+    if (trace)
+        diagnostic_trace(p, "changing");
+    if (eq_level(p) == cur_level)
+        eq_destroy(eqtb[p]);
+    else if (cur_level > level_one)
+        eq_save(p, eq_level(p));
+    set_eq_level(p, cur_level);
+    set_eq_type(p, t);
+    set_equiv(p, e);
+    if (trace)
+        diagnostic_trace(p, "into");
+}
+
+@ The counterpart of |eq_define| for the remaining (fullword) positions in
+|eqtb| is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all
+|p|, a `|restore_zero|' will never be used in this case.
+
+@c
+void eq_word_define(halfword p, int w)
+{
+    boolean trace = tracing_assigns_par > 0;
+    if (eqtb[p].cint == w) {
+        if (trace)
+            diagnostic_trace(p, "reassigning");
+        return;
+    }
+    if (trace)
+        diagnostic_trace(p, "changing");
+    if (xeq_level[p] != cur_level) {
+        eq_save(p, xeq_level[p]);
+        xeq_level[p] = cur_level;
+    }
+    eqtb[p].cint = w;
+    if (trace)
+        diagnostic_trace(p, "into");
+}
+
+
+@ The |eq_define| and |eq_word_define| routines take care of local definitions.
+@^global definitions@>
+Global definitions are done in almost the same way, but there is no need
+to save old values, and the new value is associated with |level_one|.
+
+@c
+void geq_define(halfword p, quarterword t, halfword e)
+{                               /* global |eq_define| */
+    boolean trace = tracing_assigns_par > 0;
+    if (trace)
+        diagnostic_trace(p, "globally changing");
+    eq_destroy(eqtb[p]);
+    set_eq_level(p, level_one);
+    set_eq_type(p, t);
+    set_equiv(p, e);
+    if (trace)
+        diagnostic_trace(p, "into");
+}
+
+void geq_word_define(halfword p, int w)
+{                               /* global |eq_word_define| */
+    boolean trace = tracing_assigns_par > 0;
+    if (trace)
+        diagnostic_trace(p, "globally changing");
+    eqtb[p].cint = w;
+    xeq_level[p] = level_one;
+    if (trace)
+        diagnostic_trace(p, "into");
+}
+
+@ Subroutine |save_for_after| puts a token on the stack for save-keeping.
+
+@c
+void save_for_after(halfword t)
+{
+    if (cur_level > level_one) {
+        check_full_save_stack();
+        save_type(save_ptr) = insert_token;
+        save_level(save_ptr) = level_zero;
+        save_value(save_ptr) = t;
+        incr(save_ptr);
+    }
+}
+
+@ The |unsave| routine goes the other way, taking items off of |save_stack|.
+This routine takes care of restoration when a level ends; everything
+belonging to the topmost group is cleared off of the save stack.
+
+@c
+void unsave(void)
+{                               /* pops the top level off the save stack */
+    halfword p;                 /* position to be restored */
+    quarterword l = level_one;  /* saved level, if in fullword regions of |eqtb| */
+    boolean a = false;          /* have we already processed an \.{\\aftergroup} ? */
+    unsave_math_codes(cur_level);
+    unsave_cat_codes(cat_code_table_par, cur_level);
+    unsave_text_codes(cur_level);
+    unsave_math_data(cur_level);
+    if (cur_level > level_one) {
+        boolean trace = tracing_restores_par > 0;
+        decr(cur_level);
+        /* Clear off top level from |save_stack| */
+        while (true) {
+            decr(save_ptr);
+            if (save_type(save_ptr) == level_boundary)
+                break;
+            p = save_value(save_ptr);
+            if (save_type(save_ptr) == insert_token) {
+                reinsert_token(a, p);
+                a = true; /* always ... always etex now */
+            } else {
+                if (save_type(save_ptr) == restore_old_value) {
+                    l = save_level(save_ptr);
+                    decr(save_ptr);
+                } else {
+                    save_word(save_ptr) = eqtb[undefined_control_sequence];
+                }
+                /* Store |save_stack[save_ptr]| in |eqtb[p]|, unless
+                   |eqtb[p]| holds a global value */
+                /* A global definition, which sets the level to |level_one|,
+                   will not be undone by |unsave|. If at least one global definition of
+                   |eqtb[p]| has been carried out within the group that just ended, the
+                   last such definition will therefore survive.
+                 */
+                if (p < int_base || p > eqtb_size) {
+                    if (eq_level(p) == level_one) {
+                        eq_destroy(save_word(save_ptr));        /* destroy the saved value */
+                        if (trace)
+                            diagnostic_trace(p, "retaining");
+                    } else {
+                        eq_destroy(eqtb[p]);    /* destroy the current value */
+                        eqtb[p] = save_word(save_ptr);  /* restore the saved value */
+                        if (trace)
+                            diagnostic_trace(p, "restoring");
+                    }
+                } else if (xeq_level[p] != level_one) {
+                    eqtb[p] = save_word(save_ptr);
+                    xeq_level[p] = l;
+                    if (trace)
+                        diagnostic_trace(p, "restoring");
+                } else {
+                    if (trace)
+                        diagnostic_trace(p, "retaining");
+                }
+            }
+        }
+        if (tracing_groups_par > 0)
+            group_trace(true);
+        if (grp_stack[in_open] == cur_boundary)
+            group_warning();    /* groups possibly not properly nested with files */
+        cur_group = save_level(save_ptr);
+        cur_boundary = save_value(save_ptr);
+        decr(save_ptr);
+    } else {
+        confusion("curlevel");  /* |unsave| is not used when |cur_group=bottom_level| */
+    }
+    attr_list_cache = cache_disabled;
+}
+
+@ Most of the parameters kept in |eqtb| can be changed freely, but there's
+an exception:  The magnification should not be used with two different
+values during any \TeX\ job, since a single magnification is applied to an
+entire run. The global variable |mag_set| is set to the current magnification
+whenever it becomes necessary to ``freeze'' it at a particular value.
+
+@c
+int mag_set = 0; /* if nonzero, this magnification should be used henceforth */
+
+@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag|
+for magnification.
+
+@c
+void prepare_mag(void)
+{
+    if ((mag_set > 0) && (mag_par != mag_set)) {
+        print_err("Incompatible magnification (");
+        print_int(mag_par);
+        tprint(");");
+        tprint_nl(" the previous value will be retained");
+        help2("I can handle only one magnification ratio per job. So I've",
+              "reverted to the magnification you used earlier on this run.");
+        int_error(mag_set);
+        geq_word_define(int_base + mag_code, mag_set);  /* |mag:=mag_set| */
+    }
+    if ((mag_par <= 0) || (mag_par > 32768)) {
+        print_err("Illegal magnification has been changed to 1000");
+        help1("The magnification ratio must be between 1 and 32768.");
+        int_error(mag_par);
+        geq_word_define(int_base + mag_code, 1000);
+    }
+    if ((mag_set == 0) && (mag_par != mag_set)) {
+        if (mag_par != 1000)
+            one_true_inch = xn_over_d(one_hundred_inch, 10, mag_par);
+        else
+            one_true_inch = one_inch;
+    }
+    mag_set = mag_par;
+}
+
+@ Let's pause a moment now and try to look at the Big Picture.
+The \TeX\ program consists of three main parts: syntactic routines,
+semantic routines, and output routines. The chief purpose of the
+syntactic routines is to deliver the user's input to the semantic routines,
+one token at a time. The semantic routines act as an interpreter
+responding to these tokens, which may be regarded as commands. And the
+output routines are periodically called on to convert box-and-glue
+lists into a compact set of instructions that will be sent
+to a typesetter. We have discussed the basic data structures and utility
+routines of \TeX, so we are good and ready to plunge into the real activity by
+considering the syntactic routines.
+
+Our current goal is to come to grips with the |get_next| procedure,
+which is the keystone of \TeX's input mechanism. Each call of |get_next|
+sets the value of three variables |cur_cmd|, |cur_chr|, and |cur_cs|,
+representing the next input token.
+$$\vbox{\halign{#\hfil\cr
+  \hbox{|cur_cmd| denotes a command code from the long list of codes
+   given above;}\cr
+  \hbox{|cur_chr| denotes a character code or other modifier of the command
+   code;}\cr
+  \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr
+  \hbox{\qquad if the current token was a control sequence,
+   otherwise it's zero.}\cr}}$$
+Underlying this external behavior of |get_next| is all the machinery
+necessary to convert from character files to tokens. At a given time we
+may be only partially finished with the reading of several files (for
+which \.{\\input} was specified), and partially finished with the expansion
+of some user-defined macros and/or some macro parameters, and partially
+finished with the generation of some text in a template for \.{\\halign},
+and so on. When reading a character file, special characters must be
+classified as math delimiters, etc.; comments and extra blank spaces must
+be removed, paragraphs must be recognized, and control sequences must be
+found in the hash table. Furthermore there are occasions in which the
+scanning routines have looked ahead for a word like `\.{plus}' but only
+part of that word was found, hence a few characters must be put back
+into the input and scanned again.
+
+To handle these situations, which might all be present simultaneously,
+\TeX\ uses various stacks that hold information about the incomplete
+activities, and there is a finite state control for each level of the
+input mechanism. These stacks record the current state of an implicitly
+recursive process, but the |get_next| procedure is not recursive.
+Therefore it will not be difficult to translate these algorithms into
+low-level languages that do not support recursion.
+
+@c
+int cur_cmd;                    /* current command set by |get_next| */
+halfword cur_chr;               /* operand of current command */
+halfword cur_cs;                /* control sequence found here, zero if none found */
+halfword cur_tok;               /* packed representative of |cur_cmd| and |cur_chr| */
+
+@ Here is a procedure that displays the current command.
+
+@c
+void show_cur_cmd_chr(void)
+{
+    int n;                      /* level of \.{\\if...\\fi} nesting */
+    int l;                      /* line where \.{\\if} started */
+    halfword p;
+    begin_diagnostic();
+    tprint_nl("{");
+    if (mode_par != shown_mode) {
+        print_mode(mode_par);
+        tprint(": ");
+        shown_mode = mode_par;
+    }
+    print_cmd_chr((quarterword) cur_cmd, cur_chr);
+    if (tracing_ifs_par > 0) {
+        if (cur_cmd >= if_test_cmd) {
+            if (cur_cmd <= fi_or_else_cmd) {
+                tprint(": ");
+                if (cur_cmd == fi_or_else_cmd) {
+                    print_cmd_chr(if_test_cmd, cur_if);
+                    print_char(' ');
+                    n = 0;
+                    l = if_line;
+                } else {
+                    n = 1;
+                    l = line;
+                }
+                p = cond_ptr;
+                while (p != null) {
+                    incr(n);
+                    p = vlink(p);
+                }
+                tprint("(level ");
+                print_int(n);
+                print_char(')');
+                print_if_line(l);
+            }
+        }
+    }
+    print_char('}');
+    end_diagnostic(false);
+}
+
+@ Here is a procedure that displays the contents of |eqtb[n]| symbolically.
+
+@c
+void show_eqtb(halfword n)
+{
+    if (n < null_cs) {
+        /* this can't happen */
+        print_char('?');
+    } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) {
+        /*
+            Show equivalent |n|, in region 1 or 2
+
+            Here is a routine that displays the current meaning of an |eqtb| entry
+            in region 1 or~2. (Similar routines for the other regions will appear
+            below.)
+        */
+
+        sprint_cs(n);
+        print_char('=');
+        print_cmd_chr(eq_type(n), equiv(n));
+        if (eq_type(n) >= call_cmd) {
+            print_char(':');
+            show_token_list(token_link(equiv(n)), null, 32);
+        }
+    } else if (n < local_base) {
+        /*
+            Show equivalent |n|, in region 3
+
+            All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'.
+        */
+        if (n < skip_base) {
+            if (n < glue_base + thin_mu_skip_code)
+                print_cmd_chr(assign_glue_cmd, n);
+            else
+                print_cmd_chr(assign_mu_glue_cmd, n);
+            print_char('=');
+            if (n < glue_base + thin_mu_skip_code)
+                print_spec(equiv(n), "pt");
+            else
+                print_spec(equiv(n), "mu");
+        } else if (n < mu_skip_base) {
+            tprint_esc("skip");
+            print_int(n - skip_base);
+            print_char('=');
+            print_spec(equiv(n), "pt");
+        } else {
+            tprint_esc("muskip");
+            print_int(n - mu_skip_base);
+            print_char('=');
+            print_spec(equiv(n), "mu");
+        }
+
+    } else if (n < int_base) {
+        /*
+            Show equivalent |n|, in region 4
+
+            We initialize most things to null or undefined values. An undefined font
+            is represented by the internal code |font_base|.
+
+            However, the character code tables are given initial values based on the
+            conventional interpretation of ASCII code. These initial values should
+            not be changed when \TeX\ is adapted for use with non-English languages;
+            all changes to the initialization conventions should be made in format
+            packages, not in \TeX\ itself, so that global interchange of formats is
+            possible.
+        */
+        if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) {
+            if (n == par_shape_loc)
+                print_cmd_chr(set_tex_shape_cmd, n);
+            else
+                print_cmd_chr(set_etex_shape_cmd, n);
+            print_char('=');
+            if (equiv(n) == null) {
+                print_char('0');
+            } else if (n > par_shape_loc) {
+                print_int(penalty(equiv(n)));
+                print_char(' ');
+                print_int(penalty(equiv(n) + 1));
+                if (penalty(equiv(n)) > 1)
+                    tprint_esc("ETC.");
+            } else {
+                print_int(vinfo(par_shape_par_ptr + 1));
+            }
+        } else if (n < toks_base) {
+            print_cmd_chr(assign_toks_cmd, n);
+            print_char('=');
+            if (equiv(n) != null)
+                show_token_list(token_link(equiv(n)), null, 32);
+        } else if (n < box_base) {
+            tprint_esc("toks");
+            print_int(n - toks_base);
+            print_char('=');
+            if (equiv(n) != null)
+                show_token_list(token_link(equiv(n)), null, 32);
+        } else if (n < cur_font_loc) {
+            tprint_esc("box");
+            print_int(n - box_base);
+            print_char('=');
+            if (equiv(n) == null) {
+                tprint("void");
+            } else {
+                depth_threshold = 0;
+                breadth_max = 1;
+                show_node_list(equiv(n));
+            }
+        } else if (n == cur_font_loc) {
+            /* Show the font identifier in |eqtb[n]| */
+            tprint("current font");
+            print_char('=');
+            print_esc(hash[font_id_base + equiv(n)].rh);        /* that's |font_id_text(equiv(n))| */
+        }
+    } else if (n < dimen_base) {
+        /* Show equivalent |n|, in region 5 */
+        if (n < dir_base) {
+            print_cmd_chr(assign_int_cmd, n);
+            print_char('=');
+            print_int(eqtb[n].cint);
+        } else if (n < count_base) {
+            print_cmd_chr(assign_dir_cmd, n);
+            print_char(' ');
+            print_dir(eqtb[n].cint);
+        } else if (n < attribute_base) {
+            tprint_esc("count");
+            print_int(n - count_base);
+            print_char('=');
+            print_int(eqtb[n].cint);
+        } else if (n < del_code_base) {
+            tprint_esc("attribute");
+            print_int(n - attribute_base);
+            print_char('=');
+            print_int(eqtb[n].cint);
+        }
+    } else if (n <= eqtb_size) {
+        /* Show equivalent |n|, in region 6 */
+        if (n < scaled_base) {
+            print_cmd_chr(assign_dimen_cmd, n);
+        } else {
+            tprint_esc("dimen");
+            print_int(n - scaled_base);
+        }
+        print_char('=');
+        print_scaled(eqtb[n].cint);
+        tprint("pt");
+    } else {
+        /* this can't happen either */
+        print_char('?');
+    }
+}
+
+@ @c
+void show_eqtb_meaning(halfword n)
+{
+    if (n < null_cs) {
+        /* this can't happen */
+        print_char('?');
+    } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) {
+        /*
+            Show equivalent |n|, in region 1 or 2
+
+            Here is a routine that displays the current meaning of an |eqtb| entry
+            in region 1 or~2. (Similar routines for the other regions will appear
+            below.)
+        */
+        sprint_cs(n);
+    } else if (n < local_base) {
+        /*
+            Show equivalent |n|, in region 3
+
+            All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'.
+        */
+        if (n < skip_base) {
+            if (n < glue_base + thin_mu_skip_code)
+                print_cmd_chr(assign_glue_cmd, n);
+            else
+                print_cmd_chr(assign_mu_glue_cmd, n);
+        } else if (n < mu_skip_base) {
+            tprint_esc("skip");
+            print_int(n - skip_base);
+        } else {
+            tprint_esc("muskip");
+            print_int(n - mu_skip_base);
+        }
+
+    } else if (n < int_base) {
+        /*
+            Show equivalent |n|, in region 4
+
+            We initialize most things to null or undefined values. An undefined font
+            is represented by the internal code |font_base|.
+
+            However, the character code tables are given initial values based on the
+            conventional interpretation of ASCII code. These initial values should
+            not be changed when \TeX\ is adapted for use with non-English languages;
+            all changes to the initialization conventions should be made in format
+            packages, not in \TeX\ itself, so that global interchange of formats is
+            possible.
+        */
+        if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) {
+            if (n == par_shape_loc)
+                print_cmd_chr(set_tex_shape_cmd, n);
+            else
+                print_cmd_chr(set_etex_shape_cmd, n);
+        } else if (n < toks_base) {
+            print_cmd_chr(assign_toks_cmd, n);
+        } else if (n < box_base) {
+            tprint_esc("toks");
+            print_int(n - toks_base);
+        } else if (n < cur_font_loc) {
+            tprint_esc("box");
+            print_int(n - box_base);
+        } else if (n == cur_font_loc) {
+            /* Show the font identifier in |eqtb[n]| */
+            tprint("current font");
+        }
+    } else if (n < dimen_base) {
+        /* Show equivalent |n|, in region 5 */
+        if (n < dir_base) {
+            print_cmd_chr(assign_int_cmd, n);
+        } else if (n < count_base) {
+            print_cmd_chr(assign_dir_cmd, n);
+        } else if (n < attribute_base) {
+            tprint_esc("count");
+            print_int(n - count_base);
+        } else if (n < del_code_base) {
+            tprint_esc("attribute");
+            print_int(n - attribute_base);
+        }
+    } else if (n <= eqtb_size) {
+        /* Show equivalent |n|, in region 6 */
+        if (n < scaled_base) {
+            print_cmd_chr(assign_dimen_cmd, n);
+        } else {
+            tprint_esc("dimen");
+            print_int(n - scaled_base);
+        }
+    } else {
+        /* this can't happen either */
+        print_char('?');
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/equivalents.c
+++ /dev/null
@@ -1,1220 +0,0 @@
-/*
-
-equivalents.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-halfword last_cs_name = null_cs;
-
-/*tex |eqtb[p]| has just been restored or retained. */
-
-static void diagnostic_trace(halfword p, const char *s)
-{
-    begin_diagnostic();
-    print_char('{');
-    tprint(s);
-    print_char(' ');
-    show_eqtb(p);
-    print_char('}');
-    end_diagnostic(false);
-}
-
-void show_eqtb_meaning(halfword n);
-
-/*tex
-
-Now that we have studied the data structures for \TeX's semantic routines, we
-ought to consider the data structures used by its syntactic routines. In other
-words, our next concern will be the tables that \TeX\ looks at when it is
-scanning what the user has written.
-
-The biggest and most important such table is called |eqtb|. It holds the current
-``equivalents'' of things; i.e., it explains what things mean or what their
-current values are, for all quantities that are subject to the nesting structure
-provided by \TeX's grouping mechanism. There are six parts to |eqtb|:
-
-1) |eqtb[null_cs]| holds the current equivalent of the zero-length control
-sequence.
-
-2) |eqtb[hash_base..(glue_base-1)]| holds the current equivalents of single- and
-multiletter control sequences.
-
-3) |eqtb[glue_base..(local_base-1)]| holds the current equivalents of glue
-parameters like the current baselineskip.
-
-4) |eqtb[local_base..(int_base-1)]| holds the current equivalents of local
-halfword quantities like the current box registers, the current ``catcodes,'' the
-current font, and a pointer to the current paragraph shape.
-
-5) |eqtb[int_base..(dimen_base-1)]| holds the current equivalents of fullword
-integer parameters like the current hyphenation penalty.
-
-6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents of fullword
-dimension parameters like the current hsize or amount of hanging indentation.
-
-Note that, for example, the current amount of baselineskip glue is determined by
-the setting of a particular location in region~3 of |eqtb|, while the current
-meaning of the control sequence `\.{\\baselineskip}' (which might have been
-changed by \.{\\def} or \.{\\let}) appears in region~2.
-
-The last two regions of |eqtb| have fullword values instead of the three fields
-|eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary, but \TeX\ needs
-to store the |eq_level| information in another array called |xeq_level|.
-
-*/
-
-memory_word *eqtb;
-halfword eqtb_top;
-quarterword xeq_level[(eqtb_size + 1)];
-
-void initialize_equivalents(void)
-{
-    int k;
-    for (k = int_base; k <= eqtb_size; k++)
-        xeq_level[k] = level_one;
-}
-
-/*tex
-
-The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups in
-\TeX\ means that |eqtb| entries valid in outer groups should be saved and
-restored later if they are overridden inside the braces. When a new |eqtb| value
-is being assigned, the program therefore checks to see if the previous entry
-belongs to an outer level. In such a case, the old value is placed on the
-|save_stack| just before the new value enters |eqtb|. At the end of a grouping
-level, i.e., when the right brace is sensed, the |save_stack| is used to restore
-the outer values, and the inner ones are destroyed.
-
-Entries on the |save_stack| are of type |save_record|. The top item on this stack
-is |save_stack[p]|, where |p=save_ptr-1|; it contains three fields called
-|save_type|, |save_level|, and |save_value|, and it is interpreted in one of four
-ways:
-
-1) If |save_type(p)=restore_old_value|, then |save_value(p)| is a location in
-|eqtb| whose current value should be destroyed at the end of the current group
-and replaced by |save_word(p-1)| (|save_type(p-1)==saved_eqtb|). Furthermore if
-|save_value(p)>=int_base|, then |save_level(p)| should replace the corresponding
-entry in |xeq_level| (if |save_value(p)<int_base|, then the level is part of
-|save_word(p-1)|).
-
-2) If |save_type(p)=restore_zero|, then |save_value(p)| is a location in |eqtb|
-whose current value should be destroyed at the end of the current group, when it
-should be replaced by the current value of |eqtb[undefined_control_sequence]|.
-
-3) If |save_type(p)=insert_token|, then |save_value(p)| is a token that should be
-inserted into \TeX's input when the current group ends.
-
-4) If |save_type(p)=level_boundary|, then |save_level(p)| is a code explaining
-what kind of group we were previously in, and |save_value(p)| points to the level
-boundary word at the bottom of the entries for that group. Furthermore,
-|save_value(p-1)| contains the source line number at which the current level of
-grouping was entered, this field has itself a type: |save_type(p-1)==saved_line|.
-
-Besides this `official' use, various subroutines push temporary variables on the
-save stack when it is handy to do so. These all have an explicit |save_type|, and
-they are:
-
-|saved_adjust| signifies an adjustment is beging scanned,
-|saved_insert| an insertion is being scanned,
-|saved_disc| the \.{\\discretionary} sublist we are working on right now,
-|saved_boxtype| whether a \.{\\localbox} is \.{\\left} or \.{\\right},
-|saved_textdir| a text direction to be restored,
-|saved_eqno| diffentiates between \.{\\eqno} and \.{\\leqno},
-|saved_choices| the \.{\\mathchoices} sublist we are working on right now,
-|saved_math| and interrupted math list,
-|saved_boxcontext| the box context value,
-|saved_boxspec| the box \.{to} or \.{spread} specification,
-|saved_boxdir| the box \.{dir} specification,
-|saved_boxattr| the box \.{attr} specification,
-|saved_boxpack| the box \.{pack} specification.
-
-The global variable |cur_group| keeps track of what sort of group we are
-currently in. Another global variable, |cur_boundary|, points to the topmost
-|level_boundary| word. And |cur_level| is the current depth of nesting. The
-routines are designed to preserve the condition that no entry in the |save_stack|
-or in |eqtb| ever has a level greater than |cur_level|.
-
-*/
-
-save_record *save_stack;
-int save_ptr;                        /* first unused entry on the save stack */
-int max_save_stack;                  /* maximum usage of save stack */
-quarterword cur_level = level_one;   /* current nesting level for groups */
-group_code cur_group = bottom_level; /* current group type */
-int cur_boundary;                    /* where the current level begins */
-
-/*tex
-
-At this time it might be a good idea for the reader to review the introduction to
-|eqtb| that was given above just before the long lists of parameter names. Recall
-that the ``outer level'' of the program is |level_one|, since undefined control
-sequences are assumed to be ``defined'' at |level_zero|.
-
-The following macro is used to test if there is room for up to eight more entries
-on |save_stack|. By making a conservative test like this, we can get by with
-testing for overflow in only a few places.
-
-*/
-
-#define check_full_save_stack() do { \
-    if (save_ptr>max_save_stack) { \
-        max_save_stack=save_ptr; \
-        if (max_save_stack>save_size-8) \
-            overflow("save size",(unsigned)save_size); \
-    } \
-} while (0)
-
-/*tex
-
-Procedure |new_save_level| is called when a group begins. The argument is a group
-identification code like `|hbox_group|'. After calling this routine, it is safe
-to put six more entries on |save_stack|.
-
-In some cases integer-valued items are placed onto the |save_stack| just below a
-|level_boundary| word, because this is a convenient place to keep information
-that is supposed to ``pop up'' just when the group has finished. For example,
-when `\.{\\hbox to 100pt}' is being treated, the 100pt dimension is stored on
-|save_stack| just before |new_save_level| is called.
-
-*/
-
-void new_save_level(group_code c)
-{   /*tex We begin a new level of grouping. */
-    check_full_save_stack();
-    set_saved_record(0, saved_line, 0, line);
-    incr(save_ptr);
-    save_type(save_ptr) = level_boundary;
-    save_level(save_ptr) = cur_group;
-    save_value(save_ptr) = cur_boundary;
-    if (cur_level == max_quarterword) {
-        overflow("grouping levels", max_quarterword - min_quarterword);
-    }
-    /*tex We quit if |(cur_level+1)| is too big to be stored in |eqtb|. */
-    cur_boundary = save_ptr;
-    cur_group = c;
-    if (tracing_groups_par > 0)
-        group_trace(false);
-    incr(cur_level);
-    incr(save_ptr);
-}
-
-static const char *save_stack_type(int v)
-{
-    const char *s = "";
-    switch (save_type(v)) {
-        case restore_old_value: s = "restore_old_value"; break;
-        case restore_zero:      s = "restore_zero";      break;
-        case insert_token:      s = "insert_token";      break;
-        case level_boundary:    s = "level_boundary";    break;
-        case saved_line:        s = "saved_line";        break;
-        case saved_adjust:      s = "saved_adjust";      break;
-        case saved_insert:      s = "saved_insert";      break;
-        case saved_disc:        s = "saved_disc";        break;
-        case saved_boxtype:     s = "saved_boxtype";     break;
-        case saved_textdir:     s = "saved_textdir";     break;
-        case saved_eqno:        s = "saved_eqno";        break;
-        case saved_choices:     s = "saved_choices";     break;
-        case saved_math:        s = "saved_math";        break;
-        case saved_boxcontext:  s = "saved_boxcontext";  break;
-        case saved_boxspec:     s = "saved_boxspec";     break;
-        case saved_boxdir:      s = "saved_boxdir";      break;
-        case saved_boxattr:     s = "saved_boxattr";     break;
-        case saved_boxpack:     s = "saved_boxpack";     break;
-        case saved_attrlist:    s = "saved_attrlist";    break;
-        case saved_eqtb:        s = "saved_eqtb";        break;
-        default: break;
-    }
-    return s;
-}
-
-void print_save_stack(void)
-{
-    int i;
-    begin_diagnostic();
-    selector = term_and_log;
-    print_ln();
-    for (i = (save_ptr - 1); i >= 0; i--) {
-        tprint("save_stack[");
-        if (i < 100)
-            print_char(' ');
-        if (i < 10)
-            print_char(' ');
-        print_int(i);
-        tprint("]: ");
-        tprint(save_stack_type(i));
-        switch (save_type(i)) {
-            case restore_old_value:
-                tprint(", ");
-                show_eqtb_meaning(save_value(i));
-                tprint("=");
-                if (save_value(i) >= int_base) {
-                    print_int(save_word(i - 1).cint);
-                } else {
-                    print_int(eq_type_field(save_word(i - 1)));
-                    print_char(',');
-                    print_int(equiv_field(save_word(i - 1)));
-                }
-                i--;
-                break;
-            case restore_zero:
-                tprint(", ");
-                show_eqtb_meaning(save_value(i));
-                break;
-            case insert_token:
-                tprint(", ");
-                {
-                    halfword p = get_avail();
-                    set_token_info(p, save_value(i));
-                    show_token_list(p, null, 1);
-                    free_avail(p);
-                }
-                break;
-            case level_boundary:
-                tprint(", old group=");
-                print_int(save_level(i));
-                tprint(", boundary = ");
-                print_int(save_value(i));
-                tprint(", line = ");
-                print_int(save_value(i - 1));
-                i--;
-                break;
-            case saved_adjust:
-                tprint(", ");
-                /*tex vadjust vs vadjust pre */
-                print_int(save_level(i));
-                break;
-            case saved_insert:
-                tprint(", ");
-                /*tex insert number */
-                print_int(save_value(i));
-                break;
-            case saved_boxtype:
-                /*tex \.{\\localleftbox} vs \.{\\localrightbox} */
-                tprint(", ");
-                print_int(save_value(i));
-                break;
-            case saved_eqno:
-                /*tex \.{\\eqno} vs \.{\\leqno} */
-                tprint(", ");
-                print_int(save_value(i));
-                break;
-            case saved_disc:
-            case saved_choices:
-                tprint(", ");
-                print_int(save_value(i));
-                break;
-            case saved_math:
-                tprint(", listptr=");
-                print_int(save_value(i));
-                break;
-            case saved_boxcontext:
-                tprint(", ");
-                print_int(save_value(i));
-                break;
-            case saved_boxspec:
-                tprint(", spec=");
-                print_int(save_level(i));
-                tprint(", dimen=");
-                print_int(save_value(i));
-                break;
-            case saved_textdir:
-            case saved_boxdir:
-                tprint(", ");
-                print_dir_text(save_value(i));
-                break;
-            case saved_boxattr:
-            case saved_boxpack:
-            case saved_attrlist:
-                tprint(", ");
-                print_int(save_value(i));
-                break;
-            case saved_line:
-            case saved_eqtb:
-                break;
-            default:
-                break;
-        }
-        print_ln();
-    }
-    end_diagnostic(true);
-}
-
-/*tex
-
-The \.{\\showgroups} command displays all currently active grouping levels.
-
-The modifications of \TeX\ required for the display produced by the
-|show_save_groups| procedure were first discussed by Donald~E. Knuth in {\sl
-TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990. @^Knuth, Donald Ervin@>
-
-In order to understand a group type we also have to know its mode. Since
-unrestricted horizontal modes are not associated with grouping, they are skipped
-when traversing the semantic nest.
-
-*/
-
-void show_save_groups(void)
-{
-    int p = nest_ptr;
-    int m;
-    save_pointer v = save_ptr;
-    quarterword l = cur_level;
-    group_code c = cur_group;
-    int a = 1; /* to keep track of alignments */
-    int i;
-    quarterword j;
-    const char *s = NULL;
-    save_ptr = cur_boundary;
-    decr(cur_level);
-    tprint_nl("");
-    print_ln();
-    while (1) {
-        tprint_nl("### ");
-        print_group(true);
-        if (cur_group == bottom_level)
-            goto DONE;
-        do {
-            m = nest[p].mode_field;
-            if (p > 0)
-                decr(p);
-            else
-                m = vmode;
-        } while (m == hmode);
-        tprint(" (");
-        switch (cur_group) {
-            case simple_group:
-                incr(p);
-                goto FOUND2;
-                break;
-            case hbox_group:
-            case adjusted_hbox_group:
-                s = "hbox";
-                break;
-            case vbox_group:
-                s = "vbox";
-                break;
-            case vtop_group:
-                s = "vtop";
-                break;
-            case align_group:
-                if (a == 0) {
-                    if (m == -vmode)
-                        s = "halign";
-                    else
-                        s = "valign";
-                    a = 1;
-                    goto FOUND1;
-                } else {
-                    if (a == 1)
-                        tprint("align entry");
-                    else
-                        tprint_esc("cr");
-                    if (p >= a)
-                        p = p - a;
-                    a = 0;
-                    goto FOUND;
-                }
-                break;
-            case no_align_group:
-                incr(p);
-                a = -1;
-                tprint_esc("noalign");
-                goto FOUND2;
-                break;
-            case output_group:
-                tprint_esc("output");
-                goto FOUND;
-                break;
-            case math_group:
-                goto FOUND2;
-                break;
-            case disc_group:
-                tprint_esc("discretionary");
-                for (i = 1; i < 3; i++) {
-                    if (i <= saved_value(-2)) {
-                        tprint("{}");
-                    }
-                }
-                goto FOUND2;
-                break;
-            case math_choice_group:
-                tprint_esc("mathchoice");
-                for (i = 1; i < 4; i++) {
-                    /*tex A different offset because |-2==saved_textdir|: */
-                    if (i <= saved_value(-3)) {
-                        tprint("{}");
-                    }
-                }
-                goto FOUND2;
-                break;
-            case insert_group:
-                if (saved_type(-1) == saved_adjust) {
-                    tprint_esc("vadjust");
-                    if (saved_level(-1) != 0)
-                        tprint(" pre");
-                } else {
-                    tprint_esc("insert");
-                    print_int(saved_value(-1));
-                }
-                goto FOUND2;
-                break;
-            case vcenter_group:
-                s = "vcenter";
-                goto FOUND1;
-                break;
-            case semi_simple_group:
-                incr(p);
-                tprint_esc("begingroup");
-                goto FOUND;
-                break;
-            case math_shift_group:
-                if (m == mmode) {
-                    print_char('$');
-                } else if (nest[p].mode_field == mmode) {
-                    print_cmd_chr(eq_no_cmd, saved_value(-2));
-                    goto FOUND;
-                }
-                print_char('$');
-                goto FOUND;
-                break;
-            case math_left_group:
-                if (subtype(nest[p + 1].eTeX_aux_field) == left_noad_side)
-                    tprint_esc("left");
-                else
-                    tprint_esc("middle");
-                goto FOUND;
-                break;
-            default:
-                confusion("showgroups");
-                break;
-        }
-        /*tex Show the box context */
-        i = saved_value(-5);
-        if (i != 0) {
-            if (i < box_flag) {
-                if (abs(nest[p].mode_field) == vmode)
-                    j = hmove_cmd;
-                else
-                    j = vmove_cmd;
-                if (i > 0)
-                    print_cmd_chr(j, 0);
-                else
-                    print_cmd_chr(j, 1);
-                print_scaled(abs(i));
-                tprint("pt");
-            } else if (i < ship_out_flag) {
-                if (i >= global_box_flag) {
-                    tprint_esc("global");
-                    i = i - (global_box_flag - box_flag);
-                }
-                tprint_esc("setbox");
-                print_int(i - box_flag);
-                print_char('=');
-            } else {
-                print_cmd_chr(leader_ship_cmd, i - (leader_flag - a_leaders));
-            }
-        }
-      FOUND1:
-        tprint_esc(s);
-        /*tex Show the box packaging info. The offsets may vary. */
-        {
-            int ii = -1;
-            while (saved_type(ii) != saved_boxspec)
-                ii--;
-            if (saved_value(ii) != 0) {
-                print_char(' ');
-                if (saved_level(ii) == exactly)
-                    tprint("to");
-                else
-                    tprint("spread");
-                print_scaled(saved_value(ii));
-                tprint("pt");
-            }
-        }
-      FOUND2:
-        print_char('{');
-      FOUND:
-        print_char(')');
-        decr(cur_level);
-        cur_group = save_level(save_ptr);
-        save_ptr = save_value(save_ptr);
-    }
-  DONE:
-    save_ptr = v;
-    cur_level = l;
-    cur_group = c;
-}
-
-/*tex
-
-Just before an entry of |eqtb| is changed, the following procedure should be
-called to update the other data structures properly. It is important to keep in
-mind that reference counts in |mem| include references from within |save_stack|,
-so these counts must be handled carefully. @^reference counts@>
-
-We don't need to destroy when an assignment has the same node:
-
-*/
-
-void eq_destroy(memory_word w)
-{
-    halfword q;
-    switch (eq_type_field(w)) {
-        case call_cmd:
-        case long_call_cmd:
-        case outer_call_cmd:
-        case long_outer_call_cmd:
-            delete_token_ref(equiv_field(w));
-            break;
-        case glue_ref_cmd:
-            flush_node(equiv_field(w));
-            break;
-        case shape_ref_cmd:
-            q = equiv_field(w);
-            if (q != null) {
-                /*tex
-                    We need to free a \.{\\parshape} block. Such a block is
-                    |2n+1| words long, where |n=vinfo(q)|. It happens in the
-                    flush function.
-                */
-                flush_node(q);
-            }
-            break;
-        case box_ref_cmd:
-            flush_node_list(equiv_field(w));
-            break;
-        default:
-            break;
-    }
-}
-
-/*tex
-
-To save a value of |eqtb[p]| that was established at level |l|, we can use the
-following subroutine.
-
-*/
-void eq_save(halfword p, quarterword l)
-{
-    check_full_save_stack();
-    if (l == level_zero) {
-        save_type(save_ptr) = restore_zero;
-    } else {
-        save_word(save_ptr) = eqtb[p];
-        save_type(save_ptr) = saved_eqtb;
-        incr(save_ptr);
-        save_type(save_ptr) = restore_old_value;
-    }
-    save_level(save_ptr) = l;
-    save_value(save_ptr) = p;
-    incr(save_ptr);
-}
-
-/*tex
-
-The procedure |eq_define| defines an |eqtb| entry having specified |eq_type| and
-|equiv| fields, and saves the former value if appropriate. This procedure is used
-only for entries in the first four regions of |eqtb|, i.e., only for entries that
-have |eq_type| and |equiv| fields. After calling this routine, it is safe to put
-four more entries on |save_stack|, provided that there was room for four more
-entries before the call, since |eq_save| makes the necessary test.
-
-*/
-
-void eq_define(halfword p, quarterword t, halfword e)
-{
-    boolean trace = tracing_assigns_par > 0;
-    if ((eq_type(p) == t) && (equiv(p) == e)) {
-        if (trace)
-            diagnostic_trace(p, "reassigning");
-        eq_destroy(eqtb[p]);
-        return;
-    }
-    if (trace)
-        diagnostic_trace(p, "changing");
-    if (eq_level(p) == cur_level)
-        eq_destroy(eqtb[p]);
-    else if (cur_level > level_one)
-        eq_save(p, eq_level(p));
-    set_eq_level(p, cur_level);
-    set_eq_type(p, t);
-    set_equiv(p, e);
-    if (trace)
-        diagnostic_trace(p, "into");
-}
-
-/*tex
-
-The counterpart of |eq_define| for the remaining (fullword) positions in |eqtb|
-is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all |p|, a
-`|restore_zero|' will never be used in this case.
-
-*/
-
-void eq_word_define(halfword p, int w)
-{
-    boolean trace = tracing_assigns_par > 0;
-    if (eqtb[p].cint == w) {
-        if (trace)
-            diagnostic_trace(p, "reassigning");
-        return;
-    }
-    if (trace)
-        diagnostic_trace(p, "changing");
-    if (xeq_level[p] != cur_level) {
-        eq_save(p, xeq_level[p]);
-        xeq_level[p] = cur_level;
-    }
-    eqtb[p].cint = w;
-    if (trace)
-        diagnostic_trace(p, "into");
-}
-
-/*tex
-
-The |eq_define| and |eq_word_define| routines take care of local definitions.
-@^global definitions@> Global definitions are done in almost the same way, but
-there is no need to save old values, and the new value is associated with
-|level_one|.
-
-*/
-
-void geq_define(halfword p, quarterword t, halfword e)
-{
-    boolean trace = tracing_assigns_par > 0;
-    if (trace)
-        diagnostic_trace(p, "globally changing");
-    eq_destroy(eqtb[p]);
-    set_eq_level(p, level_one);
-    set_eq_type(p, t);
-    set_equiv(p, e);
-    if (trace)
-        diagnostic_trace(p, "into");
-}
-
-void geq_word_define(halfword p, int w)
-{
-    boolean trace = tracing_assigns_par > 0;
-    if (trace)
-        diagnostic_trace(p, "globally changing");
-    eqtb[p].cint = w;
-    xeq_level[p] = level_one;
-    if (trace)
-        diagnostic_trace(p, "into");
-}
-
-/*tex
-
-Subroutine |save_for_after| puts a token on the stack for save-keeping.
-
-*/
-
-void save_for_after(halfword t)
-{
-    if (cur_level > level_one) {
-        check_full_save_stack();
-        save_type(save_ptr) = insert_token;
-        save_level(save_ptr) = level_zero;
-        save_value(save_ptr) = t;
-        incr(save_ptr);
-    }
-}
-
-/*tex
-
-The |unsave| routine goes the other way, taking items off of |save_stack|. This
-routine takes care of restoration when a level ends; everything belonging to the
-topmost group is cleared off of the save stack.
-
-*/
-
-void unsave(void)
-{
-    halfword p;
-    quarterword l = level_one;
-    /*tex Variable |a| registers if we already have processed an \.{\\aftergroup}. */
-    boolean a = false;
-    unsave_math_codes(cur_level);
-    unsave_cat_codes(cat_code_table_par, cur_level);
-    unsave_text_codes(cur_level);
-    unsave_math_data(cur_level);
-    if (cur_level > level_one) {
-        boolean trace = tracing_restores_par > 0;
-        decr(cur_level);
-        /*tex Clear off top level from |save_stack|. */
-        while (true) {
-            decr(save_ptr);
-            if (save_type(save_ptr) == level_boundary)
-                break;
-            p = save_value(save_ptr);
-            if (save_type(save_ptr) == insert_token) {
-                reinsert_token(a, p);
-                /*tex always |true| as we are always in \ETEX\ now. */
-                a = true;
-            } else {
-                if (save_type(save_ptr) == restore_old_value) {
-                    l = save_level(save_ptr);
-                    decr(save_ptr);
-                } else {
-                    save_word(save_ptr) = eqtb[undefined_control_sequence];
-                }
-                /*tex
-                    Store |save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]|
-                    holds a global value A global definition, which sets the
-                    level to |level_one|, will not be undone by |unsave|. If at
-                    least one global definition of |eqtb[p]| has been carried out
-                    within the group that just ended, the last such definition
-                    will therefore survive.
-                */
-                if (p < int_base || p > eqtb_size) {
-                    if (eq_level(p) == level_one) {
-                        /*tex Destroy the saved value: */
-                        eq_destroy(save_word(save_ptr));
-                        if (trace)
-                            diagnostic_trace(p, "retaining");
-                    } else {
-                        /*tex Destroy the current value: */
-                        eq_destroy(eqtb[p]);
-                        /*tex Restore the saved value: */
-                        eqtb[p] = save_word(save_ptr);
-                        if (trace)
-                            diagnostic_trace(p, "restoring");
-                    }
-                } else if (xeq_level[p] != level_one) {
-                    eqtb[p] = save_word(save_ptr);
-                    xeq_level[p] = l;
-                    if (trace)
-                        diagnostic_trace(p, "restoring");
-                } else {
-                    if (trace)
-                        diagnostic_trace(p, "retaining");
-                }
-            }
-        }
-
-        if (tracing_groups_par > 0) {
-            group_trace(true);
-        }
-        if (grp_stack[in_open] == cur_boundary) {
-            /*tex Groups are possibly not properly nested with files. */
-            group_warning();
-        }
-        cur_group = save_level(save_ptr);
-        cur_boundary = save_value(save_ptr);
-        decr(save_ptr);
-    } else {
-        /*tex |unsave| is not used when |cur_group=bottom_level| */
-        confusion("curlevel");
-    }
-    attr_list_cache = cache_disabled;
-}
-
-/*tex
-
-Most of the parameters kept in |eqtb| can be changed freely, but there's an
-exception: The magnification should not be used with two different values during
-any \TeX\ job, since a single magnification is applied to an entire run. The
-global variable |mag_set| is set to the current magnification whenever it becomes
-necessary to ``freeze'' it at a particular value.
-
-The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| for
-magnification. If nonzero, this magnification should be used henceforth. We might
-drop magnifaction at some point.
-
-*/
-
-int mag_set = 0;
-
-void prepare_mag(void)
-{
-    if ((mag_set > 0) && (mag_par != mag_set)) {
-        print_err("Incompatible magnification (");
-        print_int(mag_par);
-        tprint(");");
-        tprint_nl(" the previous value will be retained");
-        help2(
-            "I can handle only one magnification ratio per job. So I've",
-            "reverted to the magnification you used earlier on this run."
-        );
-        int_error(mag_set);
-        geq_word_define(int_base + mag_code, mag_set);  /* |mag:=mag_set| */
-    }
-    if ((mag_par <= 0) || (mag_par > 32768)) {
-        print_err("Illegal magnification has been changed to 1000");
-        help1(
-            "The magnification ratio must be between 1 and 32768."
-        );
-        int_error(mag_par);
-        geq_word_define(int_base + mag_code, 1000);
-    }
-    if ((mag_set == 0) && (mag_par != mag_set)) {
-        if (mag_par != 1000)
-            one_true_inch = xn_over_d(one_hundred_inch, 10, mag_par);
-        else
-            one_true_inch = one_inch;
-    }
-    mag_set = mag_par;
-}
-
-/*tex
-
-Let's pause a moment now and try to look at the Big Picture. The \TeX\ program
-consists of three main parts: syntactic routines, semantic routines, and output
-routines. The chief purpose of the syntactic routines is to deliver the user's
-input to the semantic routines, one token at a time. The semantic routines act as
-an interpreter responding to these tokens, which may be regarded as commands. And
-the output routines are periodically called on to convert box-and-glue lists into
-a compact set of instructions that will be sent to a typesetter. We have
-discussed the basic data structures and utility routines of \TeX, so we are good
-and ready to plunge into the real activity by considering the syntactic routines.
-
-Our current goal is to come to grips with the |get_next| procedure, which is the
-keystone of \TeX's input mechanism. Each call of |get_next| sets the value of
-three variables |cur_cmd|, |cur_chr|, and |cur_cs|, representing the next input
-token.
-
-$$
-    \vbox{\halign{#\hfil\cr
-        \hbox{|cur_cmd| denotes a command code from the long list of codes given above;}\cr
-        \hbox{|cur_chr| denotes a character code or other modifier of the command code;}\cr
-        \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr
-        \hbox{\qquad if the current token was a control sequence, otherwise it's zero.}\cr}}
-$$
-
-Underlying this external behavior of |get_next| is all the machinery necessary to
-convert from character files to tokens. At a given time we may be only partially
-finished with the reading of several files (for which \.{\\input} was specified),
-and partially finished with the expansion of some user-defined macros and/or some
-macro parameters, and partially finished with the generation of some text in a
-template for \.{\\halign}, and so on. When reading a character file, special
-characters must be classified as math delimiters, etc.; comments and extra blank
-spaces must be removed, paragraphs must be recognized, and control sequences must
-be found in the hash table. Furthermore there are occasions in which the scanning
-routines have looked ahead for a word like `\.{plus}' but only part of that word
-was found, hence a few characters must be put back into the input and scanned
-again.
-
-To handle these situations, which might all be present simultaneously, \TeX\ uses
-various stacks that hold information about the incomplete activities, and there
-is a finite state control for each level of the input mechanism. These stacks
-record the current state of an implicitly recursive process, but the |get_next|
-procedure is not recursive. Therefore it will not be difficult to translate these
-algorithms into low-level languages that do not support recursion.
-
-In general, |cur_cmd| is the current command as set by |get_next|, while
-|cur_chr| is the operand of the current command. The control sequence found here
-is registsred in |cur_cs| and is zero if none found. The |cur_tok| variable
-contains the packed representative of |cur_cmd| and |cur_chr| and like the other
-ones is global.
-
-*/
-
-int cur_cmd;
-halfword cur_chr;
-halfword cur_cs;
-halfword cur_tok;
-
-/*tex
-
-Here is a procedure that displays the current command. The variable |n| holds the
-level of \.{\\if...\\fi} nesting and |l| the line where \.{\\if} started.
-
-*/
-
-void show_cur_cmd_chr(void)
-{
-    int n, l;
-    halfword p;
-    begin_diagnostic();
-    tprint_nl("{");
-    if (mode_par != shown_mode) {
-        print_mode(mode_par);
-        tprint(": ");
-        shown_mode = mode_par;
-    }
-    print_cmd_chr((quarterword) cur_cmd, cur_chr);
-    if (tracing_ifs_par > 0) {
-        if (cur_cmd >= if_test_cmd) {
-            if (cur_cmd <= fi_or_else_cmd) {
-                tprint(": ");
-                if (cur_cmd == fi_or_else_cmd) {
-                    print_cmd_chr(if_test_cmd, cur_if);
-                    print_char(' ');
-                    n = 0;
-                    l = if_line;
-                } else {
-                    n = 1;
-                    l = line;
-                }
-                p = cond_ptr;
-                while (p != null) {
-                    incr(n);
-                    p = vlink(p);
-                }
-                tprint("(level ");
-                print_int(n);
-                print_char(')');
-                print_if_line(l);
-            }
-        }
-    }
-    print_char('}');
-    end_diagnostic(false);
-}
-
-/*tex
-
-Here is a procedure that displays the contents of |eqtb[n]| symbolically.
-
-*/
-
-void show_eqtb(halfword n)
-{
-    if (n < null_cs) {
-        /*tex
-            This can't happen in a pure \TEX\ run, but careless usage of tokens
-            at the \LUA\ end can make you end up here.
-        */
-        tprint("? bad token, case 1: ");
-        print_int(n);
-    } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) {
-        /*tex
-            This routine show the current meaning of |eqtb| entry |n| in region 1 or 2.
-            Similar routines for the other regions will appear below.
-        */
-        sprint_cs(n);
-        print_char('=');
-        print_cmd_chr(eq_type(n), equiv(n));
-        if (eq_type(n) >= call_cmd) {
-            print_char(':');
-            show_token_list(token_link(equiv(n)), null, 32);
-        }
-    } else if (n < local_base) {
-        /*tex
-            Here we show equivalent |n| in region 3. All glue parameters and registers
-            are initially `\.{0pt plus0pt minus0pt}'.
-        */
-        if (n < skip_base) {
-            if (n < glue_base + thin_mu_skip_code)
-                print_cmd_chr(assign_glue_cmd, n);
-            else
-                print_cmd_chr(assign_mu_glue_cmd, n);
-            print_char('=');
-            if (n < glue_base + thin_mu_skip_code)
-                print_spec(equiv(n), "pt");
-            else
-                print_spec(equiv(n), "mu");
-        } else if (n < mu_skip_base) {
-            tprint_esc("skip");
-            print_int(n - skip_base);
-            print_char('=');
-            print_spec(equiv(n), "pt");
-        } else {
-            tprint_esc("muskip");
-            print_int(n - mu_skip_base);
-            print_char('=');
-            print_spec(equiv(n), "mu");
-        }
-
-    } else if (n < int_base) {
-        /*tex
-            We're now at equivalent |n| in region 4. First we initialize most
-            things to null or undefined values. An undefined font is represented
-            by the internal code |font_base|.
-
-            However, the character code tables are given initial values based on
-            the conventional interpretation of ASCII code. These initial values
-            should not be changed when \TeX\ is adapted for use with non-English
-            languages; all changes to the initialization conventions should be
-            made in format packages, not in \TeX\ itself, so that global
-            interchange of formats is possible.
-        */
-        if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) {
-            if (n == par_shape_loc)
-                print_cmd_chr(set_tex_shape_cmd, n);
-            else
-                print_cmd_chr(set_etex_shape_cmd, n);
-            print_char('=');
-            if (equiv(n) == null) {
-                print_char('0');
-            } else if (n > par_shape_loc) {
-                print_int(penalty(equiv(n)));
-                print_char(' ');
-                print_int(penalty(equiv(n) + 1));
-                if (penalty(equiv(n)) > 1)
-                    tprint_esc("ETC.");
-            } else {
-                print_int(vinfo(par_shape_par_ptr + 1));
-            }
-        } else if (n < toks_base) {
-            print_cmd_chr(assign_toks_cmd, n);
-            print_char('=');
-            if (equiv(n) != null)
-                show_token_list(token_link(equiv(n)), null, 32);
-        } else if (n < box_base) {
-            tprint_esc("toks");
-            print_int(n - toks_base);
-            print_char('=');
-            if (equiv(n) != null)
-                show_token_list(token_link(equiv(n)), null, 32);
-        } else if (n < cur_font_loc) {
-            tprint_esc("box");
-            print_int(n - box_base);
-            print_char('=');
-            if (equiv(n) == null) {
-                tprint("void");
-            } else {
-                depth_threshold = 0;
-                breadth_max = 1;
-                show_node_list(equiv(n));
-            }
-        } else if (n == cur_font_loc) {
-            /*tex
-                Let's show the font identifier in |eqtb[n]|, that's
-                |font_id_text(equiv(n))|
-            */
-            tprint("current font");
-            print_char('=');
-            print_esc(hash[font_id_base + equiv(n)].rh);
-        }
-    } else if (n < dimen_base) {
-        /*tex Show equivalent |n| in region 5: */
-        if (n < dir_base) {
-            print_cmd_chr(assign_int_cmd, n);
-            print_char('=');
-            print_int(eqtb[n].cint);
-        } else if (n < count_base) {
-            print_cmd_chr(assign_dir_cmd, n);
-            print_char(' ');
-            print_dir_par(eqtb[n].cint);
-        } else if (n < attribute_base) {
-            tprint_esc("count");
-            print_int(n - count_base);
-            print_char('=');
-            print_int(eqtb[n].cint);
-        } else if (n < del_code_base) {
-            tprint_esc("attribute");
-            print_int(n - attribute_base);
-            print_char('=');
-            print_int(eqtb[n].cint);
-        }
-    } else if (n <= eqtb_size) {
-        /*tex Show equivalent |n| in region 6: */
-        if (n < scaled_base) {
-            print_cmd_chr(assign_dimen_cmd, n);
-        } else {
-            tprint_esc("dimen");
-            print_int(n - scaled_base);
-        }
-        print_char('=');
-        print_scaled(eqtb[n].cint);
-        tprint("pt");
-    } else {
-        /*tex This can't happen unless you messed up at the \LUA\ end. */
-        tprint("? bad token, case 2: ");
-        print_int(n);
-    }
-}
-
-void show_eqtb_meaning(halfword n)
-{
-    if (n < null_cs) {
-        /*tex This can't happen. */
-        print_char('?');
-    } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) {
-        /*tex
-            Here is a routine that displays the current meaning of an |eqtb|
-            entry in region 1 or~2. Similar routines for the other regions will
-            appear below.
-        */
-        sprint_cs(n);
-    } else if (n < local_base) {
-        /*
-            Show equivalent |n| in region 3. All glue parameters and registers
-            are initially `\.{0pt plus0pt minus0pt}'.
-        */
-        if (n < skip_base) {
-            if (n < glue_base + thin_mu_skip_code)
-                print_cmd_chr(assign_glue_cmd, n);
-            else
-                print_cmd_chr(assign_mu_glue_cmd, n);
-        } else if (n < mu_skip_base) {
-            tprint_esc("skip");
-            print_int(n - skip_base);
-        } else {
-            tprint_esc("muskip");
-            print_int(n - mu_skip_base);
-        }
-    } else if (n < int_base) {
-        /*tex
-            Show equivalent |n| in region 4. We initialize most things to null or
-            undefined values. An undefined font is represented by the internal
-            code |font_base|. However, the character code tables are given
-            initial values based on the conventional interpretation of ASCII
-            code. These initial values should not be changed when \TeX\ is
-            adapted for use with non-English languages; all changes to the
-            initialization conventions should be made in format packages, not in
-            \TeX\ itself, so that global interchange of formats is possible.
-        */
-        if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) {
-            if (n == par_shape_loc)
-                print_cmd_chr(set_tex_shape_cmd, n);
-            else
-                print_cmd_chr(set_etex_shape_cmd, n);
-        } else if (n < toks_base) {
-            print_cmd_chr(assign_toks_cmd, n);
-        } else if (n < box_base) {
-            tprint_esc("toks");
-            print_int(n - toks_base);
-        } else if (n < cur_font_loc) {
-            tprint_esc("box");
-            print_int(n - box_base);
-        } else if (n == cur_font_loc) {
-            /*tex Show the font identifier in |eqtb[n]|. */
-            tprint("current font");
-        }
-    } else if (n < dimen_base) {
-        /*tex Show equivalent |n| in region 5. */
-        if (n < dir_base) {
-            print_cmd_chr(assign_int_cmd, n);
-        } else if (n < count_base) {
-            print_cmd_chr(assign_dir_cmd, n);
-        } else if (n < attribute_base) {
-            tprint_esc("count");
-            print_int(n - count_base);
-        } else if (n < del_code_base) {
-            tprint_esc("attribute");
-            print_int(n - attribute_base);
-        }
-    } else if (n <= eqtb_size) {
-        /*tex Show equivalent |n| in region 6. */
-        if (n < scaled_base) {
-            print_cmd_chr(assign_dimen_cmd, n);
-        } else {
-            tprint_esc("dimen");
-            print_int(n - scaled_base);
-        }
-    } else {
-        /*tex This can't happen either. */
-        print_char('?');
-    }
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/errors.c
+++ /dev/null
@@ -1,1032 +0,0 @@
-/*
-
-errors.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/>.
-
-*/
-
-#include "ptexlib.h"
-#define edit_var "TEXEDIT"
-
-/*tex
-
-When something anomalous is detected, \TeX\ typically does something like this:
-$$\vbox{\halign{#\hfil\cr |print_err("Something anomalous has been
-detected");|\cr |help3("This is the first line of my offer to help.")|\cr |("This
-is the second line. I'm trying to")|\cr |("explain the best way for you to
-proceed.");|\cr |error;|\cr}}$$ A two-line help message would be given using
-|help2|, etc.; these informal helps should use simple vocabulary that complements
-the words used in the official error message that was printed. (Outside the
-U.S.A., the help messages should preferably be translated into the local
-vernacular. Each line of help is at most 60 characters long, in the present
-implementation, so that |max_print_line| will not be exceeded.)
-
-The |print_err| procedure supplies a `\.!' before the official message, and makes
-sure that the terminal is awake if a stop is going to occur. The |error|
-procedure supplies a `\..' after the official message, then it shows the location
-of the error; and if |interaction=error_stop_mode|, it also enters into a dialog
-with the user, during which time the help message may be printed. @^system
-dependencies@>
-
-*/
-
-/*tex The current level of interaction: */
-
-int interaction;
-
-/*tex Set from the command line: */
-
-int interactionoption;
-
-char *last_error = NULL;
-char *last_lua_error = NULL;
-char *last_warning_tag = NULL;
-char *last_warning_str = NULL;
-char *last_error_context = NULL;
-
-int err_old_setting = 0 ;
-int in_error = 0 ;
-
-void set_last_error_context(void)
-{
-    str_number str;
-    int sel = selector;
-    int saved_new_line_char;
-    int saved_new_string_line;
-    selector = new_string;
-    saved_new_line_char = new_line_char_par;
-    saved_new_string_line = new_string_line;
-    new_line_char_par = 10;
-    new_string_line = 10;
-    show_context();
-    xfree(last_error_context);
-    str = make_string();
-    last_error_context = makecstring(str);
-    flush_str(str);
-    selector = sel;
-    new_line_char_par = saved_new_line_char;
-    new_string_line = saved_new_string_line;
-    return;
-}
-
-void flush_err(void)
-{
-    str_number s_error;
-    char *s = NULL;
-    int callback_id ;
-    if (in_error) {
-        selector = err_old_setting;
-        str_room(1);
-        s_error = make_string();
-        s = makecstring(s_error);
-        flush_str(s_error);
-        if (interaction == error_stop_mode) {
-            wake_up_terminal();
-        }
-        xfree(last_error);
-        last_error = (string) xmalloc((unsigned) (strlen(s) + 1));
-        strcpy(last_error,s);
-        callback_id = callback_defined(show_error_message_callback);
-        if (callback_id > 0) {
-            run_callback(callback_id, "->");
-        } else {
-            tprint(s);
-        }
-        in_error = 0 ;
-    }
-}
-
-void print_err(const char *s)
-{
-    int callback_id = callback_defined(show_error_message_callback);
-    if (interaction == error_stop_mode) {
-        wake_up_terminal();
-    }
-    if (callback_id > 0) {
-        err_old_setting = selector;
-        selector = new_string;
-        in_error = 1 ;
-    }
-    if (filelineerrorstylep) {
-        print_file_line();
-    } else {
-        tprint_nl("! ");
-    }
-    tprint(s);
-    if (callback_id <= 0) {
-        xfree(last_error);
-        last_error = (string) xmalloc((unsigned) (strlen(s) + 1));
-        strcpy(last_error,s);
-    }
-}
-
-/*tex
-
-\TeX\ is careful not to call |error| when the print |selector| setting might be
-unusual. The only possible values of |selector| at the time of error messages are
-
-|no_print| (when |interaction=batch_mode| and |log_file| not yet open);
-
-|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
-
-|log_only| (when |interaction=batch_mode| and |log_file| is open);
-
-|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
-
-*/
-
-void fixup_selector(boolean logopened)
-{
-    if (interaction == batch_mode)
-        selector = no_print;
-    else
-        selector = term_only;
-    if (logopened)
-        selector = selector + 2;
-}
-
-/*tex
-
-A global variable |deletions_allowed| is set |false| if the |get_next| routine is
-active when |error| is called; this ensures that |get_next| and related routines
-like |get_token| will never be called recursively. A similar interlock is
-provided by |set_box_allowed|. @^recursion@>
-
-The global variable |history| records the worst level of error that has been
-detected. It has four possible values: |spotless|, |warning_issued|,
-|error_message_issued|, and |fatal_error_stop|.
-
-Another global variable, |error_count|, is increased by one when an |error|
-occurs without an interactive dialog, and it is reset to zero at the end of every
-paragraph. If |error_count| reaches 100, \TeX\ decides that there is no point in
-continuing further.
-
-*/
-
-/*tex Is it safe for |error| to call |get_token|? */
-
-boolean deletions_allowed;
-
-/*tex Is it safe to do a \.{\\setbox} assignment? */
-
-boolean set_box_allowed;
-/*tex Has the source input been clean so far? */
-
-int history;
-
-/*tex The number of scrolled errors since the last paragraph ended. */
-
-int error_count;
-
-/*tex Should \TeX\ pause for instructions? */
-
-int interrupt;
-
-/*tex Should interrupts be observed? */
-
-boolean OK_to_interrupt;
-
-/*tex
-
-The value of |history| is initially |fatal_error_stop|, but it will be changed to
-|spotless| if \TeX\ survives the initialization process.
-
-*/
-
-void initialize_errors(void)
-{
-    if (interactionoption == unspecified_mode)
-        interaction = error_stop_mode;
-    else
-        interaction = interactionoption;
-    deletions_allowed = true;
-    set_box_allowed = true;
-    OK_to_interrupt = true;
-}
-
-/*tex
-
-It is possible for |error| to be called recursively if some error arises when
-|get_token| is being used to delete a token, and/or if some fatal error occurs
-while \TeX\ is trying to fix a non-fatal one. But such recursion @^recursion@> is
-never more than two levels deep.
-
-Individual lines of help are recorded in the array |help_line|.
-
-*/
-
-const char *help_line[7];
-
-/*tex
-    Should the |err_help| list be shown?
-*/
-
-boolean use_err_help;
-
-/*tex
-
-The |jump_out| procedure just cuts across all active procedure levels and exits
-the program. It is used when there is no recovery from a particular error. The
-exit code can be overloaded.
-
-*/
-
-int defaultexitcode = 0;
-
-__attribute__ ((noreturn))
-void do_final_end(void)
-{
-    update_terminal();
-    ready_already = 0;
-    lua_close(Luas);
-    if ((history != spotless) && (history != warning_issued))
-        uexit(1);
-    else
-        uexit(defaultexitcode);
-}
-
-__attribute__ ((noreturn))
-void jump_out(void)
-{
-    close_files_and_terminate();
-    do_final_end();
-}
-
-/*tex
-
-Here is the function that calls the editor, if one is defined. This is loosely
-based on a similar function in kpathsea, but the calling convention is quite
-different.
-
-*/
-
-static const_string edit_value = EDITOR;
-
-#if defined(WIN32)
-
-static int Isspace (char c)
-{
-    return (c == ' ' || c == '\t');
-}
-
-#endif /* WIN32 */
-
-__attribute__ ((noreturn))
-static void luatex_calledit (int baseptr, int linenumber)
-{
-    char *temp, *command, *fullcmd;
-    char c;
-    int sdone, ddone, i;
-    char *filename = makecstring(input_stack[base_ptr].name_field);
-    int fnlength = strlen(filename);
-#ifdef WIN32
-    char *fp, *ffp, *env, editorname[256], buffer[256];
-    int cnt = 0;
-    int dontchange = 0;
-#endif
-    sdone = ddone = 0;
-    /*tex
-        Close any open input files, since we're going to kill the job.
-    */
-    close_files_and_terminate();
-    /*tex
-        Replace the default with the value of the appropriate environment
-        variable or config file value, if it's set.
-    */
-    temp = kpse_var_value (edit_var);
-    if (temp != NULL)
-        edit_value = temp;
-    /*tex
-        Construct the command string.  The `11' is the maximum length an
-        integer might be.
-    */
-    command = xmalloc (strlen (edit_value) + fnlength + 11);
-    /*tex
-        So we can construct it as we go.
-    */
-    temp = command;
-#ifdef WIN32
-    fp = editorname;
-    if ((isalpha(*edit_value) && *(edit_value + 1) == ':' && IS_DIR_SEP (*(edit_value + 2)))
-            || (*edit_value == '"' && isalpha(*(edit_value + 1))
-            && *(edit_value + 2) == ':' && IS_DIR_SEP (*(edit_value + 3)))) {
-        dontchange = 1;
-    }
-#endif
-    while ((c = *edit_value++) != 0) {
-        if (c == '%') {
-            switch (c = *edit_value++) {
-                case 'd':
-                    if (ddone)
-                        FATAL1 ("call_edit: `%%d' appears twice in editor command: `%s'", edit_value);
-                    sprintf (temp, "%ld", (long int)linenumber);
-                    while (*temp != '\0')
-                        temp++;
-                    ddone = 1;
-                    break;
-                case 's':
-                    if (sdone)
-                        FATAL1 ("call_edit: `%%s' appears twice in editor command: `%s'", edit_value);
-                    for (i =0; i < fnlength; i++)
-                        *temp++ = filename[i];
-                    sdone = 1;
-                    break;
-                case '\0':
-                    *temp++ = '%';
-                    /*tex
-                        Back up to the null to force termination.
-                    */
-                    edit_value--;
-                    break;
-                default:
-                    *temp++ = '%';
-                    *temp++ = c;
-                    break;
-            }
-        } else {
-#ifdef WIN32
-            if (dontchange) {
-                *temp++ = c;
-            } else if(Isspace(c) && cnt == 0) {
-                cnt++;
-                temp = command;
-                *temp++ = c;
-                *fp = '\0';
-            } else if(!Isspace(c) && cnt == 0) {
-                *fp++ = c;
-            } else {
-                *temp++ = c;
-            }
-#else
-            *temp++ = c;
-#endif
-        }
-    }
-    *temp = 0;
-#ifdef WIN32
-    if (dontchange == 0) {
-        if(editorname[0] == '.' || editorname[0] == '/' || editorname[0] == '\\') {
-            fprintf(stderr, "%s is not allowed to execute.\n", editorname);
-            do_final_end();
-        }
-        env = (char *)getenv("PATH");
-        if(SearchPath(env, editorname, ".exe", 256, buffer, &ffp)==0) {
-            if(SearchPath(env, editorname, ".bat", 256, buffer, &ffp)==0) {
-                fprintf(stderr, "I cannot find %s in the PATH.\n", editorname);
-                do_final_end();
-            }
-        }
-        fullcmd = (char *)xmalloc(strlen(buffer)+strlen(command)+5);
-        strcpy(fullcmd, "\"");
-        strcat(fullcmd, buffer);
-        strcat(fullcmd, "\"");
-        strcat(fullcmd, command);
-    }
-#endif
-    fullcmd = command;
-    /*tex Execute the command. */
-    if (system (fullcmd) != 0) {
-        fprintf (stderr, "! Trouble executing `%s'.\n", command);
-    }
-    /*tex Quit, since we found an error.  */
-    do_final_end ();
-}
-
-/*tex
-
-  This completes the job of error reporting.
-
-*/
-
-void error(void)
-{
-    /*tex What the user types :*/
-    ASCII_code c;
-    int callback_id;
-    /*tex Used to save global variables when deleting tokens: */
-    int s1, s2, s3, s4;
-    int i;
-    flush_err();
-    if (history < error_message_issued)
-        history = error_message_issued;
-    callback_id = callback_defined(show_error_hook_callback);
-    if (callback_id > 0) {
-        set_last_error_context();
-        run_callback(callback_id, "->");
-    } else {
-        print_char('.');
-        show_context();
-    }
-    if (haltonerrorp) {
-        history = fatal_error_stop;
-        jump_out();
-    }
-    if (interaction == error_stop_mode) {
-        /*tex Get user's advice and |return|. */
-        while (1) {
-          CONTINUE:
-            clear_for_error_prompt();
-            prompt_input("? ");
-            if (last == first)
-                return;
-            c = buffer[first];
-            if (c >= 'a')
-                c = c + 'A' - 'a';
-                /*tex
-                    Interpret code |c| and |return| if done. It is desirable to
-                    provide an `\.E' option here that gives the user an easy way
-                    to return from \TeX\ to the system editor, with the offending
-                    line ready to be edited. But such an extension requires some
-                    system wizardry, so the present implementation simply types
-                    out the name of the file that should be edited and the
-                    relevant line number.
-                */
-            switch (c) {
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-                if (deletions_allowed) {
-                    /*tex
-                        Delete |c-"0"| tokens and |goto continue|. We allow
-                        deletion of up to 99 tokens at a time.
-                    */
-                    s1 = cur_tok;
-                    s2 = cur_cmd;
-                    s3 = cur_chr;
-                    s4 = align_state;
-                    align_state = 1000000;
-                    OK_to_interrupt = false;
-                    if ((last > first + 1) && (buffer[first + 1] >= '0')
-                        && (buffer[first + 1] <= '9'))
-                        c = c * 10 + buffer[first + 1] - '0' * 11;
-                    else
-                        c = c - '0';
-                    while (c > 0) {
-                        /*tex One-level recursive call of |error| is possible. */
-                        get_token();
-                        decr(c);
-                    }
-                    cur_tok = s1;
-                    cur_cmd = s2;
-                    cur_chr = s3;
-                    align_state = s4;
-                    OK_to_interrupt = true;
-                    help2(
-                        "I have just deleted some text, as you asked.",
-                        "You can now delete more, or insert, or whatever."
-                    );
-                    show_context();
-                    goto CONTINUE;
-                }
-                break;
-            case 'E':
-                if (base_ptr > 0) {
-                    int callback_id = callback_defined(call_edit_callback);
-                    if (callback_id>0) {
-                        (void)run_callback(callback_id, "Sd->", makecstring(input_stack[base_ptr].name_field), line);
-                        /*tex This should not be reached. */
-                        jump_out();
-                    } else {
-                        tprint_nl("You want to edit file ");
-                        print(input_stack[base_ptr].name_field);
-                        tprint(" at line ");
-                        print_int(line);
-                        interaction = scroll_mode;
-                        if (kpse_init) {
-                            luatex_calledit(base_ptr, line);
-                        } else {
-                            /*tex This should not be reached. */
-                            tprint_nl("There is no valid callback defined.");
-                            jump_out();
-                        }
-                    }
-                }
-                break;
-            case 'H':
-                /*tex Print the help information and |goto continue| */
-                if (use_err_help) {
-                    give_err_help();
-                } else {
-                    if (help_line[0] == NULL) {
-                        help2(
-                            "Sorry, I don't know how to help in this situation.",
-                            "Maybe you should try asking a human?"
-                        );
-                    }
-                    i = 0;
-                    while (help_line[i] != NULL)
-                        tprint_nl(help_line[i++]);
-                    help4(
-                        "Sorry, I already gave what help I could...",
-                        "Maybe you should try asking a human?",
-                        "An error might have occurred before I noticed any problems.",
-                        "``If all else fails, read the instructions.''"
-                    );
-                    goto CONTINUE;
-                }
-                break;
-            case 'I':
-                /*tex
-
-                    Introduce new material from the terminal and |return|. When
-                    the following code is executed, |buffer[(first+1)..(last-1)]|
-                    may contain the material inserted by the user; otherwise
-                    another prompt will be given. In order to understand this
-                    part of the program fully, you need to be familiar with
-                    \TeX's input stacks.
-
-                    We enter a new syntactic level for terminal input:
-
-                */
-                begin_file_reading();
-                /*tex
-                    Now |state=mid_line|, so an initial blank space will count as
-                    a blank.
-                */
-                if (last > first + 1) {
-                    iloc = first + 1;
-                    buffer[first] = ' ';
-                } else {
-                    prompt_input("insert>");
-                    iloc = first;
-                }
-                first = last;
-                /*tex No |end_line_char| ends this line. */
-                ilimit = last - 1;
-                return;
-                break;
-            case 'Q':
-            case 'R':
-            case 'S':
-                /*tex
-
-                    Change the interaction level and |return|. Here the author of
-                    \TeX\ apologizes for making use of the numerical relation
-                    between |"Q"|, |"R"|, |"S"|, and the desired interaction
-                    settings |batch_mode|, |nonstop_mode|, |scroll_mode|.
-
-                */
-                error_count = 0;
-                interaction = batch_mode + c - 'Q';
-                tprint("OK, entering ");
-                switch (c) {
-                case 'Q':
-                    tprint_esc("batchmode");
-                    decr(selector);
-                    break;
-                case 'R':
-                    tprint_esc("nonstopmode");
-                    break;
-                case 'S':
-                    tprint_esc("scrollmode");
-                    break;
-                }
-                tprint("...");
-                print_ln();
-                update_terminal();
-                return;
-                break;
-            case 'X':
-                interaction = scroll_mode;
-                jump_out();
-                break;
-            default:
-                break;
-            }
-            if (!use_err_help) {
-                /* Print the menu of available options */
-                tprint("Type <return> to proceed, S to scroll future error messages,");
-                tprint_nl("R to run without stopping, Q to run quietly,");
-                tprint_nl("I to insert something, ");
-                if (base_ptr > 0)
-                    tprint("E to edit your file,");
-                if (deletions_allowed)
-                    tprint_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
-                tprint_nl("H for help, X to quit.");
-            }
-            use_err_help = false;
-        }
-
-    }
-    incr(error_count);
-    if (error_count == 100) {
-        tprint_nl("(That makes 100 errors; please try again.)");
-        history = fatal_error_stop;
-        jump_out();
-    }
-    /*tex Put help message on the transcript file. */
-    if (interaction > batch_mode) {
-        /*tex Avoid terminal output: */
-        decr(selector);
-    }
-    if (use_err_help) {
-        print_ln();
-        give_err_help();
-    } else {
-        int i1 = 0;
-        while (help_line[i1] != NULL)
-            tprint_nl(help_line[i1++]);
-    }
-    print_ln();
-    if (interaction > batch_mode) {
-        /*tex Re-enable terminal output: */
-        incr(selector);
-    }
-    print_ln();
-}
-
-/*tex
-
-A dozen or so error messages end with a parenthesized integer, so we save a teeny
-bit of program space by declaring the following procedure:
-
-*/
-
-void int_error(int n)
-{
-    tprint(" (");
-    print_int(n);
-    print_char(')');
-    error();
-}
-
-/*tex
-
-In anomalous cases, the print selector might be in an unknown state; the
-following subroutine is called to fix things just enough to keep running a bit
-longer.
-
-*/
-
-void normalize_selector(void)
-{
-    if (log_opened_global)
-        selector = term_and_log;
-    else
-        selector = term_only;
-    if (job_name == 0)
-        open_log_file();
-    if (interaction == batch_mode)
-        decr(selector);
-}
-
-/*tex
-
-The following procedure prints \TeX's last words before dying.
-
-*/
-
-void succumb(void)
-{
-    if (interaction == error_stop_mode) {
-        /*tex No more interaction: */
-        interaction = scroll_mode;
-    }
-    if (log_opened_global) {
-        error();
-    }
-    history = fatal_error_stop;
-    /*tex Irrecoverable error: */
-    jump_out();
-}
-
-/*tex
-
-This prints |s|, and that's it.
-
-*/
-
-void fatal_error(const char *s)
-{
-    normalize_selector();
-    print_err("Emergency stop");
-    help1(s);
-    succumb();
-}
-
-/*tex
-
-Here is the most dreaded error message. We stop due to finiteness.
-
-*/
-
-void overflow(const char *s, unsigned int n)
-{
-    normalize_selector();
-    print_err("TeX capacity exceeded, sorry [");
-    tprint(s);
-    print_char('=');
-    print_int((int) n);
-    print_char(']');
-    if (varmem == NULL) {
-      print_err("Sorry, I ran out of memory.");
-      print_ln();
-      exit(EXIT_FAILURE);
-    }
-    help2(
-        "If you really absolutely need more capacity,",
-        "you can ask a wizard to enlarge me."
-    );
-    succumb();
-}
-
-/*tex
-
-The program might sometime run completely amok, at which point there is no choice
-but to stop. If no previous error has been detected, that's bad news; a message
-is printed that is really intended for the \TeX\ maintenance person instead of
-the user (unless the user has been particularly diabolical). The index entries
-for `this can't happen' may help to pinpoint the problem. @^dry rot@>
-
-*/
-
-void confusion(const char *s)
-{                               /* consistency check violated; |s| tells where */
-    normalize_selector();
-    if (history < error_message_issued) {
-        print_err("This can't happen (");
-        tprint(s);
-        print_char(')');
-        help1(
-            "I'm broken. Please show this to someone who can fix"
-        );
-    } else {
-        print_err("I can't go on meeting you like this");
-        help2(
-            "One of your faux pas seems to have wounded me deeply...",
-            "in fact, I'm barely conscious. Please fix it and try again."
-        );
-    }
-    succumb();
-}
-
-/*tex
-
-Users occasionally want to interrupt \TeX\ while it's running. If the runtime
-system allows this, one can implement a routine that sets the global variable
-|interrupt| to some nonzero value when such an interrupt is signalled. Otherwise
-there is probably at least a way to make |interrupt| nonzero using the debugger.
-@^system dependencies@> @^debugging@>
-
-*/
-
-void check_interrupt(void)
-{
-    if (interrupt != 0)
-        pause_for_instructions();
-}
-
-/*tex
-
-When an interrupt has been detected, the program goes into its highest
-interaction level and lets the user have nearly the full flexibility of the
-|error| routine. \TeX\ checks for interrupts only at times when it is safe to do
-this.
-
-*/
-
-void pause_for_instructions(void)
-{
-    if (OK_to_interrupt) {
-        interaction = error_stop_mode;
-        if ((selector == log_only) || (selector == no_print))
-            incr(selector);
-        print_err("Interruption");
-        help3(
-            "You rang?",
-            "Try to insert some instructions for me (e.g.,`I\\showlists'),",
-            "unless you just want to quit by typing `X'."
-        );
-        deletions_allowed = false;
-        error();
-        deletions_allowed = true;
-        interrupt = 0;
-    }
-}
-
-void tex_error(const char *msg, const char **hlp)
-{
-    print_err(msg);
-    if (hlp != NULL) {
-        int i;
-        for (i = 0; (hlp[i] != NULL && i <= 5); i++) {
-            help_line[i] = hlp[i];
-        }
-        help_line[i] = NULL;
-    } else {
-        help_line[0] = NULL;
-    }
-    error();
-}
-
-/*tex
-
-The |back_error| routine is used when we want to replace an offending token just
-before issuing an error message. This routine, like |back_input|, requires that
-|cur_tok| has been set. We disable interrupts during the call of |back_input| so
-that the help message won't be lost.
-
-*/
-
-void back_error(void)
-{
-    OK_to_interrupt = false;
-    back_input();
-    OK_to_interrupt = true;
-    error();
-}
-
-/*tex
-
-    Back up one inserted token and call |error|.
-*/
-
-void ins_error(void)
-{
-    OK_to_interrupt = false;
-    back_input();
-    token_type = inserted;
-    OK_to_interrupt = true;
-    error();
-}
-
-/*tex
-
-When \TeX\ wants to typeset a character that doesn't exist, the character node is
-not created; thus the output routine can assume that characters exist when it
-sees them. The following procedure prints a warning message unless the user has
-suppressed it.
-
-*/
-
-void char_warning(internal_font_number f, int c)
-{
-    /*tex saved value of |tracing_online| */
-    int old_setting;
-    /* index to current digit; we assume that $0\L n<16^{22}$ */
-    int k;
-    if (tracing_lost_chars_par > 0) {
-        old_setting = tracing_online_par;
-        if (tracing_lost_chars_par > 1)
-            tracing_online_par = 1;
-        begin_diagnostic();
-        tprint_nl("Missing character: There is no ");
-        print(c);
-        tprint(" (U+");
-        k = 0;
-        if (c < 16)
-            print_char('0');
-        if (c < 256)
-            print_char('0');
-        if (c < 4096)
-            print_char('0');
-        do {
-            dig[k] = c % 16;
-            c = c / 16;
-            incr(k);
-        } while (c != 0);
-        print_the_digs((eight_bits) k);
-        tprint(") in font ");
-        print_font_name(f);
-        print_char('!');
-        end_diagnostic(false);
-        tracing_online_par = old_setting;
-    }
-}
-
-void wrapup_backend(void) {
-    ensure_output_state(static_pdf, ST_OMODE_FIX);
-    if (output_mode_used == OMODE_NONE) {
-        print_err(" ==> Fatal error occurred, no FMT file produced!");
-    } else {
-        backend_out_control[backend_control_finish_file](static_pdf,history == fatal_error_stop);
-    }
-}
-
-void normal_error(const char *t, const char *p)
-{
-    normalize_selector();
-    if (interaction == error_stop_mode) {
-        wake_up_terminal();
-    }
-    if (filelineerrorstylep) {
-        print_file_line();
-    } else {
-        tprint_nl("! ");
-    }
-    tprint("error: ");
-    if (cur_file_name) {
-        tprint(" (file ");
-        tprint(cur_file_name);
-        tprint(")");
-    }
-    if (t != NULL) {
-        tprint(" (");
-        tprint(t);
-        tprint(")");
-    }
-    tprint(": ");
-    if (p != NULL)
-        tprint(p);
-    history = fatal_error_stop;
-    wrapup_backend();
-    exit(EXIT_FAILURE);
-}
-
-void normal_warning(const char *t, const char *p)
-{
-    int report_id ;
-    if (strcmp(t,"lua") == 0) {
-        int saved_new_line_char;
-        saved_new_line_char = new_line_char_par;
-        new_line_char_par = 10;
-        report_id = callback_defined(show_lua_error_hook_callback);
-        if (report_id == 0) {
-            tprint(p);
-            help2(
-                "The lua interpreter ran into a problem, so the",
-                "remainder of this lua chunk will be ignored."
-            );
-        } else {
-            (void) run_callback(report_id, "->");
-        }
-        error();
-        new_line_char_par = saved_new_line_char;
-    } else {
-        report_id = callback_defined(show_warning_message_callback);
-        if (report_id > 0) {
-            /*tex Free the last ones, */
-            xfree(last_warning_str);
-            xfree(last_warning_tag);
-            last_warning_str = (string) xmalloc(strlen(p) + 1);
-            last_warning_tag = (string) xmalloc(strlen(t) + 1);
-            strcpy(last_warning_str,p);
-            strcpy(last_warning_tag,t);
-            run_callback(report_id, "->");
-        } else {
-            print_ln();
-            tprint("warning ");
-            if (cur_file_name) {
-                tprint(" (file ");
-                tprint(cur_file_name);
-                tprint(")");
-            }
-            if (t != NULL) {
-                tprint(" (");
-                tprint(t);
-                tprint(")");
-            }
-            tprint(": ");
-            if (p != NULL)
-                tprint(p);
-            print_ln();
-        }
-        if (history == spotless)
-            history = warning_issued;
-    }
-}
-
-static char print_buf[PRINTF_BUF_SIZE];
-
-__attribute__ ((format(printf, 2,3)))
-void formatted_error(const char *t, const char *fmt, ...)
-{
-    va_list args;
-    va_start(args, fmt);
-    vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
-    normal_error(t,print_buf);
-    va_end(args);
-}
-
-__attribute__ ((format(printf, 2,3)))
-void formatted_warning(const char *t, const char *fmt, ...)
-{
-    va_list args;
-    va_start(args, fmt);
-    vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
-    normal_warning(t,print_buf);
-    va_end(args);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/errors.w
@@ -0,0 +1,972 @@
+% errors.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/>.
+
+@ TODO: we need to use a formatted normal_error in:
+
+hyphen.w
+luafflib.c
+
+@ @c
+#include "ptexlib.h"
+#define edit_var "TEXEDIT"
+
+@ When something anomalous is detected, \TeX\ typically does something like this:
+$$\vbox{\halign{#\hfil\cr
+|print_err("Something anomalous has been detected");|\cr
+|help3("This is the first line of my offer to help.")|\cr
+|("This is the second line. I'm trying to")|\cr
+|("explain the best way for you to proceed.");|\cr
+|error;|\cr}}$$
+A two-line help message would be given using |help2|, etc.; these informal
+helps should use simple vocabulary that complements the words used in the
+official error message that was printed. (Outside the U.S.A., the help
+messages should preferably be translated into the local vernacular. Each
+line of help is at most 60 characters long, in the present implementation,
+so that |max_print_line| will not be exceeded.)
+
+The |print_err| procedure supplies a `\.!' before the official message,
+and makes sure that the terminal is awake if a stop is going to occur.
+The |error| procedure supplies a `\..' after the official message, then it
+shows the location of the error; and if |interaction=error_stop_mode|,
+it also enters into a dialog with the user, during which time the help
+message may be printed.
+@^system dependencies@>
+
+@c
+int interaction;                /* current level of interaction */
+int interactionoption;          /* set from command line */
+
+/* ls-hh: so, new code only kicks in when we have a callback defined */
+
+char *last_error = NULL;
+char *last_lua_error = NULL;
+char *last_warning_tag = NULL;
+char *last_warning_str = NULL;
+char *last_error_context = NULL;
+
+int err_old_setting = 0 ;
+int in_error = 0 ;
+
+void set_last_error_context(void)
+{
+    str_number str;
+    int sel = selector;
+    int saved_new_line_char;
+    int saved_new_string_line;
+    selector = new_string;
+    saved_new_line_char = new_line_char_par;
+    saved_new_string_line = new_string_line;
+    new_line_char_par = 10;
+    new_string_line = 10;
+    show_context();
+    xfree(last_error_context);
+    str = make_string();
+    last_error_context = makecstring(str);
+    flush_str(str);
+    selector = sel;
+    new_line_char_par = saved_new_line_char;
+    new_string_line = saved_new_string_line;
+    return;
+}
+
+void flush_err(void)
+{
+    str_number s_error;
+    char *s = NULL;
+    int callback_id ;
+    if (in_error) {
+        selector = err_old_setting;
+        str_room(1);
+        s_error = make_string();
+        s = makecstring(s_error);
+        flush_str(s_error);
+        if (interaction == error_stop_mode) {
+            wake_up_terminal();
+        }
+        xfree(last_error);
+        last_error = (string) xmalloc((unsigned) (strlen(s) + 1));
+        strcpy(last_error,s);
+        callback_id = callback_defined(show_error_message_callback);
+        if (callback_id > 0) {
+            run_callback(callback_id, "->");
+        } else {
+            tprint(s);
+        }
+        in_error = 0 ;
+    }
+}
+
+void print_err(const char *s)
+{
+    int callback_id = callback_defined(show_error_message_callback);
+    if (interaction == error_stop_mode) {
+        wake_up_terminal();
+    }
+    if (callback_id > 0) {
+        err_old_setting = selector;
+        selector = new_string;
+        in_error = 1 ;
+    }
+    if (filelineerrorstylep) {
+        print_file_line();
+    } else {
+        tprint_nl("! ");
+    }
+    tprint(s);
+    if (callback_id <= 0) {
+        xfree(last_error);
+        last_error = (string) xmalloc((unsigned) (strlen(s) + 1));
+        strcpy(last_error,s);
+    }
+}
+
+@ \TeX\ is careful not to call |error| when the print |selector| setting
+might be unusual. The only possible values of |selector| at the time of
+error messages are
+
+\yskip
+\hang|no_print| (when |interaction=batch_mode| and |log_file| not yet open);
+
+\hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
+
+\hang|log_only| (when |interaction=batch_mode| and |log_file| is open);
+
+\hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
+
+@c
+void fixup_selector(boolean logopened)
+{
+    if (interaction == batch_mode)
+        selector = no_print;
+    else
+        selector = term_only;
+    if (logopened)
+        selector = selector + 2;
+}
+
+@ A global variable |deletions_allowed| is set |false| if the |get_next|
+routine is active when |error| is called; this ensures that |get_next|
+and related routines like |get_token| will never be called recursively.
+A similar interlock is provided by |set_box_allowed|.
+@^recursion@>
+
+The global variable |history| records the worst level of error that
+has been detected. It has four possible values: |spotless|, |warning_issued|,
+|error_message_issued|, and |fatal_error_stop|.
+
+Another global variable, |error_count|, is increased by one when an
+|error| occurs without an interactive dialog, and it is reset to zero at
+the end of every paragraph.  If |error_count| reaches 100, \TeX\ decides
+that there is no point in continuing further.
+
+@c
+boolean deletions_allowed;      /* is it safe for |error| to call |get_token|? */
+boolean set_box_allowed;        /* is it safe to do a \.{\\setbox} assignment? */
+int history;                    /* has the source input been clean so far? */
+int error_count;                /* the number of scrolled errors since the last paragraph ended */
+int interrupt;                  /* should \TeX\ pause for instructions? */
+boolean OK_to_interrupt;        /* should interrupts be observed? */
+
+@ The value of |history| is initially |fatal_error_stop|, but it will
+be changed to |spotless| if \TeX\ survives the initialization process.
+
+@c
+void initialize_errors(void)
+{
+    if (interactionoption == unspecified_mode)
+        interaction = error_stop_mode;
+    else
+        interaction = interactionoption;
+    deletions_allowed = true;
+    set_box_allowed = true;
+    OK_to_interrupt = true;
+    /* |history| is initialized elsewhere */
+}
+
+@ It is possible for |error| to be called recursively if some error arises
+when |get_token| is being used to delete a token, and/or if some fatal error
+occurs while \TeX\ is trying to fix a non-fatal one. But such recursion
+@^recursion@>
+is never more than two levels deep.
+
+@ Individual lines of help are recorded in the array |help_line|.
+
+@c
+const char *help_line[7];       /* helps for the next |error| */
+boolean use_err_help;           /* should the |err_help| list be shown? */
+
+@ The |jump_out| procedure just cuts across all active procedure levels and
+exits the program. It is used when there is no recovery from a particular error.
+
+@c
+int defaultexitcode = 0; /* the exit code can be overloaded */
+
+__attribute__ ((noreturn))
+void do_final_end(void)
+{
+    update_terminal();
+    ready_already = 0;
+    lua_close(Luas); /* new per 0.99 */
+    if ((history != spotless) && (history != warning_issued))
+        uexit(1);
+    else
+        uexit(defaultexitcode);
+}
+
+__attribute__ ((noreturn))
+void jump_out(void)
+{
+    close_files_and_terminate();
+    do_final_end();
+}
+@ Here is the function that calls the editor, if one is defined. This
+is loosely based on a similar function in kpathsea, but the calling
+convention is quite different.
+
+@c
+static const_string edit_value = EDITOR;
+
+#if defined(WIN32)
+static int
+Isspace (char c)
+{
+  return (c == ' ' || c == '\t');
+}
+#endif /* WIN32 */
+
+__attribute__ ((noreturn))
+static void luatex_calledit (int baseptr, int linenumber)
+{
+  char *temp, *command, *fullcmd;
+  char c;
+  int sdone, ddone, i;
+  char *filename = makecstring(input_stack[base_ptr].name_field);
+  int fnlength = strlen(filename);
+
+#ifdef WIN32
+  char *fp, *ffp, *env, editorname[256], buffer[256];
+  int cnt = 0;
+  int dontchange = 0;
+#endif
+
+  sdone = ddone = 0;
+
+  /* Close any open input files, since we're going to kill the job.  */
+  close_files_and_terminate();
+
+  /* Replace the default with the value of the appropriate environment
+     variable or config file value, if it's set.  */
+  temp = kpse_var_value (edit_var);
+  if (temp != NULL)
+    edit_value = temp;
+
+  /* Construct the command string.  The `11' is the maximum length an
+     integer might be.  */
+  command = xmalloc (strlen (edit_value) + fnlength + 11);
+
+  /* So we can construct it as we go.  */
+  temp = command;
+
+#ifdef WIN32
+  fp = editorname;
+  if ((isalpha(*edit_value) && *(edit_value + 1) == ':'
+        && IS_DIR_SEP (*(edit_value + 2)))
+      || (*edit_value == '"' && isalpha(*(edit_value + 1))
+        && *(edit_value + 2) == ':'
+        && IS_DIR_SEP (*(edit_value + 3)))
+     )
+    dontchange = 1;
+#endif
+
+  while ((c = *edit_value++) != 0)
+    {
+      if (c == '%')
+        {
+          switch (c = *edit_value++)
+            {
+            case 'd':
+              if (ddone)
+                FATAL1 ("call_edit: `%%d' appears twice in editor command: `%s'", edit_value);
+              sprintf (temp, "%ld", (long int)linenumber);
+              while (*temp != '\0')
+                temp++;
+              ddone = 1;
+              break;
+
+            case 's':
+              if (sdone)
+                FATAL1 ("call_edit: `%%s' appears twice in editor command: `%s'", edit_value);
+              for (i =0; i < fnlength; i++)
+                *temp++ = filename[i];
+              sdone = 1;
+              break;
+
+            case '\0':
+              *temp++ = '%';
+              /* Back up to the null to force termination.  */
+              edit_value--;
+              break;
+
+            default:
+              *temp++ = '%';
+              *temp++ = c;
+              break;
+            }
+        }
+      else {
+#ifdef WIN32
+        if (dontchange)
+          *temp++ = c;
+        else { if(Isspace(c) && cnt == 0) {
+            cnt++;
+            temp = command;
+            *temp++ = c;
+            *fp = '\0';
+          } else if(!Isspace(c) && cnt == 0) {
+            *fp++ = c;
+          } else {
+            *temp++ = c;
+          }
+        }
+#else
+        *temp++ = c;
+#endif
+      }
+    }
+
+  *temp = 0;
+
+#ifdef WIN32
+  if (dontchange == 0) {
+    if(editorname[0] == '.' ||
+       editorname[0] == '/' ||
+       editorname[0] == '\\') {
+      fprintf(stderr, "%s is not allowed to execute.\n", editorname);
+      do_final_end();
+    }
+    env = (char *)getenv("PATH");
+    if(SearchPath(env, editorname, ".exe", 256, buffer, &ffp)==0) {
+      if(SearchPath(env, editorname, ".bat", 256, buffer, &ffp)==0) {
+        fprintf(stderr, "I cannot find %s in the PATH.\n", editorname);
+        do_final_end();
+      }
+    }
+    fullcmd = (char *)xmalloc(strlen(buffer)+strlen(command)+5);
+    strcpy(fullcmd, "\"");
+    strcat(fullcmd, buffer);
+    strcat(fullcmd, "\"");
+    strcat(fullcmd, command);
+  } else
+#endif
+  fullcmd = command;
+
+  /* Execute the command.  */
+  if (system (fullcmd) != 0)
+    fprintf (stderr, "! Trouble executing `%s'.\n", command);
+
+  /* Quit, since we found an error.  */
+  do_final_end ();
+}
+
+
+@ @c
+void error(void)
+{                               /* completes the job of error reporting */
+    ASCII_code c;               /* what the user types */
+    int callback_id;
+    int s1, s2, s3, s4;         /* used to save global variables when deleting tokens */
+    int i;
+    flush_err(); /* hh-ls */
+    if (history < error_message_issued)
+        history = error_message_issued;
+    callback_id = callback_defined(show_error_hook_callback);
+    if (callback_id > 0) {
+        set_last_error_context();
+        run_callback(callback_id, "->");
+    } else {
+        print_char('.');
+        show_context();
+    }
+    if (haltonerrorp) {
+        history = fatal_error_stop;
+        jump_out();
+    }
+    if (interaction == error_stop_mode) {
+        /* Get user's advice and |return| */
+        while (1) {
+          CONTINUE:
+            clear_for_error_prompt();
+            prompt_input("? ");
+            if (last == first)
+                return;
+            c = buffer[first];
+            if (c >= 'a')
+                c = c + 'A' - 'a';      /* convert to uppercase */
+            /* Interpret code |c| and |return| if done */
+
+            /* It is desirable to provide an `\.E' option here that gives the user
+               an easy way to return from \TeX\ to the system editor, with the offending
+               line ready to be edited. But such an extension requires some system
+               wizardry, so the present implementation simply types out the name of the
+               file that should be  edited and the relevant line number.
+
+               There is a secret `\.D' option available when the debugging routines haven't
+               been commented~out. */
+
+            switch (c) {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if (deletions_allowed) {
+                    /* Delete |c-"0"| tokens and |goto continue| */
+                    /* We allow deletion of up to 99 tokens at a time */
+                    s1 = cur_tok;
+                    s2 = cur_cmd;
+                    s3 = cur_chr;
+                    s4 = align_state;
+                    align_state = 1000000;
+                    OK_to_interrupt = false;
+                    if ((last > first + 1) && (buffer[first + 1] >= '0')
+                        && (buffer[first + 1] <= '9'))
+                        c = c * 10 + buffer[first + 1] - '0' * 11;
+                    else
+                        c = c - '0';
+                    while (c > 0) {
+                        get_token();    /* one-level recursive call of |error| is possible */
+                        decr(c);
+                    }
+                    cur_tok = s1;
+                    cur_cmd = s2;
+                    cur_chr = s3;
+                    align_state = s4;
+                    OK_to_interrupt = true;
+                    help2("I have just deleted some text, as you asked.",
+                          "You can now delete more, or insert, or whatever.");
+                    show_context();
+                    goto CONTINUE;
+                }
+                break;
+#ifdef DEBUG
+            case 'D':
+                debug_help();
+                goto CONTINUE;
+                break;
+#endif
+            case 'E':
+                if (base_ptr > 0) {
+                    int callback_id = callback_defined(call_edit_callback);
+                    if (callback_id>0) {
+                        (void)run_callback(callback_id, "Sd->", makecstring(input_stack[base_ptr].name_field), line);
+                        jump_out(); /* should not be reached */
+                    } else {
+                        tprint_nl("You want to edit file ");
+                        print(input_stack[base_ptr].name_field);
+                        tprint(" at line ");
+                        print_int(line);
+                        interaction = scroll_mode;
+                        if (kpse_init) {
+                            luatex_calledit(base_ptr, line);
+                        } else {
+                            tprint_nl("There is no valid callback defined.");
+                            jump_out(); /* should not be reached */
+                        }
+                    }
+                }
+                break;
+            case 'H':
+                /* Print the help information and |goto continue| */
+                if (use_err_help) {
+                    give_err_help();
+                } else {
+                    if (help_line[0] == NULL) {
+                        help2
+                            ("Sorry, I don't know how to help in this situation.",
+                             "Maybe you should try asking a human?");
+                    }
+                    i = 0;
+                    while (help_line[i] != NULL)
+                        tprint_nl(help_line[i++]);
+                    help4("Sorry, I already gave what help I could...",
+                          "Maybe you should try asking a human?",
+                          "An error might have occurred before I noticed any problems.",
+                          "``If all else fails, read the instructions.''");
+                    goto CONTINUE;
+                }
+                break;
+            case 'I':
+                /* Introduce new material from the terminal and |return| */
+                /* When the following code is executed, |buffer[(first+1)..(last-1)]| may
+                   contain the material inserted by the user; otherwise another prompt will
+                   be given. In order to understand this part of the program fully, you need
+                   to be familiar with \TeX's input stacks. */
+
+                begin_file_reading();   /* enter a new syntactic level for terminal input */
+                /* now |state=mid_line|, so an initial blank space will count as a blank */
+                if (last > first + 1) {
+                    iloc = first + 1;
+                    buffer[first] = ' ';
+                } else {
+                    prompt_input("insert>");
+                    iloc = first;
+                }
+                first = last;
+                ilimit = last - 1;      /* no |end_line_char| ends this line */
+                return;
+                break;
+            case 'Q':
+            case 'R':
+            case 'S':
+                /* Change the interaction level and |return| */
+                /* Here the author of \TeX\ apologizes for making use of the numerical
+                   relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings
+                   |batch_mode|, |nonstop_mode|, |scroll_mode|. */
+                error_count = 0;
+                interaction = batch_mode + c - 'Q';
+                tprint("OK, entering ");
+                switch (c) {
+                case 'Q':
+                    tprint_esc("batchmode");
+                    decr(selector);
+                    break;
+                case 'R':
+                    tprint_esc("nonstopmode");
+                    break;
+                case 'S':
+                    tprint_esc("scrollmode");
+                    break;
+                }
+                tprint("...");
+                print_ln();
+                update_terminal();
+                return;
+                break;
+            case 'X':
+                interaction = scroll_mode;
+                jump_out();
+                break;
+            default:
+                break;
+            }
+            if (!use_err_help) {
+                /* Print the menu of available options */
+                tprint("Type <return> to proceed, S to scroll future error messages,");
+                tprint_nl("R to run without stopping, Q to run quietly,");
+                tprint_nl("I to insert something, ");
+                if (base_ptr > 0)
+                    tprint("E to edit your file,");
+                if (deletions_allowed)
+                    tprint_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
+                tprint_nl("H for help, X to quit.");
+            }
+            use_err_help = false;
+        }
+
+    }
+    incr(error_count);
+    if (error_count == 100) {
+        tprint_nl("(That makes 100 errors; please try again.)");
+        history = fatal_error_stop;
+        jump_out();
+    }
+    /* Put help message on the transcript file */
+    if (interaction > batch_mode)
+        decr(selector);         /* avoid terminal output */
+    if (use_err_help) {
+        print_ln();
+        give_err_help();
+    } else {
+        int i1 = 0;
+        while (help_line[i1] != NULL)
+            tprint_nl(help_line[i1++]);
+    }
+    print_ln();
+    if (interaction > batch_mode)
+        incr(selector);         /* re-enable terminal output */
+    print_ln();
+}
+
+@ A dozen or so error messages end with a parenthesized integer, so we
+save a teeny bit of program space by declaring the following procedure:
+
+@c
+void int_error(int n)
+{
+    tprint(" (");
+    print_int(n);
+    print_char(')');
+    error();
+}
+
+@ In anomalous cases, the print selector might be in an unknown state;
+the following subroutine is called to fix things just enough to keep
+running a bit longer.
+
+@c
+void normalize_selector(void)
+{
+    if (log_opened_global)
+        selector = term_and_log;
+    else
+        selector = term_only;
+    if (job_name == 0)
+        open_log_file();
+    if (interaction == batch_mode)
+        decr(selector);
+}
+
+@ The following procedure prints \TeX's last words before dying
+@c
+void succumb(void)
+{
+    if (interaction == error_stop_mode)
+        interaction = scroll_mode;      /* no more interaction */
+    if (log_opened_global)
+        error();
+#ifdef DEBUG
+    if (interaction > batch_mode)
+        debug_help();
+#endif
+    history = fatal_error_stop;
+    jump_out();                 /* irrecoverable error */
+}
+
+@ @c
+void fatal_error(const char *s)
+{                               /* prints |s|, and that's it */
+    normalize_selector();
+    print_err("Emergency stop");
+    help1(s);
+    succumb();
+}
+
+@ Here is the most dreaded error message
+@c
+void overflow(const char *s, unsigned int n)
+{                               /* stop due to finiteness */
+    normalize_selector();
+    print_err("TeX capacity exceeded, sorry [");
+    tprint(s);
+    print_char('=');
+    print_int((int) n);
+    print_char(']');
+    if (varmem == NULL) {
+      print_err("Sorry, I ran out of memory.");
+      print_ln();
+      exit(EXIT_FAILURE);
+    }
+    help2("If you really absolutely need more capacity,",
+          "you can ask a wizard to enlarge me.");
+    succumb();
+}
+
+@ The program might sometime run completely amok, at which point there is
+no choice but to stop. If no previous error has been detected, that's bad
+news; a message is printed that is really intended for the \TeX\
+maintenance person instead of the user (unless the user has been
+particularly diabolical).  The index entries for `this can't happen' may
+help to pinpoint the problem.
+@^dry rot@>
+
+@c
+void confusion(const char *s)
+{                               /* consistency check violated; |s| tells where */
+    normalize_selector();
+    if (history < error_message_issued) {
+        print_err("This can't happen (");
+        tprint(s);
+        print_char(')');
+        help1("I'm broken. Please show this to someone who can fix can fix");
+    } else {
+        print_err("I can't go on meeting you like this");
+        help2("One of your faux pas seems to have wounded me deeply...",
+              "in fact, I'm barely conscious. Please fix it and try again.");
+    }
+    succumb();
+}
+
+@ Users occasionally want to interrupt \TeX\ while it's running.
+If the runtime system allows this, one can implement
+a routine that sets the global variable |interrupt| to some nonzero value
+when such an interrupt is signalled. Otherwise there is probably at least
+a way to make |interrupt| nonzero using the debugger.
+@^system dependencies@>
+@^debugging@>
+
+@c
+void check_interrupt(void)
+{
+    if (interrupt != 0)
+        pause_for_instructions();
+}
+
+@ When an interrupt has been detected, the program goes into its
+highest interaction level and lets the user have nearly the full flexibility of
+the |error| routine.  \TeX\ checks for interrupts only at times when it is
+safe to do this.
+
+@c
+void pause_for_instructions(void)
+{
+    if (OK_to_interrupt) {
+        interaction = error_stop_mode;
+        if ((selector == log_only) || (selector == no_print))
+            incr(selector);
+        print_err("Interruption");
+        help3("You rang?",
+              "Try to insert some instructions for me (e.g.,`I\\showlists'),",
+              "unless you just want to quit by typing `X'.");
+        deletions_allowed = false;
+        error();
+        deletions_allowed = true;
+        interrupt = 0;
+    }
+}
+
+@ @c
+void tex_error(const char *msg, const char **hlp)
+{
+    print_err(msg);
+    if (hlp != NULL) {
+        int i;
+        for (i = 0; (hlp[i] != NULL && i <= 5); i++) {
+            help_line[i] = hlp[i];
+        }
+        help_line[i] = NULL;
+    } else {
+        help_line[0] = NULL;
+    }
+    error();
+}
+
+@ The |back_error| routine is used when we want to replace an offending token
+just before issuing an error message. This routine, like |back_input|,
+requires that |cur_tok| has been set. We disable interrupts during the
+call of |back_input| so that the help message won't be lost.
+
+@c
+void back_error(void)
+{                               /* back up one token and call |error| */
+    OK_to_interrupt = false;
+    back_input();
+    OK_to_interrupt = true;
+    error();
+}
+
+@ @c
+void ins_error(void)
+{                               /* back up one inserted token and call |error| */
+    OK_to_interrupt = false;
+    back_input();
+    token_type = inserted;
+    OK_to_interrupt = true;
+    error();
+}
+
+@ When \TeX\ wants to typeset a character that doesn't exist, the
+character node is not created; thus the output routine can assume
+that characters exist when it sees them. The following procedure
+prints a warning message unless the user has suppressed it.
+
+@c
+void char_warning(internal_font_number f, int c)
+{
+    int old_setting;            /* saved value of |tracing_online| */
+    int k;                      /* index to current digit; we assume that $0\L n<16^{22}$ */
+    if (tracing_lost_chars_par > 0) {
+        old_setting = tracing_online_par;
+        if (tracing_lost_chars_par > 1)
+            tracing_online_par = 1;
+        begin_diagnostic();
+        tprint_nl("Missing character: There is no ");
+        print(c);
+        tprint(" (U+");
+        k = 0;
+        if (c < 16)
+            print_char('0');
+        if (c < 256)
+            print_char('0');
+        if (c < 4096)
+            print_char('0');
+        do {
+            dig[k] = c % 16;
+            c = c / 16;
+            incr(k);
+        } while (c != 0);
+        print_the_digs((eight_bits) k);
+        tprint(") in font ");
+        print_font_name(f);
+        print_char('!');
+        end_diagnostic(false);
+        tracing_online_par = old_setting;
+    }
+}
+
+@ @c
+
+void wrapup_backend(void) {
+    ensure_output_state(static_pdf, ST_OMODE_FIX);
+    switch (output_mode_used) {
+        case OMODE_NONE:
+            print_err(" ==> Fatal error occurred, no FMT file produced!");
+            break;
+        case OMODE_PDF:
+            if (history == fatal_error_stop) {
+                remove_pdffile(static_pdf); /* will become remove_output_file */
+                print_err(" ==> Fatal error occurred, no output PDF file produced!");
+            } else {
+                finish_pdf_file(static_pdf, luatex_version, get_luatexrevision());
+            }
+            break;
+        case OMODE_DVI:
+            if (history == fatal_error_stop) {
+                print_err(" ==> Fatal error occurred, bad output DVI file produced!");
+                finish_dvi_file(static_pdf, luatex_version, get_luatexrevision());
+            } else {
+                finish_dvi_file(static_pdf, luatex_version, get_luatexrevision());
+            }
+            break;
+    }
+}
+
+void normal_error(const char *t, const char *p)
+{
+    normalize_selector();
+    if (interaction == error_stop_mode) {
+        wake_up_terminal();
+    }
+    if (filelineerrorstylep) {
+        print_file_line();
+    } else {
+        tprint_nl("! ");
+    }
+    tprint("error: ");
+    if (cur_file_name) {
+        tprint(" (file ");
+        tprint(cur_file_name);
+        tprint(")");
+    }
+    if (t != NULL) {
+        tprint(" (");
+        tprint(t);
+        tprint(")");
+    }
+    tprint(": ");
+    if (p != NULL)
+        tprint(p);
+    history = fatal_error_stop;
+    wrapup_backend();
+    exit(EXIT_FAILURE);
+}
+
+/*
+void normal_error(const char *t, const char *p)
+{
+    normalize_selector();
+    if (interaction == error_stop_mode) {
+        wake_up_terminal();
+    }
+    tprint("error : ");
+    if (p != NULL)
+        tprint(p);
+    history = fatal_error_stop;
+    wrapup_backend();
+    exit(EXIT_FAILURE);
+}
+*/
+
+@ @c
+void normal_warning(const char *t, const char *p)
+{
+    int report_id ;
+    if (strcmp(t,"lua") == 0) {
+        int saved_new_line_char;
+        saved_new_line_char = new_line_char_par;
+        new_line_char_par = 10;
+        report_id = callback_defined(show_lua_error_hook_callback);
+        if (report_id == 0) {
+            tprint(p);
+            help2("The lua interpreter ran into a problem, so the",
+                  "remainder of this lua chunk will be ignored.");
+        } else {
+            (void) run_callback(report_id, "->");
+        }
+        error();
+        new_line_char_par = saved_new_line_char;
+    } else {
+        report_id = callback_defined(show_warning_message_callback);
+        if (report_id > 0) {
+            /* free last ones */
+            xfree(last_warning_str);
+            xfree(last_warning_tag);
+            last_warning_str = (string) xmalloc(strlen(p) + 1);
+            last_warning_tag = (string) xmalloc(strlen(t) + 1);
+            strcpy(last_warning_str,p);
+            strcpy(last_warning_tag,t);
+            run_callback(report_id, "->");
+        } else {
+            print_ln();
+            tprint("warning ");
+            if (cur_file_name) {
+                tprint(" (file ");
+                tprint(cur_file_name);
+                tprint(")");
+            }
+            if (t != NULL) {
+                tprint(" (");
+                tprint(t);
+                tprint(")");
+            }
+            tprint(": ");
+            if (p != NULL)
+                tprint(p);
+            print_ln();
+        }
+        if (history == spotless)
+            history = warning_issued;
+    }
+}
+
+@ @c
+static char print_buf[PRINTF_BUF_SIZE];
+__attribute__ ((format(printf, 2,3)))
+void formatted_error(const char *t, const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
+    normal_error(t,print_buf);
+    va_end(args);
+}
+
+__attribute__ ((format(printf, 2,3)))
+void formatted_warning(const char *t, const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
+    normal_warning(t,print_buf);
+    va_end(args);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/expand.c
+++ /dev/null
@@ -1,967 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    Only a dozen or so command codes |>max_command| can possibly be returned by
-    |get_next|; in increasing order, they are |undefined_cs|, |expand_after|,
-    |no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|,
-    |top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and
-    |end_template|.
-
-    Sometimes, recursive calls to the following |expand| routine may cause
-    exhaustion of the run-time calling stack, resulting in forced execution stops
-    by the operating system. To diminish the chance of this happening, a counter
-    is used to keep track of the recursion depth, in conjunction with a constant
-    called |expand_depth|.
-
-    Note that this does not catch all possible infinite recursion loops, just the
-    ones that exhaust the application calling stack. The actual maximum value of
-    |expand_depth| is outside of our control, but the initial setting of |100|
-    should be enough to prevent problems.
-
-*/
-
-static int expand_depth_count = 0;
-
-/*tex
-
-    The |expand| subroutine is used when |cur_cmd>max_command|. It removes a
-    ``call'' or a conditional or one of the other special operations just listed.
-    It follows that |expand| might invoke itself recursively. In all cases,
-    |expand| destroys the current token, but it sets things up so that the next
-    |get_next| will deliver the appropriate next token. The value of |cur_tok|
-    need not be known when |expand| is called.
-
-    Since several of the basic scanning routines communicate via global
-    variables, their values are saved as local variables of |expand| so that
-    recursive calls don't invalidate them.
-
-*/
-
-int is_in_csname = 0;
-
-void expand(void)
-{
-    /*tex token that is being ``expanded after'' */
-    halfword t;
-    /*tex for list manipulation */
-    halfword p;
-    /*tex for a local token list pointer */
-    halfword cur_ptr;
-    /*tex to save the global quantity |cur_val| */
-    int cv_backup;
-    /*tex to save |cur_val_level|, etc. */
-    int cvl_backup, radix_backup, co_backup;
-    /*tex to save |link(backup_head)| */
-    halfword backup_backup;
-    /*tex temporary storage of |scanner_status| */
-    int save_scanner_status;
-    incr(expand_depth_count);
-    if (expand_depth_count >= expand_depth)
-        overflow("expansion depth", (unsigned) expand_depth);
-    cv_backup = cur_val;
-    cvl_backup = cur_val_level;
-    radix_backup = radix;
-    co_backup = cur_order;
-    backup_backup = token_link(backup_head);
-  RESWITCH:
-    if (cur_cmd < call_cmd) {
-        /*tex Expand a nonmacro. */
-        if (tracing_commands_par > 1)
-            show_cur_cmd_chr();
-        switch (cur_cmd) {
-            case top_bot_mark_cmd:
-                /*tex Insert the appropriate mark text into the scanner. */
-                t = cur_chr % marks_code;
-                if (cur_chr >= marks_code)
-                    scan_mark_num();
-                else
-                    cur_val = 0;
-                switch (t) {
-                    case first_mark_code:
-                        cur_ptr = first_mark(cur_val);
-                        break;
-                    case bot_mark_code:
-                        cur_ptr = bot_mark(cur_val);
-                        break;
-                    case split_first_mark_code:
-                        cur_ptr = split_first_mark(cur_val);
-                        break;
-                    case split_bot_mark_code:
-                        cur_ptr = split_bot_mark(cur_val);
-                        break;
-                    default:
-                        cur_ptr = top_mark(cur_val);
-                        break;
-                }
-                if (cur_ptr != null)
-                    begin_token_list(cur_ptr, mark_text);
-                break;
-            case expand_after_cmd:
-                if (cur_chr == 0) {
-                    /*tex
-
-                        Expand the token after the next token. It takes only a
-                        little shuffling to do what \TeX\ calls
-                        \.{\\expandafter}.
-
-                    */
-                    get_token();
-                    t = cur_tok;
-                    get_token();
-                    if (cur_cmd > max_command_cmd)
-                        expand();
-                    else
-                        back_input();
-                    cur_tok = t;
-                    back_input();
-                } else {
-                    /*tex
-
-                        Negate a boolean conditional and |goto reswitch|. The
-                        result of a boolean condition is reversed when the
-                        conditional is preceded by \.{\\unless}.
-
-                    */
-                    get_token();
-                    if ((cur_cmd == if_test_cmd) && (cur_chr != if_case_code)) {
-                        cur_chr = cur_chr + unless_code;
-                        goto RESWITCH;
-                    }
-                    print_err("You can't use `\\unless' before `");
-                    print_cmd_chr((quarterword) cur_cmd, cur_chr);
-                    print_char('\'');
-                    help1("Continue, and I'll forget that it ever happened.");
-                    back_error();
-                }
-                break;
-            case no_expand_cmd:
-                if (cur_chr == 0) {
-                    /*tex
-
-                        Suppress expansion of the next token. The implementation
-                        of \.{\\noexpand} is a bit trickier, because it is
-                        necessary to insert a special `|dont_expand|' marker into
-                        \TeX's reading mechanism. This special marker is
-                        processed by |get_next|, but it does not slow down the
-                        inner loop.
-
-                        Since \.{\\outer} macros might arise here, we must also
-                        clear the |scanner_status| temporarily.
-
-                    */
-                    save_scanner_status = scanner_status;
-                    scanner_status = normal;
-                    get_token();
-                    scanner_status = save_scanner_status;
-                    t = cur_tok;
-                    back_input();
-                    /*tex Now |start| and |loc| point to the backed-up token |t|. */
-                    if (t >= cs_token_flag) {
-                        p = get_avail();
-                        set_token_info(p, cs_token_flag + frozen_dont_expand);
-                        set_token_link(p, iloc);
-                        istart = p;
-                        iloc = p;
-                    }
-
-                } else {
-                    /*tex
-
-                        The \.{\\primitive} handling. If the primitive meaning of
-                        the next token is an expandable command, it suffices to
-                        replace the current token with the primitive one and
-                        restart |expand|.
-
-                        Otherwise, the token we just read has to be pushed back,
-                        as well as a token matching the internal form of
-                        \.{\\primitive}, that is sneaked in as an alternate form
-                        of |ignore_spaces|.
-
-                        An implementation problem surfaces: There really is no
-                        |cur_cs| attached to the inserted primitive command, so
-                        it is safer to set |cur_cs| to zero. |cur_tok| has a
-                        similar problem. And for the non-expanded branch, simply
-                        pushing back a token that matches the correct internal
-                        command does not work, because that approach would not
-                        survive roundtripping to a temporary file or even a token
-                        list.
-
-                        It would be smart to create |frozen_| versions of all the
-                        primitives. Then, this problem would not happen, at the
-                        expense of a few hundred extra control sequences.
-
-                    */
-                    save_scanner_status = scanner_status;
-                    scanner_status = normal;
-                    get_token();
-                    scanner_status = save_scanner_status;
-                    cur_cs = prim_lookup(cs_text(cur_cs));
-                    if (cur_cs != undefined_primitive) {
-                        t = get_prim_eq_type(cur_cs);
-                        if (t > max_command_cmd) {
-                            cur_cmd = t;
-                            cur_chr = get_prim_equiv(cur_cs);
-                            cur_tok = token_val(cur_cmd, cur_chr);
-                            cur_cs = 0;
-                            goto RESWITCH;
-                        } else {
-                            back_input();
-                            /*tex  Now |loc| and |start| point to a one-item list. */
-                            p = get_avail();
-                            set_token_info(p, cs_token_flag + frozen_primitive);
-                            set_token_link(p, iloc);
-                            iloc = p;
-                            istart = p;
-                        }
-                    } else if (suppress_primitive_error_par == 0) {
-                        print_err("Missing primitive name");
-                        help2(
-                            "The control sequence marked <to be read again> does not",
-                            "represent any known primitive."
-                        );
-                        back_error();
-                    }
-                }
-                break;
-            case cs_name_cmd:
-                /*tex Manufacture a control sequence name. */
-                if (cur_chr == 0) {
-                    manufacture_csname(0);
-                } else if (cur_chr == 1) {
-                    inject_last_tested_cs();
-                } else {
-                    manufacture_csname(1);
-                }
-                break;
-            case convert_cmd:
-                conv_toks();
-                break;
-            case the_cmd:
-                ins_the_toks();
-                break;
-            case combine_toks_cmd:
-                combine_the_toks(cur_chr);
-                break;
-            case if_test_cmd:
-                /*tex An experiment. */
-                if (cur_chr == if_condition_code) {
-                    break;
-                }
-                /*tex End of experiment. */
-                conditional();
-                break;
-            case fi_or_else_cmd:
-                /*tex
-
-                    Terminate the current conditional and skip to \.{\\fi} The
-                    processing of conditionals is complete except for the
-                    following code, which is actually part of |expand|. It comes
-                    into play when \.{\\or}, \.{\\else}, or \.{\\fi} is scanned.
-
-                */
-                if (tracing_ifs_par > 0)
-                    if (tracing_commands_par <= 1)
-                        show_cur_cmd_chr();
-                if (cur_chr > if_limit) {
-                    if (if_limit == if_code) {
-                        /*tex Condition is not yet evaluated. */
-                        insert_relax();
-                    } else {
-                        print_err("Extra ");
-                        print_cmd_chr(fi_or_else_cmd, cur_chr);
-                        help1("I'm ignoring this; it doesn't match any \\if.");
-                        error();
-                    }
-                } else {
-                    while (cur_chr != fi_code) {
-                        /*tex Skip to \.{\\fi}. */
-                        pass_text();
-                    }
-                    pop_condition_stack();
-                }
-                break;
-            case input_cmd:
-                /*tex Initiate or terminate input from a file */
-                if (cur_chr == 1)
-                    force_eof = true;
-                else if (cur_chr == 2)
-                    pseudo_start();
-                else if (cur_chr == 3) {
-                    pseudo_start();
-                    iname = 19;
-                } else if (name_in_progress)
-                    insert_relax();
-                else
-                    start_input();
-                break;
-            case lua_expandable_call_cmd:
-                if (cur_chr <= 0) {
-                    normal_error("luacall", "invalid number");
-                } else {
-                    str_number u = save_cur_string();
-                    luacstrings = 0;
-                    luafunctioncall(cur_chr);
-                    restore_cur_string(u);
-                    if (luacstrings > 0)
-                        lua_string_start();
-                }
-                break;
-            case lua_local_call_cmd:
-                if (cur_chr <= 0) {
-                    normal_error("luacall", "invalid number");
-                } else {
-                    str_number u = save_cur_string();
-                    luacstrings = 0;
-                    lua_rawgeti(Luas, LUA_REGISTRYINDEX, cur_chr);
-                    lua_call(Luas,0,0);
-                    restore_cur_string(u);
-                    if (luacstrings > 0)
-                        lua_string_start();
-                }
-                break;
-            case variable_cmd:
-                do_variable();
-                break;
-            case feedback_cmd:
-                do_feedback();
-                break;
-            default:
-                /*tex Complain about an undefined macro */
-                print_err("Undefined control sequence");
-                help5(
-                    "The control sequence at the end of the top line",
-                    "of your error message was never \\def'ed. If you have",
-                    "misspelled it (e.g., `\\hobx'), type `I' and the correct",
-                    "spelling (e.g., `I\\hbox'). Otherwise just continue,",
-                    "and I'll forget about whatever was undefined."
-                );
-                error();
-                break;
-        }
-    } else if (cur_cmd < end_template_cmd) {
-        macro_call();
-    } else {
-        /*tex
-
-            Insert a token containing |frozen_endv|. An |end_template| command is
-            effectively changed to an |endv| command by the following code. (The
-            reason for this is discussed below; the |frozen_end_template| at the
-            end of the template has passed the |check_outer_validity| test, so
-            its mission of error detection has been accomplished.)
-
-        */
-        cur_tok = cs_token_flag + frozen_endv;
-        back_input();
-    }
-    cur_val = cv_backup;
-    cur_val_level = cvl_backup;
-    radix = radix_backup;
-    cur_order = co_backup;
-    set_token_link(backup_head, backup_backup);
-    decr(expand_depth_count);
-}
-
-void complain_missing_csname(void)
-{
-    print_err("Missing \\endcsname inserted");
-    help2(
-        "The control sequence marked <to be read again> should",
-        "not appear between \\csname and \\endcsname."
-    );
-    back_error();
-}
-
-void manufacture_csname(boolean use)
-{
-    halfword p, q, r;
-    lstring *ss;
-    r = get_avail();
-    p = r;
-    is_in_csname += 1;
-    do {
-        get_x_token();
-        if (cur_cs == 0)
-            store_new_token(cur_tok);
-    } while (cur_cs == 0);
-    if (cur_cmd != end_cs_name_cmd) {
-        /*tex Complain about missing \.{\\endcsname}. */
-        complain_missing_csname();
-    }
-    /*tex Look up the characters of list |r| in the hash table, and set |cur_cs|. */
-    ss = tokenlist_to_lstring(r, true);
-    is_in_csname -= 1;
-    if (use) {
-        if (ss->l > 0) {
-            cur_cs = string_lookup((char *) ss->s, ss->l);
-        } else {
-            cur_cs = null_cs;
-        }
-        last_cs_name = cur_cs ;
-        free_lstring(ss);
-        flush_list(r);
-        if (cur_cs == null_cs) {
-            /*tex skip */
-        } else if (eq_type(cur_cs) == undefined_cs_cmd) {
-            /*tex skip */
-        } else {
-            cur_tok = cur_cs + cs_token_flag;
-            back_input();
-        }
-    } else {
-        if (ss->l > 0) {
-            no_new_control_sequence = false;
-            cur_cs = string_lookup((char *) ss->s, ss->l);
-            no_new_control_sequence = true;
-        } else {
-            /*tex the list is empty */
-            cur_cs = null_cs;
-        }
-        last_cs_name = cur_cs ;
-        free_lstring(ss);
-        flush_list(r);
-        if (eq_type(cur_cs) == undefined_cs_cmd) {
-            /*tex The |save_stack| might change! */
-            eq_define(cur_cs, relax_cmd, too_big_char);
-        };
-        /*tex The control sequence will now match \.{\\relax} */
-        cur_tok = cur_cs + cs_token_flag;
-        back_input();
-    }
-}
-
-void inject_last_tested_cs(void)
-{
-    if (last_cs_name != null_cs) {
-        cur_cs = last_cs_name;
-        cur_tok = last_cs_name + cs_token_flag;
-        back_input();
-    }
-}
-
-/*tex
-
-    Sometimes the expansion looks too far ahead, so we want to insert a harmless
-    \.{\\relax} into the user's input.
-
-*/
-
-void insert_relax(void)
-{
-    cur_tok = cs_token_flag + cur_cs;
-    back_input();
-    cur_tok = cs_token_flag + frozen_relax;
-    back_input();
-    token_type = inserted;
-}
-
-
-/*tex
-
-    Here is a recursive procedure that is \TeX's usual way to get the next token
-    of input. It has been slightly optimized to take account of common cases.
-
-*/
-
-void get_x_token(void)
-{
-    /*tex This code sets |cur_cmd|, |cur_chr|, |cur_tok|, and expands macros. */
-  RESTART:
-    get_next();
-    if (cur_cmd <= max_command_cmd)
-        goto DONE;
-    if (cur_cmd >= call_cmd) {
-        if (cur_cmd < end_template_cmd) {
-            macro_call();
-        } else {
-            cur_cs = frozen_endv;
-            cur_cmd = endv_cmd;
-            /*tex Now |cur_chr=null_list|. */
-            goto DONE;
-        }
-    } else {
-        expand();
-    }
-    goto RESTART;
-  DONE:
-    if (cur_cs == 0)
-        cur_tok = token_val(cur_cmd, cur_chr);
-    else
-        cur_tok = cs_token_flag + cur_cs;
-}
-
-/*tex
-
-    The |get_x_token| procedure is equivalent to two consecutive procedure calls:
-    |get_next; x_token|. It's |get_x_token| without the initial |get_next|.
-
-*/
-
-void x_token(void)
-{
-    while (cur_cmd > max_command_cmd) {
-        expand();
-        get_next();
-   }
-    if (cur_cs == 0)
-        cur_tok = token_val(cur_cmd, cur_chr);
-    else
-        cur_tok = cs_token_flag + cur_cs;
-}
-
-/*tex
-
-    A control sequence that has been \.{\\def}'ed by the user is expanded by
-    \TeX's |macro_call| procedure.
-
-    Before we get into the details of |macro_call|, however, let's consider the
-    treatment of primitives like \.{\\topmark}, since they are essentially macros
-    without parameters. The token lists for such marks are kept in five global
-    arrays of pointers; we refer to the individual entries of these arrays by
-    symbolic macros |top_mark|, etc. The value of |top_mark(x)|, etc. is either
-    |null| or a pointer to the reference count of a token list.
-
-    The variable |biggest_used_mark| is an aid to try and keep the code somehwat
-    efficient without too much extra work: it registers the highest mark class
-    ever instantiated by the user, so the loops in |fire_up| and |vsplit| do not
-    have to traverse the full range |0..biggest_mark|.
-
-*/
-
-halfword top_marks_array[(biggest_mark + 1)];
-halfword first_marks_array[(biggest_mark + 1)];
-halfword bot_marks_array[(biggest_mark + 1)];
-halfword split_first_marks_array[(biggest_mark + 1)];
-halfword split_bot_marks_array[(biggest_mark + 1)];
-halfword biggest_used_mark;
-
-void initialize_marks(void)
-{
-    int i;
-    biggest_used_mark = 0;
-    for (i = 0; i <= biggest_mark; i++) {
-        top_mark(i) = null;
-        first_mark(i) = null;
-        bot_mark(i) = null;
-        split_first_mark(i) = null;
-        split_bot_mark(i) = null;
-    }
-}
-
-/*tex
-
-    Now let's consider |macro_call| itself, which is invoked when \TeX\ is
-    scanning a control sequence whose |cur_cmd| is either |call|, |long_call|,
-    |outer_call|, or |long_outer_call|. The control sequence definition appears
-    in the token list whose reference count is in location |cur_chr| of |mem|.
-
-    The global variable |long_state| will be set to |call| or to |long_call|,
-    depending on whether or not the control sequence disallows \.{\\par} in its
-    parameters. The |get_next| routine will set |long_state| to |outer_call| and
-    emit \.{\\par}, if a file ends or if an \.{\\outer} control sequence occurs
-    in the midst of an argument.
-
-*/
-
-/*tex Governs the acceptance of \.{\\par}: */
-
-int long_state;
-
-/*tex
-
-    The parameters, if any, must be scanned before the macro is expanded.
-    Parameters are token lists without reference counts. They are placed on an
-    auxiliary stack called |pstack| while they are being scanned, since the
-    |param_stack| may be losing entries during the matching process. (Note that
-    |param_stack| can't be gaining entries, since |macro_call| is the only
-    routine that puts anything onto |param_stack|, and it is not recursive.)
-
-*/
-
-/*tex The arguments supplied to a macro: */
-
-halfword pstack[9];
-
-/*tex
-
-    After parameter scanning is complete, the parameters are moved to the
-    |param_stack|. Then the macro body is fed to the scanner; in other words,
-    |macro_call| places the defined text of the control sequence at the top of\/
-    \TeX's input stack, so that |get_next| will proceed to read it next.
-
-    The global variable |cur_cs| contains the |eqtb| address of the control
-    sequence being expanded, when |macro_call| begins. If this control sequence
-    has not been declared \.{\\long}, i.e., if its command code in the |eq_type|
-    field is not |long_call| or |long_outer_call|, its parameters are not allowed
-    to contain the control sequence \.{\\par}. If an illegal \.{\\par} appears,
-    the macro call is aborted, and the \.{\\par} will be rescanned.
-
-*/
-
-/*tex This invokes a user-defined control sequence. */
-
-void macro_call(void)
-{
-    /*tex current node in the macro's token list */
-    halfword r;
-    /*tex current node in parameter token list being built */
-    halfword p = null;
-    /*tex new node being put into the token list */
-    halfword q;
-    /*tex backup pointer for parameter matching */
-    halfword s;
-    /*tex cycle pointer for backup recovery */
-    halfword t;
-    /*tex auxiliary pointers for backup recovery */
-    halfword u, v;
-    /*tex one step before the last |right_brace| token */
-    halfword rbrace_ptr = null;
-    /*tex the number of parameters scanned */
-    int n = 0;
-    /*tex unmatched left braces in current parameter */
-    halfword unbalance;
-    /*tex the number of tokens or groups (usually) */
-    halfword m = 0;
-    /*tex start of the token list */
-    halfword ref_count;
-    /*tex |scanner_status| upon entry */
-    int save_scanner_status = scanner_status;
-    /*tex |warning_index| upon entry */
-    halfword save_warning_index = warning_index;
-    /*tex character used in parameter */
-    int match_chr = 0;
-    warning_index = cur_cs;
-    ref_count = cur_chr;
-    r = token_link(ref_count);
-    if (tracing_macros_par > 0) {
-        /*tex Show the text of the macro being expanded. */
-        begin_diagnostic();
-        print_ln();
-        print_cs(warning_index);
-        token_show(ref_count);
-        end_diagnostic(false);
-    }
-    if (token_info(r) == protected_token)
-        r = token_link(r);
-    if (token_info(r) != end_match_token) {
-        /*tex
-
-            Scan the parameters and make |link(r)| point to the macro body; but
-            |return| if an illegal \.{\\par} is detected.
-
-            At this point, the reader will find it advisable to review the
-            explanation of token list format that was presented earlier, since
-            many aspects of that format are of importance chiefly in the
-            |macro_call| routine.
-
-            The token list might begin with a string of compulsory tokens before
-            the first |match| or |end_match|. In that case the macro name is
-            supposed to be followed by those tokens; the following program will
-            set |s=null| to represent this restriction. Otherwise |s| will be set
-            to the first token of a string that will delimit the next parameter.
-
-        */
-        scanner_status = matching;
-        unbalance = 0;
-        long_state = eq_type(cur_cs);
-        if (long_state >= outer_call_cmd)
-            long_state = long_state - 2;
-        do {
-            set_token_link(temp_token_head, null);
-            if ((token_info(r) >= end_match_token)
-                || (token_info(r) < match_token)) {
-                s = null;
-            } else {
-                match_chr = token_info(r) - match_token;
-                s = token_link(r);
-                r = s;
-                p = temp_token_head;
-                m = 0;
-            }
-            /*tex
-
-                Scan a parameter until its delimiter string has been found; or,
-                if |s=null|, simply scan the delimiter string.
-
-                If |info(r)| is a |match| or |end_match| command, it cannot be
-                equal to any token found by |get_token|. Therefore an undelimited
-                parameter---i.e., a |match| that is immediately followed by
-                |match| or |end_match|---will always fail the test
-                `|cur_tok=info(r)|' in the following algorithm.
-
-            */
-          CONTINUE:
-            /*tex Set |cur_tok| to the next token of input: */
-            get_token();
-            if (cur_tok == token_info(r)) {
-                /*tex
-
-                    Advance |r|; |goto found| if the parameter delimiter has been
-                    fully matched, otherwise |goto continue|.
-
-                    A slightly subtle point arises here: When the parameter
-                    delimiter ends with `\.{\#\{}', the token list will have a
-                    left brace both before and after the |end_match|\kern-.4pt.
-                    Only one of these should affect the |align_state|, but both
-                    will be scanned, so we must make a correction.
-
-                */
-                r = token_link(r);
-                if ((token_info(r) >= match_token)
-                    && (token_info(r) <= end_match_token)) {
-                    if (cur_tok < left_brace_limit)
-                        decr(align_state);
-                    goto FOUND;
-                } else {
-                    goto CONTINUE;
-                }
-
-            }
-            /*tex
-
-                Contribute the recently matched tokens to the current parameter,
-                and |goto continue| if a partial match is still in effect; but
-                abort if |s=null|.
-
-                When the following code becomes active, we have matched tokens
-                from |s| to the predecessor of |r|, and we have found that
-                |cur_tok<>info(r)|. An interesting situation now presents itself:
-                If the parameter is to be delimited by a string such as `\.{ab}',
-                and if we have scanned `\.{aa}', we want to contribute one `\.a'
-                to the current parameter and resume looking for a `\.b'. The
-                program must account for such partial matches and for others that
-                can be quite complex. But most of the time we have |s=r| and
-                nothing needs to be done.
-
-                Incidentally, it is possible for \.{\\par} tokens to sneak in to
-                certain parameters of non-\.{\\long} macros. For example,
-                consider a case like `\.{\\def\\a\#1\\par!\{...\}}' where the
-                first \.{\\par} is not followed by an exclamation point. In such
-                situations it does not seem appropriate to prohibit the
-                \.{\\par}, so \TeX\ keeps quiet about this bending of the rules.
-
-            */
-            if (s != r) {
-                if (s == null) {
-                    /*tex Report an improper use of the macro and abort. */
-                    print_err("Use of ");
-                    sprint_cs(warning_index);
-                    tprint(" doesn't match its definition");
-                    help4(
-                        "If you say, e.g., `\\def\\a1{...}', then you must always",
-                        "put `1' after `\\a', since control sequence names are",
-                        "made up of letters only. The macro here has not been",
-                        "followed by the required stuff, so I'm ignoring it."
-                    );
-                    error();
-                    goto EXIT;
-
-                } else {
-                    t = s;
-                    do {
-                        store_new_token(token_info(t));
-                        incr(m);
-                        u = token_link(t);
-                        v = s;
-                        while (1) {
-                            if (u == r) {
-                                if (cur_tok != token_info(v)) {
-                                    goto DONE;
-                                } else {
-                                    r = token_link(v);
-                                    goto CONTINUE;
-                                }
-                            }
-                            if (token_info(u) != token_info(v))
-                                goto DONE;
-                            u = token_link(u);
-                            v = token_link(v);
-                        }
-                      DONE:
-                        t = token_link(t);
-                    } while (t != r);
-                    r = s;
-                    /*tex At this point, no tokens are recently matched. */
-                }
-            }
-            if (cur_tok == par_token)
-                if (long_state != long_call_cmd)
-                    if (!suppress_long_error_par) {
-                        goto RUNAWAY;
-                    }
-            if (cur_tok < right_brace_limit) {
-                if (cur_tok < left_brace_limit) {
-                    /*tex Contribute an entire group to the current parameter. */
-                    unbalance = 1;
-                    while (1) {
-                        fast_store_new_token(cur_tok);
-                        get_token();
-                        if (cur_tok == par_token) {
-                            if (long_state != long_call_cmd) {
-                                if (!suppress_long_error_par) {
-                                    goto RUNAWAY;
-
-                                }
-                            }
-                        }
-                        if (cur_tok < right_brace_limit) {
-                            if (cur_tok < left_brace_limit) {
-                                incr(unbalance);
-                            } else {
-                                decr(unbalance);
-                                if (unbalance == 0)
-                                    break;
-                            }
-                        }
-                    }
-                    rbrace_ptr = p;
-                    store_new_token(cur_tok);
-                } else {
-                    /*tex Report an extra right brace and |goto continue|. */
-                    back_input();
-                    print_err("Argument of ");
-                    sprint_cs(warning_index);
-                    tprint(" has an extra }");
-                    help6(
-                        "I've run across a `}' that doesn't seem to match anything.",
-                        "For example, `\\def\\a#1{...}' and `\\a}' would produce",
-                        "this error. If you simply proceed now, the `\\par' that",
-                        "I've just inserted will cause me to report a runaway",
-                        "argument that might be the root of the problem. But if",
-                        "your `}' was spurious, just type `2' and it will go away."
-                    );
-                    incr(align_state);
-                    long_state = call_cmd;
-                    cur_tok = par_token;
-                    ins_error();
-                    goto CONTINUE;
-                    /*tex A white lie; the \.{\\par} won't always trigger a runaway. */
-                }
-            } else {
-                /*tex
-
-                    Store the current token, but |goto continue| if it is a blank
-                    space that would become an undelimited parameter.
-
-                */
-                if (cur_tok == space_token && token_info(r) <= end_match_token && token_info(r) >= match_token)
-                    goto CONTINUE;
-                store_new_token(cur_tok);
-            }
-            incr(m);
-            if (token_info(r) > end_match_token)
-                goto CONTINUE;
-            if (token_info(r) < match_token)
-                goto CONTINUE;
-          FOUND:
-            if (s != null) {
-                /*
-
-                    Tidy up the parameter just scanned, and tuck it away. If the
-                    parameter consists of a single group enclosed in braces, we
-                    must strip off the enclosing braces. That's why |rbrace_ptr|
-                    was introduced.
-
-                */
-                if ((m == 1) && (token_info(p) < right_brace_limit)
-                    && (p != temp_token_head)) {
-                    set_token_link(rbrace_ptr, null);
-                    free_avail(p);
-                    p = token_link(temp_token_head);
-                    pstack[n] = token_link(p);
-                    free_avail(p);
-                } else {
-                    pstack[n] = token_link(temp_token_head);
-                }
-                incr(n);
-                if (tracing_macros_par > 0) {
-                    begin_diagnostic();
-                    print_nl(match_chr);
-                    print_int(n);
-                    tprint("<-");
-                    show_token_list(pstack[n - 1], null, 1000);
-                    end_diagnostic(false);
-                }
-            }
-            /*tex
-
-                Now |info(r)| is a token whose command code is either |match| or
-                |end_match|.
-
-            */
-        } while (token_info(r) != end_match_token);
-    }
-    /*tex
-
-        Feed the macro body and its parameters to the scanner Before we put a new
-        token list on the input stack, it is wise to clean off all token lists
-        that have recently been depleted. Then a user macro that ends with a call
-        to itself will not require unbounded stack space.
-
-    */
-    while ((istate == token_list) && (iloc == null) && (token_type != v_template)) {
-        /*tex Conserve stack space. */
-        end_token_list();
-    }
-    begin_token_list(ref_count, macro);
-    iname = warning_index;
-    iloc = token_link(r);
-    if (n > 0) {
-        if (param_ptr + n > max_param_stack) {
-            max_param_stack = param_ptr + n;
-            if (max_param_stack > param_size)
-                overflow("parameter stack size", (unsigned) param_size);
-        }
-        for (m = 0; m <= n - 1; m++)
-            param_stack[param_ptr + m] = pstack[m];
-        param_ptr = param_ptr + n;
-    }
-    goto EXIT;
-  RUNAWAY:
-    /*tex
-
-        Report a runaway argument and abort. If |long_state=outer_call|, a
-        runaway argument has already been reported.
-
-    */
-    if (long_state == call_cmd) {
-        runaway();
-        print_err("Paragraph ended before ");
-        sprint_cs(warning_index);
-        tprint(" was complete");
-        help3(
-            "I suspect you've forgotten a `}', causing me to apply this",
-            "control sequence to too much text. How can we recover?",
-            "My plan is to forget the whole thing and hope for the best."
-        );
-        back_error();
-    }
-    pstack[n] = token_link(temp_token_head);
-    align_state = align_state - unbalance;
-    for (m = 0; m <= n; m++)
-        flush_list(pstack[m]);
-  EXIT:
-    scanner_status = save_scanner_status;
-    warning_index = save_warning_index;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/expand.w
@@ -0,0 +1,829 @@
+% expand.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"
+
+@ Only a dozen or so command codes |>max_command| can possibly be returned by
+|get_next|; in increasing order, they are |undefined_cs|, |expand_after|,
+|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|,
+|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and
+|end_template|.{\emergencystretch=40pt\par}
+
+Sometimes, recursive calls to the following |expand| routine may
+cause exhaustion of the run-time calling stack, resulting in
+forced execution stops by the operating system. To diminish the chance
+of this happening, a counter is used to keep track of the recursion
+depth, in conjunction with a constant called |expand_depth|.
+
+Note that this does not catch all possible infinite recursion loops,
+just the ones that exhaust the application calling stack. The
+actual maximum value of |expand_depth| is outside of our control, but
+the initial setting of |100| should be enough to prevent problems.
+@^system dependencies@>
+
+@c
+static int expand_depth_count = 0;
+
+
+@ The |expand| subroutine is used when |cur_cmd>max_command|. It removes a
+``call'' or a conditional or one of the other special operations just
+listed.  It follows that |expand| might invoke itself recursively. In all
+cases, |expand| destroys the current token, but it sets things up so that
+the next |get_next| will deliver the appropriate next token. The value of
+|cur_tok| need not be known when |expand| is called.
+
+Since several of the basic scanning routines communicate via global variables,
+their values are saved as local variables of |expand| so that
+recursive calls don't invalidate them.
+@^recursion@>
+
+@c
+int is_in_csname = 0;
+
+@ @c
+void expand(void)
+{
+    halfword t;                 /* token that is being ``expanded after'' */
+    halfword p;                 /* for list manipulation */
+    halfword cur_ptr;           /* for a local token list pointer */
+    int cv_backup;              /* to save the global quantity |cur_val| */
+    int cvl_backup, radix_backup, co_backup;    /* to save |cur_val_level|, etc. */
+    halfword backup_backup;     /* to save |link(backup_head)| */
+    int save_scanner_status;    /* temporary storage of |scanner_status| */
+    incr(expand_depth_count);
+    if (expand_depth_count >= expand_depth)
+        overflow("expansion depth", (unsigned) expand_depth);
+    cv_backup = cur_val;
+    cvl_backup = cur_val_level;
+    radix_backup = radix;
+    co_backup = cur_order;
+    backup_backup = token_link(backup_head);
+  RESWITCH:
+    if (cur_cmd < call_cmd) {
+        /* Expand a nonmacro */
+        if (tracing_commands_par > 1)
+            show_cur_cmd_chr();
+        switch (cur_cmd) {
+        case top_bot_mark_cmd:
+            /* Insert the appropriate mark text into the scanner */
+            t = cur_chr % marks_code;
+            if (cur_chr >= marks_code)
+                scan_mark_num();
+            else
+                cur_val = 0;
+            switch (t) {
+            case first_mark_code:
+                cur_ptr = first_mark(cur_val);
+                break;
+            case bot_mark_code:
+                cur_ptr = bot_mark(cur_val);
+                break;
+            case split_first_mark_code:
+                cur_ptr = split_first_mark(cur_val);
+                break;
+            case split_bot_mark_code:
+                cur_ptr = split_bot_mark(cur_val);
+                break;
+            default:
+                cur_ptr = top_mark(cur_val);
+                break;
+            }
+            if (cur_ptr != null)
+                begin_token_list(cur_ptr, mark_text);
+            break;
+        case expand_after_cmd:
+            if (cur_chr == 0) {
+                /* Expand the token after the next token */
+                /* It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}. */
+                get_token();
+                t = cur_tok;
+                get_token();
+                if (cur_cmd > max_command_cmd)
+                    expand();
+                else
+                    back_input();
+                cur_tok = t;
+                back_input();
+
+            } else {            /* \\unless */
+                /* Negate a boolean conditional and |goto reswitch| */
+                /* The result of a boolean condition is reversed when the conditional is
+                   preceded by \.{\\unless}. */
+                get_token();
+                if ((cur_cmd == if_test_cmd) && (cur_chr != if_case_code)) {
+                    cur_chr = cur_chr + unless_code;
+                    goto RESWITCH;
+                }
+                print_err("You can't use `\\unless' before `");
+                print_cmd_chr((quarterword) cur_cmd, cur_chr);
+                print_char('\'');
+                help1("Continue, and I'll forget that it ever happened.");
+                back_error();
+            }
+            break;
+        case no_expand_cmd:
+            if (cur_chr == 0) {
+                /* Suppress expansion of the next token */
+                /* The implementation of \.{\\noexpand} is a bit trickier, because it is
+                   necessary to insert a special `|dont_expand|' marker into \TeX's reading
+                   mechanism.  This special marker is processed by |get_next|, but it does
+                   not slow down the inner loop.
+
+                   Since \.{\\outer} macros might arise here, we must also
+                   clear the |scanner_status| temporarily.
+                 */
+
+                save_scanner_status = scanner_status;
+                scanner_status = normal;
+                get_token();
+                scanner_status = save_scanner_status;
+                t = cur_tok;
+                back_input();   /* now |start| and |loc| point to the backed-up token |t| */
+                if (t >= cs_token_flag) {
+                    p = get_avail();
+                    set_token_info(p, cs_token_flag + frozen_dont_expand);
+                    set_token_link(p, iloc);
+                    istart = p;
+                    iloc = p;
+                }
+
+            } else {
+                /* Implement \.{\\primitive} */
+                /*
+                   The \.{\\primitive} handling. If the primitive meaning of the next
+                   token is an expandable command, it suffices to replace the current
+                   token with the primitive one and restart |expand|.
+
+                   Otherwise, the token we just read has to be pushed back, as well
+                   as a token matching the internal form of \.{\\primitive}, that is
+                   sneaked in as an alternate form of |ignore_spaces|.
+
+                   An implementation problem surfaces: There really is no |cur_cs|
+                   attached to the inserted primitive command, so it is safer to set
+                   |cur_cs| to zero.  |cur_tok| has a similar problem. And for the
+                   non-expanded branch, simply pushing back a token that matches the
+                   correct internal command does not work, because that approach would
+                   not survive roundtripping to a temporary file or even a token list.
+
+                   In a next version, it would be smart to create |frozen_| versions of
+                   all the primitives.  Then, this problem would not happen, at the
+                   expense of a few hundred extra control sequences.
+                 */
+                save_scanner_status = scanner_status;
+                scanner_status = normal;
+                get_token();
+                scanner_status = save_scanner_status;
+                cur_cs = prim_lookup(cs_text(cur_cs));
+                if (cur_cs != undefined_primitive) {
+                    t = get_prim_eq_type(cur_cs);
+                    if (t > max_command_cmd) {
+                        cur_cmd = t;
+                        cur_chr = get_prim_equiv(cur_cs);
+                        cur_tok = token_val(cur_cmd, cur_chr);
+                        cur_cs = 0;
+                        goto RESWITCH;
+                    } else {
+                        back_input();   /*  now |loc| and |start| point to a one-item list */
+                        p = get_avail();
+                        set_token_info(p, cs_token_flag + frozen_primitive);
+                        set_token_link(p, iloc);
+                        iloc = p;
+                        istart = p;
+                    }
+                } else if (suppress_primitive_error_par == 0) {
+                    print_err("Missing primitive name");
+                    help2
+                        ("The control sequence marked <to be read again> does not",
+                         "represent any known primitive.");
+                    back_error();
+                }
+
+            }
+            break;
+        case cs_name_cmd:
+            /* Manufacture a control sequence name; */
+            if (cur_chr == 0) {
+                manufacture_csname(0);
+            } else if (cur_chr == 1) {
+                inject_last_tested_cs();
+            } else {
+                manufacture_csname(1);
+            }
+            break;
+        case convert_cmd:
+            conv_toks();        /* this procedure is discussed in Part 27 below */
+            break;
+        case the_cmd:
+            ins_the_toks();     /* this procedure is discussed in Part 27 below */
+            break;
+        case combine_toks_cmd:
+            combine_the_toks(cur_chr);
+            break;
+        case if_test_cmd:
+            conditional();      /* this procedure is discussed in Part 28 below */
+            break;
+        case fi_or_else_cmd:
+            /* Terminate the current conditional and skip to \.{\\fi} */
+            /* The processing of conditionals is complete except for the following
+               code, which is actually part of |expand|. It comes into play when
+               \.{\\or}, \.{\\else}, or \.{\\fi} is scanned. */
+
+            if (tracing_ifs_par > 0)
+                if (tracing_commands_par <= 1)
+                    show_cur_cmd_chr();
+            if (cur_chr > if_limit) {
+                if (if_limit == if_code) {
+                    insert_relax();     /*  condition not yet evaluated */
+                } else {
+                    print_err("Extra ");
+                    print_cmd_chr(fi_or_else_cmd, cur_chr);
+                    help1("I'm ignoring this; it doesn't match any \\if.");
+                    error();
+                }
+            } else {
+                while (cur_chr != fi_code)
+                    pass_text();        /* skip to \.{\\fi} */
+                pop_condition_stack();
+            }
+
+            break;
+        case input_cmd:
+            /* Initiate or terminate input from a file */
+            if (cur_chr == 1)
+                force_eof = true;
+            else if (cur_chr == 2)
+                pseudo_start();
+            else if (cur_chr == 3) {
+                pseudo_start();
+                iname = 19;
+            } else if (name_in_progress)
+                insert_relax();
+            else
+                start_input();
+            break;
+        case variable_cmd:
+            do_variable();
+            break;
+        case feedback_cmd:
+            do_feedback();
+            break;
+        default:
+            /* Complain about an undefined macro */
+            print_err("Undefined control sequence");
+            help5("The control sequence at the end of the top line",
+                  "of your error message was never \\def'ed. If you have",
+                  "misspelled it (e.g., `\\hobx'), type `I' and the correct",
+                  "spelling (e.g., `I\\hbox'). Otherwise just continue,",
+                  "and I'll forget about whatever was undefined.");
+            error();
+            break;
+        }
+    } else if (cur_cmd < end_template_cmd) {
+        macro_call();
+    } else {
+        /* Insert a token containing |frozen_endv| */
+        /* An |end_template| command is effectively changed to an |endv| command
+           by the following code. (The reason for this is discussed below; the
+           |frozen_end_template| at the end of the template has passed the
+           |check_outer_validity| test, so its mission of error detection has been
+           accomplished.)
+         */
+        cur_tok = cs_token_flag + frozen_endv;
+        back_input();
+
+    }
+    cur_val = cv_backup;
+    cur_val_level = cvl_backup;
+    radix = radix_backup;
+    cur_order = co_backup;
+    set_token_link(backup_head, backup_backup);
+    decr(expand_depth_count);
+}
+
+@ @c
+void complain_missing_csname(void)
+{
+    print_err("Missing \\endcsname inserted");
+    help2("The control sequence marked <to be read again> should",
+          "not appear between \\csname and \\endcsname.");
+    back_error();
+}
+
+@ @c
+void manufacture_csname(boolean use)
+{
+    halfword p, q, r;
+    lstring *ss;
+    r = get_avail();
+    p = r;                      /* head of the list of characters */
+    is_in_csname += 1;
+    do {
+        get_x_token();
+        if (cur_cs == 0)
+            store_new_token(cur_tok);
+    } while (cur_cs == 0);
+    if (cur_cmd != end_cs_name_cmd) {
+        /* Complain about missing \.{\\endcsname} */
+        complain_missing_csname();
+    }
+    /* Look up the characters of list |r| in the hash table, and set |cur_cs| */
+    ss = tokenlist_to_lstring(r, true);
+    is_in_csname -= 1;
+    if (use) {
+        if (ss->l > 0) {
+            cur_cs = string_lookup((char *) ss->s, ss->l);
+        } else {
+            cur_cs = null_cs;
+        }
+        last_cs_name = cur_cs ;
+        free_lstring(ss);
+        flush_list(r);
+        if (cur_cs == null_cs) {
+            /* skip */
+        } else if (eq_type(cur_cs) == undefined_cs_cmd) {
+            /* skip */
+        } else {
+            cur_tok = cur_cs + cs_token_flag;
+            back_input();
+        }
+    } else {
+        if (ss->l > 0) {
+            no_new_control_sequence = false;
+            cur_cs = string_lookup((char *) ss->s, ss->l);
+            no_new_control_sequence = true;
+        } else {
+            cur_cs = null_cs;       /* the list is empty */
+        }
+        last_cs_name = cur_cs ;
+        free_lstring(ss);
+        flush_list(r);
+        if (eq_type(cur_cs) == undefined_cs_cmd) {
+            eq_define(cur_cs, relax_cmd, too_big_char);     /* N.B.: The |save_stack| might change */
+        };                          /* the control sequence will now match `\.{\\relax}' */
+        cur_tok = cur_cs + cs_token_flag;
+        back_input();
+    }
+}
+
+void inject_last_tested_cs(void)
+{
+    if (last_cs_name != null_cs) {
+        cur_cs = last_cs_name;
+        cur_tok = last_cs_name + cs_token_flag;
+        back_input();
+    }
+}
+
+@ Sometimes the expansion looks too far ahead, so we want to insert
+a harmless \.{\\relax} into the user's input.
+
+@c
+void insert_relax(void)
+{
+    cur_tok = cs_token_flag + cur_cs;
+    back_input();
+    cur_tok = cs_token_flag + frozen_relax;
+    back_input();
+    token_type = inserted;
+}
+
+
+@ Here is a recursive procedure that is \TeX's usual way to get the
+next token of input. It has been slightly optimized to take account of
+common cases.
+
+@c
+void get_x_token(void)
+{                               /* sets |cur_cmd|, |cur_chr|, |cur_tok|,  and expands macros */
+  RESTART:
+    get_next();
+    if (cur_cmd <= max_command_cmd)
+        goto DONE;
+    if (cur_cmd >= call_cmd) {
+        if (cur_cmd < end_template_cmd) {
+            macro_call();
+        } else {
+            cur_cs = frozen_endv;
+            cur_cmd = endv_cmd;
+            goto DONE;          /* |cur_chr=null_list| */
+        }
+    } else {
+        expand();
+    }
+    goto RESTART;
+  DONE:
+    if (cur_cs == 0)
+        cur_tok = token_val(cur_cmd, cur_chr);
+    else
+        cur_tok = cs_token_flag + cur_cs;
+}
+
+
+@ The |get_x_token| procedure is equivalent to two consecutive
+procedure calls: |get_next; x_token|.
+
+@c
+void x_token(void)
+{                               /* |get_x_token| without the initial |get_next| */
+    while (cur_cmd > max_command_cmd) {
+        expand();
+        get_next();
+   }
+    if (cur_cs == 0)
+        cur_tok = token_val(cur_cmd, cur_chr);
+    else
+        cur_tok = cs_token_flag + cur_cs;
+}
+
+
+@ A control sequence that has been \.{\\def}'ed by the user is expanded by
+\TeX's |macro_call| procedure.
+
+Before we get into the details of |macro_call|, however, let's consider the
+treatment of primitives like \.{\\topmark}, since they are essentially
+macros without parameters. The token lists for such marks are kept in five
+global arrays of pointers; we refer to the individual entries of these
+arrays by symbolic macros |top_mark|, etc. The value of |top_mark(x)|, etc.
+is either |null| or a pointer to the reference count of a token list.
+
+The variable |biggest_used_mark| is an aid to try and keep the code
+somehwat efficient without too much extra work: it registers the
+highest mark class ever instantiated by the user, so the loops
+in |fire_up| and |vsplit| do not have to traverse the full range
+|0..biggest_mark|.
+
+@c
+halfword top_marks_array[(biggest_mark + 1)];
+halfword first_marks_array[(biggest_mark + 1)];
+halfword bot_marks_array[(biggest_mark + 1)];
+halfword split_first_marks_array[(biggest_mark + 1)];
+halfword split_bot_marks_array[(biggest_mark + 1)];
+halfword biggest_used_mark;
+
+@ @c
+void initialize_marks(void)
+{
+    int i;
+    biggest_used_mark = 0;
+    for (i = 0; i <= biggest_mark; i++) {
+        top_mark(i) = null;
+        first_mark(i) = null;
+        bot_mark(i) = null;
+        split_first_mark(i) = null;
+        split_bot_mark(i) = null;
+    }
+}
+
+
+@ Now let's consider |macro_call| itself, which is invoked when \TeX\ is
+scanning a control sequence whose |cur_cmd| is either |call|, |long_call|,
+|outer_call|, or |long_outer_call|.  The control sequence definition
+appears in the token list whose reference count is in location |cur_chr|
+of |mem|.
+
+The global variable |long_state| will be set to |call| or to |long_call|,
+depending on whether or not the control sequence disallows \.{\\par}
+in its parameters. The |get_next| routine will set |long_state| to
+|outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer}
+control sequence occurs in the midst of an argument.
+
+@c
+int long_state;                 /* governs the acceptance of \.{\\par} */
+
+@ The parameters, if any, must be scanned before the macro is expanded.
+Parameters are token lists without reference counts. They are placed on
+an auxiliary stack called |pstack| while they are being scanned, since
+the |param_stack| may be losing entries during the matching process.
+(Note that |param_stack| can't be gaining entries, since |macro_call| is
+the only routine that puts anything onto |param_stack|, and it
+is not recursive.)
+
+@c
+halfword pstack[9];             /* arguments supplied to a macro */
+
+
+@ After parameter scanning is complete, the parameters are moved to the
+|param_stack|. Then the macro body is fed to the scanner; in other words,
+|macro_call| places the defined text of the control sequence at the
+top of\/ \TeX's input stack, so that |get_next| will proceed to read it
+next.
+
+The global variable |cur_cs| contains the |eqtb| address of the control sequence
+being expanded, when |macro_call| begins. If this control sequence has not been
+declared \.{\\long}, i.e., if its command code in the |eq_type| field is
+not |long_call| or |long_outer_call|, its parameters are not allowed to contain
+the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro
+call is aborted, and the \.{\\par} will be rescanned.
+
+@c
+void macro_call(void)
+{                               /* invokes a user-defined control sequence */
+    halfword r;                 /* current node in the macro's token list */
+    halfword p = null;          /* current node in parameter token list being built */
+    halfword q;                 /* new node being put into the token list */
+    halfword s;                 /* backup pointer for parameter matching */
+    halfword t;                 /* cycle pointer for backup recovery */
+    halfword u, v;              /* auxiliary pointers for backup recovery */
+    halfword rbrace_ptr = null; /* one step before the last |right_brace| token */
+    int n = 0;                  /* the number of parameters scanned */
+    halfword unbalance;         /* unmatched left braces in current parameter */
+    halfword m = 0;             /* the number of tokens or groups (usually) */
+    halfword ref_count;         /* start of the token list */
+    int save_scanner_status = scanner_status;   /* |scanner_status| upon entry */
+    halfword save_warning_index = warning_index;        /* |warning_index| upon entry */
+    int match_chr = 0;          /* character used in parameter */
+    warning_index = cur_cs;
+    ref_count = cur_chr;
+    r = token_link(ref_count);
+    if (tracing_macros_par > 0) {
+        /* Show the text of the macro being expanded */
+        begin_diagnostic();
+        print_ln();
+        print_cs(warning_index);
+        token_show(ref_count);
+        end_diagnostic(false);
+    }
+    if (token_info(r) == protected_token)
+        r = token_link(r);
+    if (token_info(r) != end_match_token) {
+        /* Scan the parameters and make |link(r)| point to the macro body; but
+           |return| if an illegal \.{\\par} is detected */
+        /* At this point, the reader will find it advisable to review the explanation
+           of token list format that was presented earlier, since many aspects of that
+           format are of importance chiefly in the |macro_call| routine.
+
+           The token list might begin with a string of compulsory tokens before the
+           first |match| or |end_match|. In that case the macro name is supposed to be
+           followed by those tokens; the following program will set |s=null| to
+           represent this restriction. Otherwise |s| will be set to the first token of
+           a string that will delimit the next parameter.
+         */
+
+        scanner_status = matching;
+        unbalance = 0;
+        long_state = eq_type(cur_cs);
+        if (long_state >= outer_call_cmd)
+            long_state = long_state - 2;
+        do {
+            set_token_link(temp_token_head, null);
+            if ((token_info(r) >= end_match_token)
+                || (token_info(r) < match_token)) {
+                s = null;
+            } else {
+                match_chr = token_info(r) - match_token;
+                s = token_link(r);
+                r = s;
+                p = temp_token_head;
+                m = 0;
+            }
+            /* Scan a parameter until its delimiter string has been found; or, if |s=null|,
+               simply scan the delimiter string; */
+
+            /* If |info(r)| is a |match| or |end_match| command, it cannot be equal to
+               any token found by |get_token|. Therefore an undelimited parameter---i.e.,
+               a |match| that is immediately followed by |match| or |end_match|---will
+               always fail the test `|cur_tok=info(r)|' in the following algorithm. */
+          CONTINUE:
+            get_token();        /* set |cur_tok| to the next token of input */
+            if (cur_tok == token_info(r)) {
+                /* Advance |r|; |goto found| if the parameter delimiter has been
+                   fully matched, otherwise |goto continue| */
+                /* A slightly subtle point arises here: When the parameter delimiter ends
+                   with `\.{\#\{}', the token list will have a left brace both before and
+                   after the |end_match|\kern-.4pt. Only one of these should affect the
+                   |align_state|, but both will be scanned, so we must make a correction.
+                 */
+                r = token_link(r);
+                if ((token_info(r) >= match_token)
+                    && (token_info(r) <= end_match_token)) {
+                    if (cur_tok < left_brace_limit)
+                        decr(align_state);
+                    goto FOUND;
+                } else {
+                    goto CONTINUE;
+                }
+
+            }
+            /* Contribute the recently matched tokens to the current parameter, and
+               |goto continue| if a partial match is still in effect; but abort if |s=null| */
+
+            /* When the following code becomes active, we have matched tokens from |s| to
+               the predecessor of |r|, and we have found that |cur_tok<>info(r)|. An
+               interesting situation now presents itself: If the parameter is to be
+               delimited by a string such as `\.{ab}', and if we have scanned `\.{aa}',
+               we want to contribute one `\.a' to the current parameter and resume
+               looking for a `\.b'. The program must account for such partial matches and
+               for others that can be quite complex.  But most of the time we have |s=r|
+               and nothing needs to be done.
+
+               Incidentally, it is possible for \.{\\par} tokens to sneak in to certain
+               parameters of non-\.{\\long} macros. For example, consider a case like
+               `\.{\\def\\a\#1\\par!\{...\}}' where the first \.{\\par} is not followed
+               by an exclamation point. In such situations it does not seem appropriate
+               to prohibit the \.{\\par}, so \TeX\ keeps quiet about this bending of
+               the rules. */
+
+            if (s != r) {
+                if (s == null) {
+                    /* Report an improper use of the macro and abort */
+                    print_err("Use of ");
+                    sprint_cs(warning_index);
+                    tprint(" doesn't match its definition");
+                    help4
+                        ("If you say, e.g., `\\def\\a1{...}', then you must always",
+                         "put `1' after `\\a', since control sequence names are",
+                         "made up of letters only. The macro here has not been",
+                         "followed by the required stuff, so I'm ignoring it.");
+                    error();
+                    goto EXIT;
+
+                } else {
+                    t = s;
+                    do {
+                        store_new_token(token_info(t));
+                        incr(m);
+                        u = token_link(t);
+                        v = s;
+                        while (1) {
+                            if (u == r) {
+                                if (cur_tok != token_info(v)) {
+                                    goto DONE;
+                                } else {
+                                    r = token_link(v);
+                                    goto CONTINUE;
+                                }
+                            }
+                            if (token_info(u) != token_info(v))
+                                goto DONE;
+                            u = token_link(u);
+                            v = token_link(v);
+                        }
+                      DONE:
+                        t = token_link(t);
+                    } while (t != r);
+                    r = s;      /* at this point, no tokens are recently matched */
+                }
+            }
+
+            if (cur_tok == par_token)
+                if (long_state != long_call_cmd)
+                    if (!suppress_long_error_par) {
+                        goto RUNAWAY;
+                    }
+            if (cur_tok < right_brace_limit) {
+                if (cur_tok < left_brace_limit) {
+                    /* Contribute an entire group to the current parameter */
+                    unbalance = 1;
+                    while (1) {
+                        fast_store_new_token(cur_tok);
+                        get_token();
+                        if (cur_tok == par_token) {
+                            if (long_state != long_call_cmd) {
+                                if (!suppress_long_error_par) {
+                                    goto RUNAWAY;
+
+                                }
+                            }
+                        }
+                        if (cur_tok < right_brace_limit) {
+                            if (cur_tok < left_brace_limit) {
+                                incr(unbalance);
+                            } else {
+                                decr(unbalance);
+                                if (unbalance == 0)
+                                    break;
+                            }
+                        }
+                    }
+                    rbrace_ptr = p;
+                    store_new_token(cur_tok);
+
+                } else {
+                    /* Report an extra right brace and |goto continue| */
+                    back_input();
+                    print_err("Argument of ");
+                    sprint_cs(warning_index);
+                    tprint(" has an extra }");
+                    help6
+                        ("I've run across a `}' that doesn't seem to match anything.",
+                         "For example, `\\def\\a#1{...}' and `\\a}' would produce",
+                         "this error. If you simply proceed now, the `\\par' that",
+                         "I've just inserted will cause me to report a runaway",
+                         "argument that might be the root of the problem. But if",
+                         "your `}' was spurious, just type `2' and it will go away.");
+                    incr(align_state);
+                    long_state = call_cmd;
+                    cur_tok = par_token;
+                    ins_error();
+                    goto CONTINUE;
+                    /* a white lie; the \.{\\par} won't always trigger a runaway */
+                }
+            } else {
+                /* Store the current token, but |goto continue| if it is
+                   a blank space that would become an undelimited parameter */
+                if (cur_tok == space_token)
+                    if (token_info(r) <= end_match_token)
+                        if (token_info(r) >= match_token)
+                            goto CONTINUE;
+                store_new_token(cur_tok);
+
+            }
+            incr(m);
+            if (token_info(r) > end_match_token)
+                goto CONTINUE;
+            if (token_info(r) < match_token)
+                goto CONTINUE;
+          FOUND:
+            if (s != null) {
+                /* Tidy up the parameter just scanned, and tuck it away */
+                /* If the parameter consists of a single group enclosed in braces, we must
+                   strip off the enclosing braces. That's why |rbrace_ptr| was introduced. */
+                if ((m == 1) && (token_info(p) < right_brace_limit)
+                    && (p != temp_token_head)) {
+                    set_token_link(rbrace_ptr, null);
+                    free_avail(p);
+                    p = token_link(temp_token_head);
+                    pstack[n] = token_link(p);
+                    free_avail(p);
+                } else {
+                    pstack[n] = token_link(temp_token_head);
+                }
+                incr(n);
+                if (tracing_macros_par > 0) {
+                    begin_diagnostic();
+                    print_nl(match_chr);
+                    print_int(n);
+                    tprint("<-");
+                    show_token_list(pstack[n - 1], null, 1000);
+                    end_diagnostic(false);
+                }
+
+            }
+
+            /* now |info(r)| is a token whose command code is either |match| or |end_match| */
+        } while (token_info(r) != end_match_token);
+
+    }
+    /* Feed the macro body and its parameters to the scanner */
+    /* Before we put a new token list on the input stack, it is wise to clean off
+       all token lists that have recently been depleted. Then a user macro that ends
+       with a call to itself will not require unbounded stack space. */
+    while ((istate == token_list) && (iloc == null) && (token_type != v_template)) {
+        /* conserve stack space */
+        end_token_list();
+    }
+    begin_token_list(ref_count, macro);
+    iname = warning_index;
+    iloc = token_link(r);
+    if (n > 0) {
+        if (param_ptr + n > max_param_stack) {
+            max_param_stack = param_ptr + n;
+            if (max_param_stack > param_size)
+                overflow("parameter stack size", (unsigned) param_size);
+        }
+        for (m = 0; m <= n - 1; m++)
+            param_stack[param_ptr + m] = pstack[m];
+        param_ptr = param_ptr + n;
+    }
+    goto EXIT;
+  RUNAWAY:
+    /* Report a runaway argument and abort */
+    /* If |long_state=outer_call|, a runaway argument has already been reported. */
+    if (long_state == call_cmd) {
+        runaway();
+        print_err("Paragraph ended before ");
+        sprint_cs(warning_index);
+        tprint(" was complete");
+        help3("I suspect you've forgotten a `}', causing me to apply this",
+              "control sequence to too much text. How can we recover?",
+              "My plan is to forget the whole thing and hope for the best.");
+        back_error();
+    }
+    pstack[n] = token_link(temp_token_head);
+    align_state = align_state - unbalance;
+    for (m = 0; m <= n; m++)
+        flush_list(pstack[m]);
+
+  EXIT:
+    scanner_status = save_scanner_status;
+    warning_index = save_warning_index;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/extensions.c
+++ /dev/null
@@ -1,1329 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#define mode     mode_par
-#define tail     tail_par
-#define head     head_par
-#define dir_save dirs_par
-
-/*tex
-
-    The program above includes a bunch of ``hooks'' that allow further
-    capabilities to be added without upsetting \TeX's basic structure. Most of
-    these hooks are concerned with ``whatsit'' nodes, which are intended to be
-    used for special purposes; whenever a new extension to \TeX\ involves a new
-    kind of whatsit node, a corresponding change needs to be made to the routines
-    below that deal with such nodes, but it will usually be unnecessary to make
-    many changes to the other parts of this program.
-
-    In order to demonstrate how extensions can be made, we shall treat
-    `\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}', and
-    `\.{\\special}' as if they were extensions. These commands are actually
-    primitives of \TeX, and they should appear in all implementations of the
-    system; but let's try to imagine that they aren't. Then the program below
-    illustrates how a person could add them.
-
-    Sometimes, of course, an extension will require changes to \TeX\ itself; no
-    system of hooks could be complete enough for all conceivable extensions. The
-    features associated with `\.{\\write}' are almost all confined to the
-    following paragraphs, but there are small parts of the |print_ln| and
-    |print_char| procedures that were introduced specifically to \.{\\write}
-    characters. Furthermore one of the token lists recognized by the scanner is a
-    |write_text|; and there are a few other miscellaneous places where we have
-    already provided for some aspect of \.{\\write}. The goal of a \TeX\ extender
-    should be to minimize alterations to the standard parts of the program, and
-    to avoid them completely if possible. He or she should also be quite sure
-    that there's no easy way to accomplish the desired goals with the standard
-    features that \TeX\ already has. ``Think thrice before extending,'' because
-    that may save a lot of work, and it will also keep incompatible extensions of
-    \TeX\ from proliferating.
-
-    First let's consider the format of whatsit nodes that are used to represent
-    the data associated with \.{\\write} and its relatives. Recall that a whatsit
-    has |type=whatsit_node|, and the |subtype| is supposed to distinguish
-    different kinds of whatsits. Each node occupies two or more words; the exact
-    number is immaterial, as long as it is readily determined from the |subtype|
-    or other data.
-
-    We shall introduce five |subtype| values here, corresponding to the control
-    sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, and \.{\\special}. The
-    second word of I/O whatsits has a |write_stream| field that identifies the
-    write-stream number (0 to 15, or 16 for out-of-range and positive, or 17 for
-    out-of-range and negative). In the case of \.{\\write} and \.{\\special},
-    there is also a field that points to the reference count of a token list that
-    should be sent. In the case of \.{\\openout}, we need three words and three
-    auxiliary subfields to hold the string numbers for name, area, and extension.
-
-    Extensions might introduce new command codes; but it's best to use
-    |extension| with a modifier, whenever possible, so that |main_control| stays
-    the same.
-
-    The sixteen possible \.{\\write} streams are represented by the |write_file|
-    array. The |j|th file is open if and only if |write_open[j]=true|. The last
-    two streams are special; |write_open[16]| represents a stream number greater
-    than 15, while |write_open[17]| represents a negative stream number, and both
-    of these variables are always |false|.
-
-*/
-
-alpha_file write_file[last_file_selector+1];
-halfword write_file_mode[last_file_selector+1];
-halfword write_file_translation[last_file_selector+1];
-boolean write_open[last_file_selector+1];
-
-scaled neg_wd;
-scaled pos_wd;
-scaled neg_ht;
-
-/*tex
-
-    The variable |write_loc| just introduced is used to provide an appropriate
-    error message in case of ``runaway'' write texts.
-
-*/
-
-/*tex The |eqtb| address of \.{\\write}: */
-
-halfword write_loc;
-
-/*tex
-
-    When an |extension| command occurs in |main_control|, in any mode, the
-    |do_extension| routine is called.
-
-*/
-
-int last_saved_image_index ;
-int last_saved_image_pages ;
-int last_saved_box_index ;
-scaledpos last_position = { 0, 0 };
-
-static void do_extension_dvi(int immediate)
-{
-    if (scan_keyword("literal")) {
-        new_whatsit(special_node);
-        write_stream(tail) = null;
-        scan_toks(false, true);
-        write_tokens(tail) = def_ref;
-    } else {
-        tex_error("unexpected use of \\dviextension",null);
-    }
-}
-
-static void do_extension_pdf(int immediate)
-{
-    int i;
-    if (scan_keyword("literal")) {
-        new_whatsit(pdf_literal_node);
-        if (scan_keyword("direct"))
-            set_pdf_literal_mode(tail, direct_always);
-        else if (scan_keyword("page"))
-            set_pdf_literal_mode(tail, direct_page);
-        else if (scan_keyword("text"))
-            set_pdf_literal_mode(tail, direct_text);
-        else if (scan_keyword("raw"))
-            set_pdf_literal_mode(tail, direct_raw);
-        else if (scan_keyword("origin"))
-            set_pdf_literal_mode(tail, set_origin);
-        else
-            set_pdf_literal_mode(tail, set_origin);
-        scan_toks(false, true);
-        set_pdf_literal_type(tail, normal);
-        set_pdf_literal_data(tail, def_ref);
-    } else if (scan_keyword("dest")) {
-        scan_pdfdest(static_pdf);
-    } else if (scan_keyword("annot")) {
-        scan_annot(static_pdf);
-    } else if (scan_keyword("save")) {
-        new_whatsit(pdf_save_node);
-    } else if (scan_keyword("restore")) {
-        new_whatsit(pdf_restore_node);
-    } else if (scan_keyword("setmatrix")) {
-        new_whatsit(pdf_setmatrix_node);
-        scan_toks(false, true);
-        set_pdf_setmatrix_data(tail, def_ref);
-    } else if (scan_keyword("obj")) {
-        scan_obj(static_pdf);
-        if (immediate) {
-            if (obj_data_ptr(static_pdf, pdf_last_obj) == 0) {
-                /*tex This object has not been initialized yet. */
-                normal_error("pdf backend","\\pdfextension obj 'reserveobjnum' cannot be used with \\immediate");
-            }
-            pdf_write_obj(static_pdf, pdf_last_obj);
-        }
-    } else if (scan_keyword("refobj")) {
-        scan_refobj(static_pdf);
-    } else if (scan_keyword("colorstack")) {
-        scan_int();
-        if (cur_val >= colorstackused()) {
-            print_err("Unknown color stack number ");
-            print_int(cur_val);
-            help3(
-                "Allocate and initialize a color stack with \\pdfextension colorstackinit.",
-                "I'll use default color stack 0 here.",
-                "Proceed, with fingers crossed."
-            );
-            error();
-            cur_val = 0;
-        }
-        if (cur_val < 0) {
-            print_err("Invalid negative color stack number");
-            help2(
-                "I'll use default color stack 0 here.",
-                "Proceed, with fingers crossed."
-            );
-            error();
-            cur_val = 0;
-        }
-        if (scan_keyword("set"))
-            i = colorstack_set;
-        else if (scan_keyword("push"))
-            i = colorstack_push;
-        else if (scan_keyword("pop"))
-            i = colorstack_pop;
-        else if (scan_keyword("current"))
-            i = colorstack_current;
-        else
-            i = -1;
-        if (i >= 0) {
-            new_whatsit(pdf_colorstack_node);
-            set_pdf_colorstack_stack(tail, cur_val);
-            set_pdf_colorstack_cmd(tail, i);
-            set_pdf_colorstack_data(tail, null);
-            if (i <= colorstack_data) {
-                scan_toks(false, true);
-                set_pdf_colorstack_data(tail, def_ref);
-            }
-        } else {
-            print_err("Color stack action is missing");
-            help3(
-                "The expected actions for \\pdfextension colorstack:",
-                "    set, push, pop, current",
-                "I'll ignore the color stack command."
-            );
-            error();
-        }
-    } else if (scan_keyword("startlink")) {
-        scan_startlink(static_pdf);
-    } else if (scan_keyword("endlink")) {
-        if (abs(mode) == vmode)
-            normal_error("pdf backend", "\\pdfextension endlink cannot be used in vertical mode");
-        new_whatsit(pdf_end_link_node);
-    } else if (scan_keyword("startthread")) {
-        new_annot_whatsit(pdf_start_thread_node);
-        scan_thread_id();
-    } else if (scan_keyword("endthread")) {
-        new_whatsit(pdf_end_thread_node);
-    } else if (scan_keyword("thread")) {
-        new_annot_whatsit(pdf_thread_node);
-        scan_thread_id();
-    } else if (scan_keyword("outline")) {
-        scan_pdfoutline(static_pdf);
-    } else if (scan_keyword("glyphtounicode")) {
-        glyph_to_unicode();
-    } else if (scan_keyword("catalog")) {
-        scan_pdfcatalog(static_pdf);
-    } else if (scan_keyword("fontattr")) {
-        /*tex
-
-            The font attributes are simply initialized to zero now, this is
-            easier to deal with from C than an empty \TeX{} string, and surely
-            nobody will want to set font attr to a string containing a single
-            zero, as that would be nonsensical in the PDF output.
-
-        */
-        scan_font_ident();
-        i = cur_val;
-        if (i == null_font)
-            normal_error("pdf backend", "invalid font identifier");
-        scan_toks(false, true);
-        set_pdf_font_attr(i, tokens_to_string(def_ref));
-        if (str_length(pdf_font_attr(i)) == 0) {
-            /*tex From |tokens_to_string|. */
-            flush_str((str_ptr - 1));
-            set_pdf_font_attr(i, 0);
-        }
-    } else if (scan_keyword("mapfile")) {
-        scan_toks(false, true);
-        pdfmapfile(def_ref);
-        delete_token_ref(def_ref);
-    } else if (scan_keyword("mapline")) {
-        scan_toks(false, true);
-        pdfmapline(def_ref);
-        delete_token_ref(def_ref);
-    } else if (scan_keyword("includechars")) {
-        pdf_include_chars(static_pdf);
-    } else if (scan_keyword("info")) {
-        scan_toks(false, true);
-        pdf_info_toks = concat_tokens(pdf_info_toks, def_ref);
-    } else if (scan_keyword("names")) {
-        scan_toks(false, true);
-        pdf_names_toks = concat_tokens(pdf_names_toks, def_ref);
-    } else if (scan_keyword("trailer")) {
-        scan_toks(false, true);
-        pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref);
-    } else {
-        tex_error("unexpected use of \\pdfextension",null);
-    }
-}
-
-static void do_resource_dvi(int immediate, int code)
-{
-    /*tex Nothing is done here. */
-}
-
-static void do_resource_pdf(int immediate, int code)
-{
-    switch (code) {
-        case use_box_resource_code:
-            scan_pdfrefxform(static_pdf);
-            break;
-        case use_image_resource_code:
-            scan_pdfrefximage(static_pdf);
-            break;
-        case save_box_resource_code:
-            scan_pdfxform(static_pdf);
-            if (immediate) {
-                pdf_cur_form = last_saved_box_index;
-                ship_out(static_pdf, obj_xform_box(static_pdf, last_saved_box_index), SHIPPING_FORM);
-            }
-            break;
-        case save_image_resource_code:
-            scan_pdfximage(static_pdf);
-            if (immediate) {
-                pdf_write_image(static_pdf, last_saved_image_index);
-            }
-            break;
-    }
-}
-
-/*tex
-
-    Ad immediate:
-
-    To write a token list, we must run it through \TeX's scanner, expanding
-    macros and \.{\\the} and \.{\\number}, etc. This might cause runaways, if a
-    delimited macro parameter isn't matched, and runaways would be extremely
-    confusing since we are calling on \TeX's scanner in the middle of a
-    \.{\\shipout} command. Therefore we will put a dummy control sequence as a
-    ``stopper,'' right after the token list. This control sequence is
-    artificially defined to be \.{\\outer}.
-
-    The presence of `\.{\\immediate}' causes the |do_extension| procedure to
-    descend to one level of recursion. Nothing happens unless \.{\\immediate} is
-    followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'.
-
-*/
-
-/*tex
-
-    The extensions are backend related. The next subroutine uses |cur_chr| to
-    decide what sort of whatsit is involved, and also inserts a |write_stream|
-    number.
-
-*/
-
-static void new_write_whatsit(int w, int check)
-{
-    new_whatsit(cur_chr);
-    if (check) {
-        /*tex So we check with open and close. */
-        scan_limited_int(last_file_selector,NULL);
-    } else {
-        /*tex But we're tolerant with the rest. */
-        scan_int();
-        if (cur_val < 0)
-            cur_val = term_only;
-        else if (cur_val > last_file_selector) {
-            cur_val = term_and_log;
-        }
-    }
-    write_stream(tail) = cur_val;
-}
-
-void do_extension(int immediate)
-{
-    /*tex An all-purpose pointer. */
-    halfword p;
-    if (cur_cmd == extension_cmd) {
-        /*tex These have their own range starting at 0. */
-        switch (cur_chr) {
-            case open_code:
-                p = tail;
-                new_write_whatsit(open_node_size,1);
-                scan_optional_equals();
-                scan_file_name();
-                open_name(tail) = cur_name;
-                open_area(tail) = cur_area;
-                open_ext(tail) = cur_ext;
-                if (immediate) {
-                    wrapup_leader(tail);
-                    flush_node_list(tail);
-                    tail = p;
-                    vlink(p) = null;
-                }
-                break;
-            case write_code:
-                /*tex
-
-                    When `\.{\\write 12\{...\}}' appears, we scan the token list
-                    `\.{\{...\}}' without expanding its macros; the macros will
-                    be expanded later when this token list is rescanned.
-
-                */
-                p = tail;
-                new_write_whatsit(write_node_size,0);
-                cur_cs = write_stream(tail);
-                scan_toks(false, false);
-                write_tokens(tail) = def_ref;
-                if (immediate) {
-                    wrapup_leader(tail);
-                    flush_node_list(tail);
-                    tail = p;
-                    vlink(p) = null;
-                }
-                break;
-            case close_code:
-                p = tail;
-                new_write_whatsit(close_node_size,1);
-                write_tokens(tail) = null;
-                if (immediate) {
-                    wrapup_leader(tail);
-                    flush_node_list(tail);
-                    tail = p;
-                    vlink(p) = null;
-                }
-                break;
-            case special_code:
-                /*tex
-
-                    When `\.{\\special\{...\}}' appears, we expand the macros in
-                    the token list as in \.{\\xdef} and \.{\\mark}.
-
-                */
-                new_whatsit(special_node);
-                write_stream(tail) = null;
-                p = scan_toks(false, true);
-                write_tokens(tail) = def_ref;
-                break;
-            case immediate_code:
-                get_x_token();
-                do_extension(1);
-                break;
-            case end_local_code:
-                if (tracing_nesting_par > 2) {
-                    local_control_message("leaving token scanner");
-                }
-                end_local_control();
-                break;
-            case use_box_resource_code:
-            case use_image_resource_code:
-            case save_box_resource_code:
-            case save_image_resource_code:
-                switch (get_o_mode()) {
-                    case OMODE_DVI:
-                        do_resource_dvi(immediate,cur_chr);
-                        break;
-                    case OMODE_PDF:
-                        do_resource_pdf(immediate,cur_chr);
-                        break;
-                    default:
-                        break;
-                }
-                break;
-            /*tex Backend extensions have their own range starting at 32. */
-            case dvi_extension_code:
-                if (get_o_mode() == OMODE_DVI)
-                    do_extension_dvi(immediate);
-                break;
-            case pdf_extension_code:
-                if (get_o_mode() == OMODE_PDF)
-                    do_extension_pdf(immediate);
-                break;
-            /*tex Done. */
-            default:
-                if (immediate) {
-                    back_input();
-                } else {
-                    confusion("invalid extension");
-                }
-                break;
-        }
-    } else {
-        /*tex No extension command, quite certainly following |\immediate|. */
-        back_input();
-    }
-}
-
-/*tex
-
-    Here is a subroutine that creates a whatsit node having a given |subtype| and
-    a given number of words. It initializes only the first word of the whatsit,
-    and appends it to the current list.
-
-*/
-
-void new_whatsit(int s)
-{
-    halfword p = new_node(whatsit_node, s);
-    couple_nodes(tail, p);
-    tail = p;
-}
-
-/*tex
-
-    The final line of this routine is slightly subtle; at least, the author
-    didn't think about it until getting burnt! There is a used-up token list on
-    the stack, namely the one that contained |end_write_token|. (We insert this
-    artificial `\.{\\endwrite}' to prevent runaways, as explained above.) If it
-    were not removed, and if there were numerous writes on a single page, the
-    stack would overflow.
-
-*/
-
-void expand_macros_in_tokenlist(halfword p)
-{
-    int old_mode;
-    pointer q = get_avail();
-    pointer r = get_avail();
-    token_info(q) = right_brace_token + '}';
-    token_link(q) = r;
-    token_info(r) = end_write_token;
-    begin_token_list(q, inserted);
-    begin_token_list(write_tokens(p), write_text);
-    q = get_avail();
-    token_info(q) = left_brace_token + '{';
-    begin_token_list(q, inserted);
-    /*tex
-
-        Now we're ready to scan `\.\{$\langle\,$token list$\,\rangle$\.{\}
-        \\endwrite}'.
-
-    */
-    old_mode = mode;
-    mode = 0;
-    /*tex
-
-        Disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip},
-        \.{\\prevgraf}.
-
-    */
-    cur_cs = write_loc;
-    /*tex Expand macros, etc. */
-    q = scan_toks(false, true);
-    get_token();
-    if (cur_tok != end_write_token) {
-        /*tex Recover from an unbalanced write command */
-        const char *hlp[] = {
-            "On this page there's a \\write with fewer real {'s than }'s.",
-            "I can't handle that very well; good luck.",
-            NULL
-        };
-        tex_error("Unbalanced write command", hlp);
-        do {
-            get_token();
-        } while (cur_tok != end_write_token);
-    }
-    mode = old_mode;
-    /*tex Conserve stack space. */
-    end_token_list();
-}
-
-void write_out(halfword p)
-{
-    /*tex holds print |selector| */
-    int old_setting;
-    /*tex write stream number */
-    int j;
-    /*tex line to be written, as a C string */
-    char *s, *ss;
-    int callback_id;
-    int lua_retval;
-    expand_macros_in_tokenlist(p);
-    old_setting = selector;
-    j = write_stream(p);
-    if (file_can_be_written(j)) {
-        selector = j;
-    } else if ((j == term_only) && (selector == term_and_log)) {
-        /*tex write to the terminal if file isn't open */
-        selector = log_only;
-        tprint_nl("");
-    } else {
-        tprint_nl("");
-    }
-    s = tokenlist_to_cstring(def_ref, false, NULL);
-    if (selector < no_print) {
-        /*tex selector is a file */
-        callback_id = callback_defined(process_output_buffer_callback);
-        if (callback_id > 0) {
-            /*tex fix up the output buffer using callbacks */
-            lua_retval = run_callback(callback_id, "S->S", s, &ss);
-            if ((lua_retval == true) && (ss != NULL)) {
-                xfree(s);
-                s = ss;
-            }
-        }
-    }
-    tprint(s);
-    xfree(s);
-    print_ln();
-    flush_list(def_ref);
-    selector = old_setting;
-}
-
-void finalize_write_files(void) {
-    int k;
-    for (k = 0; k <= last_file_selector; k++) {
-        if (write_open[k]) {
-            lua_a_close_out(write_file[k]);
-        }
-    }
-}
-
-void initialize_write_files(void) {
-    int k;
-    for (k = 0; k <= last_file_selector; k++) {
-        write_open[k] = false;
-    }
-}
-
-void close_write_file(int id) {
-    if (write_open[id]) {
-        lua_a_close_out(write_file[id]);
-        write_open[id] = false;
-    }
-}
-
-boolean open_write_file(int id, char *fn) {
-    if (lua_a_open_out(&(write_file[id]), fn, (id + 1))) {
-        write_open[id] = true;
-        return true;
-    } else {
-        return false;
-    }
-}
-
-/*tex
-
-    To implement primitives as \.{\\pdfextension info}, \.{\\pdfextension
-    catalog} or \.{\\pdfextension names} we need to concatenate tokens lists.
-
-*/
-
-halfword concat_tokens(halfword q, halfword r)
-{
-    /*tex concat |q| and |r| and returns the result tokens list */
-    halfword p;
-    if (q == null)
-        return r;
-    p = q;
-    while (token_link(p) != null)
-        p = token_link(p);
-    set_token_link(p, token_link(r));
-    free_avail(r);
-    return q;
-}
-
-/*tex
-
-    The \eTeX\ features available in extended mode are grouped into two
-    categories: (1)~Some of them are permanently enabled and have no semantic
-    effect as long as none of the additional primitives are executed. (2)~The
-    remaining \eTeX\ features are optional and can be individually enabled and
-    disabled. For each optional feature there is an \eTeX\ state variable named
-    \.{\\...state}; the feature is enabled, resp.\ disabled by assigning a
-    positive, resp.\ non-positive value to that integer.
-
-    In order to handle \.{\\everyeof} we need an array |eof_seen| of boolean
-    variables.
-
-*/
-
-boolean *eof_seen;
-
-/*tex
-
-    The |print_group| procedure prints the current level of grouping and the name
-    corresponding to |cur_group|.
-
-*/
-
-void print_group(boolean e)
-{
-    switch (cur_group) {
-        case bottom_level:
-            tprint("bottom level");
-            return;
-            break;
-        case simple_group:
-        case semi_simple_group:
-            if (cur_group == semi_simple_group)
-                tprint("semi ");
-            tprint("simple");
-            break;;
-        case hbox_group:
-        case adjusted_hbox_group:
-            if (cur_group == adjusted_hbox_group)
-                tprint("adjusted ");
-            tprint("hbox");
-            break;
-        case vbox_group:
-            tprint("vbox");
-            break;
-        case vtop_group:
-            tprint("vtop");
-            break;
-        case align_group:
-        case no_align_group:
-            if (cur_group == no_align_group)
-                tprint("no ");
-            tprint("align");
-            break;
-        case output_group:
-            tprint("output");
-            break;
-        case disc_group:
-            tprint("disc");
-            break;
-        case insert_group:
-            tprint("insert");
-            break;
-        case vcenter_group:
-            tprint("vcenter");
-            break;
-        case math_group:
-        case math_choice_group:
-        case math_shift_group:
-        case math_left_group:
-            tprint("math");
-            if (cur_group == math_choice_group)
-                tprint(" choice");
-            else if (cur_group == math_shift_group)
-                tprint(" shift");
-            else if (cur_group == math_left_group)
-                tprint(" left");
-            break;
-    }
-    tprint(" group (level ");
-    print_int(cur_level);
-    print_char(')');
-    if (saved_value(-1) != 0) {
-        /*tex |saved_line| */
-        if (e)
-            tprint(" entered at line ");
-        else
-            tprint(" at line ");
-        print_int(saved_value(-1));
-    }
-}
-
-/*tex
-
-    The |group_trace| procedure is called when a new level of grouping begins
-    (|e=false|) or ends (|e=true|) with |saved_value(-1)| containing the line
-    number.
-
-*/
-
-void group_trace(boolean e)
-{
-    begin_diagnostic();
-    print_char('{');
-    if (e)
-        tprint("leaving ");
-    else
-        tprint("entering ");
-    print_group(e);
-    print_char('}');
-    end_diagnostic(false);
-}
-
-/*tex
-
-    A group entered (or a conditional started) in one file may end in a different
-    file. Such slight anomalies, although perfectly legitimate, may cause errors
-    that are difficult to locate. In order to be able to give a warning message
-    when such anomalies occur, \eTeX\ uses the |grp_stack| and |if_stack| arrays
-    to record the initial |cur_boundary| and |cond_ptr| values for each input
-    file.
-
-*/
-
-/*tex initial |cur_boundary| */
-
-save_pointer *grp_stack;
-
-/*tex initial |cond_ptr| */
-
-halfword *if_stack;
-
-/*tex
-
-    When a group ends that was apparently entered in a different input file, the
-    |group_warning| procedure is invoked in order to update the |grp_stack|. If
-    moreover \.{\\tracingnesting} is positive we want to give a warning message.
-    The situation is, however, somewhat complicated by two facts: (1)~There may
-    be |grp_stack| elements without a corresponding \.{\\input} file or
-    \.{\\scantokens} pseudo file (e.g., error insertions from the terminal); and
-    (2)~the relevant information is recorded in the |name_field| of the
-    |input_stack| only loosely synchronized with the |in_open| variable indexing
-    |grp_stack|.
-
-*/
-
-void group_warning(void)
-{
-    /*tex do we need a warning? */
-    boolean w = false;
-    /*tex index into |grp_stack| */
-    int i = in_open;
-    base_ptr = input_ptr;
-    /*tex store current state */
-    input_stack[base_ptr] = cur_input;
-    while ((grp_stack[i] == cur_boundary) && (i > 0)) {
-        /*tex
-
-            Set variable |w| to indicate if this case should be reported. This
-            code scans the input stack in order to determine the type of the
-            current input file.
-
-        */
-        if (tracing_nesting_par > 0) {
-            while ((input_stack[base_ptr].state_field == token_list) || (input_stack[base_ptr].index_field > i))
-                decr(base_ptr);
-            if (input_stack[base_ptr].name_field > 17)
-                w = true;
-        }
-        grp_stack[i] = save_value(save_ptr);
-        decr(i);
-    }
-    if (w) {
-        tprint_nl("Warning: end of ");
-        print_group(true);
-        tprint(" of a different file");
-        print_ln();
-        if (tracing_nesting_par > 1)
-            show_context();
-        if (history == spotless)
-            history = warning_issued;
-    }
-}
-
-/*tex
-
-    When a conditional ends that was apparently started in a different input
-    file, the |if_warning| procedure is invoked in order to update the
-    |if_stack|. If moreover \.{\\tracingnesting} is positive we want to give a
-    warning message (with the same complications as above).
-
-*/
-
-void if_warning(void)
-{
-    /*tex Do we need a warning? */
-    boolean w = false;
-    int i = in_open;
-    base_ptr = input_ptr;
-    /*tex Store current state. */
-    input_stack[base_ptr] = cur_input;
-    while (if_stack[i] == cond_ptr) {
-        /*tex Set variable |w| to. */
-        if (tracing_nesting_par > 0) {
-            while ((input_stack[base_ptr].state_field == token_list) || (input_stack[base_ptr].index_field > i))
-                decr(base_ptr);
-            if (input_stack[base_ptr].name_field > 17)
-                w = true;
-        }
-        if_stack[i] = vlink(cond_ptr);
-        decr(i);
-    }
-    if (w) {
-        tprint_nl("Warning: end of ");
-        print_cmd_chr(if_test_cmd, cur_if);
-        print_if_line(if_line);
-        tprint(" of a different file");
-        print_ln();
-        if (tracing_nesting_par > 1)
-            show_context();
-        if (history == spotless)
-            history = warning_issued;
-    }
-}
-
-/*tex
-
-    Conversely, the |file_warning| procedure is invoked when a file ends and some
-    groups entered or conditionals started while reading from that file are still
-    incomplete.
-
-*/
-
-void file_warning(void)
-{
-    /*tex saved value of |save_ptr| or |cond_ptr| */
-    halfword p = save_ptr;
-    /*tex saved value of |cur_level| or |if_limit| */
-    int l = cur_level;
-    /*tex saved value of |cur_group| or |cur_if| */
-    int c = cur_group;
-    /*tex saved value of |if_line| */
-    int i;
-    save_ptr = cur_boundary;
-    while (grp_stack[in_open] != save_ptr) {
-        decr(cur_level);
-        tprint_nl("Warning: end of file when ");
-        print_group(true);
-        tprint(" is incomplete");
-        cur_group = save_level(save_ptr);
-        save_ptr = save_value(save_ptr);
-    }
-    save_ptr = p;
-    cur_level = (quarterword) l;
-    /*tex Restore old values. */
-    cur_group = (group_code) c;
-    p = cond_ptr;
-    l = if_limit;
-    c = cur_if;
-    i = if_line;
-    while (if_stack[in_open] != cond_ptr) {
-        tprint_nl("Warning: end of file when ");
-        print_cmd_chr(if_test_cmd, cur_if);
-        if (if_limit == fi_code)
-            tprint_esc("else");
-        print_if_line(if_line);
-        tprint(" is incomplete");
-        if_line = if_line_field(cond_ptr);
-        cur_if = if_limit_subtype(cond_ptr);
-        if_limit = if_limit_type(cond_ptr);
-        cond_ptr = vlink(cond_ptr);
-    }
-    /*tex restore old values */
-    cond_ptr = p;
-    if_limit = l;
-    cur_if = c;
-    if_line = i;
-    print_ln();
-    if (tracing_nesting_par > 1)
-        show_context();
-    if (history == spotless)
-        history = warning_issued;
-}
-
-/*tex The |par_fill_skip| glue node of the new paragraph. */
-
-halfword last_line_fill;
-
-/*tex
-
-    The lua interface needs some extra functions. The functions themselves are
-    quite boring, but they are handy because otherwise this internal stuff has to
-    be accessed from C directly, where lots of the defines are not available.
-
-*/
-
-#define get_tex_dimen_register(j) dimen(j)
-#define get_tex_skip_register(j) skip(j)
-#define get_tex_mu_skip_register(j) mu_skip(j)
-#define get_tex_count_register(j) count(j)
-#define get_tex_attribute_register(j) attribute(j)
-#define get_tex_box_register(j) box(j)
-
-/*tex These can now be macros (todo). */
-
-int get_tex_extension_count_register(int i)
-{
-    return (int) int_par(backend_int_base-int_base+i);
-}
-
-void set_tex_extension_count_register(int i, int d)
-{
-    int_par(backend_int_base-int_base+i) = d;
-}
-
-int get_tex_extension_dimen_register(int i)
-{
-    return (int) dimen_par(backend_dimen_base-dimen_base+i);
-}
-
-void set_tex_extension_dimen_register(int i, int d)
-{
-    dimen_par(backend_dimen_base-dimen_base+i) = d;
-}
-
-int get_tex_extension_toks_register(int i)
-{
-    return equiv(backend_toks_base+i);
-}
-
-int set_tex_dimen_register(int j, scaled v)
-{
-    int a;
-    if (global_defs_par > 0)
-        a = 4;
-    else
-        a = 0;
-    word_define(j + scaled_base, v);
-    return 0;
-}
-
-int set_tex_skip_register(int j, halfword v)
-{
-    int a;
-    if (global_defs_par > 0)
-        a = 4;
-    else
-        a = 0;
-    if (type(v) != glue_spec_node)
-        return 1;
-    word_define(j + skip_base, v);
-    return 0;
-}
-
-int set_tex_mu_skip_register(int j, halfword v)
-{
-    int a;
-    if (global_defs_par > 0)
-        a = 4;
-    else
-        a = 0;
-    if (type(v) != glue_spec_node)
-        return 1;
-    word_define(j + mu_skip_base, v);
-    return 0;
-}
-
-int set_tex_count_register(int j, scaled v)
-{
-    int a;
-    if (global_defs_par > 0)
-        a = 4;
-    else
-        a = 0;
-    word_define(j + count_base, v);
-    return 0;
-}
-
-int set_tex_box_register(int j, scaled v)
-{
-    int a;
-    if (global_defs_par > 0)
-        a = 4;
-    else
-        a = 0;
-    define(j + box_base, box_ref_cmd, v);
-    return 0;
-}
-
-int set_tex_attribute_register(int j, scaled v)
-{
-    int a;
-    if (global_defs_par > 0)
-        a = 4;
-    else
-        a = 0;
-    if (j > max_used_attr)
-        max_used_attr = j;
-    attr_list_cache = cache_disabled;
-    word_define(j + attribute_base, v);
-    return 0;
-}
-
-int get_tex_toks_register(int j)
-{
-    str_number s = get_nullstr();
-    if (toks(j) != null) {
-        s = tokens_to_string(toks(j));
-    }
-    return s;
-}
-
-int set_tex_toks_register(int j, lstring s)
-{
-    int a;
-    halfword ref = get_avail();
-    (void) str_toks(s);
-    set_token_ref_count(ref, 0);
-    set_token_link(ref, token_link(temp_token_head));
-    if (global_defs_par > 0)
-        a = 4;
-    else
-        a = 0;
-    define(j + toks_base, call_cmd, ref);
-    return 0;
-}
-
-int scan_tex_toks_register(int j, int c, lstring s)
-{
-    int a;
-    halfword ref = get_avail();
-    (void) str_scan_toks(c,s);
-    set_token_ref_count(ref, 0);
-    set_token_link(ref, token_link(temp_token_head));
-    if (global_defs_par > 0)
-        a = 4;
-    else
-        a = 0;
-    define(j + toks_base, call_cmd, ref);
-    return 0;
-}
-
-scaled get_tex_box_width(int j)
-{
-    halfword q = box(j);
-    if (q != null)
-        return width(q);
-    return 0;
-}
-
-int set_tex_box_width(int j, scaled v)
-{
-    halfword q = box(j);
-    if (q == null)
-        return 1;
-    width(q) = v;
-    return 0;
-}
-
-scaled get_tex_box_height(int j)
-{
-    halfword q = box(j);
-    if (q != null)
-        return height(q);
-    return 0;
-}
-
-int set_tex_box_height(int j, scaled v)
-{
-    halfword q = box(j);
-    if (q == null)
-        return 1;
-    height(q) = v;
-    return 0;
-}
-
-scaled get_tex_box_depth(int j)
-{
-    halfword q = box(j);
-    if (q != null)
-        return depth(q);
-    return 0;
-}
-
-int set_tex_box_depth(int j, scaled v)
-{
-    halfword q = box(j);
-    if (q == null)
-        return 1;
-    depth(q) = v;
-    return 0;
-}
-
-/*tex
-
-    This section is devoted to the {\sl Synchronize \TeX nology} - or simply {\sl
-    Sync\TeX} - used to synchronize between input and output. This section
-    explains how synchronization basics are implemented. Before we enter into
-    more technical details, let us recall in a few words what is synchronization.
-
-    \TeX\ typesetting system clearly separates the input and the output material,
-    and synchronization will provide a new link between both that can help text
-    editors and viewers to work together. More precisely, forwards
-    synchronization is the ability, given a location in the input source file, to
-    find what is the corresponding place in the output. Backwards synchronization
-    just performs the opposite: given a location in the output, retrieve the
-    corresponding material in the input source file.
-
-    For better code management and maintainance, we adopt a naming convention.
-    Throughout this program, code related to the {\sl Synchronize \TeX nology} is
-    tagged with the ``{\sl synctex}'' key word. Any code extract where {\sl
-    Sync\TeX} plays its part, either explicitly or implicitly, (should) contain
-    the string ``{\sl synctex}''. This naming convention also holds for external
-    files. Moreover, all the code related to {\sl Sync\TeX} is gathered in this
-    section, except the definitions.
-
-    Enabling synchronization should be performed from the command line,
-    |synctexoption| is used for that purpose. This global integer variable is
-    declared here but it is not used here. This is just a placeholder where the
-    command line controller will put the {\sl Sync\TeX} related options, and the
-    {\sl Sync\TeX} controller will read them.
-
-*/
-
-int synctexoption;
-
-/*tex
-
-    A convenient primitive is provided: \.{\\synctex=1} in the input source file
-    enables synchronization whereas \.{\\synctex=0} disables it. Its memory
-    address is |synctex_code|. It is initialized by the {\sl Sync\TeX} controller
-    to the command-line option if given. The controller may filter some reserved
-    bits.
-
-    In order to give the {\sl Sync\TeX} controller read and write access to the
-    contents of the \.{\\synctex} primitive, we declare |synctexoffset|, such
-    that |mem[synctexoffset]| and \.{\\synctex} correspond to the same memory
-    storage. |synctexoffset| is initialized to the correct value when quite
-    everything is initialized.
-
-*/
-
-/*tex Holds the true value of |synctex_code|: */
-
-int synctexoffset;
-
-/*tex
-
-    Synchronization is achieved with the help of an auxiliary file named `\.{{\sl
-    jobname}.synctex}' ({\sl jobname} is the contents of the \.{\\jobname}
-    macro), where a {\sl Sync\TeX} controller implemented in the external
-    |synctex.c| file will store geometrical information. This {\sl Sync\TeX}
-    controller will take care of every technical details concerning the {\sl
-    Sync\TeX} file, we will only focus on the messages the controller will
-    receive from the \TeX\ program.
-
-    The most accurate synchronization information should allow to map any
-    character of the input source file to the corresponding location in the
-    output, if relevant. Ideally, the synchronization information of the input
-    material consists of the file name, the line and column numbers of every
-    character. The synchronization information in the output is simply the page
-    number and either point coordinates, or box dimensions and position. The
-    problem is that the mapping between these informations is only known at ship
-    out time, which means that we must keep track of the input synchronization
-    information until the pages ship out.
-
-    As \TeX\ only knows about file names and line numbers, but forgets the column
-    numbers, we only consider a restricted input synchronization information
-    called {\sl Sync\TeX\ information}. It consists of a unique file name
-    identifier, the {\sl Sync\TeX\ file tag}, and the line number.
-
-    Keeping track of such information, should be different whether characters or
-    nodes are involved. Actually, only certain nodes are involved in {\sl
-    Sync\TeX}, we call them {\sl synchronized nodes}. Synchronized nodes store
-    the {\sl Sync\TeX} information in their last two words: the first one
-    contains a {\sl Sync\TeX\ file tag} uniquely identifying the input file, and
-    the second one contains the current line number, as returned by the
-    \.{\\inputlineno} primitive. The |synctex_field_size| macro contains the
-    necessary size to store the {\sl Sync\TeX} information in a node.
-
-    When declaring the size of a new node, it is recommanded to use the following
-    convention: if the node is synchronized, use a definition similar to
-    |my_synchronized_node_size|={\sl xxx}+|synctex_field_size|. Moreover, one
-    should expect that the {\sl Sync\TeX} information is always stored in the
-    last two words of a synchronized node.
-
-    By default, every node with a sufficiently big size is initialized at
-    creation time in the |get_node| routine with the current {\sl Sync\TeX}
-    information, whether or not the node is synchronized. One purpose is to set
-    this information very early in order to minimize code dependencies, including
-    forthcoming extensions. Another purpose is to avoid the assumption that every
-    node type has a dedicated getter, where initialization should take place.
-    Actually, it appears that some nodes are created using directly the
-    |get_node| routine and not the dedicated constructor. And finally,
-    initializing the node at only one place is less error prone.
-
-    Instead of storing the input file name, it is better to store just an
-    identifier. Each time \TeX\ opens a new file, it notifies the {\sl Sync\TeX}
-    controller with a |synctex_start_input| message. This controller will create
-    a new {\sl Sync\TeX} file tag and will update the current input state record
-    accordingly. If the input comes from the terminal or a pseudo file, the
-    |synctex_tag| is set to 0. It results in automatically disabling
-    synchronization for material input from the terminal or pseudo files.
-
-    Synchronized nodes are boxes, math, kern and glue nodes. Other nodes should
-    be synchronized too, in particular math noads. \TeX\ assumes that math, kern
-    and glue nodes have the same size, this is why both are synchronized. {\sl In
-    fine}, only horizontal lists are really used in {\sl Sync\TeX}, but all box
-    nodes are considered the same with respect to synchronization, because a box
-    node type is allowed to change at execution time.
-
-    {\em Nota Bene:} The {\sl Sync\TeX} code is very close to the memory model.
-    It is not connected to any other part of the code, except for memory
-    management. It is possible to neutralize the {\sl Sync\TeX} code rather
-    simply. The first step is to define a null |synctex_field_size|. The second
-    step is to comment out the code in ``Initialize bigger nodes...'' and every
-    ``Copy ... {\sl Sync\TeX} information''. The last step will be to comment out
-    the |synctex_tag_field| related code in the definition of |synctex_tag| and
-    the various ``Prepare ... {\sl Sync\TeX} information''. Then all the
-    remaining code should be just harmless. The resulting program would behave
-    exactly the same as if absolutely no {\sl Sync\TeX} related code was there,
-    including memory management. Of course, all this assumes that {\sl Sync\TeX}
-    is turned off from the command line.
-
-    Here are extra variables for Web2c. (This numbering of the system-dependent
-    section allows easy integration of Web2c and e-\TeX, etc.)
-
-*/
-
-/*tex where the filename to switch to starts */
-
-pool_pointer edit_name_start;
-
-/*tex what line to start editing at */
-
-int edit_name_length, edit_line;
-
-/*tex whether |more_name| returns false for space */
-
-boolean stop_at_space;
-
-/*tex
-
-    The |edit_name_start| will be set to point into |str_pool| somewhere after
-    its beginning if \TeX\ is supposed to switch to an editor on exit.
-
-*/
-
-int shellenabledp;
-int restrictedshell;
-char *output_comment;
-
-/*tex
-
-    Are we printing extra info as we read the format file?
-
-*/
-
-boolean debug_format_file;
-
-void wrapup_leader(halfword p)
-{
-    /*tex Do some work that has been queued up for \.{\\write}. */
-    if (!doing_leaders) {
-        int j = write_stream(p);
-        if (subtype(p) == write_node) {
-            write_out(p);
-        } else if (subtype(p) == close_node) {
-            close_write_file(j);
-        } else if (valid_write_file(j)) {
-            char *fn;
-            close_write_file(j);
-            cur_name = open_name(p);
-            cur_area = open_area(p);
-            cur_ext = open_ext(p);
-            if (cur_ext == get_nullstr())
-                cur_ext = maketexstring(".tex");
-            fn = pack_file_name(cur_name, cur_area, cur_ext);
-            while (! open_write_file(j,fn)) {
-                fn = prompt_file_name("output file name", ".tex");
-            }
-        }
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/extensions.w
@@ -0,0 +1,1211 @@
+% extensions.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}
+\def\pdfTeX{pdf\TeX}
+
+@ @c
+#include "ptexlib.h"
+
+@ @c
+#define mode     mode_par
+#define tail     tail_par
+#define head     head_par
+#define dir_save dirs_par
+
+@ The program above includes a bunch of ``hooks'' that allow further
+capabilities to be added without upsetting \TeX's basic structure.
+Most of these hooks are concerned with ``whatsit'' nodes, which are
+intended to be used for special purposes; whenever a new extension to
+\TeX\ involves a new kind of whatsit node, a corresponding change needs
+to be made to the routines below that deal with such nodes,
+but it will usually be unnecessary to make many changes to the
+other parts of this program.
+
+In order to demonstrate how extensions can be made, we shall treat
+`\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}',
+and `\.{\\special}' as if they were extensions.
+These commands are actually primitives of \TeX, and they should
+appear in all implementations of the system; but let's try to imagine
+that they aren't. Then the program below illustrates how a person
+could add them.
+
+Sometimes, of course, an extension will require changes to \TeX\ itself;
+no system of hooks could be complete enough for all conceivable extensions.
+The features associated with `\.{\\write}' are almost all confined to the
+following paragraphs, but there are small parts of the |print_ln| and
+|print_char| procedures that were introduced specifically to \.{\\write}
+characters. Furthermore one of the token lists recognized by the scanner
+is a |write_text|; and there are a few other miscellaneous places where we
+have already provided for some aspect of \.{\\write}.  The goal of a \TeX\
+extender should be to minimize alterations to the standard parts of the
+program, and to avoid them completely if possible. He or she should also
+be quite sure that there's no easy way to accomplish the desired goals
+with the standard features that \TeX\ already has. ``Think thrice before
+extending,'' because that may save a lot of work, and it will also keep
+incompatible extensions of \TeX\ from proliferating.
+@^system dependencies@>
+@^extensions to \TeX@>
+
+First let's consider the format of whatsit nodes that are used to represent
+the data associated with \.{\\write} and its relatives. Recall that a whatsit
+has |type=whatsit_node|, and the |subtype| is supposed to distinguish
+different kinds of whatsits. Each node occupies two or more words; the
+exact number is immaterial, as long as it is readily determined from the
+|subtype| or other data.
+
+We shall introduce five |subtype| values here, corresponding to the
+control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, and \.{\\special}.
+The second word of I/O whatsits has a |write_stream| field
+that identifies the write-stream number (0 to 15, or 16 for out-of-range and
+positive, or 17 for out-of-range and negative).
+In the case of \.{\\write} and \.{\\special}, there is also a field that
+points to the reference count of a token list that should be sent. In the
+case of \.{\\openout}, we need three words and three auxiliary subfields
+to hold the string numbers for name, area, and extension.
+
+@ Extensions might introduce new command codes; but it's best to use
+|extension| with a modifier, whenever possible, so that |main_control|
+stays the same.
+
+@ The sixteen possible \.{\\write} streams are represented by the |write_file|
+array. The |j|th file is open if and only if |write_open[j]=true|. The last
+two streams are special; |write_open[16]| represents a stream number
+greater than 15, while |write_open[17]| represents a negative stream number,
+and both of these variables are always |false|.
+
+@c
+alpha_file write_file[last_file_selector+1];
+halfword write_file_mode[last_file_selector+1];
+halfword write_file_translation[last_file_selector+1];
+boolean write_open[last_file_selector+1];
+
+scaled neg_wd;
+scaled pos_wd;
+scaled neg_ht;
+
+@ The variable |write_loc| just introduced is used to provide an
+appropriate error message in case of ``runaway'' write texts.
+
+@c
+halfword write_loc;             /* |eqtb| address of \.{\\write} */
+
+/*
+    hh: eventually i'll make \pdfextension a lua token parsed function;
+    a complication is that sometimes token lists are delayed
+*/
+
+@ When an |extension| command occurs in |main_control|, in any mode,
+the |do_extension| routine is called.
+
+@c
+int last_saved_image_index ;
+int last_saved_image_pages ;
+int last_saved_box_index ;
+scaledpos last_position = { 0, 0 };
+
+static void do_extension_dvi(int immediate)
+{
+    if (scan_keyword("literal")) {
+        new_whatsit(special_node);
+        write_stream(tail) = null;
+        scan_toks(false, true);
+        write_tokens(tail) = def_ref;
+    } else {
+        tex_error("unexpected use of \\dviextension",null);
+    }
+}
+
+static void do_extension_pdf(int immediate)
+{
+    int i;
+
+    if (scan_keyword("literal")) {
+        new_whatsit(pdf_literal_node);
+        if (scan_keyword("direct"))
+            set_pdf_literal_mode(tail, direct_always);
+        else if (scan_keyword("page"))
+            set_pdf_literal_mode(tail, direct_page);
+        else if (scan_keyword("text"))
+            set_pdf_literal_mode(tail, direct_text);
+        else if (scan_keyword("raw"))
+            set_pdf_literal_mode(tail, direct_raw);
+        else if (scan_keyword("origin"))
+            set_pdf_literal_mode(tail, set_origin);
+        else
+            set_pdf_literal_mode(tail, set_origin);
+        scan_toks(false, true);
+        set_pdf_literal_type(tail, normal);
+        set_pdf_literal_data(tail, def_ref);
+    } else if (scan_keyword("dest")) {
+        scan_pdfdest(static_pdf);
+    } else if (scan_keyword("annot")) {
+        scan_annot(static_pdf);
+    } else if (scan_keyword("save")) {
+        new_whatsit(pdf_save_node);
+    } else if (scan_keyword("restore")) {
+        new_whatsit(pdf_restore_node);
+    } else if (scan_keyword("setmatrix")) {
+        new_whatsit(pdf_setmatrix_node);
+        scan_toks(false, true);
+        set_pdf_setmatrix_data(tail, def_ref);
+    } else if (scan_keyword("obj")) {
+        scan_obj(static_pdf);
+        if (immediate) {
+            if (obj_data_ptr(static_pdf, pdf_last_obj) == 0)   /* this object has not been initialized yet */
+                normal_error("pdf backend","\\pdfextension obj 'reserveobjnum' cannot be used with \\immediate");
+            pdf_write_obj(static_pdf, pdf_last_obj);
+        }
+    } else if (scan_keyword("refobj")) {
+        scan_refobj(static_pdf);
+    } else if (scan_keyword("colorstack")) {
+        scan_int();
+        if (cur_val >= colorstackused()) {
+            print_err("Unknown color stack number ");
+            print_int(cur_val);
+            help3
+                ("Allocate and initialize a color stack with \\pdfextension colorstackinit.",
+                 "I'll use default color stack 0 here.",
+                 "Proceed, with fingers crossed.");
+            error();
+            cur_val = 0;
+        }
+        if (cur_val < 0) {
+            print_err("Invalid negative color stack number");
+            help2("I'll use default color stack 0 here.",
+                  "Proceed, with fingers crossed.");
+            error();
+            cur_val = 0;
+        }
+        if (scan_keyword("set"))
+            i = colorstack_set;
+        else if (scan_keyword("push"))
+            i = colorstack_push;
+        else if (scan_keyword("pop"))
+            i = colorstack_pop;
+        else if (scan_keyword("current"))
+            i = colorstack_current;
+        else
+            i = -1; /* error */
+        if (i >= 0) {
+            new_whatsit(pdf_colorstack_node);
+            set_pdf_colorstack_stack(tail, cur_val);
+            set_pdf_colorstack_cmd(tail, i);
+            set_pdf_colorstack_data(tail, null);
+            if (i <= colorstack_data) {
+                scan_toks(false, true);
+                set_pdf_colorstack_data(tail, def_ref);
+            }
+        } else {
+            print_err("Color stack action is missing");
+            help3("The expected actions for \\pdfextension colorstack:",
+                  "    set, push, pop, current",
+                  "I'll ignore the color stack command.");
+            error();
+        }
+    } else if (scan_keyword("startlink")) {
+        scan_startlink(static_pdf);
+    } else if (scan_keyword("endlink")) {
+        if (abs(mode) == vmode)
+            normal_error("pdf backend", "\\pdfextension endlink cannot be used in vertical mode");
+        new_whatsit(pdf_end_link_node);
+    } else if (scan_keyword("startthread")) {
+        new_annot_whatsit(pdf_start_thread_node);
+        scan_thread_id();
+    } else if (scan_keyword("endthread")) {
+        new_whatsit(pdf_end_thread_node);
+    } else if (scan_keyword("thread")) {
+        new_annot_whatsit(pdf_thread_node);
+        scan_thread_id();
+    } else if (scan_keyword("outline")) {
+        scan_pdfoutline(static_pdf);
+    } else if (scan_keyword("glyphtounicode")) {
+        glyph_to_unicode();
+    } else if (scan_keyword("catalog")) {
+        scan_pdfcatalog(static_pdf);
+    } else if (scan_keyword("fontattr")) {
+        /*
+            The font attributes are simply initialized to zero now, this is easier to deal with from C than an
+            empty \TeX{} string, and surely nobody will want to set font attr to a string containing a single zero,
+            as that would be nonsensical in the PDF output.
+        */
+        scan_font_ident();
+        i = cur_val;
+        if (i == null_font)
+            normal_error("pdf backend", "invalid font identifier");
+        scan_toks(false, true);
+        set_pdf_font_attr(i, tokens_to_string(def_ref));
+        if (str_length(pdf_font_attr(i)) == 0) {
+            flush_str((str_ptr - 1));   /* from |tokens_to_string| */
+            set_pdf_font_attr(i, 0);
+        }
+    } else if (scan_keyword("mapfile")) {
+        scan_toks(false, true);
+        pdfmapfile(def_ref);
+        delete_token_ref(def_ref);
+    } else if (scan_keyword("mapline")) {
+        scan_toks(false, true);
+        pdfmapline(def_ref);
+        delete_token_ref(def_ref);
+    } else if (scan_keyword("includechars")) {
+        pdf_include_chars(static_pdf);
+    } else if (scan_keyword("info")) {
+        scan_toks(false, true);
+        pdf_info_toks = concat_tokens(pdf_info_toks, def_ref);
+    } else if (scan_keyword("names")) {
+        scan_toks(false, true);
+        pdf_names_toks = concat_tokens(pdf_names_toks, def_ref);
+    } else if (scan_keyword("trailer")) {
+        scan_toks(false, true);
+        pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref);
+    } else {
+        tex_error("unexpected use of \\pdfextension",null);
+    }
+}
+
+static void do_resource_dvi(int immediate, int code)
+{
+}
+
+static void do_resource_pdf(int immediate, int code)
+{
+    switch (code) {
+        case use_box_resource_code:
+            scan_pdfrefxform(static_pdf);
+            break;
+        case use_image_resource_code:
+            scan_pdfrefximage(static_pdf);
+            break;
+        case save_box_resource_code:
+            scan_pdfxform(static_pdf);
+            if (immediate) {
+                pdf_cur_form = last_saved_box_index;
+                ship_out(static_pdf, obj_xform_box(static_pdf, last_saved_box_index), SHIPPING_FORM);
+            }
+            break;
+        case save_image_resource_code:
+            scan_pdfximage(static_pdf);
+            if (immediate) {
+                pdf_write_image(static_pdf, last_saved_image_index);
+            }
+            break;
+    }
+}
+
+/*
+
+    Ad immediate:
+
+    To write a token list, we must run it through \TeX's scanner, expanding
+    macros and \.{\\the} and \.{\\number}, etc. This might cause runaways,
+    if a delimited macro parameter isn't matched, and runaways would be
+    extremely confusing since we are calling on \TeX's scanner in the middle
+    of a \.{\\shipout} command. Therefore we will put a dummy control sequence as
+    a ``stopper,'' right after the token list. This control sequence is
+    artificially defined to be \.{\\outer}.
+
+    The presence of `\.{\\immediate}' causes the |do_extension| procedure
+    to descend to one level of recursion. Nothing happens unless \.{\\immediate}
+    is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'.
+
+*/
+
+/* extensions are backend related */
+
+@ The next subroutine uses |cur_chr| to decide what sort of whatsit is
+involved, and also inserts a |write_stream| number.
+
+@c
+static void new_write_whatsit(int w, int check)
+{
+    new_whatsit(cur_chr);
+    if (check) {
+        /* so we check with open and close */
+        scan_limited_int(last_file_selector,NULL);
+    } else {
+        /* but we're tolerant with the rest */
+        scan_int();
+        if (cur_val < 0)
+            cur_val = term_only;
+        else if (cur_val > last_file_selector) {
+            cur_val = term_and_log;
+        }
+    }
+    write_stream(tail) = cur_val;
+}
+
+void do_extension(int immediate)
+{
+    halfword p; /* all-purpose pointer */
+    if (cur_cmd == extension_cmd) {
+        /* these have their own range starting at 0 */
+        switch (cur_chr) {
+        case open_code:
+            p = tail;
+            new_write_whatsit(open_node_size,1);
+            scan_optional_equals();
+            scan_file_name();
+            open_name(tail) = cur_name;
+            open_area(tail) = cur_area;
+            open_ext(tail) = cur_ext;
+            if (immediate) {
+                out_what(static_pdf, tail);
+                flush_node_list(tail);
+                tail = p;
+                vlink(p) = null;
+            }
+            break;
+        case write_code:
+            /*
+                When `\.{\\write 12\{...\}}' appears, we scan the token list `\.{\{...\}}'
+                without expanding its macros; the macros will be expanded later when this
+                token list is rescanned.
+            */
+            p = tail;
+            new_write_whatsit(write_node_size,0);
+            cur_cs = write_stream(tail);
+            scan_toks(false, false);
+            write_tokens(tail) = def_ref;
+            if (immediate) {
+                out_what(static_pdf, tail);
+                flush_node_list(tail);
+                tail = p;
+                vlink(p) = null;
+            }
+            break;
+        case close_code:
+            p = tail;
+            new_write_whatsit(close_node_size,1);
+            write_tokens(tail) = null;
+            if (immediate) {
+                out_what(static_pdf, tail);
+                flush_node_list(tail);
+                tail = p;
+                vlink(p) = null;
+            }
+            break;
+        case special_code:
+            /*
+                When `\.{\\special\{...\}}' appears, we expand the macros in the token
+                list as in \.{\\xdef} and \.{\\mark}.
+            */
+            new_whatsit(special_node);
+            write_stream(tail) = null;
+            p = scan_toks(false, true);
+            write_tokens(tail) = def_ref;
+            break;
+        case immediate_code:
+            get_x_token();
+            do_extension(1);
+            break;
+        case use_box_resource_code:
+        case use_image_resource_code:
+        case save_box_resource_code:
+        case save_image_resource_code:
+            switch (get_o_mode()) {
+                case OMODE_DVI:
+                    do_resource_dvi(immediate,cur_chr);
+                    break;
+                case OMODE_PDF:
+                    do_resource_pdf(immediate,cur_chr);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        /* backend extensions have their own range starting at 32 */
+        case dvi_extension_code:
+            if (get_o_mode() == OMODE_DVI)
+                do_extension_dvi(immediate);
+            break;
+        case pdf_extension_code:
+            if (get_o_mode() == OMODE_PDF)
+                do_extension_pdf(immediate);
+            break;
+        /* done */
+        default:
+            if (immediate) {
+                back_input();
+            } else {
+                confusion("invalid extension");
+            }
+            break;
+        }
+    } else {
+        /* no extension command, quite certainly following \immediate */
+        back_input();
+    }
+}
+
+@ Here is a subroutine that creates a whatsit node having a given |subtype|
+and a given number of words. It initializes only the first word of the whatsit,
+and appends it to the current list.
+
+@c
+void new_whatsit(int s)
+{
+    halfword p = new_node(whatsit_node, s);
+    couple_nodes(tail, p);
+    tail = p;
+}
+
+@ The final line of this routine is slightly subtle; at least, the author
+didn't think about it until getting burnt! There is a used-up token list
+@^Knuth, Donald Ervin@>
+on the stack, namely the one that contained |end_write_token|. (We
+insert this artificial `\.{\\endwrite}' to prevent runaways, as explained
+above.) If it were not removed, and if there were numerous writes on a
+single page, the stack would overflow.
+
+@c
+void expand_macros_in_tokenlist(halfword p)
+{
+    int old_mode;
+    pointer q = get_avail();
+    pointer r = get_avail();
+    token_info(q) = right_brace_token + '}';
+    token_link(q) = r;
+    token_info(r) = end_write_token;
+    begin_token_list(q, inserted);
+    begin_token_list(write_tokens(p), write_text);
+    q = get_avail();
+    token_info(q) = left_brace_token + '{';
+    begin_token_list(q, inserted);
+    /* now we're ready to scan
+       `\.\{$\langle\,$token list$\,\rangle$\.{\} \\endwrite}' */
+    old_mode = mode;
+    mode = 0;
+    /* disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, \.{\\prevgraf} */
+    cur_cs = write_loc;
+    q = scan_toks(false, true); /* expand macros, etc. */
+    get_token();
+    if (cur_tok != end_write_token) {
+        /* Recover from an unbalanced write command */
+        const char *hlp[] = {
+            "On this page there's a \\write with fewer real {'s than }'s.",
+            "I can't handle that very well; good luck.", NULL
+        };
+        tex_error("Unbalanced write command", hlp);
+        do {
+            get_token();
+        } while (cur_tok != end_write_token);
+    }
+    mode = old_mode;
+    end_token_list();           /* conserve stack space */
+}
+
+void write_out(halfword p)
+{
+    int old_setting;            /* holds print |selector| */
+    int j;                      /* write stream number */
+    char *s, *ss;               /* line to be written, as a C string */
+    int callback_id;
+    int lua_retval;
+    expand_macros_in_tokenlist(p);
+    old_setting = selector;
+    j = write_stream(p);
+    if (file_can_be_written(j)) {
+        selector = j;
+    } else if ((j == term_only) && (selector == term_and_log)) {
+        /* write to the terminal if file isn't open */
+        selector = log_only;
+        tprint_nl("");
+    } else {
+        tprint_nl("");
+    }
+    s = tokenlist_to_cstring(def_ref, false, NULL);
+    if (selector < no_print) {
+        /* selector is a file */
+        callback_id = callback_defined(process_output_buffer_callback);
+        if (callback_id > 0) {
+            /* fix up the output buffer using callbacks */
+            lua_retval = run_callback(callback_id, "S->S", s, &ss);
+            if ((lua_retval == true) && (ss != NULL)) {
+                xfree(s);
+                s = ss;
+            }
+        }
+    }
+    tprint(s);
+    xfree(s);
+    print_ln();
+    flush_list(def_ref);
+    selector = old_setting;
+}
+
+void finalize_write_files(void) {
+    int k;
+    for (k = 0; k <= last_file_selector; k++) {
+        if (write_open[k]) {
+            lua_a_close_out(write_file[k]);
+        }
+    }
+}
+
+void initialize_write_files(void) {
+    int k;
+    for (k = 0; k <= last_file_selector; k++) {
+        write_open[k] = false;
+    }
+}
+
+void close_write_file(int id) {
+    if (write_open[id]) {
+        lua_a_close_out(write_file[id]);
+        write_open[id] = false;
+    }
+}
+
+boolean open_write_file(int id, char *fn) {
+    if (lua_a_open_out(&(write_file[id]), fn, (id + 1))) {
+        write_open[id] = true;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+
+@ To implement primitives as \.{\\pdfextension info}, \.{\\pdfextension catalog} or
+\.{\\pdfextension names} we need to concatenate tokens lists.
+
+@c
+halfword concat_tokens(halfword q, halfword r)
+{                               /* concat |q| and |r| and returns the result tokens list */
+    halfword p;
+    if (q == null)
+        return r;
+    p = q;
+    while (token_link(p) != null)
+        p = token_link(p);
+    set_token_link(p, token_link(r));
+    free_avail(r);
+    return q;
+}
+
+@ The \eTeX\ features available in extended mode are grouped into two
+categories:  (1)~Some of them are permanently enabled and have no
+semantic effect as long as none of the additional primitives are
+executed.  (2)~The remaining \eTeX\ features are optional and can be
+individually enabled and disabled.  For each optional feature there is
+an \eTeX\ state variable named \.{\\...state}; the feature is enabled,
+resp.\ disabled by assigning a positive, resp.\ non-positive value to
+that integer.
+
+@ In order to handle \.{\\everyeof} we need an array |eof_seen| of
+boolean variables.
+
+@c
+boolean *eof_seen;              /* has eof been seen? */
+
+@ The |print_group| procedure prints the current level of grouping and
+the name corresponding to |cur_group|.
+
+@c
+void print_group(boolean e)
+{
+    switch (cur_group) {
+        case bottom_level:
+            tprint("bottom level");
+            return;
+            break;
+        case simple_group:
+        case semi_simple_group:
+            if (cur_group == semi_simple_group)
+                tprint("semi ");
+            tprint("simple");
+            break;;
+        case hbox_group:
+        case adjusted_hbox_group:
+            if (cur_group == adjusted_hbox_group)
+                tprint("adjusted ");
+            tprint("hbox");
+            break;
+        case vbox_group:
+            tprint("vbox");
+            break;
+        case vtop_group:
+            tprint("vtop");
+            break;
+        case align_group:
+        case no_align_group:
+            if (cur_group == no_align_group)
+                tprint("no ");
+            tprint("align");
+            break;
+        case output_group:
+            tprint("output");
+            break;
+        case disc_group:
+            tprint("disc");
+            break;
+        case insert_group:
+            tprint("insert");
+            break;
+        case vcenter_group:
+            tprint("vcenter");
+            break;
+        case math_group:
+        case math_choice_group:
+        case math_shift_group:
+        case math_left_group:
+            tprint("math");
+            if (cur_group == math_choice_group)
+                tprint(" choice");
+            else if (cur_group == math_shift_group)
+                tprint(" shift");
+            else if (cur_group == math_left_group)
+                tprint(" left");
+            break;
+    }                           /* there are no other cases */
+    tprint(" group (level ");
+    print_int(cur_level);
+    print_char(')');
+    if (saved_value(-1) != 0) { /* |saved_line| */
+        if (e)
+            tprint(" entered at line ");
+        else
+            tprint(" at line ");
+        print_int(saved_value(-1));     /* |saved_line| */
+    }
+}
+
+@ The |group_trace| procedure is called when a new level of grouping
+begins (|e=false|) or ends (|e=true|) with |saved_value(-1)| containing the
+line number.
+
+@c
+void group_trace(boolean e)
+{
+    begin_diagnostic();
+    print_char('{');
+    if (e)
+        tprint("leaving ");
+    else
+        tprint("entering ");
+    print_group(e);
+    print_char('}');
+    end_diagnostic(false);
+}
+
+@ A group entered (or a conditional started) in one file may end in a
+different file.  Such slight anomalies, although perfectly legitimate,
+may cause errors that are difficult to locate.  In order to be able to
+give a warning message when such anomalies occur, \eTeX\ uses the
+|grp_stack| and |if_stack| arrays to record the initial |cur_boundary|
+and |cond_ptr| values for each input file.
+
+@c
+save_pointer *grp_stack;        /* initial |cur_boundary| */
+halfword *if_stack;             /* initial |cond_ptr| */
+
+@ When a group ends that was apparently entered in a different input
+file, the |group_warning| procedure is invoked in order to update the
+|grp_stack|.  If moreover \.{\\tracingnesting} is positive we want to
+give a warning message.  The situation is, however, somewhat complicated
+by two facts:  (1)~There may be |grp_stack| elements without a
+corresponding \.{\\input} file or \.{\\scantokens} pseudo file (e.g.,
+error insertions from the terminal); and (2)~the relevant information is
+recorded in the |name_field| of the |input_stack| only loosely
+synchronized with the |in_open| variable indexing |grp_stack|.
+
+@c
+void group_warning(void)
+{
+    boolean w = false;                  /* do we need a warning? */
+    int i = in_open;                    /* index into |grp_stack| */
+    base_ptr = input_ptr;
+    input_stack[base_ptr] = cur_input;  /* store current state */
+    while ((grp_stack[i] == cur_boundary) && (i > 0)) {
+        /* Set variable |w| to indicate if this case should be reported */
+        /* This code scans the input stack in order to determine the type of the
+           current input file. */
+        if (tracing_nesting_par > 0) {
+            while ((input_stack[base_ptr].state_field == token_list) ||
+                   (input_stack[base_ptr].index_field > i))
+                decr(base_ptr);
+            if (input_stack[base_ptr].name_field > 17)
+                w = true;
+        }
+
+        grp_stack[i] = save_value(save_ptr);
+        decr(i);
+    }
+    if (w) {
+        tprint_nl("Warning: end of ");
+        print_group(true);
+        tprint(" of a different file");
+        print_ln();
+        if (tracing_nesting_par > 1)
+            show_context();
+        if (history == spotless)
+            history = warning_issued;
+    }
+}
+
+@ When a conditional ends that was apparently started in a different
+input file, the |if_warning| procedure is invoked in order to update the
+|if_stack|.  If moreover \.{\\tracingnesting} is positive we want to
+give a warning message (with the same complications as above).
+
+@c
+void if_warning(void)
+{
+    boolean w = false;                  /* do we need a warning? */
+    int i = in_open;
+    base_ptr = input_ptr;
+    input_stack[base_ptr] = cur_input;  /* store current state */
+    while (if_stack[i] == cond_ptr) {
+        /* Set variable |w| to... */
+        if (tracing_nesting_par > 0) {
+            while ((input_stack[base_ptr].state_field == token_list) ||
+                   (input_stack[base_ptr].index_field > i))
+                decr(base_ptr);
+            if (input_stack[base_ptr].name_field > 17)
+                w = true;
+        }
+
+        if_stack[i] = vlink(cond_ptr);
+        decr(i);
+    }
+    if (w) {
+        tprint_nl("Warning: end of ");
+        print_cmd_chr(if_test_cmd, cur_if);
+        print_if_line(if_line);
+        tprint(" of a different file");
+        print_ln();
+        if (tracing_nesting_par > 1)
+            show_context();
+        if (history == spotless)
+            history = warning_issued;
+    }
+}
+
+
+@ Conversely, the |file_warning| procedure is invoked when a file ends
+and some groups entered or conditionals started while reading from that
+file are still incomplete.
+
+@c
+void file_warning(void)
+{
+    halfword p = save_ptr; /* saved value of |save_ptr| or |cond_ptr| */
+    int l = cur_level;     /* saved value of |cur_level| or |if_limit| */
+    int c = cur_group;     /* saved value of |cur_group| or |cur_if| */
+    int i;                 /* saved value of |if_line| */
+    save_ptr = cur_boundary;
+    while (grp_stack[in_open] != save_ptr) {
+        decr(cur_level);
+        tprint_nl("Warning: end of file when ");
+        print_group(true);
+        tprint(" is incomplete");
+        cur_group = save_level(save_ptr);
+        save_ptr = save_value(save_ptr);
+    }
+    save_ptr = p;
+    cur_level = (quarterword) l;
+    cur_group = (group_code) c; /* restore old values */
+    p = cond_ptr;
+    l = if_limit;
+    c = cur_if;
+    i = if_line;
+    while (if_stack[in_open] != cond_ptr) {
+        tprint_nl("Warning: end of file when ");
+        print_cmd_chr(if_test_cmd, cur_if);
+        if (if_limit == fi_code)
+            tprint_esc("else");
+        print_if_line(if_line);
+        tprint(" is incomplete");
+        if_line = if_line_field(cond_ptr);
+        cur_if = if_limit_subtype(cond_ptr);
+        if_limit = if_limit_type(cond_ptr);
+        cond_ptr = vlink(cond_ptr);
+    }
+    cond_ptr = p;
+    if_limit = l;
+    cur_if = c;
+    if_line = i;                /* restore old values */
+    print_ln();
+    if (tracing_nesting_par > 1)
+        show_context();
+    if (history == spotless)
+        history = warning_issued;
+}
+
+@ @c
+halfword last_line_fill;        /* the |par_fill_skip| glue node of the new paragraph */
+
+@ The lua interface needs some extra functions. The functions
+themselves are quite boring, but they are handy because otherwise this
+internal stuff has to be accessed from C directly, where lots of the
+defines are not available.
+
+@c
+#define get_tex_dimen_register(j) dimen(j)
+#define get_tex_skip_register(j) skip(j)
+#define get_tex_mu_skip_register(j) mu_skip(j)
+#define get_tex_count_register(j) count(j)
+#define get_tex_attribute_register(j) attribute(j)
+#define get_tex_box_register(j) box(j)
+
+/* these can now be macros (todo) */
+
+int get_tex_extension_count_register(int i)
+{
+    return (int) int_par(backend_int_base-int_base+i);
+}
+
+void set_tex_extension_count_register(int i, int d)
+{
+    int_par(backend_int_base-int_base+i) = d;
+}
+
+int get_tex_extension_dimen_register(int i)
+{
+    return (int) dimen_par(backend_dimen_base-dimen_base+i);
+}
+
+void set_tex_extension_dimen_register(int i, int d)
+{
+    dimen_par(backend_dimen_base-dimen_base+i) = d;
+}
+
+int get_tex_extension_toks_register(int i)
+{
+    return equiv(backend_toks_base+i);
+}
+
+int set_tex_dimen_register(int j, scaled v)
+{
+    int a;                      /* return non-nil for error */
+    if (global_defs_par > 0)
+        a = 4;
+    else
+        a = 0;
+    word_define(j + scaled_base, v);
+    return 0;
+}
+
+int set_tex_skip_register(int j, halfword v)
+{
+    int a;                      /* return non-nil for error */
+    if (global_defs_par > 0)
+        a = 4;
+    else
+        a = 0;
+    if (type(v) != glue_spec_node)
+        return 1;
+    word_define(j + skip_base, v);
+    return 0;
+}
+
+int set_tex_mu_skip_register(int j, halfword v)
+{
+    int a;                      /* return non-nil for error */
+    if (global_defs_par > 0)
+        a = 4;
+    else
+        a = 0;
+    if (type(v) != glue_spec_node)
+        return 1;
+    word_define(j + mu_skip_base, v);
+    return 0;
+}
+
+int set_tex_count_register(int j, scaled v)
+{
+    int a;                      /* return non-nil for error */
+    if (global_defs_par > 0)
+        a = 4;
+    else
+        a = 0;
+    word_define(j + count_base, v);
+    return 0;
+}
+
+int set_tex_box_register(int j, scaled v)
+{
+    int a;                      /* return non-nil for error */
+    if (global_defs_par > 0)
+        a = 4;
+    else
+        a = 0;
+    define(j + box_base, box_ref_cmd, v);
+    return 0;
+}
+
+int set_tex_attribute_register(int j, scaled v)
+{
+    int a;                      /* return non-nil for error */
+    if (global_defs_par > 0)
+        a = 4;
+    else
+        a = 0;
+    if (j > max_used_attr)
+        max_used_attr = j;
+    attr_list_cache = cache_disabled;
+    word_define(j + attribute_base, v);
+    return 0;
+}
+
+int get_tex_toks_register(int j)
+{
+    str_number s = get_nullstr();
+    if (toks(j) != null) {
+        s = tokens_to_string(toks(j));
+    }
+    return s;
+}
+
+int set_tex_toks_register(int j, lstring s)
+{
+    int a;
+    halfword ref = get_avail();
+    (void) str_toks(s);
+    set_token_ref_count(ref, 0);
+    set_token_link(ref, token_link(temp_token_head));
+    if (global_defs_par > 0)
+        a = 4;
+    else
+        a = 0;
+    define(j + toks_base, call_cmd, ref);
+    return 0;
+}
+
+int scan_tex_toks_register(int j, int c, lstring s)
+{
+    int a;
+    halfword ref = get_avail();
+    (void) str_scan_toks(c,s);
+    set_token_ref_count(ref, 0);
+    set_token_link(ref, token_link(temp_token_head));
+    if (global_defs_par > 0)
+        a = 4;
+    else
+        a = 0;
+    define(j + toks_base, call_cmd, ref);
+    return 0;
+}
+
+scaled get_tex_box_width(int j)
+{
+    halfword q = box(j);
+    if (q != null)
+        return width(q);
+    return 0;
+}
+
+int set_tex_box_width(int j, scaled v)
+{
+    halfword q = box(j);
+    if (q == null)
+        return 1;
+    width(q) = v;
+    return 0;
+}
+
+scaled get_tex_box_height(int j)
+{
+    halfword q = box(j);
+    if (q != null)
+        return height(q);
+    return 0;
+}
+
+int set_tex_box_height(int j, scaled v)
+{
+    halfword q = box(j);
+    if (q == null)
+        return 1;
+    height(q) = v;
+    return 0;
+}
+
+scaled get_tex_box_depth(int j)
+{
+    halfword q = box(j);
+    if (q != null)
+        return depth(q);
+    return 0;
+}
+
+int set_tex_box_depth(int j, scaled v)
+{
+    halfword q = box(j);
+    if (q == null)
+        return 1;
+    depth(q) = v;
+    return 0;
+}
+
+@ This section is devoted to the {\sl Synchronize \TeX nology}
+- or simply {\sl Sync\TeX} - used to synchronize between input and output.
+This section explains how synchronization basics are implemented.
+Before we enter into more technical details,
+let us recall in a few words what is synchronization.
+
+\TeX\ typesetting system clearly separates the input and the output material,
+and synchronization will provide a new link between both that can help
+text editors and viewers to work together.
+More precisely, forwards synchronization is the ability,
+given a location in the input source file,
+to find what is the corresponding place in the output.
+Backwards synchronization just performs the opposite:
+given a location in the output,
+retrieve the corresponding material in the input source file.
+
+For better code management and maintainance, we adopt a naming convention.
+Throughout this program, code related to the {\sl Synchronize \TeX nology} is tagged
+with the ``{\sl synctex}'' key word. Any code extract where {\sl Sync\TeX} plays
+its part, either explicitly or implicitly, (should) contain the string ``{\sl synctex}''.
+This naming convention also holds for external files.
+Moreover, all the code related to {\sl Sync\TeX} is gathered in this section,
+except the definitions.
+
+Enabling synchronization should be performed from the command line,
+|synctexoption| is used for that purpose.
+This global integer variable is declared here but it is not used here.
+This is just a placeholder where the command line controller will put
+the {\sl Sync\TeX} related options, and the {\sl Sync\TeX} controller will read them.
+
+@c
+int synctexoption;
+
+@ A convenient primitive is provided:
+\.{\\synctex=1} in the input source file enables synchronization whereas
+\.{\\synctex=0} disables it.
+Its memory address is |synctex_code|.
+It is initialized by the {\sl Sync\TeX} controller to the command-line option if given.
+The controller may filter some reserved bits.
+
+In order to give the {\sl Sync\TeX} controller read and write access to
+the contents of the \.{\\synctex} primitive, we declare |synctexoffset|,
+such that |mem[synctexoffset]| and \.{\\synctex} correspond to
+the same memory storage. |synctexoffset| is initialized to
+the correct value when quite everything is initialized.
+
+@c
+int synctexoffset;              /* holds the true value of |synctex_code| */
+
+@ Synchronization is achieved with the help of an auxiliary file named
+`\.{{\sl jobname}.synctex}' ({\sl jobname} is the contents of the
+\.{\\jobname} macro), where a {\sl Sync\TeX} controller implemented
+in the external |synctex.c| file will store geometrical information.
+This {\sl Sync\TeX} controller will take care of every technical details
+concerning the {\sl Sync\TeX} file, we will only focus on the messages
+the controller will receive from the \TeX\ program.
+
+The most accurate synchronization information should allow to map
+any character of the input source file to the corresponding location
+in the output, if relevant.
+Ideally, the synchronization information of the input material consists of
+the file name, the line and column numbers of every character.
+The synchronization information in the output is simply the page number and
+either point coordinates, or box dimensions and position.
+The problem is that the mapping between these informations is only known at
+ship out time, which means that we must keep track of the input
+synchronization information until the pages ship out.
+
+As \TeX\ only knows about file names and line numbers,
+but forgets the column numbers, we only consider a
+restricted input synchronization information called {\sl Sync\TeX\ information}.
+It consists of a unique file name identifier, the {\sl Sync\TeX\ file tag},
+and the line number.
+
+Keeping track of such information,
+should be different whether characters or nodes are involved.
+Actually, only certain nodes are involved in {\sl Sync\TeX},
+we call them {\sl synchronized nodes}.
+Synchronized nodes store the {\sl Sync\TeX} information in their last two words:
+the first one contains a {\sl Sync\TeX\ file tag} uniquely identifying
+the input file, and the second one contains the current line number,
+as returned by the \.{\\inputlineno} primitive.
+The |synctex_field_size| macro contains the necessary size to store
+the {\sl Sync\TeX} information in a node.
+
+When declaring the size of a new node, it is recommanded to use the following
+convention: if the node is synchronized, use a definition similar to
+|my_synchronized_node_size|={\sl xxx}+|synctex_field_size|.
+Moreover, one should expect that the {\sl Sync\TeX} information is always stored
+in the last two words of a synchronized node.
+
+By default, every node with a sufficiently big size is initialized
+at creation time in the |get_node| routine with the current
+{\sl Sync\TeX} information, whether or not the node is synchronized.
+One purpose is to set this information very early in order to minimize code
+dependencies, including forthcoming extensions.
+Another purpose is to avoid the assumption that every node type has a dedicated getter,
+where initialization should take place. Actually, it appears that some nodes are created
+using directly the |get_node| routine and not the dedicated constructor.
+And finally, initializing the node at only one place is less error prone.
+
+Instead of storing the input file name, it is better to store just an identifier.
+Each time \TeX\ opens a new file, it notifies the {\sl Sync\TeX} controller with
+a |synctex_start_input| message.
+This controller will create a new {\sl Sync\TeX} file tag and
+will update the current input state record accordingly.
+If the input comes from the terminal or a pseudo file, the |synctex_tag| is set to 0.
+It results in automatically disabling synchronization for material
+input from the terminal or pseudo files.
+
+Synchronized nodes are boxes, math, kern and glue nodes.
+Other nodes should be synchronized too, in particular math noads.
+\TeX\ assumes that math, kern and glue nodes have the same size,
+this is why both are synchronized.
+{\sl In fine}, only horizontal lists are really used in {\sl Sync\TeX},
+but all box nodes are considered the same with respect to synchronization,
+because a box node type is allowed to change at execution time.
+
+{\sl Nota Bene:}
+The {\sl Sync\TeX} code is very close to the memory model.
+It is not connected to any other part of the code,
+except for memory management. It is possible to neutralize the {\sl Sync\TeX} code
+rather simply. The first step is to define a null |synctex_field_size|.
+The second step is to comment out the code in ``Initialize bigger nodes...'' and every
+``Copy ... {\sl Sync\TeX} information''.
+The last step will be to comment out the |synctex_tag_field| related code in the
+definition of |synctex_tag| and the various ``Prepare ... {\sl Sync\TeX} information''.
+Then all the remaining code should be just harmless.
+The resulting program would behave exactly the same as if absolutely no {\sl Sync\TeX}
+related code was there, including memory management.
+Of course, all this assumes that {\sl Sync\TeX} is turned off from the command line.
+@^synctex@>
+@^synchronization@>
+
+@ Here are extra variables for Web2c.  (This numbering of the
+system-dependent section allows easy integration of Web2c and e-\TeX, etc.)
+@^<system dependencies@>
+
+@c
+pool_pointer edit_name_start;   /* where the filename to switch to starts */
+int edit_name_length, edit_line;        /* what line to start editing at */
+boolean stop_at_space;          /* whether |more_name| returns false for space */
+
+@ The |edit_name_start| will be set to point into |str_pool| somewhere after
+its beginning if \TeX\ is supposed to switch to an editor on exit.
+
+@c
+int shellenabledp;
+int restrictedshell;
+char *output_comment;
+
+@ Are we printing extra info as we read the format file?
+
+@c
+boolean debug_format_file;
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/filename.w
@@ -0,0 +1,361 @@
+% filename.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"
+
+
+@  In order to isolate the system-dependent aspects of file names, the
+  @^system dependencies@>
+  system-independent parts of \TeX\ are expressed in terms
+  of three system-dependent
+  procedures called |begin_name|, |more_name|, and |end_name|. In
+  essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
+  the system-independent driver program does the operations
+  $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
+  \,|end_name|.$$
+  These three procedures communicate with each other via global variables.
+  Afterwards the file name will appear in the string pool as three strings
+  called |cur_name|\penalty10000\hskip-.05em,
+  |cur_area|, and |cur_ext|; the latter two are null (i.e.,
+  |""|), unless they were explicitly specified by the user.
+  
+  Actually the situation is slightly more complicated, because \TeX\ needs
+  to know when the file name ends. The |more_name| routine is a function
+  (with side effects) that returns |true| on the calls |more_name|$(c_1)$,
+  \dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
+  returns |false|; or, it returns |true| and the token following $c_n$ is
+  something like `\.{\\hbox}' (i.e., not a character). In other words,
+  |more_name| is supposed to return |true| unless it is sure that the
+  file name has been completely scanned; and |end_name| is supposed to be able
+  to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
+  whether $|more_name|(c_n)$ returned |true| or |false|.
+
+
+@ Here now is the first of the system-dependent routines for file name scanning. 
+@^system dependencies@> 
+
+@c
+static void begin_name(void)
+{
+    area_delimiter = 0;
+    ext_delimiter = 0;
+    quoted_filename = false;
+}
+
+@ And here's the second. The string pool might change as the file name is
+   being scanned, since a new \.{\\csname} might be entered; therefore we keep
+   |area_delimiter| and |ext_delimiter| relative to the beginning of the current
+   string, instead of assigning an absolute address like |pool_ptr| to them.
+   @^system dependencies@> 
+
+@c
+static boolean more_name(ASCII_code c)
+{
+    if (c == ' ' && stop_at_space && (!quoted_filename)) {
+        return false;
+    } else if (c == '"') {
+        quoted_filename = !quoted_filename;
+        return true;
+    } else {
+        str_room(1);
+        append_char(c);         /* contribute |c| to the current string */
+        if (IS_DIR_SEP(c)) {
+            area_delimiter = (pool_pointer) cur_length;
+            ext_delimiter = 0;
+        } else if (c == '.')
+            ext_delimiter = (pool_pointer) cur_length;
+        return true;
+    }
+}
+
+@ The third.
+@^system dependencies@>
+
+@c
+static void end_name(void)
+{
+    unsigned char *s;
+    if (str_ptr + 3 > (max_strings + STRING_OFFSET))
+        overflow("number of strings",
+                 (unsigned) (max_strings - init_str_ptr + STRING_OFFSET));
+    /* at this point, the full string lives in |cur_string| */
+    if (area_delimiter == 0) {
+        cur_area = get_nullstr();
+    } else {
+        s = (unsigned char *) xstrdup((char *) (cur_string + area_delimiter));
+        cur_string[area_delimiter] = '\0';
+        cur_length = (unsigned) strlen((char *) cur_string);
+        cur_area = make_string();
+	xfree(cur_string);
+        cur_length = (unsigned) strlen((char *) s);
+        cur_string = s;
+    }
+    if (ext_delimiter == 0) {
+        cur_name = make_string();
+        cur_ext = get_nullstr();
+    } else {
+        int l = (ext_delimiter - area_delimiter - 1);
+        s = (unsigned char *) xstrdup((char *) (cur_string + l));
+        cur_string[l] = '\0';
+        cur_length = (unsigned) strlen((char *) cur_string);
+        cur_name = make_string();
+	xfree(cur_string);
+        cur_length = (unsigned) strlen((char *) s);
+        cur_string = s;
+        cur_ext = make_string();
+    }
+}
+
+@ Now let's consider the ``driver'' routines by which \TeX\ deals with file names
+   in a system-independent manner.  First comes a procedure that looks for a
+   file name in the input by calling |get_x_token| for the information.
+
+@c
+void scan_file_name(void)
+{
+    str_number u = 0;
+    name_in_progress = true;
+    begin_name();
+    /* Get the next non-blank non-call token; */
+    do {
+        get_x_token();
+    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+    while (true) {
+        if ((cur_cmd > other_char_cmd) || (cur_chr > biggest_char)) {   /* not a character */
+            back_input();
+            break;
+        }
+        /* If |cur_chr| is a space and we're not scanning a token list, check
+           whether we're at the end of the buffer. Otherwise we end up adding
+           spurious spaces to file names in some cases. */
+        if ((cur_chr == ' ') && (istate != token_list) && (iloc > ilimit)
+            && !quoted_filename)
+            break;
+        if (cur_chr > 127) {
+            unsigned char *bytes;
+            unsigned char *thebytes;
+            thebytes = uni2str((unsigned) cur_chr);
+            bytes = thebytes;
+            while (*bytes) {
+                if (!more_name(*bytes))
+                    break;
+                bytes++;
+            }
+            xfree(thebytes);
+        } else {
+            if (!more_name(cur_chr))
+                break;
+        }
+        u = save_cur_string();
+        get_x_token();
+        restore_cur_string(u);
+    }
+    end_name();
+    name_in_progress = false;
+}
+
+@ This function constructs a the three file name strings from a token list 
+@c
+void scan_file_name_toks(void)
+{
+    char *a, *n, *e, *s = NULL;
+    int i, l = 0;
+    (void) scan_toks(false, true);
+    s = tokenlist_to_cstring(def_ref, true, &l);
+    a = n = s;
+    e = NULL;
+    for (i = 0; i < l; i++) {
+        if (IS_DIR_SEP(s[i])) {
+            n = s + i + 1;
+            e = NULL;
+        } else if (s[i] == '.') {
+            e = s + i;
+        }
+    }
+    if (n != s) {               /* explicit area */
+        cur_area = maketexlstring(a, (size_t) (n - a));
+    } else {
+        cur_area = get_nullstr();
+    }
+    if (e != NULL) {            /* explicit extension */
+        cur_name = maketexlstring(n, (size_t) (e - n));
+        cur_ext = maketexstring(e);
+    } else {
+        cur_name = maketexstring(n);
+        cur_ext = get_nullstr();
+    }
+    xfree(s);
+  
+}
+
+
+
+
+@  Here is a routine that manufactures the output file names, assuming that
+  |job_name<>0|. It ignores and changes the current settings of |cur_area|
+  and |cur_ext|.
+
+@c
+char *pack_job_name(const char *s)
+{                               /* |s = ".log"|, |".dvi"|, or |format_extension| */
+    cur_area = get_nullstr();
+    cur_ext = maketexstring(s);
+    cur_name = job_name;
+    return pack_file_name(cur_name, cur_area, cur_ext);
+}
+
+@ If some trouble arises when \TeX\ tries to open a file, the following
+   routine calls upon the user to supply another file name. Parameter~|s|
+   is used in the error message to identify the type of file; parameter~|e|
+   is the default extension if none is given. Upon exit from the routine,
+   variables |cur_name|, |cur_area|, and |cur_ext| are
+   ready for another attempt at file opening.
+
+@c
+char *prompt_file_name(const char *s, const char *e)
+{
+    int k;                      /* index into |buffer| */
+    str_number saved_cur_name;  /* to catch empty terminal input */
+    int callback_id ;
+    char prompt[256];
+    char *ar, *na, *ex;
+    saved_cur_name = cur_name;
+    if (interaction == scroll_mode) {
+        wake_up_terminal();
+    }
+    ar = makecstring(cur_area);
+    na = makecstring(cur_name);
+    ex = makecstring(cur_ext);
+    if (strcmp(s, "input file name") == 0) {
+        snprintf(prompt, 255, "I can't find file `%s%s%s'.", ar, na, ex);
+    } else {
+        snprintf(prompt, 255, "I can't write on file `%s%s%s'.", ar, na, ex);
+    }
+    free(ar);
+    free(na);
+    free(ex);
+    print_err(prompt);
+    callback_id = callback_defined(show_error_hook_callback);
+    if (callback_id > 0) {
+        flush_err();
+        run_callback(callback_id, "->");
+    } else {
+        if ((strcmp(e, ".tex") == 0) || (strcmp(e, "") == 0))
+            show_context();
+        if (strcmp(s, "input file name") == 0)
+            tprint_nl(promptfilenamehelpmsg ")");
+    }
+    tprint_nl("Please type another ");
+    tprint(s);
+    if (interaction < scroll_mode)
+        fatal_error("*** (job aborted, file error in nonstop mode)");
+    clear_terminal();
+    prompt_input(": ");
+    begin_name();
+    k = first;
+    while ((buffer[k] == ' ') && (k < last))
+        k++;
+    while (true) {
+        if (k == last)
+            break;
+        if (!more_name(buffer[k]))
+            break;
+        k++;
+    }
+    end_name();
+    if (cur_ext == get_nullstr())
+        cur_ext = maketexstring(e);
+    if (str_length(cur_name) == 0)
+        cur_name = saved_cur_name;
+    return pack_file_name(cur_name, cur_area, cur_ext);
+}
+
+
+@ @c
+void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e)
+{
+    boolean must_quote;         /* whether to quote the filename */
+    unsigned char *j;           /* index into string */
+    must_quote = false;
+    if (a != NULL) {
+        j = a;
+        while ((!must_quote) && (*j)) {
+            must_quote = (*j == ' ');
+            j++;
+        }
+    }
+    if (n != NULL) {
+        j = n;
+        while ((!must_quote) && (*j)) {
+            must_quote = (*j == ' ');
+            j++;
+        }
+    }
+    if (e != NULL) {
+        j = e;
+        while ((!must_quote) && (*j)) {
+            must_quote = (*j == ' ');
+            j++;
+        }
+    }
+    /* FIXME: Alternative is to assume that any filename that has to be quoted has
+       at least one quoted component...if we pick this, a number of insertions
+       of |print_file_name| should go away.
+       |must_quote|:=((|a|<>0)and(|str_pool|[|str_start|[|a|]]=""""))or
+       ((|n|<>0)and(|str_pool|[|str_start|[|n|]]=""""))or
+       ((|e|<>0)and(|str_pool|[|str_start|[|e|]]="""")); */
+
+    if (must_quote)
+        print_char('"');
+    if (a != NULL) {
+        for (j = a; *j; j++)
+            if (*j != '"')
+                print_char(*j);
+    }
+    if (n != NULL) {
+        for (j = n; *j; j++)
+            if (*j != '"')
+                print_char(*j);
+    }
+    if (e != NULL) {
+        for (j = e; *j; j++)
+            if (*j != '"')
+                print_char(*j);
+    }
+    if (must_quote)
+        print_char('"');
+}
+
+@ @c
+void print_file_name(str_number n, str_number a, str_number e)
+{
+    char *nam, *are, *ext;
+    nam = makecstring(n);
+    are = makecstring(a);
+    ext = makecstring(e);
+    tprint_file_name((unsigned char *) nam, (unsigned char *) are,
+                     (unsigned char *) ext);
+    free(nam);
+    free(are);
+    free(ext);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/filename.c
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
-
-filename.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-
-/*tex
-
-    In order to isolate the system-dependent aspects of file names, the @^system
-    dependencies@> system-independent parts of \TeX\ are expressed in terms of
-    three system-dependent procedures called |begin_name|, |more_name|, and
-    |end_name|. In essence, if the user-specified characters of the file name are
-    $c_1\ldots c_n$, the system-independent driver program does the operations
-    $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
-    \,|end_name|.$$
-
-    These three procedures communicate with each other via global variables.
-    Afterwards the file name will appear in the string pool as three strings
-    called |cur_name|\penalty10000\hskip-.05em, |cur_area|, and |cur_ext|; the
-    latter two are null (i.e., |""|), unless they were explicitly specified by
-    the user.
-
-    Actually the situation is slightly more complicated, because \TeX\ needs to
-    know when the file name ends. The |more_name| routine is a function (with
-    side effects) that returns |true| on the calls |more_name|$(c_1)$, \dots,
-    |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ returns |false|;
-    or, it returns |true| and the token following $c_n$ is something like
-    `\.{\\hbox}' (i.e., not a character). In other words, |more_name| is supposed
-    to return |true| unless it is sure that the file name has been completely
-    scanned; and |end_name| is supposed to be able to finish the assembly of
-    |cur_name|, |cur_area|, and |cur_ext| regardless of whether
-    $|more_name|(c_n)$ returned |true| or |false|.
-
-
-    Here now is the first of the system-dependent routines for file name
-    scanning. @^system dependencies@>
-
-*/
-
-static void begin_name(void)
-{
-    area_delimiter = 0;
-    ext_delimiter = 0;
-    quoted_filename = false;
-}
-
-/*tex
-
-    And here's the second. The string pool might change as the file name is being
-    scanned, since a new \.{\\csname} might be entered; therefore we keep
-    |area_delimiter| and |ext_delimiter| relative to the beginning of the current
-    string, instead of assigning an absolute address like |pool_ptr| to them.
-    @^system dependencies@>
-
-*/
-
-static boolean more_name(ASCII_code c)
-{
-    if (c == ' ' && stop_at_space && (!quoted_filename)) {
-        return false;
-    } else if (c == '"') {
-        quoted_filename = !quoted_filename;
-        return true;
-    } else {
-        str_room(1);
-        append_char(c);
-        if (IS_DIR_SEP(c)) {
-            area_delimiter = (pool_pointer) cur_length;
-            ext_delimiter = 0;
-        } else if (c == '.')
-            ext_delimiter = (pool_pointer) cur_length;
-        return true;
-    }
-}
-
-/*tex
-
-    The third. @^system dependencies@>
-
-*/
-
-static void end_name(void)
-{
-    unsigned char *s;
-    if (str_ptr + 3 > (max_strings + STRING_OFFSET))
-        overflow(
-            "number of strings",
-            (unsigned) (max_strings - init_str_ptr + STRING_OFFSET)
-        );
-    /*tex At this point, the full string lives in |cur_string|. */
-    if (area_delimiter == 0) {
-        cur_area = get_nullstr();
-    } else {
-        s = (unsigned char *) xstrdup((char *) (cur_string + area_delimiter));
-        cur_string[area_delimiter] = '\0';
-        cur_length = (unsigned) strlen((char *) cur_string);
-        cur_area = make_string();
-        xfree(cur_string);
-        cur_length = (unsigned) strlen((char *) s);
-        cur_string = s;
-    }
-    if (ext_delimiter == 0) {
-        cur_name = make_string();
-        cur_ext = get_nullstr();
-    } else {
-        int l = (ext_delimiter - area_delimiter - 1);
-        s = (unsigned char *) xstrdup((char *) (cur_string + l));
-        cur_string[l] = '\0';
-        cur_length = (unsigned) strlen((char *) cur_string);
-        cur_name = make_string();
-        xfree(cur_string);
-        cur_length = (unsigned) strlen((char *) s);
-        cur_string = s;
-        cur_ext = make_string();
-    }
-}
-
-/*tex
-
-   Now let's consider the ``driver'' routines by which \TeX\ deals with file
-   names in a system-independent manner. First comes a procedure that looks for a
-   file name in the input by calling |get_x_token| for the information.
-
-*/
-
-void scan_file_name(void)
-{
-    str_number u = 0;
-    name_in_progress = true;
-    begin_name();
-    /*tex Get the next non-blank non-call token: */
-    do {
-        get_x_token();
-    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
-
-    while (true) {
-        if ((cur_cmd > other_char_cmd) || (cur_chr > biggest_char)) {   /* not a character */
-            back_input();
-            break;
-        }
-        /*tex
-            If |cur_chr| is a space and we're not scanning a token list, check
-            whether we're at the end of the buffer. Otherwise we end up adding
-            spurious spaces to file names in some cases.
-        */
-        if ((cur_chr == ' ') && (istate != token_list) && (iloc > ilimit)
-            && !quoted_filename)
-            break;
-        if (cur_chr > 127) {
-            unsigned char *bytes;
-            unsigned char *thebytes;
-            thebytes = uni2str((unsigned) cur_chr);
-            bytes = thebytes;
-            while (*bytes) {
-                if (!more_name(*bytes))
-                    break;
-                bytes++;
-            }
-            xfree(thebytes);
-        } else {
-            if (!more_name(cur_chr))
-                break;
-        }
-        u = save_cur_string();
-        get_x_token();
-        restore_cur_string(u);
-    }
-    end_name();
-    name_in_progress = false;
-}
-
-/*
-    This function constructs a the three file name strings from a token list.
-*/
-
-void scan_file_name_toks(void)
-{
-    char *a, *n, *e, *s = NULL;
-    int i, l = 0;
-    (void) scan_toks(false, true);
-    s = tokenlist_to_cstring(def_ref, true, &l);
-    a = n = s;
-    e = NULL;
-    for (i = 0; i < l; i++) {
-        if (IS_DIR_SEP(s[i])) {
-            n = s + i + 1;
-            e = NULL;
-        } else if (s[i] == '.') {
-            e = s + i;
-        }
-    }
-    if (n != s) {
-        /*tex explicit area */
-        cur_area = maketexlstring(a, (size_t) (n - a));
-    } else {
-        cur_area = get_nullstr();
-    }
-    if (e != NULL) {
-        /*tex explicit extension */
-        cur_name = maketexlstring(n, (size_t) (e - n));
-        cur_ext = maketexstring(e);
-    } else {
-        cur_name = maketexstring(n);
-        cur_ext = get_nullstr();
-    }
-    xfree(s);
-
-}
-
-/*tex
-
-    Here is a routine that manufactures the output file names, assuming that
-    |job_name<>0|. It ignores and changes the current settings of |cur_area| and
-    |cur_ext|; |s = ".log"|, |".dvi"|, or |format_extension|
-*/
-
-char *pack_job_name(const char *s)
-{
-    cur_area = get_nullstr();
-    cur_ext = maketexstring(s);
-    cur_name = job_name;
-    return pack_file_name(cur_name, cur_area, cur_ext);
-}
-
-/*tex
-
-    If some trouble arises when \TeX\ tries to open a file, the following routine
-    calls upon the user to supply another file name. Parameter~|s| is used in the
-    error message to identify the type of file; parameter~|e| is the default
-    extension if none is given. Upon exit from the routine, variables |cur_name|,
-    |cur_area|, and |cur_ext| are ready for another attempt at file opening.
-
-*/
-
-char *prompt_file_name(const char *s, const char *e)
-{
-    int k;                      /* index into |buffer| */
-    str_number saved_cur_name;  /* to catch empty terminal input */
-    int callback_id ;
-    char prompt[256];
-    char *ar, *na, *ex;
-    saved_cur_name = cur_name;
-    if (interaction == scroll_mode) {
-        wake_up_terminal();
-    }
-    ar = makecstring(cur_area);
-    na = makecstring(cur_name);
-    ex = makecstring(cur_ext);
-    if (strcmp(s, "input file name") == 0) {
-        snprintf(prompt, 255, "I can't find file `%s%s%s'.", ar, na, ex);
-    } else {
-        snprintf(prompt, 255, "I can't write on file `%s%s%s'.", ar, na, ex);
-    }
-    free(ar);
-    free(na);
-    free(ex);
-    print_err(prompt);
-    callback_id = callback_defined(show_error_hook_callback);
-    if (callback_id > 0) {
-        flush_err();
-        run_callback(callback_id, "->");
-    } else {
-        if ((strcmp(e, ".tex") == 0) || (strcmp(e, "") == 0))
-            show_context();
-        if (strcmp(s, "input file name") == 0)
-            tprint_nl(promptfilenamehelpmsg ")");
-    }
-    tprint_nl("Please type another ");
-    tprint(s);
-    if (interaction < scroll_mode)
-        fatal_error("*** (job aborted, file error in nonstop mode)");
-    clear_terminal();
-    prompt_input(": ");
-    begin_name();
-    k = first;
-    while ((buffer[k] == ' ') && (k < last))
-        k++;
-    while (true) {
-        if (k == last)
-            break;
-        if (!more_name(buffer[k]))
-            break;
-        k++;
-    }
-    end_name();
-    if (cur_ext == get_nullstr())
-        cur_ext = maketexstring(e);
-    if (str_length(cur_name) == 0)
-        cur_name = saved_cur_name;
-    return pack_file_name(cur_name, cur_area, cur_ext);
-}
-
-void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e)
-{
-    boolean must_quote; /* whether to quote the filename */
-    unsigned char *j;   /* index into string */
-    must_quote = false;
-    if (a != NULL) {
-        j = a;
-        while ((!must_quote) && (*j)) {
-            must_quote = (*j == ' ');
-            j++;
-        }
-    }
-    if (n != NULL) {
-        j = n;
-        while ((!must_quote) && (*j)) {
-            must_quote = (*j == ' ');
-            j++;
-        }
-    }
-    if (e != NULL) {
-        j = e;
-        while ((!must_quote) && (*j)) {
-            must_quote = (*j == ' ');
-            j++;
-        }
-    }
-    /*tex
-        Alternative is to assume that any filename that has to be quoted has at
-        least one quoted component...if we pick this, a number of insertions of
-        |print_file_name| should go away.
-    */
-    if (must_quote)
-        print_char('"');
-    if (a != NULL) {
-        for (j = a; *j; j++)
-            if (*j != '"')
-                print_char(*j);
-    }
-    if (n != NULL) {
-        for (j = n; *j; j++)
-            if (*j != '"')
-                print_char(*j);
-    }
-    if (e != NULL) {
-        for (j = e; *j; j++)
-            if (*j != '"')
-                print_char(*j);
-    }
-    if (must_quote)
-        print_char('"');
-}
-
-void print_file_name(str_number n, str_number a, str_number e)
-{
-    char *nam, *are, *ext;
-    nam = makecstring(n);
-    are = makecstring(a);
-    ext = makecstring(e);
-    tprint_file_name(
-        (unsigned char *) nam,
-        (unsigned char *) are,
-        (unsigned char *) ext
-    );
-    free(nam);
-    free(are);
-    free(ext);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/inputstack.w
@@ -0,0 +1,822 @@
+% inputstack.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"
+
+@ @c
+in_state_record *input_stack = NULL;
+int input_ptr = 0;              /* first unused location of |input_stack| */
+int max_in_stack = 0;           /* largest value of |input_ptr| when pushing */
+in_state_record cur_input;      /* the ``top'' input state */
+
+int in_open = 0;                /* the number of lines in the buffer, less one */
+int open_parens = 0;            /* the number of open text files */
+alpha_file *input_file = NULL;
+int line = 0;                   /* current line number in the current source file */
+int *line_stack = NULL;
+str_number *source_filename_stack = NULL;
+char **full_source_filename_stack = NULL;
+
+int scanner_status = 0;         /* can a subfile end now? */
+pointer warning_index = null;   /* identifier relevant to non-|normal| scanner status */
+pointer def_ref = null;         /* reference count of token list being defined */
+
+@ Here is a procedure that uses |scanner_status| to print a warning message
+when a subfile has ended, and at certain other crucial times:
+
+@c
+void runaway(void)
+{
+    pointer p = null; /* head of runaway list */
+    if (scanner_status > skipping) {
+        switch (scanner_status) {
+        case defining:
+            tprint_nl("Runaway definition");
+            p = def_ref;
+            break;
+        case matching:
+            tprint_nl("Runaway argument");
+            p = temp_token_head;
+            break;
+        case aligning:
+            tprint_nl("Runaway preamble");
+            p = hold_token_head;
+            break;
+        case absorbing:
+            tprint_nl("Runaway text");
+            p = def_ref;
+            break;
+        default:
+            /* there are no other cases */
+            break;
+        }
+        print_char('?');
+        print_ln();
+        show_token_list(token_link(p), null, error_line - 10);
+    }
+}
+
+@ The |param_stack| is an auxiliary array used to hold pointers to the token
+lists for parameters at the current level and subsidiary levels of input.
+This stack is maintained with convention (2), and it grows at a different
+rate from the others.
+
+@c
+pointer *param_stack = NULL;    /* token list pointers for parameters */
+int param_ptr = 0;              /* first unused entry in |param_stack| */
+int max_param_stack = 0;        /* largest value of |param_ptr|, will be |<=param_size+9| */
+
+@ The input routines must also interact with the processing of
+\.{\\halign} and \.{\\valign}, since the appearance of tab marks and
+\.{\\cr} in certain places is supposed to trigger the beginning of special
+$v_j$ template text in the scanner. This magic is accomplished by an
+|align_state| variable that is increased by~1 when a `\.{\char'173}' is
+scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state|
+is nonzero during the $u_j$ template, after which it is set to zero; the
+$v_j$ template begins when a tab mark or \.{\\cr} occurs at a time that
+|align_state=0|.
+
+@c
+int align_state = 0;            /* group level with respect to current alignment */
+
+@ Thus, the ``current input state'' can be very complicated indeed; there
+can be many levels and each level can arise in a variety of ways. The
+|show_context| procedure, which is used by \TeX's error-reporting routine to
+print out the current input state on all levels down to the most recent
+line of characters from an input file, illustrates most of these conventions.
+The global variable |base_ptr| contains the lowest level that was
+displayed by this procedure.
+
+@c
+int base_ptr = 0;               /* shallowest level shown by |show_context| */
+
+@ The status at each level is indicated by printing two lines, where the first
+line indicates what was read so far and the second line shows what remains
+to be read. The context is cropped, if necessary, so that the first line
+contains at most |half_error_line| characters, and the second contains
+at most |error_line|. Non-current input levels whose |token_type| is
+`|backed_up|' are shown only if they have not been fully read.
+
+@c
+static void print_token_list_type(int t)
+{
+    switch (t) {
+        case parameter:
+            tprint_nl("<argument> ");
+            break;
+        case u_template:
+        case v_template:
+            tprint_nl("<template> ");
+            break;
+        case backed_up:
+            if (iloc == null)
+                tprint_nl("<recently read> ");
+            else
+                tprint_nl("<to be read again> ");
+            break;
+        case inserted:
+            tprint_nl("<inserted text> ");
+            break;
+        case macro:
+            print_ln();
+            print_cs(iname);
+            break;
+        case output_text:
+            tprint_nl("<output> ");
+            break;
+        case every_par_text:
+            tprint_nl("<everypar> ");
+            break;
+        case every_math_text:
+            tprint_nl("<everymath> ");
+            break;
+        case every_display_text:
+            tprint_nl("<everydisplay> ");
+            break;
+        case every_hbox_text:
+            tprint_nl("<everyhbox> ");
+            break;
+        case every_vbox_text:
+            tprint_nl("<everyvbox> ");
+            break;
+        case every_job_text:
+            tprint_nl("<everyjob> ");
+            break;
+        case every_cr_text:
+            tprint_nl("<everycr> ");
+            break;
+        case mark_text:
+            tprint_nl("<mark> ");
+            break;
+        case every_eof_text:
+            tprint_nl("<everyeof> ");
+            break;
+        case write_text:
+            tprint_nl("<write> ");
+            break;
+        default:
+            tprint_nl("?");
+            /* this should never happen */
+            break;
+    }
+}
+
+@ Here it is necessary to explain a little trick. We don't want to store a long
+string that corresponds to a token list, because that string might take up
+lots of memory; and we are printing during a time when an error message is
+being given, so we dare not do anything that might overflow one of \TeX's
+tables. So `pseudoprinting' is the answer: We enter a mode of printing
+that stores characters into a buffer of length |error_line|, where character
+$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
+|k<trick_count|, otherwise character |k| is dropped. Initially we set
+|tally:=0| and |trick_count:=1000000|; then when we reach the
+point where transition from line 1 to line 2 should occur, we
+set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
+tally+1+error_line-half_error_line)|. At the end of the
+pseudoprinting, the values of |first_count|, |tally|, and
+|trick_count| give us all the information we need to print the two lines,
+and all of the necessary text is in |trick_buf|.
+
+Namely, let |l| be the length of the descriptive information that appears
+on the first line. The length of the context information gathered for that
+line is |k=first_count|, and the length of the context information
+gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
+where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
+descriptive information on line~1, and set |n:=l+k|; here |n| is the
+length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
+and print `\.{...}' followed by
+$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
+where subscripts of |trick_buf| are circular modulo |error_line|. The
+second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
+unless |n+m>error_line|; in the latter case, further cropping is done.
+This is easier to program than to explain.
+
+@ The following code sets up the print routines so that they will gather
+the desired information.
+
+@c
+void set_trick_count(void)
+{
+    first_count = tally;
+    trick_count = tally + 1 + error_line - half_error_line;
+    if (trick_count < error_line)
+        trick_count = error_line;
+}
+
+#define begin_pseudoprint() do { \
+    l=tally; \
+    tally=0; \
+    selector=pseudo; \
+    trick_count=1000000; \
+  } while (0)
+
+#define PSEUDO_PRINT_THE_LINE()	do { \
+    begin_pseudoprint(); \
+    if (buffer[ilimit]==end_line_char_par) \
+        j=ilimit; \
+    else \
+        j=ilimit+1; /* determine the effective end of the line */ \
+    if (j>0) { \
+        for (i=istart;i<=j-1;i++) { \
+            if (i==iloc) \
+                set_trick_count(); \
+            print_char(buffer[i]); \
+        } \
+    } \
+} while (0)
+
+/*
+    We don't care too much if we stay a bit too much below the max error_line
+    even if we have more room on the line. If length is really an issue then
+    any length is. After all one can set the length larger.
+*/
+
+#define print_valid_utf8(q) do { \
+    c = (int)trick_buf[q % error_line]; \
+    if (c < 128) { \
+        print_char(c); \
+    } else if (c < 194) { \
+        /* invalid */ \
+    } else if (c < 224) { \
+        print_char(c); \
+        print_char(trick_buf[(q+1) % error_line]); \
+    } else if (c < 240) { \
+        print_char(c); \
+        print_char(trick_buf[(q+1) % error_line]); \
+        print_char(trick_buf[(q+2) % error_line]); \
+    } else if (c < 245) { \
+        print_char(c); \
+        print_char(trick_buf[(q+1) % error_line]); \
+        print_char(trick_buf[(q+2) % error_line]); \
+        print_char(trick_buf[(q+3) % error_line]); \
+    } else { \
+        /* invalid */ \
+    } \
+} while (0)
+
+@ @c
+void show_context(void)
+{                                /* prints where the scanner is */
+    int old_setting;             /* saved |selector| setting */
+    int nn = -1;                 /* number of contexts shown so far, less one */
+    boolean bottom_line = false; /* have we reached the final context to be shown? */
+    int i;                       /* index into |buffer| */
+    int j;                       /* end of current line in |buffer| */
+    int l;                       /* length of descriptive information on line 1 */
+    int m;                       /* context information gathered for line 2 */
+    int n;                       /* length of line 1 */
+    int p;                       /* starting or ending place in |trick_buf| */
+    int q;                       /* temporary index */
+    int c;                       /* used in sanitizer */
+    base_ptr = input_ptr;
+    input_stack[base_ptr] = cur_input;
+    /* store current state */
+    while (true) {
+        cur_input = input_stack[base_ptr];      /* enter into the context */
+        if (istate != token_list) {
+            if ((iname > 21) || (base_ptr == 0))
+                bottom_line = true;
+        }
+        if ((base_ptr == input_ptr) || bottom_line || (nn < error_context_lines_par)) {
+            /* Display the current context */
+            if ((base_ptr == input_ptr) || (istate != token_list) || (token_type != backed_up) || (iloc != null)) {
+                /* we omit backed-up token lists that have already been read */
+                tally = 0;      /* get ready to count characters */
+                old_setting = selector;
+                if (istate != token_list) {
+                    /*
+                        Print location of current line
+
+                        This routine should be changed, if necessary, to give the best possible
+                        indication of where the current line resides in the input file. For example,
+                        on some systems it is best to print both a page and line number.
+                     */
+                    if (iname <= 17) {
+                        if (terminal_input) {
+                            if (base_ptr == 0)
+                                tprint_nl("<*>");
+                            else
+                                tprint_nl("<insert> ");
+                        } else {
+                            tprint_nl("<read ");
+                            if (iname == 17)
+                                print_char('*');
+                            else
+                                print_int(iname - 1);
+                            print_char('>');
+                        };
+                    } else {
+                        tprint_nl("l.");
+                        if (iindex == in_open) {
+                            print_int(line);
+                        } else {     /* input from a pseudo file */
+                            print_int(line_stack[iindex + 1]);
+                        }
+                    }
+                    print_char(' ');
+                    PSEUDO_PRINT_THE_LINE();
+                } else {
+                    print_token_list_type(token_type);
+
+                    begin_pseudoprint();
+                    if (token_type < macro)
+                        show_token_list(istart, iloc, 100000);
+                    else
+                        show_token_list(token_link(istart), iloc, 100000);      /* avoid reference count */
+                }
+                /* stop pseudoprinting */
+                selector = old_setting;
+                /* Print two lines using the tricky pseudoprinted information */
+                if (trick_count == 1000000)
+                    set_trick_count();
+                /* |set_trick_count| must be performed */
+                if (tally < trick_count)
+                    m = tally - first_count;
+                else
+                    m = trick_count - first_count;      /* context on line 2 */
+                if (l + first_count <= half_error_line) {
+                    p = 0;
+                    n = l + first_count;
+                } else {
+                    tprint("...");
+                    p = l + first_count - half_error_line + 3;
+                    n = half_error_line;
+                }
+                for (q = p; q <= first_count - 1; q++)
+                    print_valid_utf8(q);
+                    print_ln();
+                /* print |n| spaces to begin line~2 */
+                for (q = 1; q <= n; q++)
+                    print_char(' ');
+                if (m + n <= error_line)
+                    p = first_count + m;
+                else
+                    p = first_count + (error_line - n - 3);
+                for (q = first_count; q <= p - 1; q++)
+                    print_valid_utf8(q);
+                if (m + n > error_line)
+                    tprint("...");
+                incr(nn);
+            }
+        } else if (nn == error_context_lines_par) {
+            tprint_nl("...");
+            incr(nn);
+            /* omitted if |error_context_lines_par<0| */
+        }
+        if (bottom_line)
+            break;
+        decr(base_ptr);
+    }
+    /* restore original state */
+    cur_input = input_stack[input_ptr];
+}
+
+@ The following subroutines change the input status in commonly needed ways.
+
+First comes |push_input|, which stores the current state and creates a
+new level (having, initially, the same properties as the old).
+
+@c
+
+/* enter a new input level, save the old */
+
+# define pop_input() \
+    cur_input=input_stack[--input_ptr]
+
+# define push_input() \
+    if (input_ptr > max_in_stack) { \
+        max_in_stack = input_ptr; \
+        if (input_ptr == stack_size) \
+            overflow("input stack size", (unsigned) stack_size); \
+    } \
+    input_stack[input_ptr] = cur_input; \
+    nofilter = false; \
+    incr(input_ptr);
+
+@
+Here is a procedure that starts a new level of token-list input, given
+a token list |p| and its type |t|. If |t=macro|, the calling routine should
+set |name| and |loc|.
+
+@c
+void begin_token_list(halfword p, quarterword t)
+{
+    push_input();
+    istate = token_list;
+    istart = p;
+    token_type = (unsigned char) t;
+    if (t >= macro) {
+        /* the token list starts with a reference count */
+        add_token_ref(p);
+        if (t == macro) {
+            param_start = param_ptr;
+        } else {
+            iloc = token_link(p);
+            if (tracing_macros_par > 1) {
+                begin_diagnostic();
+                tprint_nl("");
+                if (t == mark_text)
+                    tprint_esc("mark");
+                else if (t == write_text)
+                    tprint_esc("write");
+                else
+                    print_cmd_chr(assign_toks_cmd,
+                                  t - output_text + output_routine_loc);
+                tprint("->");
+                token_show(p);
+                end_diagnostic(false);
+            }
+        }
+    } else {
+        iloc = p;
+    }
+}
+
+@ When a token list has been fully scanned, the following computations
+should be done as we leave that level of input. The |token_type| tends
+to be equal to either |backed_up| or |inserted| about 2/3 of the time.
+@^inner loop@>
+
+@c
+void end_token_list(void)
+{
+    /* leave a token-list input level */
+    if (token_type >= backed_up) {
+        /* token list to be deleted */
+        if (token_type <= inserted) {
+            flush_list(istart);
+        } else {
+            /* update reference count */
+            delete_token_ref(istart);
+            if (token_type == macro) {
+                /* parameters must be flushed */
+                while (param_ptr > param_start) {
+                    decr(param_ptr);
+                    flush_list(param_stack[param_ptr]);
+                }
+            }
+        }
+    } else if (token_type == u_template) {
+        if (align_state > 500000)
+            align_state = 0;
+        else
+            fatal_error("(interwoven alignment preambles are not allowed)");
+    }
+    pop_input();
+    check_interrupt();
+}
+
+@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has
+seen. The |back_input| procedure takes care of this by putting the token
+just scanned back into the input stream, ready to be read again. This
+procedure can be used only if |cur_tok| represents the token to be
+replaced. Some applications of \TeX\ use this procedure a lot,
+so it has been slightly optimized for speed.
+@^inner loop@>
+
+@c
+
+/* undoes one token of input */
+
+void back_input(void)
+{
+    halfword p; /* a token list of length one */
+    while ((istate == token_list) && (iloc == null) && (token_type != v_template)) {
+        /* conserve stack space */
+        end_token_list();
+    }
+    p = get_avail();
+    set_token_info(p, cur_tok);
+    if (cur_tok < right_brace_limit) {
+        if (cur_tok < left_brace_limit)
+            decr(align_state);
+        else
+            incr(align_state);
+    }
+    push_input();
+    istate = token_list;
+    istart = p;
+    token_type = backed_up;
+    iloc = p;                   /* that was |back_list(p)|, without procedure overhead */
+}
+
+@ Insert token |p| into \TeX's input
+@c
+
+void reinsert_token(boolean a, halfword pp)
+{
+    halfword t;
+    t = cur_tok;
+    cur_tok = pp;
+    if (a) {
+        halfword p;
+        p = get_avail();
+        set_token_info(p, cur_tok);
+        set_token_link(p, iloc);
+        iloc = p;
+        istart = p;
+        if (cur_tok < right_brace_limit) {
+            if (cur_tok < left_brace_limit)
+                decr(align_state);
+            else
+                incr(align_state);
+        }
+    } else {
+        back_input();
+    }
+    cur_tok = t;
+}
+
+@ The |begin_file_reading| procedure starts a new level of input for lines
+of characters to be read from a file, or as an insertion from the
+terminal. It does not take care of opening the file, nor does it set |loc|
+or |limit| or |line|.
+@^system dependencies@>
+
+@c
+void begin_file_reading(void)
+{
+    if (in_open == max_in_open)
+        overflow("text input levels", (unsigned) max_in_open);
+    if (first == buf_size)
+        check_buffer_overflow(first);
+    incr(in_open);
+    push_input();
+    iindex = (unsigned char) in_open;
+    source_filename_stack[iindex] = 0;
+    full_source_filename_stack[iindex] = NULL;
+    eof_seen[iindex] = false;
+    grp_stack[iindex] = cur_boundary;
+    if_stack[iindex] = cond_ptr;
+    line_stack[iindex] = line;
+    istart = first;
+    istate = mid_line;
+    iname = 0;                  /* |terminal_input| is now |true| */
+    line_catcode_table = DEFAULT_CAT_TABLE;
+    line_partial = false;
+    /* Prepare terminal input {\sl Sync\TeX} information */
+    synctex_tag = 0;
+}
+
+@ Conversely, the variables must be downdated when such a level of input
+is finished:
+
+@c
+void end_file_reading(void)
+{
+    first = istart;
+    line = line_stack[iindex];
+    if ((iname >= 18) && (iname <= 20))
+        pseudo_close();
+    else if (iname == 21)
+        luacstring_close(iindex);
+    else if (iname > 17)
+        lua_a_close_in(cur_file, 0);    /* forget it */
+    pop_input();
+    decr(in_open);
+}
+
+@ In order to keep the stack from overflowing during a long sequence of
+inserted `\.{\\show}' commands, the following routine removes completed
+error-inserted lines from memory.
+
+@c
+void clear_for_error_prompt(void)
+{
+    while ((istate != token_list) && terminal_input
+           && (input_ptr > 0) && (iloc > ilimit))
+        end_file_reading();
+    print_ln();
+    clear_terminal();
+}
+
+@ To get \TeX's whole input mechanism going, we perform the following actions.
+
+@c
+void initialize_inputstack(void)
+{
+    input_ptr = 0;
+    max_in_stack = 0;
+    source_filename_stack[0] = 0;
+
+    full_source_filename_stack[0] = NULL;
+    in_open = 0;
+    open_parens = 0;
+    max_buf_stack = 0;
+
+    grp_stack[0] = 0;
+    if_stack[0] = null;
+    param_ptr = 0;
+    max_param_stack = 0;
+    first = buf_size;
+    do {
+        buffer[first] = 0;
+        decr(first);
+    } while (first != 0);
+    scanner_status = normal;
+    warning_index = null;
+    first = 1;
+    istate = new_line;
+    istart = 1;
+    iindex = 0;
+    line = 0;
+    iname = 0;
+    nofilter = false;
+    force_eof = false;
+    luacstrings = 0;
+    line_catcode_table = DEFAULT_CAT_TABLE;
+    line_partial = false;
+    align_state = 1000000;
+    if (!init_terminal())
+        exit(EXIT_FAILURE);     /* |goto final_end|; */
+    ilimit = last;
+    first = last + 1;           /* |init_terminal| has set |loc| and |last| */
+}
+
+@ The global variable |pseudo_files| is used to maintain a stack of
+pseudo files.  The |pseudo_lines| field of each pseudo file points to
+a linked list of variable size nodes representing lines not yet
+processed: the |subtype| field contains the size of this node,
+all the following words contain ASCII codes.
+
+/*
+
+    hh: todo: if this is really critical code (which it isn't) then we can
+    consider a c stack and store a pointer to a line in the line node instead
+    which saves splitting here and reconstructing later.
+
+*/
+
+
+@c
+halfword pseudo_files; /* stack of pseudo files */
+
+static halfword string_to_pseudo(str_number str, int nl)
+{
+    halfword i, r, q = null;
+    unsigned l, len;
+    four_quarters w;
+    int sz;
+    halfword h = new_node(pseudo_file_node, 0);
+    unsigned char *s = str_string(str);
+    len = (unsigned) str_length(str);
+    l = 0;
+    while (l < len) {
+        unsigned m = l; /* start of current line */
+        while ((l < len) && (s[l] != nl))
+            l++;
+        sz = (int) (l - m + 7) / 4;
+        if (sz == 1)
+            sz = 2;
+        r = new_node(pseudo_line_node, sz);
+        i = r;
+        while (--sz > 1) {
+            w.b0 = s[m++];
+            w.b1 = s[m++];
+            w.b2 = s[m++];
+            w.b3 = s[m++];
+            varmem[++i].qqqq = w;
+        }
+        w.b0 = (quarterword) (l > m ? s[m++] : ' ');
+        w.b1 = (quarterword) (l > m ? s[m++] : ' ');
+        w.b2 = (quarterword) (l > m ? s[m++] : ' ');
+        w.b3 = (quarterword) (l > m ? s[m] : ' ');
+        varmem[++i].qqqq = w;
+        if (q == null) {
+            pseudo_lines(h) = r;
+        } else {
+            vlink(q) = r ; /* no prev node here so no couple_nodes !*/
+        }
+        q = r ;
+        if (s[l] == nl)
+            l++;
+    }
+    return h;
+}
+
+@ The |pseudo_start| procedure initiates reading from a pseudo file.
+
+@c
+void pseudo_from_string(void)
+{
+    str_number s;  /* string to be converted into a pseudo file */
+    halfword p;    /* for list construction */
+    s = make_string();
+    /* Convert string |s| into a new pseudo file */
+    p = string_to_pseudo(s, new_line_char_par);
+    vlink(p) = pseudo_files;
+    pseudo_files = p;
+    flush_str(s);
+    /* Initiate input from new pseudo file */
+    begin_file_reading(); /* set up |cur_file| and new level of input */
+    line = 0;
+    ilimit = istart;
+    iloc = ilimit + 1; /* force line read */
+    if (tracing_scan_tokens_par > 0) {
+        if (term_offset > max_print_line - 3)
+            print_ln();
+        else if ((term_offset > 0) || (file_offset > 0))
+            print_char(' ');
+        iname = 20;
+        tprint("( ");
+        incr(open_parens);
+        update_terminal();
+    } else {
+        iname = 18;
+    }
+    /* Prepare pseudo file {\sl Sync\TeX} information */
+    synctex_tag = 0;
+}
+
+void pseudo_start(void)
+{
+    int old_setting;
+    scan_general_text();
+    old_setting = selector;
+    selector = new_string;
+    token_show(temp_token_head);
+    selector = old_setting;
+    flush_list(token_link(temp_token_head));
+    str_room(1);
+    pseudo_from_string();
+}
+
+@ @c
+void lua_string_start(void)
+{
+    begin_file_reading(); /* set up |cur_file| and new level of input */
+    line = 0;
+    ilimit = istart;
+    iloc = ilimit + 1; /* force line read */
+    iname = 21;
+    luacstring_start(iindex);
+}
+
+@ Here we read a line from the current pseudo file into |buffer|.
+
+@c
+/* inputs the next line or returns |false| */
+
+boolean pseudo_input(void)
+{
+    halfword p;      /* current line from pseudo file */
+    int sz;          /* size of node |p| */
+    four_quarters w; /* four ASCII codes */
+    halfword r;      /* loop index */
+    last = first;    /* cf.\ Matthew 19\thinspace:\thinspace30 */
+    p = pseudo_lines(pseudo_files);
+    if (p == null) {
+        return false;
+    } else {
+        pseudo_lines(pseudo_files) = vlink(p);
+        sz = subtype(p);
+        if (4 * sz - 3 >= buf_size - last)
+            check_buffer_overflow(last + 4 * sz);
+        last = first;
+        for (r = p + 1; r <= p + sz - 1; r++) {
+            w = varmem[r].qqqq;
+            buffer[last]     = (packed_ASCII_code) w.b0;
+            buffer[last + 1] = (packed_ASCII_code) w.b1;
+            buffer[last + 2] = (packed_ASCII_code) w.b2;
+            buffer[last + 3] = (packed_ASCII_code) w.b3;
+            last += 4;
+        }
+        if (last >= max_buf_stack)
+            max_buf_stack = last + 1;
+        while ((last > first) && (buffer[last - 1] == ' '))
+            decr(last);
+        flush_node(p);
+    }
+    return true;
+}
+
+@ When we are done with a pseudo file we `close' it.
+
+@c
+/* close the top level pseudo file */
+
+void pseudo_close(void)
+{
+    halfword p;
+    p = vlink(pseudo_files);
+    flush_node(pseudo_files);
+    pseudo_files = p;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/inputstack.c
+++ /dev/null
@@ -1,954 +0,0 @@
-/*
-
-inputstack.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-in_state_record *input_stack = NULL;
-
-/*tex First unused location of |input_stack|: */
-
-int input_ptr = 0;
-
-/*tex Largest value of |input_ptr| when pushing: */
-
-int max_in_stack = 0;
-
-/*tex The `top' input state: */
-
-in_state_record cur_input;
-
-/*tex The number of lines in the buffer, less one: */
-
-int in_open = 0;
-
-/*tex The number of open text files: */
-
-int open_parens = 0;
-
-alpha_file *input_file = NULL;
-
-/*tex The current line number in the current source file: */
-
-int line = 0;
-
-int *line_stack = NULL;
-
-str_number *source_filename_stack = NULL;
-
-char **full_source_filename_stack = NULL;
-
-/*tex Can a subfile end now? */
-
-int scanner_status = 0;
-
-/*tex Identifier relevant to non-|normal| scanner status: */
-
-pointer warning_index = null;
-
-/*tex Reference count of token list being defined: */
-
-pointer def_ref = null;
-
-/*tex
-
-Here is a procedure that uses |scanner_status| to print a warning message when a
-subfile has ended, and at certain other crucial times:
-
-*/
-
-void runaway(void)
-{
-    /*tex The head of the runaway list: */
-    pointer p = null;
-    if (scanner_status > skipping) {
-        switch (scanner_status) {
-        case defining:
-            tprint_nl("Runaway definition");
-            p = def_ref;
-            break;
-        case matching:
-            tprint_nl("Runaway argument");
-            p = temp_token_head;
-            break;
-        case aligning:
-            tprint_nl("Runaway preamble");
-            p = hold_token_head;
-            break;
-        case absorbing:
-            tprint_nl("Runaway text");
-            p = def_ref;
-            break;
-        default:
-            /*tex There are no other cases. */
-            break;
-        }
-        print_char('?');
-        print_ln();
-        show_token_list(token_link(p), null, error_line - 10);
-    }
-}
-
-/*tex
-
-The |param_stack| is an auxiliary array used to hold pointers to the token lists
-for parameters at the current level and subsidiary levels of input. This stack is
-maintained with convention (2), and it grows at a different rate from the others.
-
-*/
-
-/*tex Token list pointers for parameters: */
-
-pointer *param_stack = NULL;
-
-/*tex First unused entry in |param_stack|: */
-
-int param_ptr = 0;
-
-/*tex Largest value of |param_ptr|, will be |<=param_size+9|: */
-
-int max_param_stack = 0;
-
-/*tex
-
-The input routines must also interact with the processing of \.{\\halign} and
-\.{\\valign}, since the appearance of tab marks and \.{\\cr} in certain places is
-supposed to trigger the beginning of special $v_j$ template text in the scanner.
-This magic is accomplished by an |align_state| variable that is increased by~1
-when a `\.{\char'173}' is scanned and decreased by~1 when a `\.{\char'175}' is
-scanned. The |align_state| is nonzero during the $u_j$ template, after which it
-is set to zero; the $v_j$ template begins when a tab mark or \.{\\cr} occurs at a
-time that |align_state=0|.
-
-*/
-
-/*tex The group level with respect to current alignment: */
-
-int align_state = 0;
-
-/*tex
-
-Thus, the ``current input state'' can be very complicated indeed; there can be
-many levels and each level can arise in a variety of ways. The |show_context|
-procedure, which is used by \TeX's error-reporting routine to print out the
-current input state on all levels down to the most recent line of characters from
-an input file, illustrates most of these conventions. The global variable
-|base_ptr| contains the lowest level that was displayed by this procedure.
-
-*/
-
-/*tex The shallowest level shown by |show_context|: */
-
-int base_ptr = 0;
-
-/*tex
-
-The status at each level is indicated by printing two lines, where the first line
-indicates what was read so far and the second line shows what remains to be read.
-The context is cropped, if necessary, so that the first line contains at most
-|half_error_line| characters, and the second contains at most |error_line|.
-Non-current input levels whose |token_type| is `|backed_up|' are shown only if
-they have not been fully read.
-
-*/
-
-static void print_token_list_type(int t)
-{
-    switch (t) {
-        case parameter:
-            tprint_nl("<argument> ");
-            break;
-        case u_template:
-        case v_template:
-            tprint_nl("<template> ");
-            break;
-        case backed_up:
-            if (iloc == null)
-                tprint_nl("<recently read> ");
-            else
-                tprint_nl("<to be read again> ");
-            break;
-        case inserted:
-            tprint_nl("<inserted text> ");
-            break;
-        case macro:
-            print_ln();
-            print_cs(iname);
-            break;
-        case output_text:
-            tprint_nl("<output> ");
-            break;
-        case every_par_text:
-            tprint_nl("<everypar> ");
-            break;
-        case every_math_text:
-            tprint_nl("<everymath> ");
-            break;
-        case every_display_text:
-            tprint_nl("<everydisplay> ");
-            break;
-        case every_hbox_text:
-            tprint_nl("<everyhbox> ");
-            break;
-        case every_vbox_text:
-            tprint_nl("<everyvbox> ");
-            break;
-        case every_job_text:
-            tprint_nl("<everyjob> ");
-            break;
-        case every_cr_text:
-            tprint_nl("<everycr> ");
-            break;
-        case mark_text:
-            tprint_nl("<mark> ");
-            break;
-        case every_eof_text:
-            tprint_nl("<everyeof> ");
-            break;
-        case write_text:
-            tprint_nl("<write> ");
-            break;
-        case local_text:
-            tprint_nl("<local> ");
-            break;
-        default:
-            tprint_nl("?");
-            /*tex This should never happen. */
-            break;
-    }
-}
-
-/*tex
-
-Here it is necessary to explain a little trick. We don't want to store a long
-string that corresponds to a token list, because that string might take up lots
-of memory; and we are printing during a time when an error message is being
-given, so we dare not do anything that might overflow one of \TeX's tables. So
-`pseudoprinting' is the answer: We enter a mode of printing that stores
-characters into a buffer of length |error_line|, where character $k+1$ is placed
-into \hbox{|trick_buf[k mod error_line]|} if |k<trick_count|, otherwise character
-|k| is dropped. Initially we set |tally:=0| and |trick_count:=1000000|; then when
-we reach the point where transition from line 1 to line 2 should occur, we set
-|first_count:=tally| and |trick_count:=@tmax@>(error_line,
-tally+1+error_line-half_error_line)|. At the end of the pseudoprinting, the
-values of |first_count|, |tally|, and |trick_count| give us all the information
-we need to print the two lines, and all of the necessary text is in |trick_buf|.
-
-Namely, let |l| be the length of the descriptive information that appears on the
-first line. The length of the context information gathered for that line is
-|k=first_count|, and the length of the context information gathered for line~2 is
-$m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|, where |h=half_error_line|, we
-print |trick_buf[0..k-1]| after the descriptive information on line~1, and set
-|n:=l+k|; here |n| is the length of line~1. If $l+k>h$, some cropping is
-necessary, so we set |n:=h| and print `\.{...}' followed by
-$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$ where subscripts of |trick_buf| are
-circular modulo |error_line|. The second line consists of |n|~spaces followed by
-|trick_buf[k..(k+m-1)]|, unless |n+m>error_line|; in the latter case, further
-cropping is done. This is easier to program than to explain.
-
-The following code sets up the print routines so that they will gather the
-desired information.
-
-*/
-
-void set_trick_count(void)
-{
-    first_count = tally;
-    trick_count = tally + 1 + error_line - half_error_line;
-    if (trick_count < error_line)
-        trick_count = error_line;
-}
-
-#define begin_pseudoprint() do { \
-    l=tally; \
-    tally=0; \
-    selector=pseudo; \
-    trick_count=1000000; \
-  } while (0)
-
-#define PSEUDO_PRINT_THE_LINE()	do { \
-    begin_pseudoprint(); \
-    if (buffer[ilimit]==end_line_char_par) { \
-        j=ilimit; \
-    } else { \
-        /*tex Determine the effective end of the line. */ \
-        j=ilimit+1; \
-    } \
-    if (j>0) { \
-        for (i=istart;i<=j-1;i++) { \
-            if (i==iloc) \
-                set_trick_count(); \
-            print_char(buffer[i]); \
-        } \
-    } \
-} while (0)
-
-/*tex
-
-We don't care too much if we stay a bit too much below the max error_line even if
-we have more room on the line. If length is really an issue then any length is.
-After all one can set the length larger.
-
-*/
-
-#define print_valid_utf8(q) do { \
-    c = (int)trick_buf[q % error_line]; \
-    if (c < 128) { \
-        print_char(c); \
-    } else if (c < 194) { \
-        /* invalid */ \
-    } else if (c < 224) { \
-        print_char(c); \
-        print_char(trick_buf[(q+1) % error_line]); \
-    } else if (c < 240) { \
-        print_char(c); \
-        print_char(trick_buf[(q+1) % error_line]); \
-        print_char(trick_buf[(q+2) % error_line]); \
-    } else if (c < 245) { \
-        print_char(c); \
-        print_char(trick_buf[(q+1) % error_line]); \
-        print_char(trick_buf[(q+2) % error_line]); \
-        print_char(trick_buf[(q+3) % error_line]); \
-    } else { \
-        /*tex Invalid character! */ \
-    } \
-} while (0)
-
-/*tex
-
-This one prints where the scanner is.
-
-*/
-
-void show_context(void)
-{
-    /*tex Saved |selector| setting: */
-    int old_setting;
-    /*tex Number of contexts shown so far, less one: */
-    int nn = -1;
-    /*tex Have we reached the final context to be shown? */
-    boolean bottom_line = false;
-    /*tex Index into |buffer|: */
-    int i;
-    /*tex End of current line in |buffer|: */
-    int j;
-    /*tex Length of descriptive information on line 1: */
-    int l;
-    /*tex Context information gathered for line 2: */
-    int m;
-    /*tex Length of line 1: */
-    int n;
-    /*tex Starting or ending place in |trick_buf|: */
-    int p;
-    /*tex Temporary index: */
-    int q;
-    /*tex Used in sanitizer: */
-    int c;
-    base_ptr = input_ptr;
-    input_stack[base_ptr] = cur_input;
-    /*tex Store the current state. */
-    while (true) {
-        /*tex Enter into the context. */
-        cur_input = input_stack[base_ptr];
-        if (istate != token_list) {
-            if ((iname > 21) || (base_ptr == 0))
-                bottom_line = true;
-        }
-        if ((base_ptr == input_ptr) || bottom_line || (nn < error_context_lines_par)) {
-            /*tex Display the current context. */
-            if ((base_ptr == input_ptr) || (istate != token_list) || (token_type != backed_up) || (iloc != null)) {
-                /*tex
-                    We omit backed-up token lists that have already been read.
-                    Get ready to count characters.
-                */
-                tally = 0;
-                old_setting = selector;
-                if (istate != token_list) {
-                    /*tex
-                        Print the location of the current line. This routine
-                        should be changed, if necessary, to give the best
-                        possible indication of where the current line resides in
-                        the input file. For example, on some systems it is best
-                        to print both a page and line number.
-                     */
-                    if (iname <= 17) {
-                        if (terminal_input) {
-                            if (base_ptr == 0)
-                                tprint_nl("<*>");
-                            else
-                                tprint_nl("<insert> ");
-                        } else {
-                            tprint_nl("<read ");
-                            if (iname == 17)
-                                print_char('*');
-                            else
-                                print_int(iname - 1);
-                            print_char('>');
-                        };
-                    } else {
-                        tprint_nl("l.");
-                        if (iindex == in_open) {
-                            print_int(line);
-                        } else {
-                            /*tex Input from a pseudo file. */
-                            print_int(line_stack[iindex + 1]);
-                        }
-                    }
-                    print_char(' ');
-                    PSEUDO_PRINT_THE_LINE();
-                } else {
-                    print_token_list_type(token_type);
-
-                    begin_pseudoprint();
-                    if (token_type < macro) {
-                        show_token_list(istart, iloc, 100000);
-                    } else {
-                        /*tex Avoid reference count. */
-                        show_token_list(token_link(istart), iloc, 100000);
-                    }
-                }
-                /*tex Stop pseudoprinting. */
-                selector = old_setting;
-                /*tex Print two lines using the tricky pseudoprinted information. */
-                if (trick_count == 1000000) {
-                    set_trick_count();
-                }
-                /*tex The |set_trick_count| must be performed. */
-                if (tally < trick_count) {
-                    m = tally - first_count;
-                } else {
-                     /* The context on line 2: */
-                    m = trick_count - first_count;
-                }
-                if (l + first_count <= half_error_line) {
-                    p = 0;
-                    n = l + first_count;
-                } else {
-                    tprint("...");
-                    p = l + first_count - half_error_line + 3;
-                    n = half_error_line;
-                }
-                for (q = p; q <= first_count - 1; q++) {
-                    print_valid_utf8(q);
-                }
-                print_ln();
-                /*tex Print |n| spaces to begin line 2. */
-                for (q = 1; q <= n; q++) {
-                    print_char(' ');
-                }
-                if (m + n <= error_line)
-                    p = first_count + m;
-                else
-                    p = first_count + (error_line - n - 3);
-                for (q = first_count; q <= p - 1; q++)
-                    print_valid_utf8(q);
-                if (m + n > error_line)
-                    tprint("...");
-                incr(nn);
-            }
-        } else if (nn == error_context_lines_par) {
-            tprint_nl("...");
-            incr(nn);
-            /*tex Omitted if |error_context_lines_par<0|. */
-        }
-        if (bottom_line)
-            break;
-        decr(base_ptr);
-    }
-    /*tex Restore the original state. */
-    cur_input = input_stack[input_ptr];
-}
-
-/*tex
-
-The following subroutines change the input status in commonly needed ways.
-
-First comes |push_input|, which stores the current state and creates a
-new level (having, initially, the same properties as the old).
-
-Enter a new input level, save the old:
-
-*/
-
-# define pop_input() \
-    cur_input=input_stack[--input_ptr]
-
-# define push_input() \
-    if (input_ptr > max_in_stack) { \
-        max_in_stack = input_ptr; \
-        if (input_ptr == stack_size) \
-            overflow("input stack size", (unsigned) stack_size); \
-    } \
-    input_stack[input_ptr] = cur_input; \
-    nofilter = false; \
-    incr(input_ptr);
-
-/*tex
-
-Here is a procedure that starts a new level of token-list input, given a token
-list |p| and its type |t|. If |t=macro|, the calling routine should set |name|
-and |loc|.
-
-*/
-
-void begin_token_list(halfword p, quarterword t)
-{
-    push_input();
-    istate = token_list;
-    istart = p;
-    token_type = (unsigned char) t;
-    if (t >= macro) {
-        /*tex The token list starts with a reference count. */
-        add_token_ref(p);
-        if (t == macro) {
-            param_start = param_ptr;
-        } else {
-            iloc = token_link(p);
-            if (tracing_macros_par > 1) {
-                begin_diagnostic();
-                tprint_nl("");
-                if (t == mark_text)
-                    tprint_esc("mark");
-                else if (t == write_text)
-                    tprint_esc("write");
-                else
-                    print_cmd_chr(assign_toks_cmd, t - output_text + output_routine_loc);
-                tprint("->");
-                token_show(p);
-                end_diagnostic(false);
-            }
-        }
-    } else {
-        iloc = p;
-    }
-}
-
-/*tex
-
-When a token list has been fully scanned, the following computations should be
-done as we leave that level of input. The |token_type| tends to be equal to
-either |backed_up| or |inserted| about 2/3 of the time. @^inner loop@>
-
-*/
-
-void end_token_list(void)
-{
-    /*tex Leave a token-list input level: */
-    if (token_type >= backed_up) {
-        /*tex The token list to be deleted: */
-        if (token_type <= inserted) {
-            flush_list(istart);
-        } else {
-            /*tex Update the reference count: */
-            delete_token_ref(istart);
-            if (token_type == macro) {
-                /*tex Parameters must be flushed: */
-                while (param_ptr > param_start) {
-                    decr(param_ptr);
-                    flush_list(param_stack[param_ptr]);
-                }
-            }
-        }
-    } else if (token_type == u_template) {
-        if (align_state > 500000)
-            align_state = 0;
-        else
-            fatal_error("(interwoven alignment preambles are not allowed)");
-    }
-    pop_input();
-    check_interrupt();
-}
-
-/*tex
-
-Sometimes \TeX\ has read too far and wants to ``unscan'' what it has seen. The
-|back_input| procedure takes care of this by putting the token just scanned back
-into the input stream, ready to be read again. This procedure can be used only if
-|cur_tok| represents the token to be replaced. Some applications of \TeX\ use
-this procedure a lot, so it has been slightly optimized for speed. @^inner loop@>
-
-*/
-
-/* undoes one token of input */
-
-void back_input(void)
-{
-    /*tex A token list of length one: */
-    halfword p;
-    while ((istate == token_list) && (iloc == null) && (token_type != v_template)) {
-        /*tex Conserve stack space. */
-        end_token_list();
-    }
-    p = get_avail();
-    set_token_info(p, cur_tok);
-    if (cur_tok < right_brace_limit) {
-        if (cur_tok < left_brace_limit)
-            decr(align_state);
-        else
-            incr(align_state);
-    }
-    push_input();
-    /*tex This is |back_list(p)|, without procedure overhead: */
-    istate = token_list;
-    istart = p;
-    token_type = backed_up;
-    iloc = p;
-}
-
-/*tex
-
-Insert token |p| into \TeX's input
-
-*/
-
-void reinsert_token(boolean a, halfword pp)
-{
-    halfword t;
-    t = cur_tok;
-    cur_tok = pp;
-    if (a) {
-        halfword p;
-        p = get_avail();
-        set_token_info(p, cur_tok);
-        set_token_link(p, iloc);
-        iloc = p;
-        istart = p;
-        if (cur_tok < right_brace_limit) {
-            if (cur_tok < left_brace_limit)
-                decr(align_state);
-            else
-                incr(align_state);
-        }
-    } else {
-        back_input();
-    }
-    cur_tok = t;
-}
-
-/*tex
-
-The |begin_file_reading| procedure starts a new level of input for lines of
-characters to be read from a file, or as an insertion from the terminal. It does
-not take care of opening the file, nor does it set |loc| or |limit| or |line|.
-@^system dependencies@>
-
-*/
-
-void begin_file_reading(void)
-{
-    if (in_open == max_in_open)
-        overflow("text input levels", (unsigned) max_in_open);
-    if (first == buf_size)
-        check_buffer_overflow(first);
-    incr(in_open);
-    push_input();
-    iindex = (unsigned char) in_open;
-    source_filename_stack[iindex] = 0;
-    full_source_filename_stack[iindex] = NULL;
-    eof_seen[iindex] = false;
-    grp_stack[iindex] = cur_boundary;
-    if_stack[iindex] = cond_ptr;
-    line_stack[iindex] = line;
-    istart = first;
-    istate = mid_line;
-    iname = 0;
-    /*tex Variable |terminal_input| is now |true|. */
-    line_catcode_table = DEFAULT_CAT_TABLE;
-    line_partial = false;
-    /*tex Prepare terminal input \SYNCTEX\ information. */
-    synctex_tag = 0;
-}
-
-/*tex
-
-Conversely, the variables must be downdated when such a level of input is
-finished:
-
-*/
-
-void end_file_reading(void)
-{
-    first = istart;
-    line = line_stack[iindex];
-    if ((iname >= 18) && (iname <= 20)) {
-        pseudo_close();
-    } else if (iname == 21) {
-        luacstring_close(iindex);
-    } else if (iname > 17) {
-        /*tex Forget it. */
-        lua_a_close_in(cur_file, 0);
-    }
-    pop_input();
-    decr(in_open);
-}
-
-/*tex
-
-In order to keep the stack from overflowing during a long sequence of
-inserted `\.{\\show}' commands, the following routine removes completed
-error-inserted lines from memory.
-
-*/
-void clear_for_error_prompt(void)
-{
-    while ((istate != token_list) && terminal_input && (input_ptr > 0) && (iloc > ilimit)) {
-        end_file_reading();
-    }
-    print_ln();
-    clear_terminal();
-}
-
-/*tex
-
-To get \TeX's whole input mechanism going, we perform the following actions.
-
-*/
-
-void initialize_inputstack(void)
-{
-    input_ptr = 0;
-    max_in_stack = 0;
-    source_filename_stack[0] = 0;
-
-    full_source_filename_stack[0] = NULL;
-    in_open = 0;
-    open_parens = 0;
-    max_buf_stack = 0;
-
-    grp_stack[0] = 0;
-    if_stack[0] = null;
-    param_ptr = 0;
-    max_param_stack = 0;
-    first = buf_size;
-    do {
-        buffer[first] = 0;
-        decr(first);
-    } while (first != 0);
-    scanner_status = normal;
-    warning_index = null;
-    first = 1;
-    istate = new_line;
-    istart = 1;
-    iindex = 0;
-    line = 0;
-    iname = 0;
-    nofilter = false;
-    force_eof = false;
-    luacstrings = 0;
-    line_catcode_table = DEFAULT_CAT_TABLE;
-    line_partial = false;
-    align_state = 1000000;
-    if (!init_terminal()) {
-        /*tex |goto final_end|; */
-        exit(EXIT_FAILURE);
-    }
-    /* |init_terminal| has set |loc| and |last| */
-    ilimit = last;
-    first = last + 1;
-}
-
-/*tex
-
-The global variable |pseudo_files| is used to maintain a stack of pseudo files.
-The |pseudo_lines| field of each pseudo file points to a linked list of variable
-size nodes representing lines not yet processed: the |subtype| field contains the
-size of this node, all the following words contain ASCII codes.
-
-If this is really critical code (which it isn't) then we can consider a c stack
-and store a pointer to a line in the line node instead which saves splitting here
-and reconstructing later.
-
-*/
-
-halfword pseudo_files;
-
-static halfword string_to_pseudo(str_number str, int nl)
-{
-    halfword i, r, q = null;
-    unsigned l, len;
-    four_quarters w;
-    int sz;
-    halfword h = new_node(pseudo_file_node, 0);
-    unsigned char *s = str_string(str);
-    len = (unsigned) str_length(str);
-    l = 0;
-    while (l < len) {
-        /*tex start of current line */
-        unsigned m = l;
-        while ((l < len) && (s[l] != nl))
-            l++;
-        sz = (int) (l - m + 7) / 4;
-        if (sz == 1)
-            sz = 2;
-        r = new_node(pseudo_line_node, sz);
-        i = r;
-        while (--sz > 1) {
-            w.b0 = s[m++];
-            w.b1 = s[m++];
-            w.b2 = s[m++];
-            w.b3 = s[m++];
-            varmem[++i].qqqq = w;
-        }
-        w.b0 = (quarterword) (l > m ? s[m++] : ' ');
-        w.b1 = (quarterword) (l > m ? s[m++] : ' ');
-        w.b2 = (quarterword) (l > m ? s[m++] : ' ');
-        w.b3 = (quarterword) (l > m ? s[m] : ' ');
-        varmem[++i].qqqq = w;
-        if (q == null) {
-            pseudo_lines(h) = r;
-        } else {
-            /*tex There is no |prev| node here so no need to couple_nodes! */
-            vlink(q) = r ;
-        }
-        q = r ;
-        if (s[l] == nl)
-            l++;
-    }
-    return h;
-}
-
-/*tex
-
-The |pseudo_start| procedure initiates reading from a pseudo file.
-
-*/
-
-void pseudo_from_string(void)
-{
-    /*tex The string to be converted into a pseudo file: */
-    str_number s;
-    /*tex A helper for list construction: */
-    halfword p;
-    s = make_string();
-    /*tex Convert string |s| into a new pseudo file */
-    p = string_to_pseudo(s, new_line_char_par);
-    vlink(p) = pseudo_files;
-    pseudo_files = p;
-    flush_str(s);
-    /*tex
-        Initiate input from new pseudo file. It sets up |cur_file| and a new level
-        of input
-    */
-    begin_file_reading();
-    line = 0;
-    ilimit = istart;
-    /*tex force line read */
-    iloc = ilimit + 1;
-    if (tracing_scan_tokens_par > 0) {
-        if (term_offset > max_print_line - 3)
-            print_ln();
-        else if ((term_offset > 0) || (file_offset > 0))
-            print_char(' ');
-        iname = 20;
-        tprint("( ");
-        incr(open_parens);
-        update_terminal();
-    } else {
-        iname = 18;
-    }
-    /*tex Prepare pseudo file \SYNCTEX\ information. */
-    synctex_tag = 0;
-}
-
-void pseudo_start(void)
-{
-    int old_setting;
-    scan_general_text();
-    old_setting = selector;
-    selector = new_string;
-    token_show(temp_token_head);
-    selector = old_setting;
-    flush_list(token_link(temp_token_head));
-    str_room(1);
-    pseudo_from_string();
-}
-
-void lua_string_start(void)
-{
-    /*tex Set up |cur_file| and a new level of input: */
-    begin_file_reading();
-    line = 0;
-    ilimit = istart;
-    /*tex Force line read: */
-    iloc = ilimit + 1;
-    iname = 21;
-    luacstring_start(iindex);
-}
-
-/*tex
-
-Here we read a line from the current pseudo file into |buffer|.
-It inputs the next line or returns |false|.
-
-*/
-
-boolean pseudo_input(void)
-{
-    /*tex The current line from pseudo file: */
-    halfword p;
-    /*tex The size of node |p|: */
-    int sz;
-    /*tex Four ASCII codes: */
-    four_quarters w;
-    /*tex The loop index: */
-    halfword r;
-    /*tex cf.\ Matthew 19\thinspace:\thinspace30 */
-    last = first;
-    p = pseudo_lines(pseudo_files);
-    if (p == null) {
-        return false;
-    } else {
-        pseudo_lines(pseudo_files) = vlink(p);
-        sz = subtype(p);
-        if (4 * sz - 3 >= buf_size - last)
-            check_buffer_overflow(last + 4 * sz);
-        last = first;
-        for (r = p + 1; r <= p + sz - 1; r++) {
-            w = varmem[r].qqqq;
-            buffer[last]     = (packed_ASCII_code) w.b0;
-            buffer[last + 1] = (packed_ASCII_code) w.b1;
-            buffer[last + 2] = (packed_ASCII_code) w.b2;
-            buffer[last + 3] = (packed_ASCII_code) w.b3;
-            last += 4;
-        }
-        if (last >= max_buf_stack)
-            max_buf_stack = last + 1;
-        while ((last > first) && (buffer[last - 1] == ' '))
-            decr(last);
-        flush_node(p);
-    }
-    return true;
-}
-
-/*tex
-
-When we are done with a pseudo file we `close' it.
-
-*/
-
-void pseudo_close(void)
-{
-    halfword p;
-    p = vlink(pseudo_files);
-    flush_node(pseudo_files);
-    pseudo_files = p;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/linebreak.c
+++ /dev/null
@@ -1,2520 +0,0 @@
-/*
-
-Copyright 2006-2008 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    We come now to what is probably the most interesting algorithm of \TeX: the
-    mechanism for choosing the ``best possible'' breakpoints that yield the
-    individual lines of a paragraph. \TeX's line-breaking algorithm takes a given
-    horizontal list and converts it to a sequence of boxes that are appended to
-    the current vertical list. In the course of doing this, it creates a special
-    data structure containing three kinds of records that are not used elsewhere
-    in \TeX. Such nodes are created while a paragraph is being processed, and
-    they are destroyed afterwards; thus, the other parts of \TeX\ do not need to
-    know anything about how line-breaking is done.
-
-    The method used here is based on an approach devised by Michael F. Plass and
-    the author in 1977, subsequently generalized and improved by the same two
-    people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice
-    \AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the
-    line-breaking problem can be regarded as a special case of the problem of
-    computing the shortest path in an acyclic network. The cited paper includes
-    numerous examples and describes the history of line breaking as it has been
-    practiced by printers through the ages. The present implementation adds two
-    new ideas to the algorithm of 1980: Memory space requirements are
-    considerably reduced by using smaller records for inactive nodes than for
-    active ones, and arithmetic overflow is avoided by using ``delta distances''
-    instead of keeping track of the total distance from the beginning of the
-    paragraph to the current point.
-
-    The |line_break| procedure should be invoked only in horizontal mode; it
-    leaves that mode and places its output into the current vlist of the
-    enclosing vertical mode (or internal vertical mode). There is one explicit
-    parameter: |d| is true for partial paragraphs preceding display math mode; in
-    this case the amount of additional penalty inserted before the final line is
-    |display_widow_penalty| instead of |widow_penalty|.
-
-    There are also a number of implicit parameters: The hlist to be broken starts
-    at |vlink(head)|, and it is nonempty. The value of |prev_graf| in the
-    enclosing semantic level tells where the paragraph should begin in the
-    sequence of line numbers, in case hanging indentation or \.{\\parshape} are
-    in use; |prev_graf| is zero unless this paragraph is being continued after a
-    displayed formula. Other implicit parameters, such as the |par_shape_ptr| and
-    various penalties to use for hyphenation, etc., appear in |eqtb|.
-
-    After |line_break| has acted, it will have updated the current vlist and the
-    value of |prev_graf|. Furthermore, the global variable |just_box| will point
-    to the final box created by |line_break|, so that the width of this line can
-    be ascertained when it is necessary to decide whether to use
-    |above_display_skip| or |above_display_short_skip| before a displayed
-    formula.
-
-*/
-
-/*tex The |hlist_node| for the last line of the new paragraph: */
-
-halfword just_box;
-
-/*tex
-
-    In it's complete form, |line_break| is a rather lengthy procedure---sort of a
-    small world unto itself---we must build it up little by little. Below you see
-    only the general outline.
-
-    The main task performed here is to move the list from |head| to |temp_head|
-    and go into the enclosing semantic level. We also append the
-    \.{\\parfillskip} glue to the end of the paragraph, removing a space (or
-    other glue node) if it was there, since spaces usually precede blank lines
-    and instances of `\.{\$\$}'. The |par_fill_skip| is preceded by an infinite
-    penalty, so it will never be considered as a potential breakpoint.
-
-    That code assumes that a |glue_node| and a |penalty_node| occupy the same
-    number of |mem|~words.
-
-    Most other processing is delegated to external functions.
-
-*/
-
-void line_break(boolean d, int line_break_context)
-{
-    /*tex Main direction of paragraph: */
-    int paragraph_dir = 0;
-    halfword final_par_glue;
-    halfword start_of_par;
-    int callback_id;
-    /*tex this is for over/underfull box messages */
-    pack_begin_line = cur_list.ml_field;
-    alink(temp_head) = null;
-    vlink(temp_head) = vlink(cur_list.head_field);
-    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,line_penalty));
-    } else if (type(cur_list.tail_field) != glue_node) {
-        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,line_penalty));
-    }
-    final_par_glue = new_param_glue(par_fill_skip_code);
-    couple_nodes(cur_list.tail_field, final_par_glue);
-    cur_list.tail_field = vlink(cur_list.tail_field);
-    lua_node_filter(pre_linebreak_filter_callback, line_break_context, temp_head, addressof(cur_list.tail_field));
-    last_line_fill = cur_list.tail_field;
-    pop_nest();
-    start_of_par = cur_list.tail_field;
-    callback_id = callback_defined(linebreak_filter_callback);
-    if (callback_id > 0) {
-        callback_id = lua_linebreak_callback(d, temp_head, addressof(cur_list.tail_field));
-        if (callback_id > 0) {
-            /*tex find the correct value for the |just_box| */
-            halfword box_search = cur_list.tail_field;
-            just_box  = null;
-            if (box_search != null) {
-                do {
-                    if (type(box_search) == hlist_node) {
-                       just_box = box_search;
-                    }
-                    box_search = vlink(box_search);
-                } while (box_search != null);
-            }
-            if (just_box == null) {
-                help3(
-                    "A linebreaking routine should return a non-empty list of nodes",
-                    "and at least one of those has to be a \\hbox.",
-                    "Sorry, I cannot recover from this."
-                );
-                print_err("Invalid linebreak_filter");
-                succumb();
-            }
-        } else {
-            if (tracing_paragraphs_par > 0) {
-                begin_diagnostic();
-                print_int(line);
-                end_diagnostic(true);
-            }
-        }
-    }
-    if (callback_id == 0) {
-        if ((!is_char_node(vlink(temp_head))) && ((type(vlink(temp_head)) == local_par_node))) {
-            paragraph_dir = local_par_dir(vlink(temp_head));
-        } else {
-            confusion("weird par dir");
-        }
-        ext_do_line_break(
-            paragraph_dir,
-            pretolerance_par,
-            tracing_paragraphs_par,
-            tolerance_par,
-            emergency_stretch_par,
-            looseness_par,
-            adjust_spacing_par,
-            par_shape_par_ptr,
-            adj_demerits_par,
-            protrude_chars_par,
-            line_penalty_par,
-            last_line_fit_par,
-            double_hyphen_demerits_par,
-            final_hyphen_demerits_par,
-            hang_indent_par,
-            hsize_par,
-            hang_after_par,
-            left_skip_par,
-            right_skip_par,
-            inter_line_penalties_par_ptr,
-            inter_line_penalty_par,
-            club_penalty_par,
-            club_penalties_par_ptr,
-            (d ? display_widow_penalties_par_ptr : widow_penalties_par_ptr),
-            (d ? display_widow_penalty_par : widow_penalty_par),
-            broken_penalty_par,
-            final_par_glue
-        );
-    }
-    lua_node_filter(post_linebreak_filter_callback, line_break_context, start_of_par, addressof(cur_list.tail_field));
-    pack_begin_line = 0;
-}
-
-/*tex
-
-    Glue nodes in a horizontal list that is being paragraphed are not supposed to
-    include ``infinite'' shrinkability; that is why the algorithm maintains four
-    registers for stretching but only one for shrinking. If the user tries to
-    introduce infinite shrinkability, the shrinkability will be reset to finite
-    and an error message will be issued. A boolean variable |no_shrink_error_yet|
-    prevents this error message from appearing more than once per paragraph.
-
-*/
-
-#define check_shrinkage(a) \
-    if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \
-        a=finite_shrink((a))
-
-/*tex Have we complained about infinite shrinkage? */
-
-static boolean no_shrink_error_yet;
-
-/*tex Recovers from infinite shrinkage. */
-
-static halfword finite_shrink(halfword p)
-{
-    const char *hlp[] = {
-        "The paragraph just ended includes some glue that has",
-        "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.",
-        "Such glue doesn't belong there---it allows a paragraph",
-        "of any length to fit on one line. But it's safe to proceed,",
-        "since the offensive shrinkability has been made finite.",
-        NULL
-    };
-    if (no_shrink_error_yet) {
-        no_shrink_error_yet = false;
-        tex_error("Infinite glue shrinkage found in a paragraph", hlp);
-    }
-    shrink_order(p) = normal;
-    return p;
-}
-
-/*tex
-
-    A pointer variable |cur_p| runs through the given horizontal list as we look
-    for breakpoints. This variable is global, since it is used both by
-    |line_break| and by its subprocedure |try_break|.
-
-    Another global variable called |threshold| is used to determine the
-    feasibility of individual lines: breakpoints are feasible if there is a way
-    to reach them without creating lines whose badness exceeds |threshold|. (The
-    badness is compared to |threshold| before penalties are added, so that
-    penalty values do not affect the feasibility of breakpoints, except that no
-    break is allowed when the penalty is 10000 or more.) If |threshold| is 10000
-    or more, all legal breaks are considered feasible, since the |badness|
-    function specified above never returns a value greater than~10000.
-
-    Up to three passes might be made through the paragraph in an attempt to find
-    at least one set of feasible breakpoints. On the first pass, we have
-    |threshold=pretolerance| and |second_pass=final_pass=false|. If this pass
-    fails to find a feasible solution, |threshold| is set to |tolerance|,
-    |second_pass| is set |true|, and an attempt is made to hyphenate as many
-    words as possible. If that fails too, we add |emergency_stretch| to the
-    background stretchability and set |final_pass=true|.
-
-*/
-
-/*tex is this our second attempt to break this paragraph? */
-
-static boolean second_pass;
-
-/*tex is this our final attempt to break this paragraph? */
-
-static boolean final_pass;
-
-/*tex maximum badness on feasible lines */
-
-static int threshold;
-
-/*tex
-
-    The maximum fill level for |hlist_stack|. Maybe good if larger than |2 *
-    max_quarterword|, so that box nesting level would overflow first.
-
-*/
-
-#define max_hlist_stack 512
-
-/*tex stack for |find_protchar_left()| and |find_protchar_right()| */
-
-static halfword hlist_stack[max_hlist_stack];
-
-/*tex fill level for |hlist_stack| */
-
-static short hlist_stack_level = 0;
-
-static void push_node(halfword p)
-{
-    if (hlist_stack_level >= max_hlist_stack)
-        normal_error("push_node","stack overflow");
-    hlist_stack[hlist_stack_level++] = p;
-}
-
-static halfword pop_node(void)
-{
-    if (hlist_stack_level <= 0) {
-        /*tex This can point to some bug. */
-        normal_error("pop_node","stack underflow (internal error)");
-    }
-    return hlist_stack[--hlist_stack_level];
-}
-
-/*tex maximal stretch ratio of expanded fonts */
-
-static int max_stretch_ratio = 0;
-
-/*tex maximal shrink ratio of expanded fonts */
-
-static int max_shrink_ratio = 0;
-
-/*tex the current step of expanded fonts */
-
-static int cur_font_step = 0;
-
-static boolean check_expand_pars(internal_font_number f)
-{
-    int m;
-    if ((font_step(f) == 0) || ((font_max_stretch(f) == 0) && (font_max_shrink(f) == 0)))
-        return false;
-    if (cur_font_step < 0)
-        cur_font_step = font_step(f);
-    else if (cur_font_step != font_step(f))
-        normal_error("font expansion","using fonts with different step of expansion in one paragraph is not allowed");
-    m = font_max_stretch(f);
-    if (m != 0) {
-        if (max_stretch_ratio < 0)
-            max_stretch_ratio = m;
-        else if (max_stretch_ratio != m)
-            normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed");
-    }
-    m = font_max_shrink(f);
-    if (m != 0) {
-        if (max_shrink_ratio < 0)
-            max_shrink_ratio = -m;
-        else if (max_shrink_ratio != -m)
-            normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed");
-    }
-    return true;
-}
-
-/*tex Search left to right from list head |l|, returns 1st non-skipable item */
-
-halfword find_protchar_left(halfword l, boolean d)
-{
-    halfword t;
-    boolean run;
-    boolean done = false ;
-    while ((vlink(l) != null) && (type(l) == hlist_node) && zero_dimensions(l) && (list_ptr(l) == null)) {
-        /*tex For paragraph start with \.{\\parindent} = 0pt or any empty hbox. */
-        l = vlink(l);
-        done = true ;
-    }
-    if ((!done) && (type(l) == local_par_node)) {
-        l = vlink(l);
-        done = true ;
-    }
-    if ((!done) && d) {
-        while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) {
-            /*tex standard discardables at line break, \TeX book, p 95 */
-            l = vlink(l);
-        }
-    }
-    if (type(l) != glyph_node) {
-        hlist_stack_level = 0;
-        run = true;
-        do {
-            t = l;
-            while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) {
-                push_node(l);
-                l = list_ptr(l);
-            }
-            while (run && cp_skipable(l)) {
-                while ((vlink(l) == null) && (hlist_stack_level > 0)) {
-                    /*tex Don't visit this node again. */
-                    l = pop_node();
-                    run = false;
-                }
-                if ((vlink(l) != null) && (type(l) == boundary_node) && (subtype(l) == protrusion_boundary) &&
-                        ((boundary_value(l) == 1) || (boundary_value(l) == 3))) {
-                    /*tex Skip next node. */
-                    l = vlink(l);
-                }
-                if (vlink(l) != null) {
-                    l = vlink(l);
-                } else if (hlist_stack_level == 0) {
-                    run = false;
-                }
-            }
-        } while (t != l);
-    }
-    return l;
-}
-
-/*tex
-
-    Search right to left from list tail |r| to head |l|, returns 1st non-skipable
-    item.
-
-*/
-
-halfword find_protchar_right(halfword l, halfword r)
-{
-    halfword t;
-    boolean run = true;
-    if (r == null)
-        return null;
-    hlist_stack_level = 0;
-    do {
-        t = r;
-        while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) {
-            push_node(l);
-            push_node(r);
-            l = list_ptr(r);
-            r = l;
-            while (vlink(r) != null) {
-                halfword s = r;
-                r = vlink(r);
-                alink(r) = s;
-            }
-        }
-        while (run && cp_skipable(r)) {
-            while ((r == l) && (hlist_stack_level > 0)) {
-                /*tex Don't visit this node again. */
-                r = pop_node();
-                l = pop_node();
-            }
-            if ((r != l) && (r != null)) {
-                if ((alink(r) != null) && (type(r) == boundary_node) && (subtype(r) == protrusion_boundary) &&
-                        ((boundary_value(r) == 2) || (boundary_value(r) == 3))) {
-                    /*tex Skip next node. */
-                    r = alink(r);
-                }
-                if (alink(r) != null) {
-                    r = alink(r);
-                } else {
-                    /*tex This is the input: \.{\\leavevmode\\penalty-10000\\penalty-10000} (bug \#268). */
-                    run = false;
-                }
-            } else if ((r == l) && (hlist_stack_level == 0))
-                run = false;
-        }
-    } while (t != r);
-    return r;
-}
-
-#define left_pw(a) char_pw((a), left_side)
-#define right_pw(a) char_pw((a), right_side)
-
-/*tex
-
-    When looking for optimal line breaks, \TeX\ creates a ``break node'' for each
-    break that is {\sl feasible}, in the sense that there is a way to end a line
-    at the given place without requiring any line to stretch more than a given
-    tolerance. A break node is characterized by three things: the position of the
-    break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|, or
-    |disc_node|); the ordinal number of the line that will follow this
-    breakpoint; and the fitness classification of the line that has just ended,
-    i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|.
-
-*/
-
-typedef enum {
-    /*tex fitness classification for lines stretching more than their stretchability */
-    very_loose_fit = 0,
-    /*tex fitness classification for lines stretching 0.5 to 1.0 of their stretchability */
-    loose_fit,
-    /*tex fitness classification for all other lines */
-    decent_fit,
-    /*tex fitness classification for lines shrinking 0.5 to 1.0 of their shrinkability */
-    tight_fit
-} fitness_value;
-
-/*tex
-
-    The algorithm essentially determines the best possible way to achieve each
-    feasible combination of position, line, and fitness. Thus, it answers
-    questions like, ``What is the best way to break the opening part of the
-    paragraph so that the fourth line is a tight line ending at such-and-such a
-    place?'' However, the fact that all lines are to be the same length after a
-    certain point makes it possible to regard all sufficiently large line numbers
-    as equivalent, when the looseness parameter is zero, and this makes it
-    possible for the algorithm to save space and time.
-
-    An ``active node'' and a ``passive node'' are created in |mem| for each
-    feasible breakpoint that needs to be considered. Active nodes are three words
-    long and passive nodes are two words long. We need active nodes only for
-    breakpoints near the place in the paragraph that is currently being examined,
-    so they are recycled within a comparatively short time after they are
-    created.
-
-    An active node for a given breakpoint contains six fields:
-
-    \startitemize[n]
-
-        \startitem
-            |vlink| points to the next node in the list of active nodes; the last
-            active node has |vlink=active|.
-        \stopitem
-
-        \startitem
-            |break_node| points to the passive node associated with this
-            breakpoint.
-        \stopitem
-
-        \startitem
-            |line_number| is the number of the line that follows this breakpoint.
-        \stopitem
-
-        \startitem
-            |fitness| is the fitness classification of the line ending at this
-            breakpoint.
-        \stopitem
-
-        \startitem
-            |type| is either |hyphenated_node| or |unhyphenated_node|, depending
-            on whether this breakpoint is a |disc_node|.
-        \stopitem
-
-        \startitem
-            |total_demerits| is the minimum possible sum of demerits over all
-            lines leading from the beginning of the paragraph to this breakpoint.
-        \stopitem
-
-    \stopitemize
-
-    The value of |vlink(active)| points to the first active node on a vlinked
-    list of all currently active nodes. This list is in order by |line_number|,
-    except that nodes with |line_number>easy_line| may be in any order relative
-    to each other.
-
-*/
-
-void initialize_active(void)
-{
-    type(active) = hyphenated_node;
-    line_number(active) = max_halfword;
-    /*tex The |subtype| is never examined. */
-    subtype(active) = 0;
-}
-
-/*tex
-
-    The passive node for a given breakpoint contains eight fields:
-
-    \startitemize
-
-        \startitem
-            |vlink| points to the passive node created just before this one, if
-            any, otherwise it is |null|.
-        \stopitem
-
-        \startitem
-            |cur_break| points to the position of this breakpoint in the
-            horizontal list for the paragraph being broken.
-        \stopitem
-
-        \startitem
-            |prev_break| points to the passive node that should precede this one
-            in an optimal path to this breakpoint.
-        \stopitem
-
-        \startitem
-            |serial| is equal to |n| if this passive node is the |n|th one
-            created during the current pass. (This field is used only when
-            printing out detailed statistics about the line-breaking
-            calculations.)
-        \stopitem
-
-        \startitem
-            |passive_pen_inter| holds the current \.{\\localinterlinepenalty}
-        \stopitem
-
-        \startitem
-            |passive_pen_broken| holds the current \.{\\localbrokenpenalty}
-        \stopitem
-
-    \stopitemize
-
-    There is a global variable called |passive| that points to the most recently
-    created passive node. Another global variable, |printed_node|, is used to
-    help print out the paragraph when detailed information about the
-    line-breaking computation is being displayed.
-
-*/
-
-/*tex most recent node on passive list */
-
-static halfword passive;
-
-/*tex most recent node that has been printed */
-
-static halfword printed_node;
-
-/*tex the number of passive nodes allocated on this pass */
-
-static halfword pass_number;
-
-/*tex
-
-    The active list also contains ``delta'' nodes that help the algorithm compute
-    the badness of individual lines. Such nodes appear only between two active
-    nodes, and they have |type=delta_node|. If |p| and |r| are active nodes and
-    if |q| is a delta node between them, so that |vlink(p)=q| and |vlink(q)=r|,
-    then |q| tells the space difference between lines in the horizontal list that
-    start after breakpoint |p| and lines that start after breakpoint |r|. In
-    other words, if we know the length of the line that starts after |p| and ends
-    at our current position, then the corresponding length of the line that
-    starts after |r| is obtained by adding the amounts in node~|q|. A delta node
-    contains seven scaled numbers, since it must record the net change in glue
-    stretchability with respect to all orders of infinity. The natural width
-    difference appears in |mem[q+1].sc|; the stretch differences in units of pt,
-    sfi, fil, fill, and filll appear in |mem[q+2..q+6].sc|; and the shrink
-    difference appears in |mem[q+7].sc|. The |subtype| field of a delta node is
-    not used.
-
-    Actually, we have two more fields that are used by |pdftex|.
-
-    As the algorithm runs, it maintains a set of seven delta-like registers for
-    the length of the line following the first active breakpoint to the current
-    position in the given hlist. When it makes a pass through the active list, it
-    also maintains a similar set of seven registers for the length following the
-    active breakpoint of current interest. A third set holds the length of an
-    empty line (namely, the sum of \.{\\leftskip} and \.{\\rightskip}); and a
-    fourth set is used to create new delta nodes.
-
-    When we pass a delta node we want to do operations like:
-
-    \starttyping
-    for k := 1 to 7 do
-        cur_active_width[k] := cur_active_width[k] + mem[q+k].sc|};
-    \stoptyping
-
-    and we want to do this without the overhead of |for| loops. The |do_all_six|
-    macro makes such six-tuples convenient.
-
-*/
-
-/*tex distance from first active node to~|cur_p| */
-
-static scaled active_width[10] = { 0 };
-
-/*tex length of an ``empty'' line */
-
-static scaled background[10] = { 0 };
-
-/*tex length being computed after current break */
-
-static scaled break_width[10] = { 0 };
-
-/*tex Make |auto_breaking| accessible out of |line_break|: */
-
-static boolean auto_breaking;
-
-/*tex
-
-    Let's state the principles of the delta nodes more precisely and concisely,
-    so that the following programs will be less obscure. For each legal
-    breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and
-    $\beta(p)$ such that the length of material in a line from breakpoint~|p| to
-    breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
-    Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from
-    the beginning of the paragraph to a point ``after'' a break at |p| and to a
-    point ``before'' a break at |q|; and $\gamma$ is the width of an empty line,
-    namely the length contributed by \.{\\leftskip} and \.{\\rightskip}.
-
-    Suppose, for example, that the paragraph consists entirely of alternating
-    boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and let the
-    skips have widths $y_1\ldots y_n$, so that the paragraph can be represented
-    by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint at $y_i$; then
-    $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)= x_1+y_1+\cdots+x_i$.
-    To check this, note that the length of material from $p_2$ to $p_5$, say, is
-    $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5) -\alpha(p_2)$.
-
-    The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and
-    shrinkability as well as a natural width. If we were to compute $\alpha(p)$
-    and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and
-    the multiprecise numbers would have to be kept in the active nodes. \TeX\
-    avoids this problem by working entirely with relative differences or
-    ``deltas.'' Suppose, for example, that the active list contains
-    $a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints
-    and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$
-    and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is
-    currently positioned at some other breakpoint |p|, the |active_width| array
-    contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through
-    the list of active nodes and considering a tentative line that runs from
-    $a_2$ to~|p|, say, the |cur_active_width| array will contain the value
-    $\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$, we
-    want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this is just
-    $\delta_2$, which appears in the active list between $a_2$ and $a_3$. The
-    |background| array contains $\gamma$. The |break_width| array will be used to
-    calculate values of new delta nodes when the active list is being updated.
-
-    The heart of the line-breaking procedure is `|try_break|', a subroutine that
-    tests if the current breakpoint |cur_p| is feasible, by running through the
-    active list to see what lines of text can be made from active nodes
-    to~|cur_p|. If feasible breaks are possible, new break nodes are created. If
-    |cur_p| is too far from an active node, that node is deactivated.
-
-    The parameter |pi| to |try_break| is the penalty associated with a break at
-    |cur_p|; we have |pi=eject_penalty| if the break is forced, and
-    |pi=inf_penalty| if the break is illegal.
-
-    The other parameter, |break_type|, is set to |hyphenated_node| or
-    |unhyphenated_node|, depending on whether or not the current break is at a
-    |disc_node|. The end of a paragraph is also regarded as `|hyphenated_node|';
-    this case is distinguishable by the condition |cur_p=null|.
-
-*/
-
-/*tex running \.{\\localinterlinepenalty} */
-
-static int internal_pen_inter;
-
-/*tex running \.{\\localbrokenpenalty} */
-
-static int internal_pen_broken;
-
-/*tex running \.{\\localleftbox} */
-
-static halfword internal_left_box;
-
-/*tex running \.{\\localleftbox} width */
-
-static int internal_left_box_width;
-
-/*tex running \.{\\localleftbox} */
-
-static halfword init_internal_left_box;
-
-/*tex running \.{\\localleftbox} width */
-
-static int init_internal_left_box_width;
-
-/*tex running \.{\\localrightbox} */
-
-static halfword internal_right_box;
-
-/*tex running \.{\\localrightbox} width */
-
-static int internal_right_box_width;
-
-/*tex the length of discretionary material preceding a break */
-
-static scaled disc_width[10] = { 0 };
-
-/*tex
-
-    As we consider various ways to end a line at |cur_p|, in a given line number
-    class, we keep track of the best total demerits known, in an array with one
-    entry for each of the fitness classifications. For example,
-    |minimal_demerits[tight_fit]| contains the fewest total demerits of feasible
-    line breaks ending at |cur_p| with a |tight_fit| line;
-    |best_place[tight_fit]| points to the passive node for the break
-    before~|cur_p| that achieves such an optimum; and |best_pl_line[tight_fit]|
-    is the |line_number| field in the active node corresponding to
-    |best_place[tight_fit]|. When no feasible break sequence is known, the
-    |minimal_demerits| entries will be equal to |awful_bad|, which is $2^{30}-1$.
-    Another variable, |minimum_demerits|, keeps track of the smallest value in
-    the |minimal_demerits| array.
-
-*/
-
-/*tex best total demerits known for current line class and position, given the fitness */
-
-static int minimal_demerits[4];
-
-/*tex best total demerits known for current line class and position */
-
-static int minimum_demerits;
-
-/*tex how to achieve  |minimal_demerits| */
-
-static halfword best_place[4];
-
-/*tex corresponding line number */
-
-static halfword best_pl_line[4];
-
-/*tex
-
-    The length of lines depends on whether the user has specified \.{\\parshape}
-    or \.{\\hangindent}. If |par_shape_ptr| is not null, it points to a
-    $(2n+1)$-word record in |mem|, where the |vinfo| in the first word contains
-    the value of |n|, and the other $2n$ words contain the left margins and line
-    lengths for the first |n| lines of the paragraph; the specifications for line
-    |n| apply to all subsequent lines. If |par_shape_ptr=null|, the shape of the
-    paragraph depends on the value of |n=hang_after|; if |n>=0|, hanging
-    indentation takes place on lines |n+1|, |n+2|, \dots, otherwise it takes
-    place on lines 1, \dots, $\vert n\vert$. When hanging indentation is active,
-    the left margin is |hang_indent|, if |hang_indent>=0|, else it is 0; the line
-    length is $|hsize|-\vert|hang_indent|\vert$. The normal setting is
-    |par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|. Note that if
-    |hang_indent=0|, the value of |hang_after| is irrelevant.
-
-*/
-
-/*tex line numbers |>easy_line| are equivalent in break nodes */
-
-static halfword easy_line;
-
-/*tex line numbers |>last_special_line| all have the same width */
-
-static halfword last_special_line;
-
-/*tex the width of all lines |<=last_special_line|, if no \.{\\parshape} has been specified */
-
-static scaled first_width;
-
-/*tex the width of all lines |>last_special_line| */
-
-static scaled second_width;
-
-/*tex left margin to go with |first_width| */
-
-static scaled first_indent;
-
-/*tex left margin to go with |second_width| */
-
-static scaled second_indent;
-
-/*tex use this passive node and its predecessors */
-
-static halfword best_bet;
-
-/*tex the demerits associated with |best_bet| */
-
-static int fewest_demerits;
-
-/*tex line number following the last line of the new paragraph */
-
-static halfword best_line;
-
-/*tex the difference between |line_number(best_bet)| and the optimum |best_line| */
-
-static int actual_looseness;
-
-/*tex the difference between the current line number and the optimum |best_line| */
-
-static int line_diff;
-
-/*tex
-
-    \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.
-
-*/
-
-#define do_all_six(a) a(1);a(2);a(3);a(4);a(5);a(6);a(7)
-#define do_seven_eight(a) if (adjust_spacing > 1) { a(8);a(9); }
-#define do_all_eight(a) do_all_six(a); do_seven_eight(a)
-#define do_one_seven_eight(a) a(1); do_seven_eight(a)
-
-#define store_background(a) {active_width[a]=background[a];}
-
-#define kern_break() { \
-    if ((!is_char_node(vlink(cur_p))) && auto_breaking) \
-        if (type(vlink(cur_p))==glue_node) \
-            ext_try_break(\
-                0, \
-                unhyphenated_node, \
-                line_break_dir, \
-                adjust_spacing, \
-                par_shape_ptr, \
-                adj_demerits, \
-                tracing_paragraphs, \
-                protrude_chars, \
-                line_penalty, \
-                last_line_fit, \
-                double_hyphen_demerits, \
-                final_hyphen_demerits, \
-                first_p, \
-                cur_p \
-            ); \
-    if (type(cur_p)!=math_node) \
-        active_width[1] += width(cur_p); \
-    else \
-        active_width[1] += surround(cur_p); \
-}
-
-#define clean_up_the_memory() { \
-    q=vlink(active); \
-    while (q!=active) { \
-        cur_p = vlink(q); \
-        if (type(q)==delta_node) \
-            flush_node(q); \
-        else \
-            flush_node(q); \
-        q = cur_p; \
-    } \
-    q = passive;  \
-    while (q!=null) { \
-        cur_p = vlink(q); \
-        flush_node(q); \
-        q = cur_p; \
-    } \
-}
-
-/*tex special algorithm for last line of paragraph? */
-
-static boolean do_last_line_fit;
-
-/*tex infinite stretch components of  |par_fill_skip| */
-
-static scaled fill_width[4];
-
-/*tex |shortfall|  corresponding to |minimal_demerits| */
-
-static scaled best_pl_short[4];
-
-/*tex corresponding glue stretch or shrink */
-
-static scaled best_pl_glue[4];
-
-#define reset_disc_width(a) disc_width[(a)] = 0
-
-#define add_disc_width_to_break_width(a)     break_width[(a)] += disc_width[(a)]
-#define sub_disc_width_from_active_width(a)  active_width[(a)] -= disc_width[(a)]
-
-#define add_char_shrink(a,b)  a += char_shrink((b))
-#define add_char_stretch(a,b) a += char_stretch((b))
-#define sub_char_shrink(a,b)  a -= char_shrink((b))
-#define sub_char_stretch(a,b) a -= char_stretch((b))
-
-#define add_kern_shrink(a,b)  a += kern_shrink((b))
-#define add_kern_stretch(a,b) a += kern_stretch((b))
-#define sub_kern_shrink(a,b)  a -= kern_shrink((b))
-#define sub_kern_stretch(a,b) a -= kern_stretch((b))
-
-/*tex
-
-    This function is used to add the width of a list of nodes (from a
-    discretionary) to one of the width arrays.
-
-    Replacement texts and discretionary texts are supposed to contain only
-    character nodes, kern nodes, and box or rule nodes.
-
-*/
-
-#define bad_node_in_disc_error(p) { \
-    if (type(p) == whatsit_node) { \
-        formatted_error("linebreak","invalid node with type %s and subtype %i found in discretionary",node_data[type(p)].name,subtype(p)); \
-    } else { \
-        formatted_error("linebreak","invalid node with type %s found in discretionary",node_data[type(p)].name); \
-    } \
-}
-
-static void add_to_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
-{
-    while (s != null) {
-        if (is_char_node(s)) {
-            widths[1] += pack_width(line_break_dir, dir_TRT, s, true);
-            if ((adjust_spacing > 1) && check_expand_pars(font(s))) {
-                set_prev_char_p(s);
-                add_char_stretch(widths[8], s);
-                add_char_shrink(widths[9], s);
-            };
-        } else {
-            switch (type(s)) {
-                case hlist_node:
-                case vlist_node:
-                    widths[1] += pack_width(line_break_dir, box_dir(s), s, false);
-                    break;
-                case kern_node:
-                    if ((adjust_spacing == 2) && (subtype(s) == normal)) {
-                        add_kern_stretch(widths[8], s);
-                        add_kern_shrink(widths[9], s);
-                    }
-                    /*tex fall through */
-                case rule_node:
-                    widths[1] += width(s);
-                    break;
-                case disc_node:
-                    break;
-                default:
-                    bad_node_in_disc_error(s);
-                    break;
-            }
-        }
-        s = vlink(s);
-    }
-}
-
-/*tex
-
-    This function is used to substract the width of a list of nodes (from a
-    discretionary) from one of the width arrays. It is used only once, but
-    deserves it own function because of orthogonality with the |add_to_widths|
-    function.
-
-*/
-
-static void sub_from_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
-{
-    while (s != null) {
-        /*tex Subtract the width of node |s| from |break_width|; */
-        if (is_char_node(s)) {
-            widths[1] -= pack_width(line_break_dir, dir_TRT, s, true);
-            if ((adjust_spacing > 1) && check_expand_pars(font(s))) {
-                set_prev_char_p(s);
-                sub_char_stretch(widths[8], s);
-                sub_char_shrink(widths[9], s);
-            }
-        } else {
-            switch (type(s)) {
-                case hlist_node:
-                case vlist_node:
-                    widths[1] -= pack_width(line_break_dir, box_dir(s), s, false);
-                    break;
-                case kern_node:
-                    if ((adjust_spacing == 2) && (subtype(s) == normal)) {
-                        sub_kern_stretch(widths[8], s);
-                        sub_kern_shrink(widths[9], s);
-                    }
-                    /*tex fall through */
-                case rule_node:
-                    widths[1] -= width(s);
-                    break;
-                case disc_node:
-                    break;
-                default:
-                    bad_node_in_disc_error(s);
-                    break;
-            }
-        }
-        s = vlink(s);
-    }
-}
-
-/*tex
-
-    When we insert a new active node for a break at |cur_p|, suppose this new
-    node is to be placed just before active node |a|; then we essentially want to
-    insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where
-    $\delta=\alpha(a)-\alpha(|cur_p|)$ and
-    $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$ in the notation explained above.
-    The |cur_active_width| array now holds $\gamma+\beta(|cur_p|)-\alpha(a)$; so
-    $\delta$ can be obtained by subtracting |cur_active_width| from the quantity
-    $\gamma+\beta(|cur_p|)- \alpha(|cur_p|)$. The latter quantity can be regarded
-    as the length of a line ``from |cur_p| to |cur_p|''; we call it the
-    |break_width| at |cur_p|.
-
-    The |break_width| is usually negative, since it consists of the background
-    (which is normally zero) minus the width of nodes following~|cur_p| that are
-    eliminated after a break. If, for example, node |cur_p| is a glue node, the
-    width of this glue is subtracted from the background; and we also look ahead
-    to eliminate all subsequent glue and penalty and kern and math nodes,
-    subtracting their widths as well.
-
-    Kern nodes do not disappear at a line break unless they are |explicit|.
-
-*/
-
-static void compute_break_width(int break_type, int line_break_dir, int adjust_spacing, halfword p)
-{
-    /*tex
-
-        Glue and other 'whitespace' to be skipped after a break; used if
-        unhyphenated, or |post_break==empty|.
-
-    */
-    halfword s = p;
-    if (break_type > unhyphenated_node && p != null) {
-        /*tex
-
-            Compute the discretionary |break_width| values.
-
-            When |p| is a discretionary break, the length of a line ``from |p| to
-            |p|'' has to be defined properly so that the other calculations work
-            out. Suppose that the pre-break text at |p| has length $l_0$, the
-            post-break text has length $l_1$, and the replacement text has length
-            |l|. Suppose also that |q| is the node following the replacement
-            text. Then length of a line from |p| to |q| will be computed as
-            $\gamma+\beta(q)-\alpha(|p|)$, where $\beta(q)=\beta(|p|)-l_0+l$. The
-            actual length will be the background plus $l_1$, so the length from
-            |p| to |p| should be $\gamma+l_0+l_1-l$. If the post-break text of
-            the discretionary is empty, a break may also discard~|q|; in that
-            unusual case we subtract the length of~|q| and any other nodes that
-            will be discarded after the discretionary break.
-
-            The value of $l_0$ need not be computed, since |line_break| will put
-            it into the global variable |disc_width| before calling |try_break|.
-
-            In case of nested discretionaries, we always follow the no-break
-            path, as we are talking about the breaking on {\it this} position.
-
-        */
-        sub_from_widths(vlink_no_break(p), line_break_dir, adjust_spacing, break_width);
-        add_to_widths(vlink_post_break(p), line_break_dir, adjust_spacing, break_width);
-        do_one_seven_eight(add_disc_width_to_break_width);
-        if (vlink_post_break(p) == null) {
-            /*tex no |post_break|: 'skip' any 'whitespace' following */
-            s = vlink(p);
-        } else {
-            s = null;
-        }
-    }
-    while (s != null) {
-        switch (type(s)) {
-            case math_node:
-                /*tex begin mathskip code */
-                if (glue_is_zero(s)) {
-                    break_width[1] -= surround(s);
-                    break;
-                } else {
-                    /*tex fall through */
-                }
-                /*tex end mathskip code */
-            case glue_node:
-                /*tex Subtract glue from |break_width|; */
-                break_width[1] -= width(s);
-                break_width[2 + stretch_order(s)] -= stretch(s);
-                break_width[7] -= shrink(s);
-                break;
-            case penalty_node:
-                break;
-            case kern_node:
-                if (subtype(s) != explicit_kern && subtype(s) != italic_kern)
-                    return;
-                else
-                    break_width[1] -= width(s);
-                break;
-            default:
-                return;
-        };
-        s = vlink(s);
-    }
-}
-
-static void print_break_node(halfword q, fitness_value fit_class, quarterword break_type, halfword cur_p)
-{
-    /*tex Print a symbolic description of the new break node. */
-    tprint_nl("@@");
-    print_int(serial(passive));
-    tprint(": line ");
-    print_int(line_number(q) - 1);
-    print_char('.');
-    print_int(fit_class);
-    if (break_type == hyphenated_node)
-        print_char('-');
-    tprint(" t=");
-    print_int(total_demerits(q));
-    if (do_last_line_fit) {
-        /*tex Print additional data in the new active node. */
-        tprint(" s=");
-        print_scaled(active_short(q));
-        if (cur_p == null)
-            tprint(" a=");
-        else
-            tprint(" g=");
-        print_scaled(active_glue(q));
-    }
-    tprint(" -> @");
-    if (prev_break(passive) == null)
-        print_char('0');
-    else
-        print_int(serial(prev_break(passive)));
-}
-
-static void print_feasible_break(halfword cur_p, pointer r, halfword b, int pi, int d, boolean artificial_demerits)
-{
-    /*tex
-
-        Print a symbolic description of this feasible break.
-
-    */
-    if (printed_node != cur_p) {
-        /*tex
-
-            Print the list between |printed_node| and |cur_p|, then set
-            |printed_node:=cur_p|.
-
-        */
-        tprint_nl("");
-        if (cur_p == null) {
-            short_display(vlink(printed_node));
-        } else {
-            halfword save_link = vlink(cur_p);
-            vlink(cur_p) = null;
-            tprint_nl("");
-            short_display(vlink(printed_node));
-            vlink(cur_p) = save_link;
-        }
-        printed_node = cur_p;
-    }
-    tprint_nl("@");
-    if (cur_p == null) {
-        tprint_esc("par");
-    } else if (type(cur_p) != glue_node) {
-        if (type(cur_p) == penalty_node)
-            tprint_esc("penalty");
-        else if (type(cur_p) == disc_node)
-            tprint_esc("discretionary");
-        else if (type(cur_p) == kern_node)
-            tprint_esc("kern");
-        else
-            tprint_esc("math");
-    }
-    tprint(" via @");
-    if (break_node(r) == null)
-        print_char('0');
-    else
-        print_int(serial(break_node(r)));
-    tprint(" b=");
-    if (b > inf_bad)
-        print_char('*');
-    else
-        print_int(b);
-    tprint(" p=");
-    print_int(pi);
-    tprint(" d=");
-    if (artificial_demerits)
-        print_char('*');
-    else
-        print_int(d);
-}
-
-#define add_disc_width_to_active_width(a)   active_width[a] += disc_width[a]
-#define update_width(a) cur_active_width[a] += varmem[(r+(a))].cint
-
-#define set_break_width_to_background(a) break_width[a]=background[(a)]
-
-#define convert_to_break_width(a) \
-  varmem[(prev_r+(a))].cint = varmem[(prev_r+(a))].cint-cur_active_width[(a)]+break_width[(a)]
-
-#define store_break_width(a) active_width[(a)]=break_width[(a)]
-
-#define new_delta_to_break_width(a) \
-  varmem[(q+(a))].cint=break_width[(a)]-cur_active_width[(a)]
-
-#define new_delta_from_break_width(a) \
-  varmem[(q+(a))].cint=cur_active_width[(a)]-break_width[(a)]
-
-#define copy_to_cur_active(a) cur_active_width[(a)]=active_width[(a)]
-
-#define combine_two_deltas(a) varmem[(prev_r+(a))].cint += varmem[(r+(a))].cint
-#define downdate_width(a) cur_active_width[(a)] -= varmem[(prev_r+(a))].cint
-#define update_active(a) active_width[(a)]+=varmem[(r+(a))].cint
-
-#define total_font_stretch cur_active_width[8]
-#define total_font_shrink cur_active_width[9]
-
-#define cal_margin_kern_var(a) { \
-    character(cp) = character((a)); \
-    font(cp) = font((a)); \
-    do_subst_font(cp, 1000); \
-    if (font(cp) != font((a))) \
-        margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \
-    font(cp) = font((a)); \
-    do_subst_font(cp, -1000); \
-    if (font(cp) != font((a))) \
-        margin_kern_shrink += (left_pw(cp) - left_pw((a))); \
-}
-
-static void ext_try_break(
-    int pi,
-    quarterword break_type,
-    int line_break_dir,
-    int adjust_spacing,
-    int par_shape_ptr,
-    int adj_demerits,
-    int tracing_paragraphs,
-    int protrude_chars,
-    int line_penalty,
-    int last_line_fit,
-    int double_hyphen_demerits,
-    int final_hyphen_demerits, halfword first_p, halfword cur_p
-)
-{
-    /*tex runs through the active list */
-    pointer r;
-    scaled margin_kern_stretch;
-    scaled margin_kern_shrink;
-    halfword lp, rp, cp;
-    /*tex stays a step behind |r| */
-    halfword prev_r = active;
-    /*tex a step behind |prev_r|, if |type(prev_r)=delta_node| */
-    halfword prev_prev_r = null;
-    /*tex maximum line number in current equivalence class of lines */
-    halfword old_l = 0;
-    /*tex have we found a feasible break at |cur_p|? */
-    boolean no_break_yet = true;
-    /*tex points to a new node being created */
-    halfword q;
-    /*tex line number of current active node */
-    halfword l;
-    /*tex should node |r| remain in the active list? */
-    boolean node_r_stays_active;
-    /*tex the current line will be justified to this width */
-    scaled line_width = 0;
-    /*tex possible fitness class of test line */
-    fitness_value fit_class;
-    /*tex badness of test line */
-    halfword b;
-    /*tex demerits of test line */
-    int d;
-    /*tex has |d| been forced to zero? */
-    boolean artificial_demerits;
-    /*tex used in badness calculations */
-    scaled shortfall;
-    /*tex glue stretch or shrink of test line, adjustment for last line */
-    scaled g = 0;
-    /*tex distance from current active node */
-    scaled cur_active_width[10] = { 0 };
-    /*tex Make sure that |pi| is in the proper range; */
-    if (pi >= inf_penalty) {
-        /*tex this breakpoint is inhibited by infinite penalty */
-        return;
-    } else if (pi <= -inf_penalty) {
-        /*tex this breakpoint will be forced */
-        pi = eject_penalty;
-    }
-
-    do_all_eight(copy_to_cur_active);
-
-    while (1) {
-        r = vlink(prev_r);
-        /*tex
-
-            If node |r| is of type |delta_node|, update |cur_active_width|, set
-            |prev_r| and |prev_prev_r|, then |goto continue|. The following code
-            uses the fact that |type(active)<>delta_node|.
-
-        */
-        if (type(r) == delta_node) {
-            /*tex implicit */
-            do_all_eight(update_width);
-            prev_prev_r = prev_r;
-            prev_r = r;
-            continue;
-        }
-        /*tex
-
-            If a line number class has ended, create new active nodes for the
-            best feasible breaks in that class; then |return| if |r=active|,
-            otherwise compute the new |line_width|.
-
-            The first part of the following code is part of \TeX's inner loop, so
-            we don't want to waste any time. The current active node, namely node
-            |r|, contains the line number that will be considered next. At the
-            end of the list we have arranged the data structure so that
-            |r=active| and |line_number(active)>old_l|.
-
-        */
-        l = line_number(r);
-        if (l > old_l) {
-            /*tex now we are no longer in the inner loop */
-            if ((minimum_demerits < awful_bad)
-                && ((old_l != easy_line) || (r == active))) {
-                /*tex
-
-                    Create new active nodes for the best feasible breaks just
-                    found. It is not necessary to create new active nodes having
-                    |minimal_demerits| greater than
-                    |minimum_demerits+abs(adj_demerits)|, since such active nodes
-                    will never be chosen in the final paragraph breaks. This
-                    observation allows us to omit a substantial number of
-                    feasible breakpoints from further consideration.
-
-                */
-                if (no_break_yet) {
-                    no_break_yet = false;
-                    do_all_eight(set_break_width_to_background);
-                    compute_break_width(break_type, line_break_dir, adjust_spacing, cur_p);
-                }
-                /*tex
-
-                    Insert a delta node to prepare for breaks at |cur_p|. We use
-                    the fact that |type(active)<>delta_node|.
-
-                */
-                if (type(prev_r) == delta_node) {
-                    /*tex modify an existing delta node */
-                    do_all_eight(convert_to_break_width);
-                } else if (prev_r == active) {
-                    /*tex no delta node needed at the beginning */
-                    do_all_eight(store_break_width);
-                } else {
-                    q = new_node(delta_node, 0);
-                    vlink(q) = r;
-                    do_all_eight(new_delta_to_break_width);
-                    vlink(prev_r) = q;
-                    prev_prev_r = prev_r;
-                    prev_r = q;
-                }
-                if (abs(adj_demerits) >= awful_bad - minimum_demerits)
-                    minimum_demerits = awful_bad - 1;
-                else
-                    minimum_demerits += abs(adj_demerits);
-                for (fit_class = very_loose_fit; fit_class <= tight_fit;
-                     fit_class++) {
-                    if (minimal_demerits[fit_class] <= minimum_demerits) {
-                        /*tex
-
-                            Insert a new active node from |best_place[fit_class]|
-                            to |cur_p|. When we create an active node, we also
-                            create the corresponding passive node.
-
-                        */
-                        q = new_node(passive_node, 0);
-                        vlink(q) = passive;
-                        passive = q;
-                        cur_break(q) = cur_p;
-                        incr(pass_number);
-                        serial(q) = pass_number;
-                        prev_break(q) = best_place[fit_class];
-                        /*tex
-
-                            Here we keep track of the subparagraph penalties in
-                            the break nodes.
-
-                        */
-                        passive_pen_inter(q) = internal_pen_inter;
-                        passive_pen_broken(q) = internal_pen_broken;
-                        passive_last_left_box(q) = internal_left_box;
-                        passive_last_left_box_width(q) =
-                            internal_left_box_width;
-                        if (prev_break(q) != null) {
-                            passive_left_box(q) = passive_last_left_box(prev_break(q));
-                            passive_left_box_width(q) = passive_last_left_box_width(prev_break(q));
-                        } else {
-                            passive_left_box(q) = init_internal_left_box;
-                            passive_left_box_width(q) = init_internal_left_box_width;
-                        }
-                        passive_right_box(q) = internal_right_box;
-                        passive_right_box_width(q) = internal_right_box_width;
-                        q = new_node(break_type, fit_class);
-                        break_node(q) = passive;
-                        line_number(q) = best_pl_line[fit_class] + 1;
-                        total_demerits(q) = minimal_demerits[fit_class];
-                        if (do_last_line_fit) {
-                            /*tex
-
-                                Store additional data in the new active node.
-                                Here we save these data in the active node
-                                representing a potential line break.
-
-                            */
-                            active_short(q) = best_pl_short[fit_class];
-                            active_glue(q) = best_pl_glue[fit_class];
-                        }
-                        vlink(q) = r;
-                        vlink(prev_r) = q;
-                        prev_r = q;
-                        if (tracing_paragraphs > 0)
-                            print_break_node(q, fit_class, break_type, cur_p);
-                    }
-                    minimal_demerits[fit_class] = awful_bad;
-                }
-                minimum_demerits = awful_bad;
-                /*tex
-
-                    Insert a delta node to prepare for the next active node. When
-                    the following code is performed, we will have just inserted
-                    at least one active node before |r|, so
-                    |type(prev_r)<>delta_node|.
-
-                */
-                if (r != active) {
-                    q = new_node(delta_node, 0);
-                    vlink(q) = r;
-                    do_all_eight(new_delta_from_break_width);
-                    vlink(prev_r) = q;
-                    prev_prev_r = prev_r;
-                    prev_r = q;
-                }
-            }
-            if (r == active)
-                return;
-            /*tex
-
-                Compute the new line width. When we come to the following code,
-                we have just encountered the first active node~|r| whose
-                |line_number| field contains |l|. Thus we want to compute the
-                length of the $l\mskip1mu$th line of the current paragraph.
-                Furthermore, we want to set |old_l| to the last number in the
-                class of line numbers equivalent to~|l|.
-
-            */
-            if (l > easy_line) {
-                old_l = max_halfword - 1;
-                line_width = second_width;
-            } else {
-                old_l = l;
-                if (l > last_special_line) {
-                    line_width = second_width;
-                } else if (par_shape_ptr == null) {
-                    line_width = first_width;
-                } else {
-                    line_width = varmem[(par_shape_ptr + 2 * l + 1)].cint;
-                }
-            }
-        }
-        /*tex
-
-            If a line number class has ended, create new active nodes for the
-            best feasible breaks in that class; then |return| if |r=active|,
-            otherwise compute the new |line_width|.
-
-            Consider the demerits for a line from |r| to |cur_p|; deactivate node
-            |r| if it should no longer be active; then |goto continue| if a line
-            from |r| to |cur_p| is infeasible, otherwise record a new feasible
-            break.
-
-        */
-        artificial_demerits = false;
-        shortfall = line_width - cur_active_width[1];
-        if (break_node(r) == null)
-            shortfall -= init_internal_left_box_width;
-        else
-            shortfall -= passive_last_left_box_width(break_node(r));
-        shortfall -= internal_right_box_width;
-        if (protrude_chars > 1) {
-            halfword l1, o;
-            l1 = (break_node(r) == null) ? first_p : cur_break(break_node(r));
-            if (cur_p == null) {
-                o = null;
-            } else {
-                o = alink(cur_p);
-                assert(vlink(o) == cur_p);
-            }
-            /*tex
-
-                The disc could be a SELECT subtype, to we might need to get the
-                last character as |pre_break| from either the |pre_break| list
-                (if the previous INIT disc was taken), or the |no_break| (sic)
-                list (if the previous INIT disc was not taken).
-
-                The last characters (hyphenation character) if these two list
-                should always be the same anyway, so we just look at |pre_break|.
-
-                Let's look at the right margin first.
-
-            */
-            if ((cur_p != null) && (type(cur_p) == disc_node) && (vlink_pre_break(cur_p) != null)) {
-                /*tex a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */
-                o = tlink_pre_break(cur_p);
-            } else {
-                o = find_protchar_right(l1, o);
-            }
-            /*tex now the left margin */
-            if ((l1 != null) && (type(l1) == disc_node) && (vlink_post_break(l1) != null)) {
-                /*tex The first char could be a disc! Protrude the first char. */
-                l1 = vlink_post_break(l1);
-            } else {
-                l1 = find_protchar_left(l1, true);
-            }
-            shortfall += (left_pw(l1) + right_pw(o));
-        }
-        if (shortfall != 0) {
-            margin_kern_stretch = 0;
-            margin_kern_shrink = 0;
-            if (protrude_chars > 1) {
-                /*tex Calculate variations of marginal kerns. */
-                lp = last_leftmost_char;
-                rp = last_rightmost_char;
-                cp = raw_glyph_node();
-                if (lp != null) {
-                    cal_margin_kern_var(lp);
-                }
-                if (rp != null) {
-                    cal_margin_kern_var(rp);
-                }
-                flush_node(cp);
-            }
-            if ((shortfall > 0) && ((total_font_stretch + margin_kern_stretch) > 0)) {
-                if ((total_font_stretch + margin_kern_stretch) > shortfall)
-                    shortfall = ((total_font_stretch + margin_kern_stretch) / (max_stretch_ratio / cur_font_step)) / 2;
-                else
-                    shortfall -= (total_font_stretch + margin_kern_stretch);
-            } else if ((shortfall < 0) && ((total_font_shrink + margin_kern_shrink) > 0)) {
-                if ((total_font_shrink + margin_kern_shrink) > -shortfall)
-                    shortfall -= ((total_font_shrink + margin_kern_shrink) / (max_shrink_ratio / cur_font_step)) / 2;
-                else
-                    shortfall += (total_font_shrink + margin_kern_shrink);
-            }
-        }
-        if (shortfall > 0) {
-            /*tex
-
-                Set the value of |b| to the badness for stretching the line, and
-                compute the corresponding |fit_class|.
-
-                When a line must stretch, the available stretchability can be
-                found in the subarray |cur_active_width[2..6]|, in units of
-                points, sfi, fil, fill and filll.
-
-                The present section is part of \TeX's inner loop, and it is most
-                often performed when the badness is infinite; therefore it is
-                worth while to make a quick test for large width excess and small
-                stretchability, before calling the |badness| subroutine.
-
-            */
-            if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) ||
-                (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) {
-                if (do_last_line_fit) {
-                    if (cur_p == null) {
-                        /*tex
-
-                            The last line of a paragraph. Perform computations
-                            for last line and |goto found|.
-
-                            Here we compute the adjustment |g| and badness |b|
-                            for a line from |r| to the end of the paragraph. When
-                            any of the criteria for adjustment is violated we
-                            fall through to the normal algorithm.
-
-                            The last line must be too short, and have infinite
-                            stretch entirely due to |par_fill_skip|.
-
-                        */
-                        if ((active_short(r) == 0) || (active_glue(r) <= 0))
-                            /*tex
-
-                                Previous line was neither stretched nor shrunk,
-                                or was infinitely bad.
-
-                            */
-                            goto NOT_FOUND;
-                        if ((cur_active_width[3] != fill_width[0]) || (cur_active_width[4] != fill_width[1]) ||
-                            (cur_active_width[5] != fill_width[2]) || (cur_active_width[6] != fill_width[3]))
-                            /*tex
-
-                                Infinite stretch of this line not entirely due to |par_fill_skip|.
-
-                            */
-                            goto NOT_FOUND;
-                        if (active_short(r) > 0)
-                            g = cur_active_width[2];
-                        else
-                            g = cur_active_width[7];
-                        if (g <= 0)
-                            /*tex No finite stretch resp.\ no shrink. */
-                            goto NOT_FOUND;
-                        arith_error = false;
-                        g = fract(g, active_short(r), active_glue(r),
-                                  max_dimen);
-                        if (last_line_fit < 1000)
-                            g = fract(g, last_line_fit, 1000, max_dimen);
-                        if (arith_error) {
-                            if (active_short(r) > 0)
-                                g = max_dimen;
-                            else
-                                g = -max_dimen;
-                        }
-                        if (g > 0) {
-                            /*tex
-
-                                Set the value of |b| to the badness of the last
-                                line for stretching, compute the corresponding
-                                |fit_class, and |goto found|. These badness
-                                computations are rather similar to those of the
-                                standard algorithm, with the adjustment amount
-                                |g| replacing the |shortfall|.
-
-                            */
-                            if (g > shortfall)
-                                g = shortfall;
-                            if (g > 7230584) {
-                                if (cur_active_width[2] < 1663497) {
-                                    b = inf_bad;
-                                    fit_class = very_loose_fit;
-                                    goto FOUND;
-                                }
-                            }
-                            b = badness(g, cur_active_width[2]);
-                            if (b > 99) {
-                                fit_class = very_loose_fit;
-                            } else if (b > 12) {
-                                fit_class = loose_fit;
-                            } else {
-                                fit_class = decent_fit;
-                            }
-                            goto FOUND;
-                        } else if (g < 0) {
-                            /*tex
-
-                                Set the value of |b| to the badness of the last
-                                line for shrinking, compute the corresponding
-                                |fit_class, and |goto found||.
-
-                            */
-                            if (-g > cur_active_width[7])
-                                g = -cur_active_width[7];
-                            b = badness(-g, cur_active_width[7]);
-                            if (b > 12)
-                                fit_class = tight_fit;
-                            else
-                                fit_class = decent_fit;
-                            goto FOUND;
-                        }
-                    }
-                  NOT_FOUND:
-                    shortfall = 0;
-                }
-                b = 0;
-                /*tex Infinite stretch. */
-                fit_class = decent_fit;
-            } else if (shortfall > 7230584 && cur_active_width[2] < 1663497) {
-                b = inf_bad;
-                fit_class = very_loose_fit;
-            } else {
-                b = badness(shortfall, cur_active_width[2]);
-                if (b > 99) {
-                    fit_class = very_loose_fit;
-                } else if (b > 12) {
-                    fit_class = loose_fit;
-                } else {
-                    fit_class = decent_fit;
-                }
-            }
-        } else {
-            /*tex
-
-                Set the value of |b| to the badness for shrinking the line, and
-                compute the corresponding |fit_class|. Shrinkability is never
-                infinite in a paragraph; we can shrink the line from |r| to
-                |cur_p| by at most |cur_active_width[7]|.
-
-            */
-            if (-shortfall > cur_active_width[7])
-                b = inf_bad + 1;
-            else
-                b = badness(-shortfall, cur_active_width[7]);
-            if (b > 12)
-                fit_class = tight_fit;
-            else
-                fit_class = decent_fit;
-        }
-        if (do_last_line_fit) {
-            /*tex Adjust the additional data for last line; */
-            if (cur_p == null)
-                shortfall = 0;
-            if (shortfall > 0) {
-                g = cur_active_width[2];
-            } else if (shortfall < 0) {
-                g = cur_active_width[7];
-            } else {
-                g = 0;
-            }
-        }
-      FOUND:
-        if ((b > inf_bad) || (pi == eject_penalty)) {
-            /*tex
-
-                Prepare to deactivate node~|r|, and |goto deactivate| unless
-                there is a reason to consider lines of text from |r| to |cur_p|.
-                During the final pass, we dare not lose all active nodes, lest we
-                lose touch with the line breaks already found. The code shown
-                here makes sure that such a catastrophe does not happen, by
-                permitting overfull boxes as a last resort. This particular part
-                of \TeX\ was a source of several subtle bugs before the correct
-                program logic was finally discovered; readers who seek to
-                ``improve'' \TeX\ should therefore think thrice before daring to
-                make any changes here.
-
-            */
-            if (final_pass && (minimum_demerits == awful_bad) &&
-                (vlink(r) == active) && (prev_r == active)) {
-                /*tex Set demerits zero, this break is forced. */
-                artificial_demerits = true;
-            } else if (b > threshold) {
-                goto DEACTIVATE;
-            }
-            node_r_stays_active = false;
-        } else {
-            prev_r = r;
-            if (b > threshold)
-                continue;
-            node_r_stays_active = true;
-        }
-        /*tex
-
-            Record a new feasible break. When we get to this part of the code,
-            the line from |r| to |cur_p| is feasible, its badness is~|b|, and its
-            fitness classification is |fit_class|. We don't want to make an
-            active node for this break yet, but we will compute the total
-            demerits and record them in the |minimal_demerits| array, if such a
-            break is the current champion among all ways to get to |cur_p| in a
-            given line-number class and fitness class.
-
-        */
-        if (artificial_demerits) {
-            d = 0;
-        } else {
-            /*tex Compute the demerits, |d|, from |r| to |cur_p|. */
-            d = line_penalty + b;
-            if (abs(d) >= 10000)
-                d = 100000000;
-            else
-                d = d * d;
-            if (pi != 0) {
-                if (pi > 0) {
-                    d += (pi * pi);
-                } else if (pi > eject_penalty) {
-                    d -= (pi * pi);
-                }
-            }
-            if ((break_type == hyphenated_node) && (type(r) == hyphenated_node)) {
-                if (cur_p != null)
-                    d += double_hyphen_demerits;
-                else
-                    d += final_hyphen_demerits;
-            }
-            if (abs(fit_class - fitness(r)) > 1)
-                d = d + adj_demerits;
-        }
-        if (tracing_paragraphs > 0) {
-            print_feasible_break(cur_p, r, b, pi, d, artificial_demerits);
-        }
-        /*tex This is the minimum total demerits from the beginning to |cur_p| via |r|. */
-        d += total_demerits(r);
-        if (d <= minimal_demerits[fit_class]) {
-            minimal_demerits[fit_class] = d;
-            best_place[fit_class] = break_node(r);
-            best_pl_line[fit_class] = l;
-            if (do_last_line_fit) {
-                /*tex
-
-                    Store additional data for this feasible break. For each
-                    feasible break we record the shortfall and glue stretch or
-                    shrink (or adjustment).
-
-                */
-                best_pl_short[fit_class] = shortfall;
-                best_pl_glue[fit_class] = g;
-            }
-            if (d < minimum_demerits)
-                minimum_demerits = d;
-        }
-        /*tex Record a new feasible break. */
-        if (node_r_stays_active) {
-            /*tex |prev_r| has been set to |r|. */
-            continue;
-        }
-      DEACTIVATE:
-        /*tex
-
-            Deactivate node |r|. When an active node disappears, we must delete
-            an adjacent delta node if the active node was at the beginning or the
-            end of the active list, or if it was surrounded by delta nodes. We
-            also must preserve the property that |cur_active_width| represents
-            the length of material from |vlink(prev_r)| to~|cur_p|.
-
-        */
-        vlink(prev_r) = vlink(r);
-        flush_node(r);
-        if (prev_r == active) {
-            /*tex
-
-                Update the active widths, since the first active node has been
-                deleted. The following code uses the fact that
-                |type(active)<>delta_node|. If the active list has just become
-                empty, we do not need to update the |active_width| array, since
-                it will be initialized when an active node is next inserted.
-
-            */
-            r = vlink(active);
-            if (type(r) == delta_node) {
-                do_all_eight(update_active);
-                do_all_eight(copy_to_cur_active);
-                vlink(active) = vlink(r);
-                flush_node(r);
-            }
-        } else if (type(prev_r) == delta_node) {
-            r = vlink(prev_r);
-            if (r == active) {
-                do_all_eight(downdate_width);
-                vlink(prev_prev_r) = active;
-                flush_node(prev_r);
-                prev_r = prev_prev_r;
-            } else if (type(r) == delta_node) {
-                do_all_eight(update_width);
-                do_all_eight(combine_two_deltas);
-                vlink(prev_r) = vlink(r);
-                flush_node(r);
-            }
-        }
-    }
-}
-
-void ext_do_line_break(
-    int paragraph_dir,
-    int pretolerance,
-    int tracing_paragraphs,
-    int tolerance,
-    scaled emergency_stretch,
-    int looseness,
-    int adjust_spacing,
-    halfword par_shape_ptr,
-    int adj_demerits,
-    int protrude_chars,
-    int line_penalty,
-    int last_line_fit,
-    int double_hyphen_demerits,
-    int final_hyphen_demerits,
-    int hang_indent,
-    int hsize,
-    int hang_after,
-    halfword left_skip,
-    halfword right_skip,
-    halfword inter_line_penalties_ptr,
-    int inter_line_penalty,
-    int club_penalty,
-    halfword club_penalties_ptr,
-    halfword widow_penalties_ptr,
-    int widow_penalty,
-    int broken_penalty,
-    halfword final_par_glue
-)
-{
-    /*tex Miscellaneous nodes of temporary interest. */
-    halfword cur_p, q, r, s;
-    int line_break_dir = paragraph_dir;
-    /*tex Get ready to start */
-    minimum_demerits = awful_bad;
-    minimal_demerits[tight_fit] = awful_bad;
-    minimal_demerits[decent_fit] = awful_bad;
-    minimal_demerits[loose_fit] = awful_bad;
-    minimal_demerits[very_loose_fit] = awful_bad;
-    fewest_demerits = 0;
-    actual_looseness = 0;
-    /*tex
-
-        We compute the values of |easy_line| and the other local variables
-        relating to line length when the |line_break| procedure is initializing
-        itself.
-
-    */
-    if (par_shape_ptr == null) {
-        if (hang_indent == 0) {
-            last_special_line = 0;
-            second_width = hsize;
-            second_indent = 0;
-        } else {
-            halfword used_hang_indent = swap_hang_indent(hang_indent);
-            /*tex
-
-                Set line length parameters in preparation for hanging
-                indentation. We compute the values of |easy_line| and the other
-                local variables relating to line length when the |line_break|
-                procedure is initializing itself.
-
-            */
-            last_special_line = abs(hang_after);
-            if (hang_after < 0) {
-                first_width = hsize - abs(used_hang_indent);
-                if (used_hang_indent >= 0)
-                    first_indent = used_hang_indent;
-                else
-                    first_indent = 0;
-                second_width = hsize;
-                second_indent = 0;
-            } else {
-                first_width = hsize;
-                first_indent = 0;
-                second_width = hsize - abs(used_hang_indent);
-                if (used_hang_indent >= 0)
-                    second_indent = used_hang_indent;
-                else
-                    second_indent = 0;
-            }
-        }
-    } else {
-        last_special_line = vinfo(par_shape_ptr + 1) - 1;
-        second_indent = varmem[(par_shape_ptr + 2 * (last_special_line + 1))].cint;
-        second_width = varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint;
-        second_indent = swap_parshape_indent(second_indent,second_width);
-    }
-    if (looseness == 0)
-        easy_line = last_special_line;
-    else
-        easy_line = max_halfword;
-    no_shrink_error_yet = true;
-    check_shrinkage(left_skip);
-    check_shrinkage(right_skip);
-    q = left_skip;
-    r = right_skip;
-    background[1] = width(q) + width(r);
-    background[2] = 0;
-    background[3] = 0;
-    background[4] = 0;
-    background[5] = 0;
-    background[6] = 0;
-    background[2 + stretch_order(q)] = stretch(q);
-    background[2 + stretch_order(r)] += stretch(r);
-    background[7] = shrink(q) + shrink(r);
-    if (adjust_spacing > 1) {
-        background[8] = 0;
-        background[9] = 0;
-        max_stretch_ratio = -1;
-        max_shrink_ratio = -1;
-        cur_font_step = -1;
-        set_prev_char_p(null);
-    }
-    /*tex
-
-        Check for special treatment of last line of paragraph. The new algorithm
-        for the last line requires that the stretchability |par_fill_skip| is
-        infinite and the stretchability of |left_skip| plus |right_skip| is
-        finite.
-
-    */
-    do_last_line_fit = false;
-    if (last_line_fit > 0) {
-        q = last_line_fill;
-        if ((stretch(q) > 0) && (stretch_order(q) > normal)) {
-            if ((background[3] == 0) && (background[4] == 0) && (background[5] == 0) && (background[6] == 0)) {
-                do_last_line_fit = true;
-                fill_width[0] = 0;
-                fill_width[1] = 0;
-                fill_width[2] = 0;
-                fill_width[3] = 0;
-                fill_width[stretch_order(q) - 1] = stretch(q);
-            }
-        }
-    }
-    /*tex Initialize |dir_ptr| for |line_break|. */
-    if (dir_ptr != null) {
-        flush_node_list(dir_ptr);
-        dir_ptr = null;
-    }
-    /*tex Find optimal breakpoints. */
-    threshold = pretolerance;
-    if (threshold >= 0) {
-        if (tracing_paragraphs > 0) {
-            begin_diagnostic();
-            tprint_nl("@firstpass");
-        }
-        second_pass = false;
-        final_pass = false;
-    } else {
-        threshold = tolerance;
-        second_pass = true;
-        final_pass = (emergency_stretch <= 0);
-        if (tracing_paragraphs > 0)
-            begin_diagnostic();
-    }
-    while (1) {
-        halfword first_p;
-        halfword nest_stack[10];
-        int nest_index = 0;
-        if (threshold > inf_bad)
-            threshold = inf_bad;
-        /*tex Create an active breakpoint representing the beginning of the paragraph. */
-        q = new_node(unhyphenated_node, decent_fit);
-        vlink(q) = active;
-        break_node(q) = null;
-        line_number(q) = cur_list.pg_field + 1;
-        total_demerits(q) = 0;
-        active_short(q) = 0;
-        active_glue(q) = 0;
-        vlink(active) = q;
-        do_all_eight(store_background);
-        passive = null;
-        printed_node = temp_head;
-        pass_number = 0;
-        font_in_short_display = null_font;
-        /*tex Create an active breakpoint representing the beginning of the paragraph. */
-        auto_breaking = true;
-        cur_p = vlink(temp_head);
-        /*tex Initialize with first |local_paragraph| node. */
-        if ((cur_p != null) && (type(cur_p) == local_par_node)) {
-            /*tex This used to be an assert, but may as well force it. */
-            alink(cur_p) = temp_head;
-            internal_pen_inter = local_pen_inter(cur_p);
-            internal_pen_broken = local_pen_broken(cur_p);
-            init_internal_left_box = local_box_left(cur_p);
-            init_internal_left_box_width = local_box_left_width(cur_p);
-            internal_left_box = init_internal_left_box;
-            internal_left_box_width = init_internal_left_box_width;
-            internal_right_box = local_box_right(cur_p);
-            internal_right_box_width = local_box_right_width(cur_p);
-        } else {
-            internal_pen_inter = 0;
-            internal_pen_broken = 0;
-            init_internal_left_box = null;
-            init_internal_left_box_width = 0;
-            internal_left_box = init_internal_left_box;
-            internal_left_box_width = init_internal_left_box_width;
-            internal_right_box = null;
-            internal_right_box_width = 0;
-        }
-        /*tex Initialize with first |local_paragraph| node. */
-        set_prev_char_p(null);
-        first_p = cur_p;
-        /*tex
-
-            To access the first node of paragraph as the first active node has
-            |break_node=null|.
-
-        */
-        while ((cur_p != null) && (vlink(active) != active)) {
-            /*tex
-
-                |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass,
-                also look at |disc_node|s.
-
-            */
-            while (is_char_node(cur_p)) {
-                /*tex
-
-                    Advance |cur_p| to the node following the present string of
-                    characters. The code that passes over the characters of words
-                    in a paragraph is part of \TeX's inner loop, so it has been
-                    streamlined for speed. We use the fact that
-                    `\.{\\parfillskip}' glue appears at the end of each
-                    paragraph; it is therefore unnecessary to check if
-                    |vlink(cur_p)=null| when |cur_p| is a character node.
-
-                */
-                active_width[1] += pack_width(line_break_dir, dir_TRT, cur_p, true);
-                if ((adjust_spacing > 1) && check_expand_pars(font(cur_p))) {
-                    set_prev_char_p(cur_p);
-                    add_char_stretch(active_width[8], cur_p);
-                    add_char_shrink(active_width[9], cur_p);
-                }
-                cur_p = vlink(cur_p);
-                while (cur_p == null && nest_index > 0) {
-                    cur_p = nest_stack[--nest_index];
-                }
-            }
-            if (cur_p == null) {
-                normal_error("linebreak","invalid list tail, probably missing glue");
-            }
-            /*tex
-
-                Determine legal breaks: As we move through the hlist, we need to
-                keep the |active_width| array up to date, so that the badness of
-                individual lines is readily calculated by |try_break|. It is
-                convenient to use the short name |active_width[1]| for the
-                component of active width that represents real width as opposed
-                to glue.
-
-            */
-            switch (type(cur_p)) {
-                case hlist_node:
-                case vlist_node:
-                    active_width[1] += pack_width(line_break_dir, box_dir(cur_p), cur_p, false);
-                    break;
-                case rule_node:
-                    active_width[1] += width(cur_p);
-                    break;
-                case dir_node:
-                    /*tex Adjust the dir stack for the |line_break| routine. */
-                    if (subtype(cur_p) == normal_dir) {
-                        line_break_dir = dir_dir(cur_p);
-                        /* Adds to |dir_ptr|. */
-                        push_dir_node(dir_ptr,cur_p);
-                    } else {
-                        pop_dir_node(dir_ptr);
-                        if (dir_ptr != null) {
-                            line_break_dir = dir_dir(dir_ptr);
-                        }
-                    }
-                    break;
-                case local_par_node:
-                    /*tex Advance past a |local_paragraph| node. */
-                    internal_pen_inter = local_pen_inter(cur_p);
-                    internal_pen_broken = local_pen_broken(cur_p);
-                    internal_left_box = local_box_left(cur_p);
-                    internal_left_box_width = local_box_left_width(cur_p);
-                    internal_right_box = local_box_right(cur_p);
-                    internal_right_box_width = local_box_right_width(cur_p);
-                    break;
-                case math_node:
-                    auto_breaking = (subtype(cur_p) == after);
-                    /*tex begin mathskip code */
-                    if (glue_is_zero(cur_p) || ignore_math_skip(cur_p)) {
-                        kern_break();
-                        break;
-                    } else {
-                        /*tex fall through */
-                    }
-                    /*tex end mathskip code */
-                case glue_node:
-                    /*tex
-
-                        If node |cur_p| is a legal breakpoint, call |try_break|;
-                        then update the active widths by including the glue in
-                        |glue_ptr(cur_p)|.
-
-                        When node |cur_p| is a glue node, we look at the previous
-                        to see whether or not a breakpoint is legal at |cur_p|,
-                        as explained above.
-
-                        We only break after certain nodes (see texnodes.h), a
-                        font related kern and a dir node when
-                        |\breakafterdirmode=1|.
-
-                    */
-                    if (auto_breaking) {
-                        halfword prev_p = alink(cur_p);
-                        if (prev_p != temp_head && (is_char_node(prev_p)
-                             || precedes_break(prev_p) || precedes_kern(prev_p) || precedes_dir(prev_p))) {
-                            ext_try_break(
-                                0,
-                                unhyphenated_node,
-                                line_break_dir,
-                                adjust_spacing,
-                                par_shape_ptr,
-                                adj_demerits,
-                                tracing_paragraphs,
-                                protrude_chars,
-                                line_penalty,
-                                last_line_fit,
-                                double_hyphen_demerits,
-                                final_hyphen_demerits,
-                                first_p,
-                                cur_p
-                            );
-                        }
-                    }
-                    check_shrinkage(cur_p);
-                    active_width[1] += width(cur_p);
-                    active_width[2 + stretch_order(cur_p)] += stretch(cur_p);
-                    active_width[7] += shrink(cur_p);
-                    break;
-                case kern_node:
-                    if (subtype(cur_p) == explicit_kern || subtype(cur_p) == italic_kern) {
-                        kern_break();
-                    } else {
-                        active_width[1] += width(cur_p);
-                        if ((adjust_spacing == 2) && (subtype(cur_p) == normal)) {
-                            add_kern_stretch(active_width[8], cur_p);
-                            add_kern_shrink(active_width[9], cur_p);
-                        }
-                    }
-                    break;
-                case disc_node:
-                    /*tex
-
-                        |select_disc|s are handled by the leading |init_disc|.
-
-                    */
-                    if (subtype(cur_p) == select_disc)
-                        break;
-                    /*tex
-
-                        Try to break after a discretionary fragment, then |goto
-                        done5|. The following code knows that discretionary texts
-                        contain only character nodes, kern nodes, box nodes, and
-                        rule nodes. This branch differs a bit from older engines
-                        because in \LUATEX\ we already have hyphenated the list.
-                        This means that we need to skip automatic disc nodes. Of
-                        better, we need to treat discretionaries and explicit
-                        hyphens always, even in the first pass.
-
-                    */
-                    if (second_pass || subtype(cur_p) <= automatic_disc) {
-                        int actual_penalty = (int) disc_penalty(cur_p);
-                        s = vlink_pre_break(cur_p);
-                        do_one_seven_eight(reset_disc_width);
-                        if (s == null) {
-                            /*tex trivial pre-break */
-                            ext_try_break(actual_penalty, hyphenated_node,
-                                          line_break_dir, adjust_spacing,
-                                          par_shape_ptr, adj_demerits,
-                                          tracing_paragraphs, protrude_chars,
-                                          line_penalty, last_line_fit,
-                                          double_hyphen_demerits,
-                                          final_hyphen_demerits, first_p, cur_p);
-                        } else {
-                            add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
-                            do_one_seven_eight(add_disc_width_to_active_width);
-                            ext_try_break(actual_penalty, hyphenated_node,
-                                          line_break_dir, adjust_spacing,
-                                          par_shape_ptr, adj_demerits,
-                                          tracing_paragraphs, protrude_chars,
-                                          line_penalty, last_line_fit,
-                                          double_hyphen_demerits,
-                                          final_hyphen_demerits, first_p, cur_p);
-                            if (subtype(cur_p) == init_disc) {
-                                /*tex
-
-                                    We should at two break points after the one
-                                    we added above:
-
-                                    \startitemize[n]
-                                        \startitem
-                                            which does a possible break in INIT's
-                                            |post_break|
-                                        \stopitem
-                                        \startitem
-                                            which means the |no_break| actually
-                                            was broken just a character later
-                                        \stopitem
-                                    \stopitemize
-
-                                    Do the select-0 case |f-f-i|:
-
-                                */
-                                s = vlink_pre_break(vlink(cur_p));
-                                add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
-                                ext_try_break(actual_penalty, hyphenated_node,
-                                              line_break_dir, adjust_spacing,
-                                              par_shape_ptr, adj_demerits,
-                                              tracing_paragraphs,
-                                              protrude_chars, line_penalty,
-                                              last_line_fit, double_hyphen_demerits,
-                                              final_hyphen_demerits, first_p,
-                                              vlink(cur_p));
-                                /*tex This does not work. */
-#if 0
-                                /*tex Go back to the starting situation. */
-                                do_one_seven_eight(sub_disc_width_from_active_width);
-                                do_one_seven_eight(reset_disc_width);
-                                /*tex Add select |no_break| to |active_width|. */
-                                s = vlink_no_break(vlink(cur_p));
-                                add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
-                                ext_try_break(actual_penalty, hyphenated_node,
-                                              line_break_dir, adjust_spacing,
-                                              par_shape_ptr, adj_demerits,
-                                              tracing_paragraphs,
-                                              protrude_chars, line_penalty,
-                                              last_line_fit, double_hyphen_demerits,
-                                              final_hyphen_demerits, first_p,
-                                              vlink(cur_p));
-#endif
-                            }
-                            do_one_seven_eight(sub_disc_width_from_active_width);
-                        }
-                    }
-                    s = vlink_no_break(cur_p);
-                    add_to_widths(s, line_break_dir, adjust_spacing, active_width);
-                    break;
-                case penalty_node:
-                    ext_try_break(penalty(cur_p), unhyphenated_node, line_break_dir,
-                                  adjust_spacing, par_shape_ptr, adj_demerits,
-                                  tracing_paragraphs, protrude_chars,
-                                  line_penalty, last_line_fit,
-                                  double_hyphen_demerits, final_hyphen_demerits,
-                                  first_p, cur_p);
-                    break;
-                case boundary_node:
-                case whatsit_node:
-                    /*tex Advance past a whatsit node in the |line_break| loop. */
-                case mark_node:
-                case ins_node:
-                case adjust_node:
-                    break;
-                case glue_spec_node:
-                    normal_warning("parbuilder","found a glue_spec in a paragraph");
-                    break;
-                default:
-                    formatted_error("parbuilder","weird node %d in paragraph",type(cur_p));
-            }
-            cur_p = vlink(cur_p);
-            while (cur_p == null && nest_index > 0) {
-                cur_p = nest_stack[--nest_index];
-            }
-        }
-        if (cur_p == null) {
-            /*tex
-
-                Try the final line break at the end of the paragraph, and |goto
-                done| if the desired breakpoints have been found.
-
-                The forced line break at the paragraph's end will reduce the list
-                of breakpoints so that all active nodes represent breaks at
-                |cur_p=null|. On the first pass, we insist on finding an active
-                node that has the correct ``looseness.'' On the final pass, there
-                will be at least one active node, and we will match the desired
-                looseness as well as we can.
-
-                The global variable |best_bet| will be set to the active node for
-                the best way to break the paragraph, and a few other variables
-                are used to help determine what is best.
-
-            */
-            ext_try_break(eject_penalty, hyphenated_node, line_break_dir,
-                          adjust_spacing, par_shape_ptr, adj_demerits,
-                          tracing_paragraphs, protrude_chars, line_penalty,
-                          last_line_fit, double_hyphen_demerits,
-                          final_hyphen_demerits, first_p, cur_p);
-            if (vlink(active) != active) {
-                /*tex Find an active node with fewest demerits; */
-                r = vlink(active);
-                fewest_demerits = awful_bad;
-                do {
-                    if (type(r) != delta_node) {
-                        if (total_demerits(r) < fewest_demerits) {
-                            fewest_demerits = total_demerits(r);
-                            best_bet = r;
-                        }
-                    }
-                    r = vlink(r);
-                } while (r != active);
-                best_line = line_number(best_bet);
-                /*tex
-                    Find an active node with fewest demerits;
-                */
-                if (looseness == 0)
-                    goto DONE;
-                /*tex
-
-                    Find the best active node for the desired looseness;
-
-                    The adjustment for a desired looseness is a slightly more
-                    complicated version of the loop just considered. Note that if
-                    a paragraph is broken into segments by displayed equations,
-                    each segment will be subject to the looseness calculation,
-                    independently of the other segments.
-
-                */
-                r = vlink(active);
-                actual_looseness = 0;
-                do {
-                    if (type(r) != delta_node) {
-                        line_diff = line_number(r) - best_line;
-                        if (((line_diff < actual_looseness)
-                             && (looseness <= line_diff))
-                            || ((line_diff > actual_looseness)
-                                && (looseness >= line_diff))) {
-                            best_bet = r;
-                            actual_looseness = line_diff;
-                            fewest_demerits = total_demerits(r);
-                        } else if ((line_diff == actual_looseness) &&
-                                   (total_demerits(r) < fewest_demerits)) {
-                            best_bet = r;
-                            fewest_demerits = total_demerits(r);
-                        }
-                    }
-                    r = vlink(r);
-                } while (r != active);
-                best_line = line_number(best_bet);
-                /*tex
-                    Find the best active node for the desired looseness.
-                */
-                if ((actual_looseness == looseness) || final_pass)
-                    goto DONE;
-            }
-        }
-        /*tex Clean up the memory by removing the break nodes. */
-        clean_up_the_memory();
-        /*tex Clean up the memory by removing the break nodes. */
-        if (!second_pass) {
-            if (tracing_paragraphs > 0)
-                tprint_nl("@secondpass");
-            threshold = tolerance;
-            second_pass = true;
-            final_pass = (emergency_stretch <= 0);
-        } else {
-            /*tex If at first you do not succeed, then: */
-            if (tracing_paragraphs > 0)
-                tprint_nl("@emergencypass");
-            background[2] += emergency_stretch;
-            final_pass = true;
-        }
-    }
-
-  DONE:
-    if (tracing_paragraphs > 0) {
-        end_diagnostic(true);
-        normalize_selector();
-    }
-    if (do_last_line_fit) {
-        /*tex
-            Adjust the final line of the paragraph; here we either reset
-            |do_last_line_fit| or adjust the |par_fill_skip| glue.
-        */
-        if (active_short(best_bet) == 0) {
-            do_last_line_fit = false;
-        } else {
-            width(last_line_fill) += (active_short(best_bet) - active_glue(best_bet));
-            stretch(last_line_fill) = 0;
-        }
-    }
-    /*tex
-        Break the paragraph at the chosen. Once the best sequence of
-        breakpoints has been found (hurray), we call on the procedure
-        |post_line_break| to finish the remainder of the work. By introducing
-        this subprocedure, we are able to keep |line_break| from getting
-        extremely long.
-
-        the first thing |ext_post_line_break| does is reset |dir_ptr|.
-
-    */
-    flush_node_list(dir_ptr);
-    dir_ptr = null;
-    ext_post_line_break(paragraph_dir,
-                        right_skip,
-                        left_skip,
-                        protrude_chars,
-                        par_shape_ptr,
-                        adjust_spacing,
-                        inter_line_penalties_par_ptr,
-                        inter_line_penalty,
-                        club_penalty,
-                        club_penalties_ptr,
-                        widow_penalties_ptr,
-                        widow_penalty,
-                        broken_penalty,
-                        final_par_glue,
-                        best_bet,
-                        last_special_line,
-                        second_width,
-                        second_indent, first_width, first_indent, best_line);
-    /*tex
-
-        Clean up the memory by removing the break nodes.
-
-    */
-    clean_up_the_memory();
-}
-
-void get_linebreak_info (int *f, int *a)
-{
-    *f = fewest_demerits;
-    *a = actual_looseness;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/linebreak.w
@@ -0,0 +1,2164 @@
+% linebreak.w
+%
+% Copyright 2006-2008 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 come now to what is probably the most interesting algorithm of \TeX:
+the mechanism for choosing the ``best possible'' breakpoints that yield
+the individual lines of a paragraph. \TeX's line-breaking algorithm takes
+a given horizontal list and converts it to a sequence of boxes that are
+appended to the current vertical list. In the course of doing this, it
+creates a special data structure containing three kinds of records that are
+not used elsewhere in \TeX. Such nodes are created while a paragraph is
+being processed, and they are destroyed afterwards; thus, the other parts
+of \TeX\ do not need to know anything about how line-breaking is done.
+
+The method used here is based on an approach devised by Michael F. Plass and
+@^Plass, Michael Frederick@>
+@^Knuth, Donald Ervin@>
+the author in 1977, subsequently generalized and improved by the same two
+people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice
+\AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the
+line-breaking problem can be regarded as a special case of the problem of
+computing the shortest path in an acyclic network. The cited paper includes
+numerous examples and describes the history of line breaking as it has been
+practiced by printers through the ages. The present implementation adds two
+new ideas to the algorithm of 1980: Memory space requirements are considerably
+reduced by using smaller records for inactive nodes than for active ones,
+and arithmetic overflow is avoided by using ``delta distances'' instead of
+keeping track of the total distance from the beginning of the paragraph to the
+current point.
+
+The |line_break| procedure should be invoked only in horizontal mode; it
+leaves that mode and places its output into the current vlist of the
+enclosing vertical mode (or internal vertical mode).
+There is one explicit parameter:  |d| is true for partial paragraphs
+preceding display math mode; in this case the amount of additional
+penalty inserted before the final line is |display_widow_penalty|
+instead of |widow_penalty|.
+
+There are also a number of implicit parameters: The hlist to be broken
+starts at |vlink(head)|, and it is nonempty. The value of |prev_graf| in the
+enclosing semantic level tells where the paragraph should begin in the
+sequence of line numbers, in case hanging indentation or \.{\\parshape}
+are in use; |prev_graf| is zero unless this paragraph is being continued
+after a displayed formula.  Other implicit parameters, such as the
+|par_shape_ptr| and various penalties to use for hyphenation, etc., appear
+in |eqtb|.
+
+After |line_break| has acted, it will have updated the current vlist and the
+value of |prev_graf|. Furthermore, the global variable |just_box| will
+point to the final box created by |line_break|, so that the width of this
+line can be ascertained when it is necessary to decide whether to use
+|above_display_skip| or |above_display_short_skip| before a displayed formula.
+
+@c
+halfword just_box;              /* the |hlist_node| for the last line of the new paragraph */
+
+@ In it's complete form, |line_break| is a rather lengthy
+procedure---sort of a small world unto itself---we must build it up
+little by little. Below you see only the general outline.
+
+The main task performed here is to move the list from |head| to
+|temp_head| and go into the enclosing semantic level. We also append
+the \.{\\parfillskip} glue to the end of the paragraph, removing a
+space (or other glue node) if it was there, since spaces usually
+precede blank lines and instances of `\.{\$\$}'. The |par_fill_skip|
+is preceded by an infinite penalty, so it will never be considered as
+a potential breakpoint.
+
+That code assumes that a |glue_node| and a |penalty_node| occupy the
+same number of |mem|~words.
+@^data structure assumptions@>
+
+Most other processing is delegated to external functions.
+
+@c
+void line_break(boolean d, int line_break_context)
+{
+    int paragraph_dir = 0;      /* main direction of paragraph */
+    halfword final_par_glue;
+    halfword start_of_par;
+    int callback_id;
+    pack_begin_line = cur_list.ml_field;        /* this is for over/underfull box messages */
+    alink(temp_head) = null; /* hh-ls */
+    vlink(temp_head) = vlink(cur_list.head_field);
+    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,line_penalty));
+    } else if (type(cur_list.tail_field) != glue_node) {
+        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,line_penalty));
+    }
+    final_par_glue = new_param_glue(par_fill_skip_code);
+    couple_nodes(cur_list.tail_field, final_par_glue);
+    cur_list.tail_field = vlink(cur_list.tail_field);
+    lua_node_filter(pre_linebreak_filter_callback,
+                    line_break_context, temp_head,
+                    addressof(cur_list.tail_field));
+    last_line_fill = cur_list.tail_field;
+    pop_nest();
+    start_of_par = cur_list.tail_field;
+    callback_id = callback_defined(linebreak_filter_callback);
+    if (callback_id > 0) {
+        callback_id = lua_linebreak_callback(d, temp_head, addressof(cur_list.tail_field));
+        if (callback_id > 0) {
+            /* find the correct value for the |just_box| */
+            halfword box_search = cur_list.tail_field;
+            just_box  = null;
+            if (box_search != null) {
+                do {
+                    if (type(box_search) == hlist_node) {
+                       just_box = box_search;
+                    }
+                    box_search = vlink(box_search);
+                } while (box_search != null);
+            }
+            if (just_box == null) {
+                help3
+                    ("A linebreaking routine should return a non-empty list of nodes",
+                     "and at least one of those has to be a \\hbox.",
+                     "Sorry, I cannot recover from this.");
+                print_err("Invalid linebreak_filter");
+                succumb();
+            }
+        } else {
+            if (tracing_paragraphs_par > 0) {
+                begin_diagnostic();
+                print_int(line);
+                end_diagnostic(true);
+            }
+        }
+    }
+    if (callback_id == 0) {
+        if ((!is_char_node(vlink(temp_head))) && ((type(vlink(temp_head)) == local_par_node))) {
+            paragraph_dir = local_par_dir(vlink(temp_head));
+        } else {
+            confusion("weird par dir"); /* assert(0); */ /* |paragraph_dir = 0|; */
+        }
+        ext_do_line_break(paragraph_dir,
+                          pretolerance_par,
+                          tracing_paragraphs_par,
+                          tolerance_par,
+                          emergency_stretch_par,
+                          looseness_par,
+                          adjust_spacing_par,
+                          par_shape_par_ptr,
+                          adj_demerits_par,
+                          protrude_chars_par,
+                          line_penalty_par,
+                          last_line_fit_par,
+                          double_hyphen_demerits_par,
+                          final_hyphen_demerits_par,
+                          hang_indent_par,
+                          hsize_par,
+                          hang_after_par,
+                          left_skip_par,
+                          right_skip_par,
+                          inter_line_penalties_par_ptr,
+                          inter_line_penalty_par,
+                          club_penalty_par,
+                          club_penalties_par_ptr,
+                          (d ? display_widow_penalties_par_ptr : widow_penalties_par_ptr),
+                          (d ? display_widow_penalty_par : widow_penalty_par),
+                          broken_penalty_par,
+                          final_par_glue);
+    }
+    lua_node_filter(post_linebreak_filter_callback,
+                    line_break_context, start_of_par,
+                    addressof(cur_list.tail_field));
+    pack_begin_line = 0;
+}
+
+@ Glue nodes in a horizontal list that is being paragraphed are not supposed to
+   include ``infinite'' shrinkability; that is why the algorithm maintains
+   four registers for stretching but only one for shrinking. If the user tries to
+   introduce infinite shrinkability, the shrinkability will be reset to finite
+   and an error message will be issued. A boolean variable |no_shrink_error_yet|
+   prevents this error message from appearing more than once per paragraph.
+
+@c
+#define check_shrinkage(a) \
+    if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \
+        a=finite_shrink((a))
+
+static boolean no_shrink_error_yet;     /*have we complained about infinite shrinkage? */
+
+static halfword finite_shrink(halfword p)
+{                               /* recovers from infinite shrinkage */
+    const char *hlp[] = {
+        "The paragraph just ended includes some glue that has",
+        "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.",
+        "Such glue doesn't belong there---it allows a paragraph",
+        "of any length to fit on one line. But it's safe to proceed,",
+        "since the offensive shrinkability has been made finite.",
+        NULL
+    };
+    if (no_shrink_error_yet) {
+        no_shrink_error_yet = false;
+        tex_error("Infinite glue shrinkage found in a paragraph", hlp);
+    }
+    shrink_order(p) = normal;
+    return p;
+}
+
+@ A pointer variable |cur_p| runs through the given horizontal list as we look
+   for breakpoints. This variable is global, since it is used both by |line_break|
+   and by its subprocedure |try_break|.
+
+   Another global variable called |threshold| is used to determine the feasibility
+   of individual lines: breakpoints are feasible if there is a way to reach
+   them without creating lines whose badness exceeds |threshold|.  (The
+   badness is compared to |threshold| before penalties are added, so that
+   penalty values do not affect the feasibility of breakpoints, except that
+   no break is allowed when the penalty is 10000 or more.) If |threshold|
+   is 10000 or more, all legal breaks are considered feasible, since the
+   |badness| function specified above never returns a value greater than~10000.
+
+   Up to three passes might be made through the paragraph in an attempt to find at
+   least one set of feasible breakpoints. On the first pass, we have
+   |threshold=pretolerance| and |second_pass=final_pass=false|.
+   If this pass fails to find a
+   feasible solution, |threshold| is set to |tolerance|, |second_pass| is set
+   |true|, and an attempt is made to hyphenate as many words as possible.
+   If that fails too, we add |emergency_stretch| to the background
+   stretchability and set |final_pass=true|.
+
+@c
+static boolean second_pass;     /* is this our second attempt to break this paragraph? */
+static boolean final_pass;      /*is this our final attempt to break this paragraph? */
+static int threshold;           /* maximum badness on feasible lines */
+
+/* maximum fill level for |hlist_stack|*/
+#define max_hlist_stack 512     /* maybe good if larger than |2 *
+                                   max_quarterword|, so that box nesting
+                                   level would overflow first */
+
+/* stack for |find_protchar_left()| and |find_protchar_right()| */
+static halfword hlist_stack[max_hlist_stack];
+
+/* fill level for |hlist_stack| */
+static short hlist_stack_level = 0;
+
+@ @c
+static void push_node(halfword p)
+{
+    if (hlist_stack_level >= max_hlist_stack)
+        normal_error("push_node","stack overflow");
+    hlist_stack[hlist_stack_level++] = p;
+}
+
+static halfword pop_node(void)
+{
+    if (hlist_stack_level <= 0) /* would point to some bug */
+        normal_error("pop_node","stack underflow (internal error)");
+    return hlist_stack[--hlist_stack_level];
+}
+
+@ @c
+static int max_stretch_ratio = 0;       /*maximal stretch ratio of expanded fonts */
+static int max_shrink_ratio = 0;        /*maximal shrink ratio of expanded fonts */
+static int cur_font_step = 0;   /*the current step of expanded fonts */
+
+static boolean check_expand_pars(internal_font_number f)
+{
+    int m;
+
+    if ((font_step(f) == 0)
+        || ((font_max_stretch(f) == 0) && (font_max_shrink(f) == 0)))
+        return false;
+    if (cur_font_step < 0)
+        cur_font_step = font_step(f);
+    else if (cur_font_step != font_step(f))
+        normal_error("font expansion","using fonts with different step of expansion in one paragraph is not allowed");
+    m = font_max_stretch(f);
+    if (m != 0) {
+        if (max_stretch_ratio < 0)
+            max_stretch_ratio = m;
+        else if (max_stretch_ratio != m)
+            normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed");
+    }
+    m = font_max_shrink(f);
+    if (m != 0) {
+        if (max_shrink_ratio < 0)
+            max_shrink_ratio = -m;
+        else if (max_shrink_ratio != -m)
+            normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed");
+    }
+    return true;
+}
+
+@ searches left to right from list head |l|, returns 1st non-skipable item
+
+@c
+/*public*/ halfword find_protchar_left(halfword l, boolean d)
+{
+    halfword t;
+    boolean run;
+    boolean done = false ;
+    while ((vlink(l) != null) && (type(l) == hlist_node) && zero_dimensions(l) && (list_ptr(l) == null)) {
+        /*for paragraph start with \.{\\parindent} = 0pt or any empty hbox */
+        l = vlink(l);
+        done = true ;
+    }
+    if ((!done) && (type(l) == local_par_node)) {
+        l = vlink(l);
+        done = true ;
+    }
+    if ((!done) && d) {
+        while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) {
+            /* std.\ discardables at line break, \TeX book, p 95 */
+            l = vlink(l);
+        }
+    }
+    if (type(l) != glyph_node) {
+        hlist_stack_level = 0;
+        run = true;
+        do {
+            t = l;
+            while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) {
+                push_node(l);
+                l = list_ptr(l);
+            }
+            while (run && cp_skipable(l)) {
+                while ((vlink(l) == null) && (hlist_stack_level > 0)) {
+                    l = pop_node(); /* don't visit this node again */
+                    run = false;
+                }
+                if ((vlink(l) != null) && (type(l) == boundary_node) && (subtype(l) == protrusion_boundary) &&
+                        ((boundary_value(l) == 1) || (boundary_value(l) == 3))) {
+                    /* skip next node */
+                    l = vlink(l);
+                }
+                if (vlink(l) != null) {
+                    l = vlink(l);
+                } else if (hlist_stack_level == 0) {
+                    run = false;
+                }
+            }
+        } while (t != l);
+    }
+    return l;
+}
+
+@ searches right to left from list tail |r| to head |l|, returns 1st non-skipable item
+
+@c
+/*public*/ halfword find_protchar_right(halfword l, halfword r)
+{
+    halfword t;
+    boolean run = true;
+    if (r == null)
+        return null;
+    hlist_stack_level = 0;
+    do {
+        t = r;
+        while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) {
+            push_node(l);
+            push_node(r);
+            l = list_ptr(r);
+            r = l;
+            while (vlink(r) != null) {
+                halfword s = r;
+                r = vlink(r);
+                alink(r) = s;
+            }
+        }
+        while (run && cp_skipable(r)) {
+            while ((r == l) && (hlist_stack_level > 0)) {
+                r = pop_node(); /* don't visit this node again */
+                l = pop_node();
+            }
+            if ((r != l) && (r != null)) {
+                if ((alink(r) != null) && (type(r) == boundary_node) && (subtype(r) == protrusion_boundary) &&
+                        ((boundary_value(r) == 2) || (boundary_value(r) == 3))) {
+                    /* skip next node */
+                    r = alink(r);
+                }
+                if (alink(r) != null) {
+                    r = alink(r);
+                } else {        /* this is the input: \.{\\leavevmode\\penalty-10000\\penalty-10000} (bug \#268) */
+                    run = false;
+                }
+            } else if ((r == l) && (hlist_stack_level == 0))
+                run = false;
+        }
+    } while (t != r);
+    return r;
+}
+
+@ @c
+#define left_pw(a) char_pw((a), left_side)
+#define right_pw(a) char_pw((a), right_side)
+
+@ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
+   each break that is {\sl feasible}, in the sense that there is a way to end
+   a line at the given place without requiring any line to stretch more than
+   a given tolerance. A break node is characterized by three things: the position
+   of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
+   or |disc_node|); the ordinal number of the line that will follow this
+   breakpoint; and the fitness classification of the line that has just
+   ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|.
+
+@c
+typedef enum {
+    very_loose_fit = 0,         /* fitness classification for lines stretching more than
+                                   their stretchability */
+    loose_fit,                  /* fitness classification for lines stretching 0.5 to 1.0 of their
+                                   stretchability */
+    decent_fit,                 /* fitness classification for all other lines */
+    tight_fit                   /* fitness classification for lines shrinking 0.5 to 1.0 of their
+                                   shrinkability */
+} fitness_value;
+
+
+@ The algorithm essentially determines the best possible way to achieve
+   each feasible combination of position, line, and fitness. Thus, it answers
+   questions like, ``What is the best way to break the opening part of the
+   paragraph so that the fourth line is a tight line ending at such-and-such
+   a place?'' However, the fact that all lines are to be the same length
+   after a certain point makes it possible to regard all sufficiently large
+   line numbers as equivalent, when the looseness parameter is zero, and this
+   makes it possible for the algorithm to save space and time.
+
+   An ``active node'' and a ``passive node'' are created in |mem| for each
+   feasible breakpoint that needs to be considered. Active nodes are three
+   words long and passive nodes are two words long. We need active nodes only
+   for breakpoints near the place in the paragraph that is currently being
+   examined, so they are recycled within a comparatively short time after
+   they are created.
+
+@ An active node for a given breakpoint contains six fields:
+
+|vlink| points to the next node in the list of active nodes; the
+last active node has |vlink=active|.
+
+|break_node| points to the passive node associated with this
+breakpoint.
+
+|line_number| is the number of the line that follows this
+breakpoint.
+
+|fitness| is the fitness classification of the line ending at this
+breakpoint.
+
+|type| is either |hyphenated_node| or |unhyphenated_node|, depending on
+whether this breakpoint is a |disc_node|.
+
+|total_demerits| is the minimum possible sum of demerits over all
+lines leading from the beginning of the paragraph to this breakpoint.
+
+The value of |vlink(active)| points to the first active node on a vlinked list
+of all currently active nodes. This list is in order by |line_number|,
+except that nodes with |line_number>easy_line| may be in any order relative
+to each other.
+
+@c
+void initialize_active(void)
+{
+    type(active) = hyphenated_node;
+    line_number(active) = max_halfword;
+    subtype(active) = 0;        /* the |subtype| is never examined */
+}
+
+@ The passive node for a given breakpoint contains EIGHT fields:
+
+|vlink| points to the passive node created just before this one,
+if any, otherwise it is |null|.
+
+|cur_break| points to the position of this breakpoint in the
+horizontal list for the paragraph being broken.
+
+|prev_break| points to the passive node that should precede this
+one in an optimal path to this breakpoint.
+
+|serial| is equal to |n| if this passive node is the |n|th
+one created during the current pass. (This field is used only when
+printing out detailed statistics about the line-breaking calculations.)
+
+|passive_pen_inter| holds the current \.{\\localinterlinepenalty}
+
+|passive_pen_broken| holds the current \.{\\localbrokenpenalty}
+
+There is a global variable called |passive| that points to the most
+recently created passive node. Another global variable, |printed_node|,
+is used to help print out the paragraph when detailed information about
+the line-breaking computation is being displayed.
+
+@c
+static halfword passive;        /* most recent node on passive list */
+static halfword printed_node;   /*most recent node that has been printed */
+static halfword pass_number;    /*the number of passive nodes allocated on this pass */
+
+@ The active list also contains ``delta'' nodes that help the algorithm
+compute the badness of individual lines. Such nodes appear only between two
+active nodes, and they have |type=delta_node|. If |p| and |r| are active nodes
+and if |q| is a delta node between them, so that |vlink(p)=q| and |vlink(q)=r|,
+then |q| tells the space difference between lines in the horizontal list that
+start after breakpoint |p| and lines that start after breakpoint |r|. In
+other words, if we know the length of the line that starts after |p| and
+ends at our current position, then the corresponding length of the line that
+starts after |r| is obtained by adding the amounts in node~|q|. A delta node
+contains seven scaled numbers, since it must record the net change in glue
+stretchability with respect to all orders of infinity. The natural width
+difference appears in |mem[q+1].sc|; the stretch differences in units of
+pt, sfi, fil, fill, and filll appear in |mem[q+2..q+6].sc|; and the shrink
+difference appears in |mem[q+7].sc|. The |subtype| field of a delta node
+is not used.
+
+Actually, we have two more fields that are used by |pdftex|.
+
+As the algorithm runs, it maintains a set of seven delta-like registers
+for the length of the line following the first active breakpoint to the
+current position in the given hlist. When it makes a pass through the
+active list, it also maintains a similar set of seven registers for the
+length following the active breakpoint of current interest. A third set
+holds the length of an empty line (namely, the sum of \.{\\leftskip} and
+\.{\\rightskip}); and a fourth set is used to create new delta nodes.
+
+When we pass a delta node we want to do operations like
+$$\hbox{\ignorespaces|for
+k:=1 to 7 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
+want to do this without the overhead of |for| loops. The |do_all_six|
+macro makes such six-tuples convenient.
+
+@c
+static scaled active_width[10] = { 0 }; /*distance from first active node to~|cur_p| */
+static scaled background[10] = { 0 };   /*length of an ``empty'' line */
+static scaled break_width[10] = { 0 };  /*length being computed after current break */
+
+static boolean auto_breaking;   /*make |auto_breaking| accessible out of |line_break| */
+
+@  Let's state the principles of the delta nodes more precisely and concisely,
+   so that the following programs will be less obscure. For each legal
+   breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and
+   $\beta(p)$ such that the length of material in a line from breakpoint~|p|
+   to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
+   Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from
+   the beginning of the paragraph to a point ``after'' a break at |p| and to a
+   point ``before'' a break at |q|; and $\gamma$ is the width of an empty line,
+   namely the length contributed by \.{\\leftskip} and \.{\\rightskip}.
+
+   Suppose, for example, that the paragraph consists entirely of alternating
+   boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and
+   let the skips have widths $y_1\ldots y_n$, so that the paragraph can be
+   represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint
+   at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)=
+   x_1+y_1+\cdots+x_i$. To check this, note that the length of material from
+   $p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5)
+   -\alpha(p_2)$.
+
+   The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and
+   shrinkability as well as a natural width. If we were to compute $\alpha(p)$
+   and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and
+   the multiprecise numbers would have to be kept in the active nodes.
+   \TeX\ avoids this problem by working entirely with relative differences
+   or ``deltas.'' Suppose, for example, that the active list contains
+   $a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints
+   and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$
+   and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is
+   currently positioned at some other breakpoint |p|, the |active_width| array
+   contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through
+   the list of active nodes and considering a tentative line that runs from
+   $a_2$ to~|p|, say, the |cur_active_width| array will contain the value
+   $\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$,
+   we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this
+   is just $\delta_2$, which appears in the active list between $a_2$ and
+   $a_3$. The |background| array contains $\gamma$. The |break_width| array
+   will be used to calculate values of new delta nodes when the active
+   list is being updated.
+
+@  The heart of the line-breaking procedure is `|try_break|', a subroutine
+   that tests if the current breakpoint |cur_p| is feasible, by running
+   through the active list to see what lines of text can be made from active
+   nodes to~|cur_p|.  If feasible breaks are possible, new break nodes are
+   created.  If |cur_p| is too far from an active node, that node is
+   deactivated.
+
+   The parameter |pi| to |try_break| is the penalty associated
+   with a break at |cur_p|; we have |pi=eject_penalty| if the break is forced,
+   and |pi=inf_penalty| if the break is illegal.
+
+   The other parameter, |break_type|, is set to |hyphenated_node| or |unhyphenated_node|,
+   depending on whether or not the current break is at a |disc_node|. The
+   end of a paragraph is also regarded as `|hyphenated_node|'; this case is
+   distinguishable by the condition |cur_p=null|.
+
+@c
+static int internal_pen_inter;  /* running \.{\\localinterlinepenalty} */
+static int internal_pen_broken; /* running \.{\\localbrokenpenalty} */
+static halfword internal_left_box;      /* running \.{\\localleftbox} */
+static int internal_left_box_width;     /* running \.{\\localleftbox} width */
+static halfword init_internal_left_box; /* running \.{\\localleftbox} */
+static int init_internal_left_box_width;        /* running \.{\\localleftbox} width */
+static halfword internal_right_box;     /* running \.{\\localrightbox} */
+static int internal_right_box_width;    /* running \.{\\localrightbox} width */
+
+static scaled disc_width[10] = { 0 };   /* the length of discretionary material preceding a break */
+
+@  As we consider various ways to end a line at |cur_p|, in a given line number
+   class, we keep track of the best total demerits known, in an array with
+   one entry for each of the fitness classifications. For example,
+   |minimal_demerits[tight_fit]| contains the fewest total demerits of feasible
+   line breaks ending at |cur_p| with a |tight_fit| line; |best_place[tight_fit]|
+   points to the passive node for the break before~|cur_p| that achieves such
+   an optimum; and |best_pl_line[tight_fit]| is the |line_number| field in the
+   active node corresponding to |best_place[tight_fit]|. When no feasible break
+   sequence is known, the |minimal_demerits| entries will be equal to
+   |awful_bad|, which is $2^{30}-1$. Another variable, |minimum_demerits|,
+   keeps track of the smallest value in the |minimal_demerits| array.
+
+@c
+static int minimal_demerits[4]; /* best total demerits known for current
+                                   line class and position, given the fitness */
+static int minimum_demerits;    /* best total demerits known for current line class
+                                   and position */
+static halfword best_place[4];  /* how to achieve  |minimal_demerits| */
+static halfword best_pl_line[4];        /*corresponding line number */
+
+@  The length of lines depends on whether the user has specified
+\.{\\parshape} or \.{\\hangindent}. If |par_shape_ptr| is not null, it
+points to a $(2n+1)$-word record in |mem|, where the |vinfo| in the first
+word contains the value of |n|, and the other $2n$ words contain the left
+margins and line lengths for the first |n| lines of the paragraph; the
+specifications for line |n| apply to all subsequent lines. If
+|par_shape_ptr=null|, the shape of the paragraph depends on the value of
+|n=hang_after|; if |n>=0|, hanging indentation takes place on lines |n+1|,
+|n+2|, \dots, otherwise it takes place on lines 1, \dots, $\vert
+n\vert$. When hanging indentation is active, the left margin is
+|hang_indent|, if |hang_indent>=0|, else it is 0; the line length is
+$|hsize|-\vert|hang_indent|\vert$. The normal setting is
+|par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|.
+Note that if |hang_indent=0|, the value of |hang_after| is irrelevant.
+@^length of lines@> @^hanging indentation@>
+
+@c
+static halfword easy_line;          /*line numbers |>easy_line| are equivalent in break nodes */
+static halfword last_special_line;  /*line numbers |>last_special_line| all have the same width */
+static scaled first_width;          /*the width of all lines |<=last_special_line|, if
+                                      no \.{\\parshape} has been specified */
+static scaled second_width;         /*the width of all lines |>last_special_line| */
+static scaled first_indent;         /*left margin to go with |first_width| */
+static scaled second_indent;        /*left margin to go with |second_width| */
+
+static halfword best_bet;           /*use this passive node and its predecessors */
+static int fewest_demerits;         /*the demerits associated with |best_bet| */
+static halfword best_line;          /*line number following the last line of the new paragraph */
+static int actual_looseness;        /*the difference between |line_number(best_bet)|
+                                      and the optimum |best_line| */
+static int line_diff;               /*the difference between the current line number and
+                                      the optimum |best_line| */
+
+@  \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.
+
+@c
+#define do_all_six(a) a(1);a(2);a(3);a(4);a(5);a(6);a(7)
+#define do_seven_eight(a) if (adjust_spacing > 1) { a(8);a(9); }
+#define do_all_eight(a) do_all_six(a); do_seven_eight(a)
+#define do_one_seven_eight(a) a(1); do_seven_eight(a)
+
+#define store_background(a) {active_width[a]=background[a];}
+
+#define kern_break() { \
+    if ((!is_char_node(vlink(cur_p))) && auto_breaking) \
+        if (type(vlink(cur_p))==glue_node) \
+            ext_try_break(0, \
+                          unhyphenated_node, \
+                          line_break_dir, \
+                          adjust_spacing, \
+                          par_shape_ptr, \
+                          adj_demerits, \
+                          tracing_paragraphs, \
+                          protrude_chars, \
+                          line_penalty, \
+                          last_line_fit, \
+                          double_hyphen_demerits, \
+                          final_hyphen_demerits, \
+                          first_p, \
+                          cur_p); \
+    if (type(cur_p)!=math_node) \
+        active_width[1] += width(cur_p); \
+    else \
+        active_width[1] += surround(cur_p); \
+}
+
+#define clean_up_the_memory() { \
+    q=vlink(active); \
+    while (q!=active) { \
+        cur_p = vlink(q); \
+        if (type(q)==delta_node) \
+            flush_node(q); \
+        else \
+            flush_node(q); \
+        q = cur_p; \
+    } \
+    q = passive;  \
+    while (q!=null) { \
+        cur_p = vlink(q); \
+        flush_node(q); \
+        q = cur_p; \
+    } \
+}
+
+static boolean do_last_line_fit;        /* special algorithm for last line of paragraph? */
+static scaled fill_width[4];    /* infinite stretch components of  |par_fill_skip| */
+static scaled best_pl_short[4]; /* |shortfall|  corresponding to |minimal_demerits| */
+static scaled best_pl_glue[4];  /*corresponding glue stretch or shrink */
+
+#define reset_disc_width(a) disc_width[(a)] = 0
+
+#define add_disc_width_to_break_width(a)     break_width[(a)] += disc_width[(a)]
+#define sub_disc_width_from_active_width(a)  active_width[(a)] -= disc_width[(a)]
+
+#define add_char_shrink(a,b)  a += char_shrink((b))
+#define add_char_stretch(a,b) a += char_stretch((b))
+#define sub_char_shrink(a,b)  a -= char_shrink((b))
+#define sub_char_stretch(a,b) a -= char_stretch((b))
+
+#define add_kern_shrink(a,b)  a += kern_shrink((b))
+#define add_kern_stretch(a,b) a += kern_stretch((b))
+#define sub_kern_shrink(a,b)  a -= kern_shrink((b))
+#define sub_kern_stretch(a,b) a -= kern_stretch((b))
+
+@ This function is used to add the width of a list of nodes
+(from a discretionary) to one of the width arrays.
+
+Replacement texts and discretionary texts are supposed to contain
+only character nodes, kern nodes, and box or rule nodes.
+
+@c
+#define bad_node_in_disc_error(p) { \
+    if (type(p) == whatsit_node) { \
+        formatted_error("linebreak","invalid node with type %s and subtype %i found in discretionary",node_data[type(p)].name,subtype(p)); \
+    } else { \
+        formatted_error("linebreak","invalid node with type %s found in discretionary",node_data[type(p)].name); \
+    } \
+}
+
+static void add_to_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
+{
+    while (s != null) {
+        if (is_char_node(s)) {
+            widths[1] += pack_width(line_break_dir, dir_TRT, s, true);
+            if ((adjust_spacing > 1) && check_expand_pars(font(s))) {
+                set_prev_char_p(s);
+                add_char_stretch(widths[8], s);
+                add_char_shrink(widths[9], s);
+            };
+        } else {
+            switch (type(s)) {
+                case hlist_node:
+                case vlist_node:
+                    widths[1] += pack_width(line_break_dir, box_dir(s), s, false);
+                    break;
+                case kern_node:
+                    if ((adjust_spacing == 2) && (subtype(s) == normal)) {
+                        add_kern_stretch(widths[8], s);
+                        add_kern_shrink(widths[9], s);
+                    }
+                    /* fall through */
+                case rule_node:
+                    widths[1] += width(s);
+                    break;
+                case disc_node:    /* TH temp */
+                    break;
+                default:
+                    bad_node_in_disc_error(s);
+                    break;
+            }
+        }
+        s = vlink(s);
+    }
+}
+
+@ This function is used to substract the width of a list of nodes
+(from a discretionary) from one of the width arrays.
+It is used only once, but deserves it own function because of orthogonality
+with the |add_to_widths| function.
+
+@c
+static void sub_from_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
+{
+    while (s != null) {
+        /* Subtract the width of node |s| from |break_width|; */
+        if (is_char_node(s)) {
+            widths[1] -= pack_width(line_break_dir, dir_TRT, s, true);
+            if ((adjust_spacing > 1) && check_expand_pars(font(s))) {
+                set_prev_char_p(s);
+                sub_char_stretch(widths[8], s);
+                sub_char_shrink(widths[9], s);
+            }
+        } else {
+            switch (type(s)) {
+                case hlist_node:
+                case vlist_node:
+                    widths[1] -= pack_width(line_break_dir, box_dir(s), s, false);
+                    break;
+                case kern_node:
+                    if ((adjust_spacing == 2) && (subtype(s) == normal)) {
+                        sub_kern_stretch(widths[8], s);
+                        sub_kern_shrink(widths[9], s);
+                    }
+                    /* fall through */
+                case rule_node:
+                    widths[1] -= width(s);
+                    break;
+                case disc_node:    /* TH temp */
+                    break;
+                default:
+                    bad_node_in_disc_error(s);
+                    break;
+            }
+        }
+        s = vlink(s);
+    }
+}
+
+@  When we insert a new active node for a break at |cur_p|, suppose this
+   new node is to be placed just before active node |a|; then we essentially
+   want to insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where
+   $\delta=\alpha(a)-\alpha(|cur_p|)$ and $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$
+   in the notation explained above.  The |cur_active_width| array now holds
+   $\gamma+\beta(|cur_p|)-\alpha(a)$; so $\delta$ can be obtained by
+   subtracting |cur_active_width| from the quantity $\gamma+\beta(|cur_p|)-
+   \alpha(|cur_p|)$. The latter quantity can be regarded as the length of a
+   line ``from |cur_p| to |cur_p|''; we call it the |break_width| at |cur_p|.
+
+   The |break_width| is usually negative, since it consists of the background
+   (which is normally zero) minus the width of nodes following~|cur_p| that are
+   eliminated after a break. If, for example, node |cur_p| is a glue node, the
+   width of this glue is subtracted from the background; and we also look
+   ahead to eliminate all subsequent glue and penalty and kern and math
+   nodes, subtracting their widths as well.
+
+   Kern nodes do not disappear at a line break unless they are |explicit|.
+
+@c
+static void compute_break_width(int break_type, int line_break_dir, int adjust_spacing, halfword p)
+{
+    halfword s = p; /* glue and other 'whitespace' to be skipped after a break;
+                       used if unhyphenated, or |post_break==empty| */
+    if (break_type > unhyphenated_node && p != null) {
+        /*Compute the discretionary |break_width| values; */
+        /* When |p| is a discretionary break, the length of a line
+           ``from |p| to |p|'' has to be defined properly so
+           that the other calculations work out.  Suppose that the
+           pre-break text at |p| has length $l_0$, the post-break
+           text has length $l_1$, and the replacement text has length
+           |l|. Suppose also that |q| is the node following the
+           replacement text. Then length of a line from |p| to |q|
+           will be computed as $\gamma+\beta(q)-\alpha(|p|)$, where
+           $\beta(q)=\beta(|p|)-l_0+l$. The actual length will be
+           the background plus $l_1$, so the length from |p| to
+           |p| should be $\gamma+l_0+l_1-l$.  If the post-break text
+           of the discretionary is empty, a break may also discard~|q|;
+           in that unusual case we subtract the length of~|q| and any
+           other nodes that will be discarded after the discretionary
+           break.
+
+           TH: I don't quite understand the above remarks.
+
+           The value of $l_0$ need not be computed, since |line_break|
+           will put it into the global variable |disc_width| before
+           calling |try_break|.
+         */
+        /* In case of nested discretionaries, we always follow the no-break
+           path, as we are talking about the breaking on {\it this} position.
+         */
+
+        sub_from_widths(vlink_no_break(p), line_break_dir, adjust_spacing, break_width);
+        add_to_widths(vlink_post_break(p), line_break_dir, adjust_spacing, break_width);
+        do_one_seven_eight(add_disc_width_to_break_width);
+        if (vlink_post_break(p) == null) {
+            s = vlink(p);       /* no |post_break|: 'skip' any 'whitespace' following */
+        } else {
+            s = null;
+        }
+    }
+    while (s != null) {
+        switch (type(s)) {
+            case math_node:
+                /* begin mathskip code */
+                if (glue_is_zero(s)) {
+                    break_width[1] -= surround(s);
+                    break;
+                } else {
+                    /* fall through */
+                }
+                /* end mathskip code */
+            case glue_node:
+                /*Subtract glue from |break_width|; */
+                break_width[1] -= width(s);
+                break_width[2 + stretch_order(s)] -= stretch(s);
+                break_width[7] -= shrink(s);
+                break;
+            case penalty_node:
+                break;
+            case kern_node:
+                if (subtype(s) != explicit_kern && subtype(s) != italic_kern)
+                    return;
+                else
+                    break_width[1] -= width(s);
+                break;
+            default:
+                return;
+        };
+        s = vlink(s);
+    }
+}
+
+@ @c
+static void print_break_node(halfword q, fitness_value fit_class,
+    quarterword break_type, halfword cur_p)
+{
+    /* Print a symbolic description of the new break node */
+    tprint_nl("@@@@");
+    print_int(serial(passive));
+    tprint(": line ");
+    print_int(line_number(q) - 1);
+    print_char('.');
+    print_int(fit_class);
+    if (break_type == hyphenated_node)
+        print_char('-');
+    tprint(" t=");
+    print_int(total_demerits(q));
+    if (do_last_line_fit) {
+        /*Print additional data in the new active node; */
+        tprint(" s=");
+        print_scaled(active_short(q));
+        if (cur_p == null)
+            tprint(" a=");
+        else
+            tprint(" g=");
+        print_scaled(active_glue(q));
+    }
+    tprint(" -> @@");
+    if (prev_break(passive) == null)
+        print_char('0');
+    else
+        print_int(serial(prev_break(passive)));
+}
+
+@ @c
+static void print_feasible_break(halfword cur_p, pointer r, halfword b, int pi,
+    int d, boolean artificial_demerits)
+{
+    /* Print a symbolic description of this feasible break; */
+    if (printed_node != cur_p) {
+        /* Print the list between |printed_node| and |cur_p|, then
+           set |printed_node:=cur_p|; */
+        tprint_nl("");
+        if (cur_p == null) {
+            short_display(vlink(printed_node));
+        } else {
+            halfword save_link = vlink(cur_p);
+            vlink(cur_p) = null;
+            tprint_nl("");
+            short_display(vlink(printed_node));
+            vlink(cur_p) = save_link;
+        }
+        printed_node = cur_p;
+    }
+    tprint_nl("@@");
+    if (cur_p == null) {
+        tprint_esc("par");
+    } else if (type(cur_p) != glue_node) {
+        if (type(cur_p) == penalty_node)
+            tprint_esc("penalty");
+        else if (type(cur_p) == disc_node)
+            tprint_esc("discretionary");
+        else if (type(cur_p) == kern_node)
+            tprint_esc("kern");
+        else
+            tprint_esc("math");
+    }
+    tprint(" via @@");
+    if (break_node(r) == null)
+        print_char('0');
+    else
+        print_int(serial(break_node(r)));
+    tprint(" b=");
+    if (b > inf_bad)
+        print_char('*');
+    else
+        print_int(b);
+    tprint(" p=");
+    print_int(pi);
+    tprint(" d=");
+    if (artificial_demerits)
+        print_char('*');
+    else
+        print_int(d);
+}
+
+@ @c
+#define add_disc_width_to_active_width(a)   active_width[a] += disc_width[a]
+#define update_width(a) cur_active_width[a] += varmem[(r+(a))].cint
+
+#define set_break_width_to_background(a) break_width[a]=background[(a)]
+
+#define convert_to_break_width(a) \
+  varmem[(prev_r+(a))].cint = varmem[(prev_r+(a))].cint-cur_active_width[(a)]+break_width[(a)]
+
+#define store_break_width(a) active_width[(a)]=break_width[(a)]
+
+#define new_delta_to_break_width(a) \
+  varmem[(q+(a))].cint=break_width[(a)]-cur_active_width[(a)]
+
+#define new_delta_from_break_width(a) \
+  varmem[(q+(a))].cint=cur_active_width[(a)]-break_width[(a)]
+
+#define copy_to_cur_active(a) cur_active_width[(a)]=active_width[(a)]
+
+#define combine_two_deltas(a) varmem[(prev_r+(a))].cint += varmem[(r+(a))].cint
+#define downdate_width(a) cur_active_width[(a)] -= varmem[(prev_r+(a))].cint
+#define update_active(a) active_width[(a)]+=varmem[(r+(a))].cint
+
+#define total_font_stretch cur_active_width[8]
+#define total_font_shrink cur_active_width[9]
+
+#define cal_margin_kern_var(a) { \
+    character(cp) = character((a)); \
+    font(cp) = font((a)); \
+    do_subst_font(cp, 1000); \
+    if (font(cp) != font((a))) \
+        margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \
+    font(cp) = font((a)); \
+    do_subst_font(cp, -1000); \
+    if (font(cp) != font((a))) \
+        margin_kern_shrink += (left_pw(cp) - left_pw((a))); \
+}
+
+static void ext_try_break(int pi,
+                          quarterword break_type,
+                          int line_break_dir,
+                          int adjust_spacing,
+                          int par_shape_ptr,
+                          int adj_demerits,
+                          int tracing_paragraphs,
+                          int protrude_chars,
+                          int line_penalty,
+                          int last_line_fit,
+                          int double_hyphen_demerits,
+                          int final_hyphen_demerits, halfword first_p, halfword cur_p)
+{
+    /* labels: |CONTINUE,DEACTIVATE,FOUND,NOT_FOUND|; */
+    pointer r;                  /* runs through the active list */
+    scaled margin_kern_stretch;
+    scaled margin_kern_shrink;
+    halfword lp, rp, cp;
+    halfword prev_r = active;    /* stays a step behind |r| */
+    halfword prev_prev_r = null; /*a step behind |prev_r|, if |type(prev_r)=delta_node| */
+    halfword old_l = 0;          /* maximum line number in current equivalence class of lines */
+    boolean no_break_yet = true; /* have we found a feasible break at |cur_p|? */
+    halfword q;                  /*points to a new node being created */
+    halfword l;                  /*line number of current active node */
+    boolean node_r_stays_active; /*should node |r| remain in the active list? */
+    scaled line_width = 0;       /*the current line will be justified to this width */
+    fitness_value fit_class;     /*possible fitness class of test line */
+    halfword b;                  /*badness of test line */
+    int d;                       /*demerits of test line */
+    boolean artificial_demerits; /*has |d| been forced to zero? */
+
+    scaled shortfall;           /*used in badness calculations */
+    scaled g = 0;               /*glue stretch or shrink of test line, adjustment for last line */
+    scaled cur_active_width[10] = { 0 }; /*distance from current active node */
+
+    /*Make sure that |pi| is in the proper range; */
+    if (pi >= inf_penalty) {
+        return;                 /* this breakpoint is inhibited by infinite penalty */
+    } else if (pi <= -inf_penalty) {
+        pi = eject_penalty;     /*this breakpoint will be forced */
+    }
+
+    do_all_eight(copy_to_cur_active);
+
+    while (1) {
+        r = vlink(prev_r);
+        /* If node |r| is of type |delta_node|, update |cur_active_width|,
+           set |prev_r| and |prev_prev_r|, then |goto continue|; */
+        /* The following code uses the fact that |type(active)<>delta_node| */
+        if (type(r) == delta_node) {
+            do_all_eight(update_width); /* IMPLICIT ,r */
+            prev_prev_r = prev_r;
+            prev_r = r;
+            continue;
+        }
+        /* If a line number class has ended, create new active nodes for
+           the best feasible breaks in that class; then |return|
+           if |r=active|, otherwise compute the new |line_width|; */
+        /* The first part of the following code is part of \TeX's inner loop, so
+           we don't want to waste any time. The current active node, namely node |r|,
+           contains the line number that will be considered next. At the end of the
+           list we have arranged the data structure so that |r=active| and
+           |line_number(active)>old_l|.
+         */
+        l = line_number(r);
+        if (l > old_l) {        /* now we are no longer in the inner loop */
+            if ((minimum_demerits < awful_bad)
+                && ((old_l != easy_line) || (r == active))) {
+                /*Create new active nodes for the best feasible breaks just found */
+                /* It is not necessary to create new active nodes having |minimal_demerits|
+                   greater than
+                   |minimum_demerits+abs(adj_demerits)|, since such active nodes will never
+                   be chosen in the final paragraph breaks. This observation allows us to
+                   omit a substantial number of feasible breakpoints from further consideration.
+                 */
+                if (no_break_yet) {
+                    no_break_yet = false;
+                    do_all_eight(set_break_width_to_background);
+                    compute_break_width(break_type, line_break_dir, adjust_spacing, cur_p);
+                }
+                /* Insert a delta node to prepare for breaks at |cur_p|; */
+                /* We use the fact that |type(active)<>delta_node|. */
+                if (type(prev_r) == delta_node) {       /* modify an existing delta node */
+                    do_all_eight(convert_to_break_width);       /* IMPLICIT |prev_r| */
+                } else if (prev_r == active) {  /* no delta node needed at the beginning */
+                    do_all_eight(store_break_width);
+                } else {
+                    q = new_node(delta_node, 0);
+                    vlink(q) = r;
+                    do_all_eight(new_delta_to_break_width);     /* IMPLICIT q */
+                    vlink(prev_r) = q;
+                    prev_prev_r = prev_r;
+                    prev_r = q;
+                }
+
+                if (abs(adj_demerits) >= awful_bad - minimum_demerits)
+                    minimum_demerits = awful_bad - 1;
+                else
+                    minimum_demerits += abs(adj_demerits);
+                for (fit_class = very_loose_fit; fit_class <= tight_fit;
+                     fit_class++) {
+                    if (minimal_demerits[fit_class] <= minimum_demerits) {
+                        /* Insert a new active node from |best_place[fit_class]|
+                           to |cur_p|; */
+                        /* When we create an active node, we also create the corresponding
+                           passive node.
+                         */
+                        q = new_node(passive_node, 0);
+                        vlink(q) = passive;
+                        passive = q;
+                        cur_break(q) = cur_p;
+                        incr(pass_number);
+                        serial(q) = pass_number;
+                        prev_break(q) = best_place[fit_class];
+                        /*Here we keep track of the subparagraph penalties in the break nodes */
+                        passive_pen_inter(q) = internal_pen_inter;
+                        passive_pen_broken(q) = internal_pen_broken;
+                        passive_last_left_box(q) = internal_left_box;
+                        passive_last_left_box_width(q) =
+                            internal_left_box_width;
+                        if (prev_break(q) != null) {
+                            passive_left_box(q) =
+                                passive_last_left_box(prev_break(q));
+                            passive_left_box_width(q) =
+                                passive_last_left_box_width(prev_break(q));
+                        } else {
+                            passive_left_box(q) = init_internal_left_box;
+                            passive_left_box_width(q) =
+                                init_internal_left_box_width;
+                        }
+                        passive_right_box(q) = internal_right_box;
+                        passive_right_box_width(q) = internal_right_box_width;
+                        q = new_node(break_type, fit_class);
+                        break_node(q) = passive;
+                        line_number(q) = best_pl_line[fit_class] + 1;
+                        total_demerits(q) = minimal_demerits[fit_class];
+                        if (do_last_line_fit) {
+                            /*Store additional data in the new active node */
+                            /* Here we save these data in the active node
+                               representing a potential line break. */
+                            active_short(q) = best_pl_short[fit_class];
+                            active_glue(q) = best_pl_glue[fit_class];
+                        }
+                        vlink(q) = r;
+                        vlink(prev_r) = q;
+                        prev_r = q;
+                        if (tracing_paragraphs > 0)
+                            print_break_node(q, fit_class, break_type, cur_p);
+                    }
+                    minimal_demerits[fit_class] = awful_bad;
+                }
+                minimum_demerits = awful_bad;
+                /* Insert a delta node to prepare for the next active node; */
+                /* When the following code is performed, we will have just inserted at
+                   least one active node before |r|, so |type(prev_r)<>delta_node|.
+                 */
+                if (r != active) {
+                    q = new_node(delta_node, 0);
+                    vlink(q) = r;
+                    do_all_eight(new_delta_from_break_width);   /* IMPLICIT q */
+                    vlink(prev_r) = q;
+                    prev_prev_r = prev_r;
+                    prev_r = q;
+                }
+            }
+            if (r == active)
+                return;
+            /*Compute the new line width; */
+            /* When we come to the following code, we have just encountered
+               the first active node~|r| whose |line_number| field contains
+               |l|. Thus we want to compute the length of the $l\mskip1mu$th
+               line of the current paragraph. Furthermore, we want to set
+               |old_l| to the last number in the class of line numbers
+               equivalent to~|l|.
+             */
+            if (l > easy_line) {
+                old_l = max_halfword - 1;
+                line_width = second_width;
+            } else {
+                old_l = l;
+                if (l > last_special_line) {
+                    line_width = second_width;
+                } else if (par_shape_ptr == null) {
+                    line_width = first_width;
+                } else {
+                    line_width = varmem[(par_shape_ptr + 2 * l + 1)].cint;
+                }
+            }
+        }
+        /* /If a line number class has ended, create new active nodes for
+           the best feasible breaks in that class; then |return|
+           if |r=active|, otherwise compute the new |line_width|; */
+
+        /* Consider the demerits for a line from |r| to |cur_p|;
+           deactivate node |r| if it should no longer be active;
+           then |goto continue| if a line from |r| to |cur_p| is infeasible,
+           otherwise record a new feasible break; */
+        artificial_demerits = false;
+        shortfall = line_width - cur_active_width[1];
+        if (break_node(r) == null)
+            shortfall -= init_internal_left_box_width;
+        else
+            shortfall -= passive_last_left_box_width(break_node(r));
+        shortfall -= internal_right_box_width;
+        if (protrude_chars > 1) {
+            halfword l1, o;
+            l1 = (break_node(r) == null) ? first_p : cur_break(break_node(r));
+            if (cur_p == null) {
+                o = null;
+            } else {            /* TODO |if (is_character_node(alink(cur_p)))| */
+                o = alink(cur_p);
+                assert(vlink(o) == cur_p);
+            }
+            /* MAGIC: the disc could be a SELECT subtype, to we might need
+             to get the last character as |pre_break| from either the
+             |pre_break| list (if the previous INIT disc was taken), or the
+             |no_break| (sic) list (if the previous INIT disc was not taken)
+
+             BUT:
+             the last characters (hyphenation character) if these two list
+             should always be the same anyway, so we just look at |pre_break|.
+             */
+            /* let's look at the right margin first */
+            if ((cur_p != null) && (type(cur_p) == disc_node)
+                && (vlink_pre_break(cur_p) != null)) {
+                /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */
+                o = tlink_pre_break(cur_p);
+            } else {
+                o = find_protchar_right(l1, o);
+            }
+            /* now the left margin */
+            if ((l1 != null) && (type(l1) == disc_node) && (vlink_post_break(l1) != null)) {
+                /* FIXME: first 'char' could be a disc! */
+                l1 = vlink_post_break(l1);        /* protrude the first char */
+            } else {
+                l1 = find_protchar_left(l1, true);
+            }
+            shortfall += (left_pw(l1) + right_pw(o));
+        }
+        if (shortfall != 0) {
+            margin_kern_stretch = 0;
+            margin_kern_shrink = 0;
+            if (protrude_chars > 1) {
+                /* Calculate variations of marginal kerns; */
+                lp = last_leftmost_char;
+                rp = last_rightmost_char;
+                cp = raw_glyph_node();
+                if (lp != null) {
+                    cal_margin_kern_var(lp);
+                }
+                if (rp != null) {
+                    cal_margin_kern_var(rp);
+                }
+                flush_node(cp);
+            }
+            if ((shortfall > 0) && ((total_font_stretch + margin_kern_stretch) > 0)) {
+                if ((total_font_stretch + margin_kern_stretch) > shortfall)
+                    shortfall = ((total_font_stretch + margin_kern_stretch) / (max_stretch_ratio / cur_font_step)) / 2;
+                else
+                    shortfall -= (total_font_stretch + margin_kern_stretch);
+            } else if ((shortfall < 0) && ((total_font_shrink + margin_kern_shrink) > 0)) {
+                if ((total_font_shrink + margin_kern_shrink) > -shortfall)
+                    shortfall = -((total_font_shrink + margin_kern_shrink) / (max_shrink_ratio / cur_font_step)) / 2;
+                else
+                    shortfall += (total_font_shrink + margin_kern_shrink);
+            }
+        }
+        if (shortfall > 0) {
+            /* Set the value of |b| to the badness for stretching the line,
+               and compute the corresponding |fit_class| */
+
+            /* When a line must stretch, the available stretchability can be
+               found in the subarray |cur_active_width[2..6]|, in units of
+               points, sfi, fil, fill and filll.
+
+               The present section is part of \TeX's inner loop, and it is
+               most often performed when the badness is infinite; therefore
+               it is worth while to make a quick test for large width excess
+               and small stretchability, before calling the |badness|
+               subroutine. */
+
+            if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) ||
+                (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) {
+                if (do_last_line_fit) {
+                    if (cur_p == null) {        /* the last line of a paragraph */
+                        /* Perform computations for last line and |goto found|; */
+
+                        /* Here we compute the adjustment |g| and badness |b| for
+                           a line from |r| to the end of the paragraph.  When any
+                           of the criteria for adjustment is violated we fall
+                           through to the normal algorithm.
+
+                           The last line must be too short, and have infinite
+                           stretch entirely due to |par_fill_skip|. */
+                        if ((active_short(r) == 0) || (active_glue(r) <= 0))
+                            /* previous line was neither stretched nor shrunk, or
+                               was infinitely bad */
+                            goto NOT_FOUND;
+                        if ((cur_active_width[3] != fill_width[0]) ||
+                            (cur_active_width[4] != fill_width[1]) ||
+                            (cur_active_width[5] != fill_width[2]) ||
+                            (cur_active_width[6] != fill_width[3]))
+                            /* infinite stretch of this line not entirely due to
+                               |par_fill_skip| */
+                            goto NOT_FOUND;
+                        if (active_short(r) > 0)
+                            g = cur_active_width[2];
+                        else
+                            g = cur_active_width[7];
+                        if (g <= 0)
+                            /*no finite stretch resp.\ no shrink */
+                            goto NOT_FOUND;
+                        arith_error = false;
+                        g = fract(g, active_short(r), active_glue(r),
+                                  max_dimen);
+                        if (last_line_fit < 1000)
+                            g = fract(g, last_line_fit, 1000, max_dimen);
+                        if (arith_error) {
+                            if (active_short(r) > 0)
+                                g = max_dimen;
+                            else
+                                g = -max_dimen;
+                        }
+                        if (g > 0) {
+                            /*Set the value of |b| to the badness of the last line
+                               for stretching, compute the corresponding |fit_class,
+                               and |goto found|| */
+                            /* These badness computations are rather similar to
+                               those of the standard algorithm, with the adjustment
+                               amount |g| replacing the |shortfall|. */
+                            if (g > shortfall)
+                                g = shortfall;
+                            if (g > 7230584) {
+                                if (cur_active_width[2] < 1663497) {
+                                    b = inf_bad;
+                                    fit_class = very_loose_fit;
+                                    goto FOUND;
+                                }
+                            }
+                            b = badness(g, cur_active_width[2]);
+                            if (b > 99) {
+                                fit_class = very_loose_fit;
+                            } else if (b > 12) {
+                                fit_class = loose_fit;
+                            } else {
+                                fit_class = decent_fit;
+                            }
+                            goto FOUND;
+                        } else if (g < 0) {
+                            /*Set the value of |b| to the badness of the last line
+                               for shrinking, compute the corresponding |fit_class,
+                               and |goto found||; */
+                            if (-g > cur_active_width[7])
+                                g = -cur_active_width[7];
+                            b = badness(-g, cur_active_width[7]);
+                            if (b > 12)
+                                fit_class = tight_fit;
+                            else
+                                fit_class = decent_fit;
+                            goto FOUND;
+                        }
+                    }
+                  NOT_FOUND:
+                    shortfall = 0;
+                }
+                b = 0;
+                fit_class = decent_fit; /* infinite stretch */
+            } else {
+                if (shortfall > 7230584 && cur_active_width[2] < 1663497) {
+                    b = inf_bad;
+                    fit_class = very_loose_fit;
+                } else {
+                    b = badness(shortfall, cur_active_width[2]);
+                    if (b > 99) {
+                        fit_class = very_loose_fit;
+                    } else if (b > 12) {
+                        fit_class = loose_fit;
+                    } else {
+                        fit_class = decent_fit;
+                    }
+                }
+            }
+        } else {
+            /* Set the value of |b| to the badness for shrinking the line,
+               and compute the corresponding |fit_class|; */
+            /* Shrinkability is never infinite in a paragraph; we can shrink
+               the line from |r| to |cur_p| by at most
+               |cur_active_width[7]|. */
+            if (-shortfall > cur_active_width[7])
+                b = inf_bad + 1;
+            else
+                b = badness(-shortfall, cur_active_width[7]);
+            if (b > 12)
+                fit_class = tight_fit;
+            else
+                fit_class = decent_fit;
+        }
+        if (do_last_line_fit) {
+            /* Adjust the additional data for last line; */
+            if (cur_p == null)
+                shortfall = 0;
+            if (shortfall > 0) {
+                g = cur_active_width[2];
+            } else if (shortfall < 0) {
+                g = cur_active_width[7];
+            } else {
+                g = 0;
+            }
+        }
+      FOUND:
+        if ((b > inf_bad) || (pi == eject_penalty)) {
+            /* Prepare to deactivate node~|r|, and |goto deactivate| unless
+               there is a reason to consider lines of text from |r| to |cur_p| */
+            /* During the final pass, we dare not lose all active nodes, lest we lose
+               touch with the line breaks already found. The code shown here makes
+               sure that such a catastrophe does not happen, by permitting overfull
+               boxes as a last resort. This particular part of \TeX\ was a source of
+               several subtle bugs before the correct program logic was finally
+               discovered; readers who seek to ``improve'' \TeX\ should therefore
+               think thrice before daring to make any changes here.
+             */
+            if (final_pass && (minimum_demerits == awful_bad) &&
+                (vlink(r) == active) && (prev_r == active)) {
+                artificial_demerits = true;     /* set demerits zero, this break is forced */
+            } else if (b > threshold) {
+                goto DEACTIVATE;
+            }
+            node_r_stays_active = false;
+        } else {
+            prev_r = r;
+            if (b > threshold)
+                continue;
+            node_r_stays_active = true;
+        }
+        /* Record a new feasible break; */
+        /* When we get to this part of the code, the line from |r| to |cur_p| is
+           feasible, its badness is~|b|, and its fitness classification is
+           |fit_class|.  We don't want to make an active node for this break yet,
+           but we will compute the total demerits and record them in the
+           |minimal_demerits| array, if such a break is the current champion among
+           all ways to get to |cur_p| in a given line-number class and fitness
+           class.
+         */
+        if (artificial_demerits) {
+            d = 0;
+        } else {
+            /* Compute the demerits, |d|, from |r| to |cur_p|; */
+            d = line_penalty + b;
+            if (abs(d) >= 10000)
+                d = 100000000;
+            else
+                d = d * d;
+            if (pi != 0) {
+                if (pi > 0) {
+                    d += (pi * pi);
+                } else if (pi > eject_penalty) {
+                    d -= (pi * pi);
+                }
+            }
+            if ((break_type == hyphenated_node) && (type(r) == hyphenated_node)) {
+                if (cur_p != null)
+                    d += double_hyphen_demerits;
+                else
+                    d += final_hyphen_demerits;
+            }
+            if (abs(fit_class - fitness(r)) > 1)
+                d = d + adj_demerits;
+        }
+        if (tracing_paragraphs > 0)
+            print_feasible_break(cur_p, r, b, pi, d, artificial_demerits);
+        d += total_demerits(r); /*this is the minimum total demerits
+                                   from the beginning to |cur_p| via |r| */
+        if (d <= minimal_demerits[fit_class]) {
+            minimal_demerits[fit_class] = d;
+            best_place[fit_class] = break_node(r);
+            best_pl_line[fit_class] = l;
+            if (do_last_line_fit) {
+                /* Store additional data for this feasible break; */
+                /* For each feasible break we record the shortfall and glue stretch or
+                   shrink (or adjustment). */
+                best_pl_short[fit_class] = shortfall;
+                best_pl_glue[fit_class] = g;
+            }
+            if (d < minimum_demerits)
+                minimum_demerits = d;
+        }
+        /* /Record a new feasible break */
+        if (node_r_stays_active)
+            continue;           /*|prev_r| has been set to |r| */
+      DEACTIVATE:
+        /* Deactivate node |r|; */
+        /* When an active node disappears, we must delete an adjacent delta node if
+           the active node was at the beginning or the end of the active list, or
+           if it was surrounded by delta nodes. We also must preserve the property
+           that |cur_active_width| represents the length of material from
+           |vlink(prev_r)| to~|cur_p|. */
+
+        vlink(prev_r) = vlink(r);
+        flush_node(r);
+        if (prev_r == active) {
+            /*Update the active widths, since the first active node has been
+               deleted */
+            /* The following code uses the fact that |type(active)<>delta_node|.
+               If the active list has just become empty, we do not need to update the
+               |active_width| array, since it will be initialized when an active
+               node is next inserted.
+             */
+            r = vlink(active);
+            if (type(r) == delta_node) {
+                do_all_eight(update_active);    /* IMPLICIT r */
+                do_all_eight(copy_to_cur_active);
+                vlink(active) = vlink(r);
+                flush_node(r);
+            }
+        } else if (type(prev_r) == delta_node) {
+            r = vlink(prev_r);
+            if (r == active) {
+                do_all_eight(downdate_width);   /* IMPLICIT |prev_r| */
+                vlink(prev_prev_r) = active;
+                flush_node(prev_r);
+                prev_r = prev_prev_r;
+            } else if (type(r) == delta_node) {
+                do_all_eight(update_width);     /* IMPLICIT ,r */
+                do_all_eight(combine_two_deltas);       /* IMPLICIT r |prev_r| */
+                vlink(prev_r) = vlink(r);
+                flush_node(r);
+            }
+        }
+    }
+}
+
+@ @c
+void ext_do_line_break(int paragraph_dir,
+                       int pretolerance,
+                       int tracing_paragraphs,
+                       int tolerance,
+                       scaled emergency_stretch,
+                       int looseness,
+                       int adjust_spacing,
+                       halfword par_shape_ptr,
+                       int adj_demerits,
+                       int protrude_chars,
+                       int line_penalty,
+                       int last_line_fit,
+                       int double_hyphen_demerits,
+                       int final_hyphen_demerits,
+                       int hang_indent,
+                       int hsize,
+                       int hang_after,
+                       halfword left_skip,
+                       halfword right_skip,
+                       halfword inter_line_penalties_ptr,
+                       int inter_line_penalty,
+                       int club_penalty,
+                       halfword club_penalties_ptr,
+                       halfword widow_penalties_ptr,
+                       int widow_penalty,
+                       int broken_penalty,
+                       halfword final_par_glue)
+{
+    /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE; */
+    halfword cur_p, q, r, s;    /* miscellaneous nodes of temporary interest */
+    int line_break_dir = paragraph_dir;
+
+    /* Get ready to start ... */
+    minimum_demerits = awful_bad;
+    minimal_demerits[tight_fit] = awful_bad;
+    minimal_demerits[decent_fit] = awful_bad;
+    minimal_demerits[loose_fit] = awful_bad;
+    minimal_demerits[very_loose_fit] = awful_bad;
+
+    fewest_demerits = 0;
+    actual_looseness = 0;
+
+    /* We compute the values of |easy_line| and the other local variables relating
+       to line length when the |line_break| procedure is initializing itself. */
+    if (par_shape_ptr == null) {
+        if (hang_indent == 0) {
+            last_special_line = 0;
+            second_width = hsize;
+            second_indent = 0;
+        } else {
+            halfword used_hang_indent = swap_hang_indent(hang_indent);
+            /*  Set line length parameters in preparation for hanging indentation */
+            /* We compute the values of |easy_line| and the other local variables relating
+               to line length when the |line_break| procedure is initializing itself. */
+            last_special_line = abs(hang_after);
+            if (hang_after < 0) {
+                first_width = hsize - abs(used_hang_indent);
+                if (used_hang_indent >= 0)
+                    first_indent = used_hang_indent;
+                else
+                    first_indent = 0;
+                second_width = hsize;
+                second_indent = 0;
+            } else {
+                first_width = hsize;
+                first_indent = 0;
+                second_width = hsize - abs(used_hang_indent);
+                if (used_hang_indent >= 0)
+                    second_indent = used_hang_indent;
+                else
+                    second_indent = 0;
+            }
+        }
+    } else {
+        last_special_line = vinfo(par_shape_ptr + 1) - 1;
+        second_indent = varmem[(par_shape_ptr + 2 * (last_special_line + 1))].cint;
+        second_width = varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint;
+        second_indent = swap_parshape_indent(second_indent,second_width);
+    }
+    if (looseness == 0)
+        easy_line = last_special_line;
+    else
+        easy_line = max_halfword;
+
+    no_shrink_error_yet = true;
+    check_shrinkage(left_skip);
+    check_shrinkage(right_skip);
+    q = left_skip;
+    r = right_skip;
+    background[1] = width(q) + width(r);
+    background[2] = 0;
+    background[3] = 0;
+    background[4] = 0;
+    background[5] = 0;
+    background[6] = 0;
+    background[2 + stretch_order(q)] = stretch(q);
+    background[2 + stretch_order(r)] += stretch(r);
+    background[7] = shrink(q) + shrink(r);
+    if (adjust_spacing > 1) {
+        background[8] = 0;
+        background[9] = 0;
+        max_stretch_ratio = -1;
+        max_shrink_ratio = -1;
+        cur_font_step = -1;
+        set_prev_char_p(null);
+    }
+    /* Check for special treatment of last line of paragraph; */
+    /* The new algorithm for the last line requires that the stretchability
+       |par_fill_skip| is infinite and the stretchability of |left_skip| plus
+       |right_skip| is finite.
+     */
+    do_last_line_fit = false;
+    if (last_line_fit > 0) {
+        q = last_line_fill;
+        if ((stretch(q) > 0) && (stretch_order(q) > normal)) {
+            if ((background[3] == 0) && (background[4] == 0) &&
+                (background[5] == 0) && (background[6] == 0)) {
+                do_last_line_fit = true;
+                fill_width[0] = 0;
+                fill_width[1] = 0;
+                fill_width[2] = 0;
+                fill_width[3] = 0;
+                fill_width[stretch_order(q) - 1] = stretch(q);
+            }
+        }
+    }
+    /* DIR: Initialize |dir_ptr| for |line_break| */
+    if (dir_ptr != null) {
+        flush_node_list(dir_ptr);
+        dir_ptr = null;
+    }
+#if 0
+    push_dir(dir_ptr,paragraph_dir); /* TODO what was the point of this? */
+#endif
+
+    /* Find optimal breakpoints; */
+    threshold = pretolerance;
+    if (threshold >= 0) {
+        if (tracing_paragraphs > 0) {
+            begin_diagnostic();
+            tprint_nl("@@firstpass");
+        }
+        second_pass = false;
+        final_pass = false;
+    } else {
+        threshold = tolerance;
+        second_pass = true;
+        final_pass = (emergency_stretch <= 0);
+        if (tracing_paragraphs > 0)
+            begin_diagnostic();
+    }
+    while (1) {
+        halfword first_p;
+        halfword nest_stack[10];
+        int nest_index = 0;
+        if (threshold > inf_bad)
+            threshold = inf_bad;
+        /* Create an active breakpoint representing the beginning of the paragraph */
+        q = new_node(unhyphenated_node, decent_fit);
+        vlink(q) = active;
+        break_node(q) = null;
+        line_number(q) = cur_list.pg_field + 1;
+        total_demerits(q) = 0;
+        active_short(q) = 0;
+        active_glue(q) = 0;
+        vlink(active) = q;
+        do_all_eight(store_background);
+        passive = null;
+        printed_node = temp_head;
+        pass_number = 0;
+        font_in_short_display = null_font;
+        /* /Create an active breakpoint representing the beginning of the paragraph */
+        auto_breaking = true;
+        cur_p = vlink(temp_head);
+        /* LOCAL: Initialize with first |local_paragraph| node */
+        if ((cur_p != null) && (type(cur_p) == local_par_node)) {
+            alink(cur_p) = temp_head; /* this used to be an assert, but may as well force it */
+            internal_pen_inter = local_pen_inter(cur_p);
+            internal_pen_broken = local_pen_broken(cur_p);
+            init_internal_left_box = local_box_left(cur_p);
+            init_internal_left_box_width = local_box_left_width(cur_p);
+            internal_left_box = init_internal_left_box;
+            internal_left_box_width = init_internal_left_box_width;
+            internal_right_box = local_box_right(cur_p);
+            internal_right_box_width = local_box_right_width(cur_p);
+        } else {
+            internal_pen_inter = 0;
+            internal_pen_broken = 0;
+            init_internal_left_box = null;
+            init_internal_left_box_width = 0;
+            internal_left_box = init_internal_left_box;
+            internal_left_box_width = init_internal_left_box_width;
+            internal_right_box = null;
+            internal_right_box_width = 0;
+        }
+        /* /LOCAL: Initialize with first |local_paragraph| node */
+        set_prev_char_p(null);
+        first_p = cur_p;
+        /* to access the first node of paragraph as the first active node
+           has |break_node=null| */
+        while ((cur_p != null) && (vlink(active) != active)) {
+            /* |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, also look at |disc_node|s. */
+
+            while (is_char_node(cur_p)) {
+                /* Advance |cur_p| to the node following the present string of characters ; */
+                /* The code that passes over the characters of words in a paragraph is part of
+                   \TeX's inner loop, so it has been streamlined for speed. We use the fact that
+                   `\.{\\parfillskip}' glue appears at the end of each paragraph; it is therefore
+                   unnecessary to check if |vlink(cur_p)=null| when |cur_p| is a character node.
+                 */
+                active_width[1] += pack_width(line_break_dir, dir_TRT, cur_p, true);
+                if ((adjust_spacing > 1) && check_expand_pars(font(cur_p))) {
+                    set_prev_char_p(cur_p);
+                    add_char_stretch(active_width[8], cur_p);
+                    add_char_shrink(active_width[9], cur_p);
+                }
+                cur_p = vlink(cur_p);
+                while (cur_p == null && nest_index > 0) {
+                    cur_p = nest_stack[--nest_index];
+                }
+            }
+            if (cur_p == null) {
+                normal_error("linebreak","invalid list tail, probably missing glue");
+            }
+            /* Determine legal breaks: As we move through the hlist, we need to keep
+               the |active_width| array up to date, so that the badness of individual
+               lines is readily calculated by |try_break|. It is convenient to use the
+               short name |active_width[1]| for the component of active width that represents
+               real width as opposed to glue. */
+
+            switch (type(cur_p)) {
+                case hlist_node:
+                case vlist_node:
+                    active_width[1] += pack_width(line_break_dir, box_dir(cur_p), cur_p, false);
+                    break;
+                case rule_node:
+                    active_width[1] += width(cur_p);
+                    break;
+                case dir_node: /* DIR: Adjust the dir stack for the |line_break| routine; */
+                    if (dir_dir(cur_p) >= 0) {
+                        line_break_dir = dir_dir(cur_p);
+                        push_dir_node(dir_ptr,cur_p);   /* adds to |dir_ptr| */
+                    } else {
+                        pop_dir_node(dir_ptr);
+                        if (dir_ptr != null) {
+                            line_break_dir = dir_dir(dir_ptr);
+                        }
+                    }
+                    break;
+                case local_par_node:   /* LOCAL: Advance past a |local_paragraph| node; */
+                    internal_pen_inter = local_pen_inter(cur_p);
+                    internal_pen_broken = local_pen_broken(cur_p);
+                    internal_left_box = local_box_left(cur_p);
+                    internal_left_box_width = local_box_left_width(cur_p);
+                    internal_right_box = local_box_right(cur_p);
+                    internal_right_box_width = local_box_right_width(cur_p);
+                    break;
+                case math_node:
+                    auto_breaking = (subtype(cur_p) == after);
+                    /* begin mathskip code */
+                    if (glue_is_zero(cur_p) || ignore_math_skip(cur_p)) {
+                        kern_break();
+                        break;
+                    } else {
+                        /* fall through */
+                    }
+                    /* end mathskip code */
+                case glue_node:
+                    /*
+                        If node |cur_p| is a legal breakpoint, call |try_break|;
+                        then update the active widths by including the glue in
+                        |glue_ptr(cur_p)|;
+
+                        When node |cur_p| is a glue node, we look at the previous
+                        to see whether or not a breakpoint is legal at |cur_p|,
+                        as explained above.
+
+                        We only break after certain nodes (see texnodes.h), a font related
+                        kern and a dir node when |\breakafterdirmode=1|.
+                    */
+                    if (auto_breaking) {
+                        halfword prev_p = alink(cur_p);
+                        if (prev_p != temp_head && (
+                                is_char_node(prev_p)
+                             || precedes_break(prev_p)
+                             || precedes_kern(prev_p)
+                             || precedes_dir(prev_p)
+                            )) {
+                            ext_try_break(0, unhyphenated_node, line_break_dir, adjust_spacing,
+                                          par_shape_ptr, adj_demerits,
+                                          tracing_paragraphs, protrude_chars,
+                                          line_penalty, last_line_fit,
+                                          double_hyphen_demerits,
+                                          final_hyphen_demerits, first_p, cur_p);
+                        }
+                    }
+                    /* *INDENT-ON* */
+                    check_shrinkage(cur_p);
+                    active_width[1] += width(cur_p);
+                    active_width[2 + stretch_order(cur_p)] += stretch(cur_p);
+                    active_width[7] += shrink(cur_p);
+                    break;
+                case kern_node:
+                    if (subtype(cur_p) == explicit_kern || subtype(cur_p) == italic_kern) {
+                        kern_break();
+                    } else {
+                        active_width[1] += width(cur_p);
+                        if ((adjust_spacing == 2) && (subtype(cur_p) == normal)) {
+                            add_kern_stretch(active_width[8], cur_p);
+                            add_kern_shrink(active_width[9], cur_p);
+                        }
+                    }
+                    break;
+                case disc_node:
+                    /* |select_disc|s are handled by the leading |init_disc| */
+                    if (subtype(cur_p) == select_disc)
+                        break;
+                    /* Try to break after a discretionary fragment, then |goto done5|; */
+                    /* The following code knows that discretionary texts contain
+                       only character nodes, kern nodes, box nodes, and rule
+                       nodes. This branch differs a bit from older engines because in LuaTeX we
+                       already have hyphenated the list. This means that we need to skip
+                       automatic disc nodes. Of better, we need to treat discretionaries
+                       and explicit hyphens always, even in the first pass (HH). */
+                    if (second_pass || subtype(cur_p) <= automatic_disc) {
+                        /*
+                        int actual_penalty = hyphen_penalty;
+                        if (disc_penalty(cur_p) != 0) {
+                            actual_penalty = (int) disc_penalty(cur_p);
+                        } else if (subtype(cur_p) == automatic_disc) {
+                            actual_penalty = ex_hyphen_penalty;
+                        }
+                        */
+                        int actual_penalty = (int) disc_penalty(cur_p);
+                        s = vlink_pre_break(cur_p);
+                        do_one_seven_eight(reset_disc_width);
+                        if (s == null) {    /* trivial pre-break */
+                            ext_try_break(actual_penalty, hyphenated_node,
+                                          line_break_dir, adjust_spacing,
+                                          par_shape_ptr, adj_demerits,
+                                          tracing_paragraphs, protrude_chars,
+                                          line_penalty, last_line_fit,
+                                          double_hyphen_demerits,
+                                          final_hyphen_demerits, first_p, cur_p);
+                        } else {
+                            add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
+                            do_one_seven_eight(add_disc_width_to_active_width);
+                            ext_try_break(actual_penalty, hyphenated_node,
+                                          line_break_dir, adjust_spacing,
+                                          par_shape_ptr, adj_demerits,
+                                          tracing_paragraphs, protrude_chars,
+                                          line_penalty, last_line_fit,
+                                          double_hyphen_demerits,
+                                          final_hyphen_demerits, first_p, cur_p);
+                            if (subtype(cur_p) == init_disc) {
+                                /* we should at two break points after the one we
+                                 added above:
+                                 \item1 which does a possible break in INIT's |post_break|
+                                 \item2 which means the |no_break| actually was broken
+                                 just a character later */
+                                /* do the select-0 case 'f-f-i' */
+                                s = vlink_pre_break(vlink(cur_p));
+                                add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
+                                ext_try_break(actual_penalty, hyphenated_node,
+                                              line_break_dir, adjust_spacing,
+                                              par_shape_ptr, adj_demerits,
+                                              tracing_paragraphs,
+                                              protrude_chars, line_penalty,
+                                              last_line_fit, double_hyphen_demerits,
+                                              final_hyphen_demerits, first_p,
+                                              vlink(cur_p));
+#if 0
+                                /* TODO this does not work */
+                                /* go back to the starting situation */
+                                do_one_seven_eight(sub_disc_width_from_active_width);
+                                do_one_seven_eight(reset_disc_width);
+                                /* add select |no_break| to |active_width| */
+                                s = vlink_no_break(vlink(cur_p));
+                                add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
+                                ext_try_break(actual_penalty, hyphenated_node,
+                                              line_break_dir, adjust_spacing,
+                                              par_shape_ptr, adj_demerits,
+                                              tracing_paragraphs,
+                                              protrude_chars, line_penalty,
+                                              last_line_fit, double_hyphen_demerits,
+                                              final_hyphen_demerits, first_p,
+                                              vlink(cur_p));
+#endif
+                            }
+                            do_one_seven_eight(sub_disc_width_from_active_width);
+                        }
+                    }
+                    s = vlink_no_break(cur_p);
+                    add_to_widths(s, line_break_dir, adjust_spacing, active_width);
+                    break;
+                case penalty_node:
+                    ext_try_break(penalty(cur_p), unhyphenated_node, line_break_dir,
+                                  adjust_spacing, par_shape_ptr, adj_demerits,
+                                  tracing_paragraphs, protrude_chars,
+                                  line_penalty, last_line_fit,
+                                  double_hyphen_demerits, final_hyphen_demerits,
+                                  first_p, cur_p);
+                    break;
+                case boundary_node:
+                case whatsit_node:
+                    /* / Advance past a whatsit node in the |line_break| loop/; */
+                case mark_node:
+                case ins_node:
+                case adjust_node:
+                    break;
+                case glue_spec_node:
+                    normal_warning("parbuilder","found a glue_spec in a paragraph");
+                    break;
+                default:
+                    formatted_error("parbuilder","weird node %d in paragraph",type(cur_p));
+            }
+            cur_p = vlink(cur_p);
+            while (cur_p == null && nest_index > 0) {
+                cur_p = nest_stack[--nest_index];
+            }
+        }
+        if (cur_p == null) {
+            /*
+                Try the final line break at the end of the paragraph,
+                and |goto done| if the desired breakpoints have been found
+
+                The forced line break at the paragraph's end will reduce the list of
+                breakpoints so that all active nodes represent breaks at |cur_p=null|.
+                On the first pass, we insist on finding an active node that has the
+                correct ``looseness.'' On the final pass, there will be at least one active
+                node, and we will match the desired looseness as well as we can.
+
+                The global variable |best_bet| will be set to the active node for the best
+                way to break the paragraph, and a few other variables are used to
+                help determine what is best.
+            */
+            ext_try_break(eject_penalty, hyphenated_node, line_break_dir,
+                          adjust_spacing, par_shape_ptr, adj_demerits,
+                          tracing_paragraphs, protrude_chars, line_penalty,
+                          last_line_fit, double_hyphen_demerits,
+                          final_hyphen_demerits, first_p, cur_p);
+            if (vlink(active) != active) {
+                /* Find an active node with fewest demerits; */
+                r = vlink(active);
+                fewest_demerits = awful_bad;
+                do {
+                    if (type(r) != delta_node) {
+                        if (total_demerits(r) < fewest_demerits) {
+                            fewest_demerits = total_demerits(r);
+                            best_bet = r;
+                        }
+                    }
+                    r = vlink(r);
+                } while (r != active);
+                best_line = line_number(best_bet);
+                /*
+                    Find an active node with fewest demerits;
+                */
+                if (looseness == 0)
+                    goto DONE;
+                /*
+                    Find the best active node for the desired looseness;
+
+                    The adjustment for a desired looseness is a slightly more complicated
+                    version of the loop just considered. Note that if a paragraph is broken
+                    into segments by displayed equations, each segment will be subject to the
+                    looseness calculation, independently of the other segments.
+                 */
+                r = vlink(active);
+                actual_looseness = 0;
+                do {
+                    if (type(r) != delta_node) {
+                        line_diff = line_number(r) - best_line;
+                        if (((line_diff < actual_looseness)
+                             && (looseness <= line_diff))
+                            || ((line_diff > actual_looseness)
+                                && (looseness >= line_diff))) {
+                            best_bet = r;
+                            actual_looseness = line_diff;
+                            fewest_demerits = total_demerits(r);
+                        } else if ((line_diff == actual_looseness) &&
+                                   (total_demerits(r) < fewest_demerits)) {
+                            best_bet = r;
+                            fewest_demerits = total_demerits(r);
+                        }
+                    }
+                    r = vlink(r);
+                } while (r != active);
+                best_line = line_number(best_bet);
+                /*
+                    Find the best active node for the desired looseness;
+                */
+                if ((actual_looseness == looseness) || final_pass)
+                    goto DONE;
+            }
+        }
+        /* Clean up the memory by removing the break nodes; */
+        clean_up_the_memory();
+        /* /Clean up the memory by removing the break nodes; */
+        if (!second_pass) {
+            if (tracing_paragraphs > 0)
+                tprint_nl("@@secondpass");
+            threshold = tolerance;
+            second_pass = true;
+            final_pass = (emergency_stretch <= 0);
+        } else {
+            /* if at first you do not succeed, \dots */
+            if (tracing_paragraphs > 0)
+                tprint_nl("@@emergencypass");
+            background[2] += emergency_stretch;
+            final_pass = true;
+        }
+    }
+
+  DONE:
+    if (tracing_paragraphs > 0) {
+        end_diagnostic(true);
+        normalize_selector();
+    }
+    if (do_last_line_fit) {
+        /*
+            Adjust the final line of the paragraph; here we either reset
+            |do_last_line_fit| or adjust the |par_fill_skip| glue.
+        */
+        if (active_short(best_bet) == 0) {
+            do_last_line_fit = false;
+        } else {
+            width(last_line_fill) += (active_short(best_bet) - active_glue(best_bet));
+            stretch(last_line_fill) = 0;
+        }
+    }
+
+    /*
+        Break the paragraph at the chosen...; Once the best sequence of breakpoints
+        has been found (hurray), we call on the procedure |post_line_break| to finish
+        the remainder of the work. By introducing this subprocedure, we are able to
+        keep |line_break| from getting extremely long.
+    */
+
+    /* first thing |ext_post_line_break| does is reset |dir_ptr| */
+    flush_node_list(dir_ptr);
+    dir_ptr = null;
+    ext_post_line_break(paragraph_dir,
+                        right_skip,
+                        left_skip,
+                        protrude_chars,
+                        par_shape_ptr,
+                        adjust_spacing,
+                        inter_line_penalties_par_ptr,
+                        inter_line_penalty,
+                        club_penalty,
+                        club_penalties_ptr,
+                        widow_penalties_ptr,
+                        widow_penalty,
+                        broken_penalty,
+                        final_par_glue,
+                        best_bet,
+                        last_special_line,
+                        second_width,
+                        second_indent, first_width, first_indent, best_line);
+    /*
+        Break the paragraph at the chosen ...Clean up the memory by removing
+        the break nodes.
+    */
+    clean_up_the_memory();
+}
+
+@ @c
+void get_linebreak_info (int *f, int *a)
+{
+    *f = fewest_demerits;
+    *a = actual_looseness;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/mainbody.c
+++ /dev/null
@@ -1,769 +0,0 @@
-/*
-
-mainbody.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-This is where the action starts. We're speaking of \LUATEX, a continuation of
-\PDFTEX\ (which included \ETEX) and \ALEPH. As \TEX, \LUATEX\ is a document
-compiler intended to simplify high quality typesetting for many of the world's
-languages. It is an extension of D. E. Knuth's \TEX, which was designed
-essentially for the typesetting of languages using the Latin alphabet. Although
-it is a direct decendant of \TEX, and therefore mostly compatible, there are
-some subtle differences that relate to \UNICODE\ support and \OPENTYPE\ math.
-
-The \ALEPH\ subsystem loosens many of the restrictions imposed by~\TeX: register
-numbers are no longer limited to 8~bits. Fonts may have more than 256~characters,
-more than 256~fonts may be used, etc. We use a similar model. We also borrowed
-the directional model but have upgraded it a bit as well as integrated it more
-tightly.
-
-This program is directly derived from Donald E. Knuth's \TEX; the change history
-which follows and the reward offered for finders of bugs refer specifically to
-\TEX; they should not be taken as referring to \LUATEX, \PDFTEX, nor \ETEX,
-although the change history is relevant in that it demonstrates the evolutionary
-path followed. This program is not \TEX; that name is reserved strictly for the
-program which is the creation and sole responsibility of Professor Knuth.
-
-\starttyping
-% Version 0 was released in September 1982 after it passed a variety of tests.
-% Version 1 was released in November 1983 after thorough testing.
-% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984).
-% Version 1.2 allowed `0' in response to an error, et alia (October 1984).
-% Version 1.3 made memory allocation more flexible and local (November 1984).
-% Version 1.4 fixed accents right after line breaks, et alia (April 1985).
-% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985).
-% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986).
-% Version 2.1 corrected anomalies in discretionary breaks (January 1987).
-% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987).
-% Version 2.3 avoided incomplete page in premature termination (August 1987).
-% Version 2.4 fixed \noaligned rules in indented displays (August 1987).
-% Version 2.5 saved cur_order when expanding tokens (September 1987).
-% Version 2.6 added 10sp slop when shipping leaders (November 1987).
-% Version 2.7 improved rounding of negative-width characters (November 1987).
-% Version 2.8 fixed weird bug if no \patterns are used (December 1987).
-% Version 2.9 made \csname\endcsname's "relax" local (December 1987).
-% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988).
-% Version 2.92 fixed \patterns, also file names with complex macros (May 1988).
-% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988).
-% Version 2.94 kept open_log_file from calling fatal_error (November 1988).
-% Version 2.95 solved that problem a better way (December 1988).
-% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989).
-% Version 2.97 corrected blunder in creating 2.95 (February 1989).
-% Version 2.98 omitted save_for_after at outer level (March 1989).
-% Version 2.99 caught $$\begingroup\halign..$$ (June 1989).
-% Version 2.991 caught .5\ifdim.6... (June 1989).
-% Version 2.992 introduced major changes for 8-bit extensions (September 1989).
-% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989).
-% Version 3.0 fixed unusual displays; was more \output robust (March 1990).
-% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990).
-% Version 3.14 fixed unprintable font names and corrected typos (March 1991).
-% Version 3.141 more of same; reconstituted ligatures better (March 1992).
-% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993).
-% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995).
-% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002).
-% Version 3.1415926 was a general cleanup with minor fixes (February 2008).
-\stoptyping
-
-Although considerable effort has been expended to make the LuaTeX program correct
-and reliable, no warranty is implied; the authors disclaim any obligation or
-liability for damages, including but not limited to special, indirect, or
-consequential damages arising out of or in connection with the use or performance
-of this software. This work has been a ``labor of love'' and the authors hope
-that users enjoy it.
-
-{\em You will find a lot of comments that originate in original \TEX. We kept them
-as a side effect of the conversion from \WEB\ to \CWEB. Because there is not much
-webbing going on here eventually the files became regular \CCODE\ files with still
-potentially typeset comments. As we add our own comments, and also comments are
-there from \PDFTEX, \ALEPH\ and \ETEX, we get a curious mix. The best comments are
-of course from Don Knuth. All bad comments are ours. All errors are ours too!
-
-Not all comments make sense, because some things are implemented differently, for
-instance some memory management. But the principles of tokens and nodes stayed.
-It anyway means that you sometimes need to keep in mind that the explanation is
-more geared to traditional \TEX. But that's not a bad thing. Sorry Don for any
-confusion we introduced. The readers should have a copy of the \TEX\ books at hand
-anyway.}
-
-A large piece of software like \TeX\ has inherent complexity that cannot be
-reduced below a certain level of difficulty, although each individual part is
-fairly simple by itself. The \.{WEB} language is intended to make the algorithms
-as readable as possible, by reflecting the way the individual program pieces fit
-together and by providing the cross-references that connect different parts.
-Detailed comments about what is going on, and about why things were done in
-certain ways, have been liberally sprinkled throughout the program. These
-comments explain features of the implementation, but they rarely attempt to
-explain the \TeX\ language itself, since the reader is supposed to be familiar
-with {\sl The \TeX book}.
-
-The present implementation has a long ancestry, beginning in the summer of~1977,
-when Michael~F. Plass and Frank~M. Liang designed and coded a prototype @^Plass,
-Michael Frederick@> @^Liang, Franklin Mark@> @^Knuth, Donald Ervin@> based on
-some specifications that the author had made in May of that year. This original
-proto\TeX\ included macro definitions and elementary manipulations on boxes and
-glue, but it did not have line-breaking, page-breaking, mathematical formulas,
-alignment routines, error recovery, or the present semantic nest; furthermore, it
-used character lists instead of token lists, so that a control sequence like
-\.{\\halign} was represented by a list of seven characters. A complete version of
-\TeX\ was designed and coded by the author in late 1977 and early 1978; that
-program, like its prototype, was written in the {\mc SAIL} language, for which an
-excellent debugging system was available. Preliminary plans to convert the {\mc
-SAIL} code into a form somewhat like the present ``web'' were developed by Luis
-Trabb~Pardo and @^Trabb Pardo, Luis Isidoro@> the author at the beginning of
-1979, and a complete implementation was created by Ignacio~A. Zabala in 1979 and
-1980. The \TeX82 program, which @^Zabala Salelles, Ignacio Andr\'es@> was written
-by the author during the latter part of 1981 and the early part of 1982, also
-incorporates ideas from the 1979 implementation of @^Guibas, Leonidas Ioannis@>
-@^Sedgewick, Robert@> @^Wyatt, Douglas Kirk@> \TeX\ in {\mc MESA} that was
-written by Leonidas Guibas, Robert Sedgewick, and Douglas Wyatt at the Xerox Palo
-Alto Research Center. Several hundred refinements were introduced into \TeX82
-based on the experiences gained with the original implementations, so that
-essentially every part of the system has been substantially improved. After the
-appearance of ``Version 0'' in September 1982, this program benefited greatly
-from the comments of many other people, notably David~R. Fuchs and Howard~W.
-Trickey. A final revision in September 1989 extended the input character set to
-eight-bit codes and introduced the ability to hyphenate words from different
-languages, based on some ideas of Michael~J. Ferguson. @^Fuchs, David Raymond@>
-@^Trickey, Howard Wellington@> @^Ferguson, Michael John@>
-
-No doubt there still is plenty of room for improvement, but the author is firmly
-committed to keeping \TeX82 ``frozen'' from now on; stability and reliability are
-to be its main virtues.
-On the other hand, the \.{WEB} description can be extended without changing the
-core of \TeX82 itself, and the program has been designed so that such extensions
-are not extremely difficult to make. The |banner| string defined here should be
-changed whenever \TeX\ undergoes any modifications, so that it will be clear
-which version of \TeX\ might be the guilty party when a problem arises.
-@^extensions to \TeX@> @^system dependencies@>
-
-This program contains code for various features extending \TeX, therefore this
-program is called `\eTeX' and not `\TeX'; the official name `\TeX' by itself is
-reserved for software systems that are fully compatible with each other. A
-special test suite called the ``\.{TRIP} test'' is available for helping to
-determine whether a particular implementation deserves to be known as `\TeX'
-[cf.~Stanford Computer Science report CS1027, November 1984].
-
-A similar test suite called the ``\.{e-TRIP} test'' is available for helping to
-determine whether a particular implementation deserves to be known as `\eTeX'.
-
-This is the first of many sections of \TeX\ where global variables are defined.
-
-*/
-
-/*tex Are we using lua for initializations? */
-
-boolean luainit;
-
-/*tex Print file open and close info? */
-
-boolean tracefilenames;
-
-/*tex
-
-This program has two important variations: (1) There is a long and slow version
-called \.{INITEX}, which does the extra calculations needed to @.INITEX@>
-initialize \TeX's internal tables; and (2)~there is a shorter and faster
-production version, which cuts the initialization to a bare minimum.
-
-*/
-
-/*tex are we \.{INITEX}? */
-
-boolean ini_version;
-
-/*tex was the dump name option used? */
-
-boolean dump_option;
-
-/*tex was a \.{\%\AM format} line seen? */
-
-boolean dump_line;
-
-/*tex temporary for setup */
-
-int bound_default;
-
-/*tex temporary for setup */
-
-char *bound_name;
-
-/*tex width of context lines on terminal error messages */
-
-int error_line;
-
-/*tex
-    width of first lines of contexts in terminal error messages; should be
-    between 30 and |error_line-15|
-*/
-
-int half_error_line;
-
-/*tex width of longest text lines output; should be at least 60 */
-
-int max_print_line;
-
-/*tex maximum number of strings; must not exceed |max_halfword| */
-
-int max_strings;
-
-/*tex strings available after format loaded */
-
-int strings_free;
-
-/*tex loop variable for initialization */
-
-int font_k;
-
-/*tex
-    maximum number of characters simultaneously present in current lines of open
-    files and in control sequences between \.{\\csname} and \.{\\endcsname}; must
-    not exceed |max_halfword|
-*/
-
-int buf_size;
-
-/*tex maximum number of simultaneous input sources */
-
-int stack_size;
-
-/*tex
-    maximum number of input files and error insertions that can be going on
-    simultaneously
-*/
-
-int max_in_open;
-
-/*tex maximum number of simultaneous macro parameters */
-
-int param_size;
-
-/*tex maximum number of semantic levels simultaneously active */
-
-int nest_size;
-
-/*tex
-    space for saving values outside of current group; must be at most
-    |max_halfword|
-*/
-
-int save_size;
-
-/*tex limits recursive calls of the |expand| procedure */
-
-int expand_depth;
-
-/*tex parse the first line for options */
-
-int parsefirstlinep;
-
-/*tex format messages as file:line:error */
-
-int filelineerrorstylep;
-
-/*tex stop at first error */
-
-int haltonerrorp;
-
-/*tex current filename is quoted */
-
-boolean quoted_filename;
-
-int get_luatexversion(void)
-{
-    return luatex_version;
-}
-
-/*tex the number of pages that have been shipped out */
-
-int total_pages = 0;
-
-/*tex recent outputs that didn't ship anything out */
-
-int dead_cycles = 0;
-
-str_number get_luatexrevision(void)
-{
-    return luatex_revision;
-}
-
-/*tex
-
-This is it: the part of \TeX\ that executes all those procedures we have written.
-
-We have noted that there are two versions of \TeX82. One, called \.{INITEX},
-@.INITEX@> has to be run first; it initializes everything from scratch, without
-reading a format file, and it has the capability of dumping a format file. The
-other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs
-@.VIRTEX@> to input a format file in order to get started.
-
-For \LUATEX\ it is important to know that we still dump a format. But, in order
-to gain speed and a smaller footprint, we gzip the format (level 3). We also
-store some information that makes an abort possible in case of an incompatible
-engine version, which is important as \LUATEX\ develops. It is possible to store
-\LUA\ code in the format but not the current upvalues so you still need to
-initialize. Also, traditional fonts are stored, as are extended fonts but any
-additional information needed for instance to deal with \OPENTYPE\ fonts is to be
-handled by \LUA\ code and therefore not present in the format.
-
-*/
-
-#define const_chk(A) do { \
-    if (A < inf_##A) \
-        A = inf_##A; \
-    if (A > sup_##A) \
-        A = sup_##A; \
-} while (0)
-
-#define setup_bound_var(A,B,C) do { \
-    if (luainit>0) { \
-        get_lua_number("texconfig",B,&C); \
-        if (C==0) \
-            C=A; \
-    } else { \
-        integer x; \
-        setupboundvariable(&x, B, A); \
-        C = (int)x; \
-    } \
-} while (0)
-
-int ready_already = 0;
-
-int main_initialize(void)
-{
-    /*
-        In case somebody has inadvertently made bad settings of the
-        ``constants,'' \LUATEX\ checks them using a variable called |bad|.
-    */
-    int bad = 0;
-    /*tex
-        Bounds that may be set from the configuration file. We want the user to
-        be able to specify the names with underscores, but \.{TANGLE} removes
-        underscores, so we're stuck giving the names twice, once as a string,
-        once as the identifier. How ugly. (We can change that now.)
-    */
-    setup_bound_var(15000, "max_strings", max_strings);
-    setup_bound_var(100, "strings_free", strings_free);
-    setup_bound_var(3000, "buf_size", buf_size);
-    setup_bound_var(50, "nest_size", nest_size);
-    setup_bound_var(15, "max_in_open", max_in_open);
-    setup_bound_var(60, "param_size", param_size);
-    setup_bound_var(4000, "save_size", save_size);
-    setup_bound_var(300, "stack_size", stack_size);
-    setup_bound_var(16384, "dvi_buf_size", dvi_buf_size);
-    setup_bound_var(79, "error_line", error_line);
-    setup_bound_var(50, "half_error_line", half_error_line);
-    setup_bound_var(79, "max_print_line", max_print_line);
-    setup_bound_var(0, "hash_extra", hash_extra);
-    setup_bound_var(72, "pk_dpi", pk_dpi);
-    setup_bound_var(10000, "expand_depth", expand_depth);
-    /*tex
-        Check other constants against their sup and inf.
-    */
-    const_chk(buf_size);
-    const_chk(nest_size);
-    const_chk(max_in_open);
-    const_chk(param_size);
-    const_chk(save_size);
-    const_chk(stack_size);
-    const_chk(dvi_buf_size);
-    const_chk(max_strings);
-    const_chk(strings_free);
-    const_chk(hash_extra);
-    const_chk(pk_dpi);
-    if (error_line > ssup_error_line) {
-        error_line = ssup_error_line;
-    }
-    /*tex
-        Array memory allocation
-    */
-    buffer = xmallocarray(packed_ASCII_code, (unsigned) buf_size);
-    nest = xmallocarray(list_state_record, (unsigned) nest_size);
-    save_stack = xmallocarray(save_record, (unsigned) save_size);
-    input_stack = xmallocarray(in_state_record, (unsigned) stack_size);
-    input_file = xmallocarray(alpha_file, (unsigned) max_in_open);
-    input_file_callback_id = xmallocarray(int, (unsigned) max_in_open);
-    line_stack = xmallocarray(int, (unsigned) max_in_open);
-    eof_seen = xmallocarray(boolean, (unsigned) max_in_open);
-    grp_stack = xmallocarray(save_pointer, (unsigned) max_in_open);
-    if_stack = xmallocarray(pointer, (unsigned) max_in_open);
-    source_filename_stack = xmallocarray(str_number, (unsigned) max_in_open);
-    full_source_filename_stack = xmallocarray(char *, (unsigned) max_in_open);
-    param_stack = xmallocarray(halfword, (unsigned) param_size);
-    dvi_buf = xmallocarray(eight_bits, (unsigned) dvi_buf_size);
-    /*tex
-        Only in ini mode:
-    */
-    if (ini_version) {
-        fixmem = xmallocarray(smemory_word, fix_mem_init + 1);
-        memset(voidcast(fixmem), 0, (fix_mem_init + 1) * sizeof(smemory_word));
-        fix_mem_min = 0;
-        fix_mem_max = fix_mem_init;
-        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) (hash_top + 1));
-        memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1));
-        eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1));
-        memset(eqtb, 0, sizeof(memory_word) * (unsigned) (eqtb_top + 1));
-        init_string_pool_array((unsigned) max_strings);
-        reset_cur_string();
-    }
-    /*tex
-        Check the ``constant'' values...
-    */
-    if ((half_error_line < 30) || (half_error_line > error_line - 15))
-        bad = 1;
-    if (max_print_line < 60)
-        bad = 2;
-    if (dvi_buf_size % 8 != 0)
-        bad = 3;
-    if (hash_prime > hash_size)
-        bad = 5;
-    if (max_in_open >= (sup_max_in_open+1)) /* 128 */
-        bad = 6;
-    /*tex
-        Here are the inequalities that the quarterword and halfword values
-        must satisfy (or rather, the inequalities that they mustn't satisfy):
-    */
-    if ((min_quarterword > 0) || (max_quarterword < 0x7FFF))
-        bad = 11;
-    if ((min_halfword > 0) || (max_halfword < 0x3FFFFFFF))
-        bad = 12;
-    if ((min_quarterword < min_halfword) || (max_quarterword > max_halfword))
-        bad = 13;
-    if (font_base < min_quarterword)
-        bad = 15;
-    if ((save_size > max_halfword) || (max_strings > max_halfword))
-        bad = 17;
-    if (buf_size > max_halfword)
-        bad = 18;
-    if (max_quarterword - min_quarterword < 0xFFFF)
-        bad = 19;
-    if (cs_token_flag + eqtb_size + hash_extra > max_halfword)
-        bad = 21;
-    if (bad > 0) {
-        wterm_cr();
-        fprintf(term_out,
-            "Ouch---my internal constants have been clobbered! ---case %d",
-            (int) bad
-        );
-    } else {
-        /*tex Set global variables to their starting values. */
-        initialize();
-        if (ini_version) {
-            /*tex Initialize all the primitives. */
-            no_new_control_sequence = false;
-            first = 0;
-            initialize_commands();
-            initialize_etex_commands();
-            init_str_ptr = str_ptr;
-            no_new_control_sequence = true;
-            fix_date_and_time();
-        }
-        ready_already = 314159;
-    }
-    return bad;
-}
-
-
-void main_body(void)
-{
-    static char pdftex_map[] = "pdftex.map";
-    int bad = main_initialize();
-    /*tex in case we quit during initialization */
-    history = fatal_error_stop;
-    /*tex open the terminal for output */
-    t_open_out();
-    if (!luainit)
-        tracefilenames = true;
-    if (bad > 0) {
-        goto FINAL_END;
-    }
-    print_banner(luatex_version_string);
-    /*tex
-        Get the first line of input and prepare to start When we begin the
-        following code, \TeX's tables may still contain garbage; the strings
-        might not even be present. Thus we must proceed cautiously to get
-        bootstrapped in.
-
-        But when we finish this part of the program, \TeX\ is ready to call on
-        the |main_control| routine to do its work.
-    */
-    /*tex
-        This copies the command line,
-    */
-    initialize_inputstack();
-    if (buffer[iloc] == '*')
-        incr(iloc);
-    if ((format_ident == 0) || (buffer[iloc] == '&') || dump_line) {
-        char *fname = NULL;
-        if (format_ident != 0 && !ini_version) {
-            /*tex Erase preloaded format. */
-            initialize();
-        }
-        if ((fname = open_fmt_file()) == NULL)
-            goto FINAL_END;
-        if (!load_fmt_file(fname)) {
-            zwclose(fmt_file);
-            goto FINAL_END;
-        }
-        zwclose(fmt_file);
-        while ((iloc < ilimit) && (buffer[iloc] == ' '))
-            incr(iloc);
-    }
-    if (output_mode_option != 0)
-        output_mode_par = output_mode_value;
-    if (draft_mode_option != 0) {
-        draft_mode_par = draft_mode_value;
-    }
-    /*tex can this be moved? */
-    pdf_init_map_file((char *) pdftex_map);
-    /* */
-    if (end_line_char_inactive)
-        decr(ilimit);
-    else
-        buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
-    fix_date_and_time();
-    random_seed = (microseconds * 1000) + (epochseconds % 1000000);
-    init_randoms(random_seed);
-    initialize_math();
-    fixup_selector(log_opened_global);
-    check_texconfig_init();
-    if ((iloc < ilimit) && (get_cat_code(cat_code_table_par, buffer[iloc]) != escape_cmd)) {
-        /*tex \.{\\input} assumed */
-        start_input();
-    }
-    /*tex Initialize |text_dir_ptr| */
-    text_dir_ptr = new_dir(0);
-    /*tex Ready to go! */
-    history = spotless;
-    /*tex Initialize synctex primitive */
-    synctexinitcommand();
-    /*tex Come to life. */
-    main_control();
-    flush_node(text_dir_ptr);
-    /*tex Prepare for death. */
-    final_cleanup();
-    close_files_and_terminate();
-  FINAL_END:
-    do_final_end();
-}
-
-/*tex
-
-Here we do whatever is needed to complete \TeX's job gracefully on the local
-operating system. The code here might come into play after a fatal error; it must
-therefore consist entirely of ``safe'' operations that cannot produce error
-messages. For example, it would be a mistake to call |str_room| or |make_string|
-at this time, because a call on |overflow| might lead to an infinite loop.
-@^system dependencies@>
-
-Actually there's one way to get error messages, via |prepare_mag|; but that can't
-cause infinite recursion. @^recursion@>
-
-This program doesn't bother to close the input files that may still be open.
-
-*/
-
-void close_files_and_terminate(void)
-{
-    int callback_id;
-    callback_id = callback_defined(stop_run_callback);
-    finalize_write_files();
-    if (tracing_stats_par > 0) {
-        if (callback_id == 0) {
-            /*tex
-                Output statistics about this job. The present section goes
-                directly to the log file instead of using |print| commands,
-                because there's no need for these strings to take up
-                |string_pool| memory when a non-{\bf stat} version of \TeX\ is
-                being used.
-            */
-            if (log_opened_global) {
-                fprintf(log_file,
-                    "\n\nHere is how much of LuaTeX's memory you used:\n"
-                );
-                fprintf(log_file, " %d string%s out of %d\n",
-                    (int) (str_ptr - init_str_ptr),
-                    (str_ptr == (init_str_ptr + 1) ? "" : "s"),
-                    (int) (max_strings - init_str_ptr + STRING_OFFSET)
-                );
-                fprintf(log_file, " %d,%d words of node,token memory allocated",
-                    (int) var_mem_max, (int) fix_mem_max
-                );
-                print_node_mem_stats();
-                fprintf(log_file,
-                    " %d multiletter control sequences out of %ld+%d\n",
-                    (int) cs_count, (long) hash_size, (int) hash_extra
-                );
-                fprintf(log_file, " %d font%s using %d bytes\n",
-                    (int) max_font_id(), (max_font_id() == 1 ? "" : "s"),
-                    (int) font_bytes
-                );
-                fprintf(log_file,
-                    " %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds\n",
-                    (int) max_in_stack, (int) max_nest_stack,
-                    (int) max_param_stack, (int) max_buf_stack,
-                    (int) max_save_stack + 6, (int) stack_size,
-                    (int) nest_size, (int) param_size, (int) buf_size,
-                    (int) save_size
-                );
-            }
-        }
-    }
-    wake_up_terminal();
-    /*tex
-        Rubish, these \PDF arguments, passed, needs to be fixed, e.g. with a
-        dummy in \DVI.
-    */
-    wrapup_backend();
-    /*tex
-        Close {\sl Sync\TeX} file and write status.
-    */
-    synctexterminate(log_opened_global);
-    /*tex
-        The following is needed because synctex removes files and we want to keep
-        them which means renaming a temp file .. we can't bypass the terminate
-        because it might do mem cleanup.
-    */
-    if (synctex_get_mode() > 0) {
-        callback_id = callback_defined(finish_synctex_callback);
-        if (callback_id > 0) {
-            run_callback(callback_id, "->");
-        }
-    }
-    /* free_text_codes(); */
-    /* free_math_codes(); */
-    if (log_opened_global) {
-        wlog_cr();
-        selector = selector - 2;
-        if ((selector == term_only) && (callback_id == 0)) {
-            tprint_nl("Transcript written on ");
-            tprint_file_name(NULL, texmf_log_name, NULL);
-            print_char('.');
-            print_ln();
-        }
-        lua_a_close_out(log_file);
-    }
-    callback_id = callback_defined(wrapup_run_callback);
-    if (callback_id > 0) {
-        run_callback(callback_id, "->");
-    }
-    free_text_codes();
-    free_math_codes();
-}
-
-/*tex
-
-We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has been
-scanned and |its_all_over|.
-
-*/
-
-void final_cleanup(void)
-{
-    /*tex This one gets the value 0 for \.{\\end}, 1 for \.{\\dump}. */
-    int c;
-    /*tex Here's one for looping marks: */
-    halfword i;
-    /*tex This was a global temp_ptr: */
-    halfword t;
-    c = cur_chr;
-    if (job_name == 0)
-        open_log_file();
-    while (input_ptr > 0)
-        if (istate == token_list)
-            end_token_list();
-        else
-            end_file_reading();
-    while (open_parens > 0) {
-        report_stop_file(filetype_tex);
-        decr(open_parens);
-    }
-    if (cur_level > level_one) {
-        tprint_nl("(\\end occurred inside a group at level ");
-        print_int(cur_level - level_one);
-        print_char(')');
-        show_save_groups();
-    }
-    while (cond_ptr != null) {
-        tprint_nl("(\\end occurred when ");
-        print_cmd_chr(if_test_cmd, cur_if);
-        if (if_line != 0) {
-            tprint(" on line ");
-            print_int(if_line);
-        }
-        tprint(" was incomplete)");
-        if_line = if_line_field(cond_ptr);
-        cur_if = subtype(cond_ptr);
-        t = cond_ptr;
-        cond_ptr = vlink(cond_ptr);
-        flush_node(t);
-    }
-    if (callback_defined(stop_run_callback) == 0)
-        if (history != spotless)
-            if ((history == warning_issued) || (interaction < error_stop_mode))
-                if (selector == term_and_log) {
-                    selector = term_only;
-                    tprint_nl("(see the transcript file for additional information)");
-                    selector = term_and_log;
-                }
-    if (c == 1) {
-        if (ini_version) {
-            for (i = 0; i <= biggest_used_mark; i++) {
-                delete_top_mark(i);
-                delete_first_mark(i);
-                delete_bot_mark(i);
-                delete_split_first_mark(i);
-                delete_split_bot_mark(i);
-            }
-            for (c = last_box_code; c <= vsplit_code; c++)
-                flush_node_list(disc_ptr[c]);
-            if (last_glue != max_halfword) {
-                flush_node(last_glue);
-            }
-            /*tex Flush the pseudo files. */
-            while (pseudo_files != null) {
-                pseudo_close();
-            }
-            store_fmt_file();
-            return;
-        }
-        tprint_nl("(\\dump is performed only by INITEX)");
-        return;
-    }
-}
-
-/*tex
-
-Once \TeX\ is working, you should be able to diagnose most errors with the
-\.{\\show} commands and other diagnostic features.
-
-Because we have made some internal changes the optional debug interface
-has been removed.
-
-*/
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/mainbody.w
@@ -0,0 +1,708 @@
+% mainbody.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}
+\def\Aleph{Aleph}
+\def\pdfTeX{pdf\TeX}
+
+@ @c
+
+
+#include "ptexlib.h"
+
+@
+pdfTeX is copyright (C) 1996-2006 Han The Thanh, <thanh@@pdftex.org>.
+
+e-TeX is copyright (C) 1994,98 by Peter Breitenlohner.
+
+This is LuaTeX, a continuation of $\pdfTeX$ and $\Aleph$.  LuaTeX is a
+document compiler intended to simplify high-quality typesetting for
+many of the world's languages.  It is an extension of D. E. Knuth's
+\TeX, which was designed essentially for the typesetting of languages
+using the Latin alphabet.
+
+The $\Aleph$ subsystem loosens many of the restrictions imposed by~\TeX:
+register numbers are no longer limited to 8~bits;  fonts may have more
+than 256~characters;  more than 256~fonts may be used;  etc.
+
+% This program is directly derived from Donald E. Knuth's TeX;
+% the change history which follows and the reward offered for finders of
+% bugs refer specifically to TeX; they should not be taken as referring
+% to LuaTeX, pdfTeX, nor e-TeX, although the change history is relevant in that it
+% demonstrates the evolutionary path followed.  This program is not TeX;
+% that name is reserved strictly for the program which is the creation
+% and sole responsibility of Professor Knuth.
+
+% Version 0 was released in September 1982 after it passed a variety of tests.
+% Version 1 was released in November 1983 after thorough testing.
+% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984).
+% Version 1.2 allowed `0' in response to an error, et alia (October 1984).
+% Version 1.3 made memory allocation more flexible and local (November 1984).
+% Version 1.4 fixed accents right after line breaks, et alia (April 1985).
+% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985).
+% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986).
+% Version 2.1 corrected anomalies in discretionary breaks (January 1987).
+% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987).
+% Version 2.3 avoided incomplete page in premature termination (August 1987).
+% Version 2.4 fixed \noaligned rules in indented displays (August 1987).
+% Version 2.5 saved cur_order when expanding tokens (September 1987).
+% Version 2.6 added 10sp slop when shipping leaders (November 1987).
+% Version 2.7 improved rounding of negative-width characters (November 1987).
+% Version 2.8 fixed weird bug if no \patterns are used (December 1987).
+% Version 2.9 made \csname\endcsname's "relax" local (December 1987).
+% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988).
+% Version 2.92 fixed \patterns, also file names with complex macros (May 1988).
+% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988).
+% Version 2.94 kept open_log_file from calling fatal_error (November 1988).
+% Version 2.95 solved that problem a better way (December 1988).
+% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989).
+% Version 2.97 corrected blunder in creating 2.95 (February 1989).
+% Version 2.98 omitted save_for_after at outer level (March 1989).
+% Version 2.99 caught $$\begingroup\halign..$$ (June 1989).
+% Version 2.991 caught .5\ifdim.6... (June 1989).
+% Version 2.992 introduced major changes for 8-bit extensions (September 1989).
+% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989).
+% Version 3.0 fixed unusual displays; was more \output robust (March 1990).
+% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990).
+% Version 3.14 fixed unprintable font names and corrected typos (March 1991).
+% Version 3.141 more of same; reconstituted ligatures better (March 1992).
+% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993).
+% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995).
+% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002).
+% Version 3.1415926 was a general cleanup with minor fixes (February 2008).
+
+
+% Although considerable effort has been expended to make the LuaTeX program
+% correct and reliable, no warranty is implied; the authors disclaim any
+% obligation or liability for damages, including but not limited to
+% special, indirect, or consequential damages arising out of or in
+% connection with the use or performance of this software. This work has
+% been a ``labor of love'' and the authors hope that users enjoy it.
+
+A large piece of software like \TeX\ has inherent complexity that cannot
+be reduced below a certain level of difficulty, although each individual
+part is fairly simple by itself. The \.{WEB} language is intended to make
+the algorithms as readable as possible, by reflecting the way the
+individual program pieces fit together and by providing the
+cross-references that connect different parts. Detailed comments about
+what is going on, and about why things were done in certain ways, have
+been liberally sprinkled throughout the program.  These comments explain
+features of the implementation, but they rarely attempt to explain the
+\TeX\ language itself, since the reader is supposed to be familiar with
+{\sl The \TeX book}.
+@.WEB@>
+@:TeXbook}{\sl The \TeX book@>
+
+The present implementation has a long ancestry, beginning in the summer
+of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded
+a prototype
+@^Plass, Michael Frederick@>
+@^Liang, Franklin Mark@>
+@^Knuth, Donald Ervin@>
+based on some specifications that the author had made in May of that year.
+This original proto\TeX\ included macro definitions and elementary
+manipulations on boxes and glue, but it did not have line-breaking,
+page-breaking, mathematical formulas, alignment routines, error recovery,
+or the present semantic nest; furthermore,
+it used character lists instead of token lists, so that a control sequence
+like \.{\\halign} was represented by a list of seven characters. A
+complete version of \TeX\ was designed and coded by the author in late
+1977 and early 1978; that program, like its prototype, was written in the
+{\mc SAIL} language, for which an excellent debugging system was
+available. Preliminary plans to convert the {\mc SAIL} code into a form
+somewhat like the present ``web'' were developed by Luis Trabb~Pardo and
+@^Trabb Pardo, Luis Isidoro@>
+the author at the beginning of 1979, and a complete implementation was
+created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which
+@^Zabala Salelles, Ignacio Andr\'es@>
+was written by the author during the latter part of 1981 and the early
+part of 1982, also incorporates ideas from the 1979 implementation of
+@^Guibas, Leonidas Ioannis@>
+@^Sedgewick, Robert@>
+@^Wyatt, Douglas Kirk@>
+\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick,
+and Douglas Wyatt at the Xerox Palo Alto Research Center.  Several hundred
+refinements were introduced into \TeX82 based on the experiences gained with
+the original implementations, so that essentially every part of the system
+has been substantially improved. After the appearance of ``Version 0'' in
+September 1982, this program benefited greatly from the comments of
+many other people, notably David~R. Fuchs and Howard~W. Trickey.
+A final revision in September 1989 extended the input character set to
+eight-bit codes and introduced the ability to hyphenate words from
+different languages, based on some ideas of Michael~J. Ferguson.
+@^Fuchs, David Raymond@>
+@^Trickey, Howard Wellington@>
+@^Ferguson, Michael John@>
+
+No doubt there still is plenty of room for improvement, but the author
+is firmly committed to keeping \TeX82 ``frozen'' from now on; stability
+and reliability are to be its main virtues.
+
+On the other hand, the \.{WEB} description can be extended without changing
+the core of \TeX82 itself, and the program has been designed so that such
+extensions are not extremely difficult to make.
+The |banner| string defined here should be changed whenever \TeX\
+undergoes any modifications, so that it will be clear which version of
+\TeX\ might be the guilty party when a problem arises.
+@^extensions to \TeX@>
+@^system dependencies@>
+
+This program contains code for various features extending \TeX,
+therefore this program is called `\eTeX' and not
+`\TeX'; the official name `\TeX' by itself is reserved
+for software systems that are fully compatible with each other.
+A special test suite called the ``\.{TRIP} test'' is available for
+helping to determine whether a particular implementation deserves to be
+known as `\TeX' [cf.~Stanford Computer Science report CS1027,
+November 1984].
+
+A similar test suite called the ``\.{e-TRIP} test'' is available for
+helping to determine whether a particular implementation deserves to be
+known as `\eTeX'.
+
+@ This is the first of many sections of \TeX\ where global variables are
+defined.
+
+@c
+boolean luainit;                /* are we using lua for initializations  */
+boolean tracefilenames;         /* print file open-close  info? */
+
+
+@ This program has two important variations: (1) There is a long and slow
+version called \.{INITEX}, which does the extra calculations needed to
+@.INITEX@>
+initialize \TeX's internal tables; and (2)~there is a shorter and faster
+production version, which cuts the initialization to a bare minimum.
+
+@c
+boolean ini_version;            /* are we \.{INITEX}? */
+boolean dump_option;            /* was the dump name option used? */
+boolean dump_line;              /* was a \.{\%\AM format} line seen? */
+int bound_default;              /* temporary for setup */
+char *bound_name;               /* temporary for setup */
+int error_line;                 /* width of context lines on terminal error messages */
+int half_error_line;            /* width of first lines of contexts in terminal
+                                   error messages; should be between 30 and |error_line-15| */
+int max_print_line;             /* width of longest text lines output; should be at least 60 */
+int max_strings;                /* maximum number of strings; must not exceed |max_halfword| */
+int strings_free;               /* strings available after format loaded */
+int font_k;                     /* loop variable for initialization */
+int buf_size;                   /* maximum number of characters simultaneously present in
+                                   current lines of open files and in control sequences between
+                                   \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword| */
+int stack_size;                 /* maximum number of simultaneous input sources */
+int max_in_open;                /* maximum number of input files and error insertions that
+                                   can be going on simultaneously */
+int param_size;                 /* maximum number of simultaneous macro parameters */
+int nest_size;                  /* maximum number of semantic levels simultaneously active */
+int save_size;                  /* space for saving values outside of current group; must be
+                                   at most |max_halfword| */
+int expand_depth;               /* limits recursive calls of the |expand| procedure */
+int parsefirstlinep;            /* parse the first line for options */
+int filelineerrorstylep;        /* format messages as file:line:error */
+int haltonerrorp;               /* stop at first error */
+boolean quoted_filename;        /* current filename is quoted */
+
+@ @c
+int get_luatexversion(void)
+{
+    return luatex_version;
+}
+
+str_number get_luatexrevision(void)
+{
+    return luatex_revision;
+}
+
+@ This is it: the part of \TeX\ that executes all those procedures we have
+written.
+
+We have noted that there are two versions of \TeX82. One, called \.{INITEX},
+@.INITEX@>
+has to be run first; it initializes everything from scratch, without
+reading a format file, and it has the capability of dumping a format file.
+The other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs
+@.VIRTEX@>
+to input a format file in order to get started.
+
+@c
+#define const_chk(A) do {			\
+	if (A < inf_##A) A = inf_##A;		\
+	if (A > sup_##A) A = sup_##A;		\
+    } while (0)
+
+#define setup_bound_var(A,B,C) do {				\
+	if (luainit>0) {					\
+	    get_lua_number("texconfig",B,&C);			\
+	    if (C==0) C=A;					\
+	} else {						\
+	    integer x;						\
+	    setupboundvariable(&x, B, A);			\
+	    C = (int)x;						\
+	}							\
+    } while (0)
+
+
+int ready_already = 0;
+
+int main_initialize(void)
+{
+    /* In case somebody has inadvertently made bad settings of the ``constants,''
+       \LuaTeX\ checks them using a variable called |bad|. */
+    int bad = 0;
+    /* Bounds that may be set from the configuration file. We want the user to
+       be able to specify the names with underscores, but \.{TANGLE} removes
+       underscores, so we're stuck giving the names twice, once as a string,
+       once as the identifier. How ugly. */
+
+    setup_bound_var(15000, "max_strings", max_strings);
+    setup_bound_var(100, "strings_free", strings_free);
+    setup_bound_var(3000, "buf_size", buf_size);
+    setup_bound_var(50, "nest_size", nest_size);
+    setup_bound_var(15, "max_in_open", max_in_open);
+    setup_bound_var(60, "param_size", param_size);
+    setup_bound_var(4000, "save_size", save_size);
+    setup_bound_var(300, "stack_size", stack_size);
+    setup_bound_var(16384, "dvi_buf_size", dvi_buf_size);
+    setup_bound_var(79, "error_line", error_line);
+    setup_bound_var(50, "half_error_line", half_error_line);
+    setup_bound_var(79, "max_print_line", max_print_line);
+    setup_bound_var(0, "hash_extra", hash_extra);
+    setup_bound_var(72, "pk_dpi", pk_dpi);
+    setup_bound_var(10000, "expand_depth", expand_depth);
+
+    /* Check other constants against their sup and inf. */
+    const_chk(buf_size);
+    const_chk(nest_size);
+    const_chk(max_in_open);
+    const_chk(param_size);
+    const_chk(save_size);
+    const_chk(stack_size);
+    const_chk(dvi_buf_size);
+    const_chk(max_strings);
+    const_chk(strings_free);
+    const_chk(hash_extra);
+    const_chk(pk_dpi);
+    if (error_line > ssup_error_line)
+        error_line = ssup_error_line;
+
+    /* array memory allocation */
+    buffer = xmallocarray(packed_ASCII_code, (unsigned) buf_size);
+    nest = xmallocarray(list_state_record, (unsigned) nest_size);
+    save_stack = xmallocarray(save_record, (unsigned) save_size);
+    input_stack = xmallocarray(in_state_record, (unsigned) stack_size);
+    input_file = xmallocarray(alpha_file, (unsigned) max_in_open);
+    input_file_callback_id = xmallocarray(int, (unsigned) max_in_open);
+    line_stack = xmallocarray(int, (unsigned) max_in_open);
+    eof_seen = xmallocarray(boolean, (unsigned) max_in_open);
+    grp_stack = xmallocarray(save_pointer, (unsigned) max_in_open);
+    if_stack = xmallocarray(pointer, (unsigned) max_in_open);
+    source_filename_stack = xmallocarray(str_number, (unsigned) max_in_open);
+    full_source_filename_stack = xmallocarray(char *, (unsigned) max_in_open);
+    param_stack = xmallocarray(halfword, (unsigned) param_size);
+    dvi_buf = xmallocarray(eight_bits, (unsigned) dvi_buf_size);
+
+    if (ini_version) {
+        fixmem = xmallocarray(smemory_word, fix_mem_init + 1);
+        memset(voidcast(fixmem), 0, (fix_mem_init + 1) * sizeof(smemory_word));
+        fix_mem_min = 0;
+        fix_mem_max = fix_mem_init;
+        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) (hash_top + 1));
+        memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1));
+        eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1));
+        memset(eqtb, 0, sizeof(memory_word) * (unsigned) (eqtb_top + 1));
+        init_string_pool_array((unsigned) max_strings);
+        reset_cur_string();
+    }
+    /* Check the ``constant'' values... */
+    if ((half_error_line < 30) || (half_error_line > error_line - 15))
+        bad = 1;
+    if (max_print_line < 60)
+        bad = 2;
+    if (dvi_buf_size % 8 != 0)
+        bad = 3;
+    if (hash_prime > hash_size)
+        bad = 5;
+    if (max_in_open >= (sup_max_in_open+1)) /* 128 */
+        bad = 6;
+    /* Here are the inequalities that the quarterword and halfword values
+       must satisfy (or rather, the inequalities that they mustn't satisfy): */
+    if ((min_quarterword > 0) || (max_quarterword < 0x7FFF))
+        bad = 11;
+    if ((min_halfword > 0) || (max_halfword < 0x3FFFFFFF))
+        bad = 12;
+    if ((min_quarterword < min_halfword) || (max_quarterword > max_halfword))
+        bad = 13;
+    if (font_base < min_quarterword)
+        bad = 15;
+    if ((save_size > max_halfword) || (max_strings > max_halfword))
+        bad = 17;
+    if (buf_size > max_halfword)
+        bad = 18;
+    if (max_quarterword - min_quarterword < 0xFFFF)
+        bad = 19;
+    if (cs_token_flag + eqtb_size + hash_extra > max_halfword)
+        bad = 21;
+    if (bad > 0) {
+        wterm_cr();
+        fprintf(term_out,
+                "Ouch---my internal constants have been clobbered! ---case %d",
+                (int) bad);
+    } else {
+        initialize();           /* set global variables to their starting values */
+        if (ini_version) {
+            /* initialize all the primitives */
+            no_new_control_sequence = false;
+            first = 0;
+            initialize_commands();
+            initialize_etex_commands();
+            init_str_ptr = str_ptr;
+            no_new_control_sequence = true;
+            fix_date_and_time();
+        }
+        ready_already = 314159;
+    }
+    return bad;
+}
+
+@ @c
+void main_body(void)
+{
+    static char pdftex_map[] = "pdftex.map";
+    int bad = main_initialize();
+    history = fatal_error_stop; /* in case we quit during initialization */
+    t_open_out();               /* open the terminal for output */
+    if (!luainit)
+        tracefilenames = true;
+    if (bad > 0) {
+        goto FINAL_END;
+    }
+    print_banner(luatex_version_string);
+
+    /* Get the first line of input and prepare to start */
+    /* When we begin the following code, \TeX's tables may still contain garbage;
+       the strings might not even be present. Thus we must proceed cautiously to get
+       bootstrapped in.
+
+       But when we finish this part of the program, \TeX\ is ready to call on the
+       |main_control| routine to do its work.
+     */
+    initialize_inputstack();    /* this copies the command-line */
+    if (buffer[iloc] == '*')
+        incr(iloc);
+    if ((format_ident == 0) || (buffer[iloc] == '&') || dump_line) {
+        char *fname = NULL;
+        if (format_ident != 0 && !ini_version)
+            initialize();       /* erase preloaded format */
+        if ((fname = open_fmt_file()) == NULL)
+            goto FINAL_END;
+        if (!load_fmt_file(fname)) {
+            zwclose(fmt_file);
+            goto FINAL_END;
+        }
+        zwclose(fmt_file);
+        while ((iloc < ilimit) && (buffer[iloc] == ' '))
+            incr(iloc);
+    }
+    if (output_mode_option != 0)
+        output_mode_par = output_mode_value;
+    if (draft_mode_option != 0) {
+        draft_mode_par = draft_mode_value;
+    }
+    /* can this be moved? */
+    pdf_init_map_file((char *) pdftex_map);
+    /* */
+    if (end_line_char_inactive)
+        decr(ilimit);
+    else
+        buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+    fix_date_and_time();
+    random_seed = (microseconds * 1000) + (epochseconds % 1000000);
+    init_randoms(random_seed);
+    initialize_math();
+    fixup_selector(log_opened_global);
+    check_texconfig_init();
+    if ((iloc < ilimit) && (get_cat_code(cat_code_table_par, buffer[iloc]) != escape_cmd))
+        start_input();          /* \.{\\input} assumed */
+    /* DIR: Initialize |text_dir_ptr| */
+    text_dir_ptr = new_dir(0);
+
+    history = spotless;         /* ready to go! */
+    /* Initialize synctex primitive */
+    synctexinitcommand();
+    main_control();             /* come to life */
+    flush_node(text_dir_ptr);
+    final_cleanup();            /* prepare for death */
+    close_files_and_terminate();
+  FINAL_END:
+    do_final_end();
+}
+
+
+@ Here we do whatever is needed to complete \TeX's job gracefully on the
+local operating system. The code here might come into play after a fatal
+error; it must therefore consist entirely of ``safe'' operations that
+cannot produce error messages. For example, it would be a mistake to call
+|str_room| or |make_string| at this time, because a call on |overflow|
+might lead to an infinite loop.
+@^system dependencies@>
+
+Actually there's one way to get error messages, via |prepare_mag|;
+but that can't cause infinite recursion.
+@^recursion@>
+
+This program doesn't bother to close the input files that may still be open.
+
+@c
+void close_files_and_terminate(void)
+{
+    int callback_id;
+    callback_id = callback_defined(stop_run_callback);
+    finalize_write_files();
+    if (tracing_stats_par > 0) {
+        if (callback_id == 0) {
+            /* Output statistics about this job */
+            /* The present section goes directly to the log file instead of using
+               |print| commands, because there's no need for these strings to take
+               up |string_pool| memory when a non-{\bf stat} version of \TeX\ is being used.
+             */
+
+            if (log_opened_global) {
+                fprintf(log_file,
+                        "\n\nHere is how much of LuaTeX's memory you used:\n");
+                fprintf(log_file, " %d string%s out of %d\n",
+                        (int) (str_ptr - init_str_ptr),
+                        (str_ptr == (init_str_ptr + 1) ? "" : "s"),
+                        (int) (max_strings - init_str_ptr + STRING_OFFSET));
+                fprintf(log_file, " %d,%d words of node,token memory allocated",
+                        (int) var_mem_max, (int) fix_mem_max);
+                print_node_mem_stats();
+                fprintf(log_file,
+                        " %d multiletter control sequences out of %ld+%d\n",
+                        (int) cs_count, (long) hash_size, (int) hash_extra);
+                fprintf(log_file, " %d font%s using %d bytes\n",
+                        (int) max_font_id(), (max_font_id() == 1 ? "" : "s"),
+                        (int) font_bytes);
+                fprintf(log_file,
+                        " %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds\n",
+                        (int) max_in_stack, (int) max_nest_stack,
+                        (int) max_param_stack, (int) max_buf_stack,
+                        (int) max_save_stack + 6, (int) stack_size,
+                        (int) nest_size, (int) param_size, (int) buf_size,
+                        (int) save_size);
+            }
+        }
+    }
+    wake_up_terminal();
+    /* rubish, these pdf arguments, passed, needs to be fixed, e.g. with a dummy in dvi */
+    wrapup_backend();
+    /* Close {\sl Sync\TeX} file and write status */
+    synctexterminate(log_opened_global);
+    /*
+        The following is needed because synctex removes files and we want to keep them which
+        means renaming a temp file .. we can't bypass the terminate because it might do mem
+        cleanup.
+    */
+    if (synctex_get_mode() > 0) {
+        callback_id = callback_defined(finish_synctex_callback);
+        if (callback_id > 0) {
+            run_callback(callback_id, "->");
+        }
+    }
+    free_text_codes();
+    free_math_codes();
+    if (log_opened_global) {
+        wlog_cr();
+        selector = selector - 2;
+        if ((selector == term_only) && (callback_id == 0)) {
+            tprint_nl("Transcript written on ");
+            tprint_file_name(NULL, texmf_log_name, NULL);
+            print_char('.');
+            print_ln();
+        }
+        lua_a_close_out(log_file);
+    }
+}
+
+
+@ We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has
+been scanned and |its_all_over|\kern-2pt.
+
+@c
+void final_cleanup(void)
+{
+    int c;                      /* 0 for \.{\\end}, 1 for \.{\\dump} */
+    halfword i;                 /* for looping marks  */
+    halfword t;                 /* was a global temp_ptr */
+    c = cur_chr;
+    if (job_name == 0)
+        open_log_file();
+    while (input_ptr > 0)
+        if (istate == token_list)
+            end_token_list();
+        else
+            end_file_reading();
+    while (open_parens > 0) {
+        report_stop_file(filetype_tex);
+        decr(open_parens);
+    }
+    if (cur_level > level_one) {
+        tprint_nl("(\\end occurred inside a group at level ");
+        print_int(cur_level - level_one);
+        print_char(')');
+        show_save_groups();
+    }
+    while (cond_ptr != null) {
+        tprint_nl("(\\end occurred when ");
+        print_cmd_chr(if_test_cmd, cur_if);
+        if (if_line != 0) {
+            tprint(" on line ");
+            print_int(if_line);
+        }
+        tprint(" was incomplete)");
+        if_line = if_line_field(cond_ptr);
+        cur_if = subtype(cond_ptr);
+        t = cond_ptr;
+        cond_ptr = vlink(cond_ptr);
+        flush_node(t);
+    }
+    if (callback_defined(stop_run_callback) == 0)
+        if (history != spotless)
+            if ((history == warning_issued) || (interaction < error_stop_mode))
+                if (selector == term_and_log) {
+                    selector = term_only;
+                    tprint_nl("(see the transcript file for additional information)");
+                    selector = term_and_log;
+                }
+    if (c == 1) {
+        if (ini_version) {
+            for (i = 0; i <= biggest_used_mark; i++) {
+                delete_top_mark(i);
+                delete_first_mark(i);
+                delete_bot_mark(i);
+                delete_split_first_mark(i);
+                delete_split_bot_mark(i);
+            }
+            for (c = last_box_code; c <= vsplit_code; c++)
+                flush_node_list(disc_ptr[c]);
+            if (last_glue != max_halfword) {
+                flush_node(last_glue);
+            }
+            while (pseudo_files != null)
+                pseudo_close(); /* flush pseudo files */
+            store_fmt_file();
+            return;
+        }
+        tprint_nl("(\\dump is performed only by INITEX)");
+        return;
+    }
+}
+
+@ Once \TeX\ is working, you should be able to diagnose most errors with
+the \.{\\show} commands and other diagnostic features.
+An additional routine called |debug_help|
+will come into play when you type `\.D' after an error message;
+|debug_help| also occurs just before a fatal error causes \TeX\ to succumb.
+@^debugging@>
+@^system dependencies@>
+
+The interface to |debug_help| is primitive, but it is good enough when used
+with a debugger that allows you to set breakpoints and to read
+variables and change their values. After getting the prompt `\.{debug \#}', you
+type either a negative number (this exits |debug_help|), or zero (this
+goes to a location where you can set a breakpoint, thereby entering into
+dialog with the debugger), or a positive number |m| followed by
+an argument |n|. The meaning of |m| and |n| will be clear from the
+program below. (If |m=13|, there is an additional argument, |l|.)
+@.debug \#@>
+
+@c
+#ifdef DEBUG
+void debug_help(void)
+{                               /* routine to display various things */
+    int k;
+    int m = 0, n = 0, l = 0;
+    while (1) {
+        wake_up_terminal();
+        tprint_nl("debug # (-1 to exit):");
+        update_terminal();
+        (void) fscanf(term_in, "%d", &m);
+        if (m < 0)
+            return;
+        else if (m == 0)
+            abort();            /* go to every label at least once */
+        else {
+            (void) fscanf(term_in, "%d", &n);
+            switch (m) {
+            case 1:
+                print_word(varmem[n]);  /* display |varmem[n]| in all forms */
+                break;
+            case 2:
+                print_int(info(n));
+                break;
+            case 3:
+                print_int(link(n));
+                break;
+            case 4:
+                print_word(eqtb[n]);
+                break;
+            case 6:
+                print_int(save_type(n));
+                print_int(save_level(n));
+                print_word(save_word(n));
+                break;
+            case 7:
+                show_box(n);    /* show a box, abbreviated by |show_box_depth| and |show_box_breadth| */
+                break;
+            case 8:
+                breadth_max = 10000;
+                depth_threshold = 0x7FFFFFFF;
+                show_node_list(n);      /* show a box in its entirety */
+                break;
+            case 9:
+                show_token_list(n, null, 1000);
+                break;
+            case 10:
+                print(n);
+                break;
+            case 13:
+                (void) fscanf(term_in, "%d", &l);
+                print_cmd_chr(n, l);
+                break;
+            case 14:
+                for (k = 0; k <= n; k++)
+                    print(buffer[k]);
+                break;
+            case 15:
+                font_in_short_display = null_font;
+                short_display(n);
+                break;
+            default:
+                tprint("?");
+                break;
+            }
+        }
+    }
+}
+#endif
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/maincontrol.w
@@ -0,0 +1,3670 @@
+% 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(no_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 + no_super_sub_script_cmd] = no_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 if (cur_chr == automatic_disc) {
+        /* - as done in hyphenator */
+        c = get_pre_exhyphen_char(cur_lang_par);
+        if (c <= 0) {
+            c = ex_hyphen_char_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_exhyphen_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));
+        }
+        c = ex_hyphen_char_par;
+        if (c > 0) {
+            vlink(no_break(tail)) = new_char(equiv(cur_font_loc), c);
+            alink(vlink(no_break(tail))) = no_break(tail);
+            tlink(no_break(tail)) = vlink(no_break(tail));
+        }
+        set_automatic_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;
+        pre_bin_op_penalty_par = inf_penalty;
+        math_script_box_mode_par = 1;
+        pre_rel_penalty_par = inf_penalty;
+        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);
+        /* bah */
+        set_pdf_major_version(1);
+        set_pdf_minor_version(0);
+    }
+    synctexoffset = int_base + synctex_code;
+
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/maincontrol.c
+++ /dev/null
@@ -1,4205 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-#define mode     mode_par
-#define tail     tail_par
-#define head     head_par
-#define dir_save dirs_par
-
-/*tex
-
-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.
-
-*/
-
-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;
-    }
-}
-
-/*tex
-
-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.
-
-*/
-
-#define goto_next 0
-#define goto_skip_token 1
-#define goto_return 2
-
-static int main_control_state;
-static int local_level = 0;
-
-/*tex
-
-@* 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.
-
-*/
-
-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));
-}
-
-static void run_node (void) {
-    halfword n = cur_chr;
-    if (copy_lua_input_nodes_par) {
-        n = copy_node_list(n);
-    }
-    tail_append(n);
-    while (vlink(n) != null) {
-        n = vlink(n);
-        tail_append(n);
-    }
-}
-
-static void run_lua_call(void) {
-    if (cur_chr <= 0) {
-        normal_error("luacall", "invalid number");
-    } else {
-        str_number u = save_cur_string();
-        luacstrings = 0;
-        luafunctioncall(cur_chr);
-        restore_cur_string(u);
-        if (luacstrings > 0)
-            lua_string_start();
-    }
-}
-
-/*tex
-
-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.
-
-*/
-
-static void run_app_space (void) {
-    halfword p; /* was a global temp_ptr */
-    int method = disable_space_par ;
-    if (method == 1) {
-        /*tex 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 {
-        /*tex Append a normal inter-word space to the current list. */
-        if (glue_is_zero(space_skip_par)) {
-            /*tex 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);
-        }
-        /*tex So from now we have a subtype with spaces: */
-        subtype(p) = space_skip_code + 1 ;
-        couple_nodes(tail,p);
-        tail = p;
-    }
-}
-
-/*tex
-
-Append a |boundary_node|
-
-*/
-
-static void run_boundary (void) {
-    halfword n ;
-    n = new_node(boundary_node,cur_chr);
-    if ((cur_chr == 1) || (cur_chr == 2) ) {
-        /*tex We expect a user boundary or protrusion boundary. */
-        scan_int();
-        boundary_value(n) = cur_val;
-    }
-    couple_nodes(tail, n);
-    tail = n;
-}
-
-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);
-    }
-}
-
-static void run_relax (void) {
-    return;
-}
-
-/*tex
-
-|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.
-
-*/
-
-static void run_ignore_spaces (void) {
-    if (cur_chr == 0) {
-        /*tex 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;
-        }
-    }
-}
-
-/*tex
-
-|stop| is the second special case. We want |main_control| to return to its caller
-if there is nothing left to do.
-
-*/
-
-static void run_stop (void) {
-    if (its_all_over()) {
-        /*tex this is the only way out */
-        main_control_state= goto_return;
-    }
-}
-
-static void run_non_math_math (void) {
-    back_input();
-    new_graf(true);
-}
-
-/*tex
-
-    We build up an argument to |set_math_char|:
-
-*/
-
-static void run_math_char_num (void) {
-    mathcodeval mval;
-    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);
-}
-
-/*tex
-
-    We build up an argument to |set_math_char|:
-*/
-
-static void run_math_given (void) {
-    mathcodeval mval;
-    mval = mathchar_from_integer(cur_chr, tex_mathcode);
-    math_char_in_text(mval);
-}
-
-/*tex
-
-    We build up an argument to |set_math_char| the \LUATEX\ way:
-*/
-
-static void run_xmath_given (void) {
-    mathcodeval mval;
-    mval = mathchar_from_integer(cur_chr, umath_mathcode);
-    math_char_in_text(mval);
-}
-
-/*tex
-
-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|.
-
-*/
-
-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;
-}
-
-/*tex
-
-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.
-
-*/
-
-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();
-    }
-}
-
-/*tex
-
-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|.
-
-*/
-
-static void run_move (void) {
-    int t = cur_chr;
-    scan_normal_dimen();
-    if (t == 0)
-        scan_box(cur_val);
-    else
-        scan_box(-cur_val);
-}
-
-static void run_leader_ship (void) {
-    scan_box(leader_flag - a_leaders + cur_chr);
-}
-
-static void run_make_box (void) {
-    begin_box(0);
-}
-
-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;
-}
-
-static void run_box_direction (void) {
-    scan_register_num();
-    cur_box = box(cur_val);
-    scan_optional_equals();
-    scan_int();
-    check_dir_value(cur_val);
-    if (cur_box != null)
-        box_dir(cur_box) = cur_val;
-}
-
-/*tex
-
-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}'.
-
-*/
-
-static void run_start_par_vmode (void) {
-    new_graf((cur_chr > 0));
-}
-
-static void run_start_par (void) {
-   if (cur_chr != 2)
-       indent_in_hmode();
-}
-
-static void run_new_graf (void) {
-   back_input();
-   new_graf(true);
-}
-
-/*tex
-
-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}.
-
-*/
-
-static void run_par_end_vmode (void) {
-    normal_paragraph();
-    if (mode > 0) {
-        checked_page_filter(vmode_par);
-        build_page();
-    }
-}
-
-static void run_par_end_hmode (void) {
-    if (align_state < 0) {
-        /*tex This tries to recover from an alignment that didn't end properly. */
-        off_save();
-    }
-    /* This takes us to the enclosing mode, if |mode>0|. */
-    end_graf(bottom_level);
-    if (mode == vmode) {
-        checked_page_filter(hmode_par);
-        build_page();
-    }
-}
-
-static void append_italic_correction_mmode (void) {
-    tail_append(new_kern(0));
-}
-
-static void run_local_box (void) {
-    append_local_box(cur_chr);
-}
-
-static void run_halign_mmode (void) {
-    if (privileged()) {
-        if (cur_group == math_shift_group)
-            init_align();
-        else
-            off_save();
-    }
-}
-
-static void run_eq_no (void) {
-    if (privileged()) {
-        if (cur_group == math_shift_group)
-            start_eq_no();
-        else
-            off_save();
-    }
-}
-
-static void run_letter_mmode (void) {
-   set_math_char(get_math_code(cur_chr));
-}
-
-static void run_char_num_mmode (void) {
-    scan_char_num();
-    cur_chr = cur_val;
-    set_math_char(get_math_code(cur_chr));
-}
-
-static void run_math_char_num_mmode (void) {
-    mathcodeval mval;
-    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);
-}
-
-static void run_math_given_mmode (void) {
-    mathcodeval mval;
-    mval = mathchar_from_integer(cur_chr, tex_mathcode);
-    set_math_char(mval);
-}
-
-static void run_xmath_given_mmode (void) {
-    mathcodeval mval;
-    mval = mathchar_from_integer(cur_chr, umath_mathcode);
-    set_math_char(mval);
-}
-
-static void run_delim_num (void) {
-    mathcodeval mval;
-    if (cur_chr == 0)
-        mval = scan_delimiter_as_mathchar(tex_mathcode);
-    else
-        mval = scan_delimiter_as_mathchar(umath_mathcode);
-    set_math_char(mval);
-}
-
-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);
-}
-
-static void run_math_style (void) {
-    tail_append(new_style((small_number) cur_chr));
-}
-
-static void run_non_script (void) {
-    tail_append(new_glue(zero_glue));
-    subtype(tail) = cond_math_glue;
-}
-
-static void run_math_choice (void) {
-    if (cur_chr == 0)
-        append_choices();
-    else
-        setup_math_style();
-}
-
-static void run_math_shift (void) {
-    if (cur_group == math_shift_group)
-        after_math();
-    else
-        off_save();
-}
-
-static void run_after_assignment (void) {
-    get_token();
-    after_token = cur_tok;
-}
-
-static void run_after_group (void) {
-    get_token();
-    save_for_after(cur_tok);
-}
-
-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 late_lua_call_code:
-            new_whatsit(late_lua_node);
-            late_lua_type(tail) = lua_refid_call;
-            scan_int();
-            late_lua_data(tail) = cur_val;
-            break;
-        case expand_font_code:
-            read_expand_font();
-            break;
-        default:
-            confusion("int1");
-            break;
-        }
-    }
-}
-
-/*tex
-
-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("umathcodemeaning")) {
-                mathoption_set_int(c_mathoption_umathcode_meaning_code);
-            */
-            } else {
-                normal_warning("mathoption","unknown key");
-            }
-            break;
-        default:
-            /* harmless */
-            break;
-    }
-}
-
-static void lua_function_call(void) {
-    scan_int();
-    if (cur_val <= 0) {
-        normal_error("luafunctioncall", "invalid number");
-    } else {
-        str_number u = save_cur_string();
-        luacstrings = 0;
-        luafunctioncall(cur_val);
-        restore_cur_string(u);
-        if (luacstrings > 0)
-            lua_string_start();
-    }
-}
-
-static void lua_bytecode_call(void) {
-    scan_int();
-    if (cur_val < 0 || cur_val > 65535) {
-        normal_error("luabytecodecall", "invalid number");
-    } else {
-        str_number u = save_cur_string();
-        luacstrings = 0;
-        luabytecodecall(cur_val);
-        restore_cur_string(u);
-        if (luacstrings > 0)
-            lua_string_start();
-    }
-}
-
-/*tex
-
-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.
-
-*/
-
-#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;
-
-/*tex
-
-The |main_control| uses a jump table, and |init_main_control| sets that table up.
-
-*/
-
-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(no_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);
-    any_mode(assign_box_direction_cmd, run_box_direction);
-    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 + no_super_sub_script_cmd] = no_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_direction_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);
-
-    any_mode(lua_function_call_cmd, lua_function_call);
-    any_mode(lua_bytecode_call_cmd, lua_bytecode_call);
-    any_mode(def_lua_call_cmd, prefixed_command);
-    any_mode(lua_call_cmd, run_lua_call);
- /* any_mode(lua_expandable_call_cmd, run_lua_call); */ /* no! outside jump table anyway, handled in expand() */
-    any_mode(node_cmd, run_node);
-
-}
-
-/*tex
-
-    And here is |main_control| itself.  It is quite short nowadays.
-
-*/
-
-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;
-        } else {
-            get_x_token();
-        }
-        /*tex
-            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();
-        }
-        /*tex run the command */
-        (jump_table[(abs(mode) + cur_cmd)])();
-        if (main_control_state == goto_return) {
-            return;
-        }
-    }
-    /*tex not reached */
-    return;
-}
-
-/*tex
-
-We assume a trailing relax: |{...}\relax|, so we don't need a |back_input()| here.
-
-*/
-
-/*int local_level = 0; */
-
-extern void local_control_message(const char *s)
-{
-    tprint("local control level ");
-    print_int(local_level);
-    tprint(": ");
-    tprint(s);
-    tprint_nl("");
-}
-
-void local_control(void)
-{
-    int ll = local_level;
-    main_control_state = goto_next;
-    local_level += 1;
-    while (1) {
-        if (main_control_state == goto_skip_token) {
-            main_control_state = goto_next;
-        } else {
-            get_x_token();
-        }
-        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)])();
-        if (local_level <= ll) {
-            main_control_state = goto_next;
-            if (tracing_nesting_par > 2) {
-                local_control_message("leaving due to level change");
-            }
-            return ;
-        } else if (main_control_state == goto_return) {
-            if (tracing_nesting_par > 2) {
-                local_control_message("leaving due to triggering");
-            }
-            return;
-        }
-    }
-    return;
-}
-
-void end_local_control(void )
-{
-    local_level -= 1;
-}
-
-/*tex
-    We need to go back to the main loop. This is rather nasty and dirty
-    and counterintuive code and there might be a cleaner way. Basically
-    we trigger the main control state from here.
-
-    \starttyping
-     0 0       \directlua{token.scan_list()}\hbox{!}
-    -1 0       \setbox0\hbox{x}\directlua{token.scan_list()}\box0
-     1 1       \toks0={\directlua{token.scan_list()}\hbox{x}}\directlua{tex.runtoks(0)}
-     0 0  1 1  \directlua{tex.box[0]=token.scan_list()}\hbox{x\directlua{node.write(token.scan_list())}\hbox{x}}
-     0 0  0 1  \setbox0\hbox{x}\directlua{tex.box[0]=token.scan_list()}\hbox{x\directlua{node.write(token.scan_list())}\box0}
-    \stoptyping
-
-    It's rather fragile code so we added some tracing options.
-
-*/
-
-halfword local_scan_box(void)
-{
-    int old_mode = mode;
-    int ll = local_level;
-    mode = -hmode;
-    scan_box(lua_scan_flag);
-    if (local_level == ll) {
-        /*tex |\directlua{print(token.scan_list())}\hbox{!}| (n n) */
-        if (tracing_nesting_par > 2) {
-            local_control_message("entering at end of box scanning");
-        }
-        local_control();
-    } else {
-        /*tex |\directlua{print(token.scan_list())}\box0| (n-1 n) */
-        /*
-            if (tracing_nesting_par > 2) {
-                local_control_message("setting level after box scanning");
-            }
-        */
-        local_level = ll;
-    }
-    mode = old_mode;
-    return cur_box;
-}
-
-/*tex
-
-    We have an issue with modes when we quit here because we're coming
-    from and still staying at the \LUA\ end. So, unless we're already
-    nested, we trigger an end_local_level token (an extension code).
-
-*/
-
-static void wrapup_local_scan_box(void)
-{
-    /*
-    if (tracing_nesting_par > 2) {
-        local_control_message("leaving box scanner");
-    }
-    */
-    local_level -= 1;
-}
-
-int current_local_level(void)
-{
-    return local_level;
-}
-
-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;
-}
-
-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();
-}
-
-/*tex
-
-    We can silently ignore  \.{\\par}s in a math formula.
-
-*/
-
-void insert_dollar_sign_par_end(void)
-{
-    if (!suppress_mathpar_error_par) {
-        insert_dollar_sign() ;
-    }
-}
-
-/*tex
-
-The `|you_cant|' procedure prints a line saying that the current command is
-illegal in the current mode; it identifies these things symbolically.
-
-*/
-
-void you_cant(void)
-{
-    print_err("You can't use `");
-    print_cmd_chr((quarterword) cur_cmd, cur_chr);
-    print_in_mode(mode);
-}
-
-/*tex
-
-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.'
-
-*/
-
-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();
-}
-
-/*tex
-
-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.
-
-*/
-
-boolean privileged(void)
-{
-    if (mode > 0) {
-        return true;
-    } else {
-        report_illegal_case();
-        return false;
-    }
-}
-
-/*tex
-
-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.''
-
-*/
-
-
-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;
-}
-
-/*tex
-
-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.
-
-*/
-
-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;
-    }
-}
-
-void append_kern(void)
-{
-    int s = cur_chr; /* |subtype| of the kern node */
-    scan_dimen((s == mu_glue), false, false);
-    tail_append(new_kern(cur_val));
-    subtype(tail) = (quarterword) s;
-}
-
-/*tex
-
-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.
-
-*/
-
-void off_save(void)
-{
-    halfword p, q;
-    if (cur_group == bottom_level) {
-        /*tex 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 ");
-        /*tex
-            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();
-    }
-}
-/*tex
-
-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.
-
-*/
-
-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;
-        /*tex
-            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.
-        */
-        case hbox_group:
-            if (fixup_boxes_par) {
-                /*tex
-                    This is unofficial! Fixing up (also elsewhere) might become default
-                    some day but for a while I will test this in ConTeXt.
-                */
-                fixup_directions_only();
-            }
-            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--;
-            /*tex
-                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:
-            /*tex
-                this is needed in case the \.{\\output} executes a \.{\\textdir} command.
-            */
-            if (dir_level(text_dir_ptr) == cur_level) {
-                /*tex 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;
-    }
-}
-
-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);
-}
-
-/*tex
-
-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.
-
-*/
-
-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);
-}
-
-/*tex
-
-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.
-
-*/
-
-halfword cur_box;               /* box to be placed into its context */
-
-/*tex
-
-The |box_end| procedure does the right thing with |cur_box|, if |box_context|
-represents the context as explained above.
-
-*/
-
-void box_end(int box_context)
-{
-    if (box_context < box_flag) {
-        /*tex
-
-            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) {
-        /*tex
-            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 (box_context == lua_scan_flag) {
-        /*tex
-            We are done with scanning so let's return to the caller.
-        */
-        wrapup_local_scan_box();
-    } else if (cur_box != null) {
-        /*tex
-            The leaders contexts come after shipout and luascan contexts.
-        */
-        /* if (box_context > lua_scan_flag) { */
-        if (box_context >= leader_flag) {
-            /*tex
-                Append a new leader node that uses |cur_box| and 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 {
-            if (box_context != ship_out_flag) {
-                normal_error("scanner","shipout expected");
-            }
-            ship_out(static_pdf, cur_box, SHIPPING_PAGE);
-        }
-    }
-}
-
-/*tex
-
-the next input should specify a box or perhaps a rule
-
-*/
-
-void scan_box(int box_context)
-{
-    /*tex 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();
-        if (box_context == lua_scan_flag) {
-            cur_box = null;
-            box_end(box_context);
-        }
-    }
-}
-
-void new_graf(boolean indented)
-{
-    halfword p, q, dir_graf_tmp;
-    halfword dir_rover;
-    int callback_id;
-    if ((mode == vmode) || (head != tail)) {
-        tail_append(new_param_glue(par_skip_code));
-    }
-    callback_id = callback_defined(new_graf_callback);
-    if (callback_id > 0) {
-        run_callback(callback_id, "db->b", cur_list.mode_field,indented,&indented);
-    }
-    prev_graf_par = 0;
-    push_nest();
-    mode = hmode;
-    space_factor_par = 1000;
-    /*tex  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);
-        /*tex put |par_skip| glue on current page */
-        build_page();
-    }
-}
-
-void indent_in_hmode(void)
-{
-    halfword p;
-    if (cur_chr > 0) {
-        /*tex \.{\\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);
-    }
-}
-
-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;
-    }
-}
-
-/*tex
-
-|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 might be wrong in some way that relates to in-paragraph
-displays.
-
-*/
-
-void end_graf(int line_break_context)
-{
-    if (mode == hmode) {
-        if ((head == tail) || (vlink(head) == tail)) {
-            if (vlink(head) == tail)
-                flush_node(vlink(head));
-            /*tex |null| paragraphs are ignored, all contain a |local_paragraph| node */
-            pop_nest();
-        } else {
-            line_break(false, line_break_context);
-        }
-        if (dir_save != null) {
-            flush_node_list(dir_save);
-            dir_save = null;
-        }
-        normal_paragraph();
-        error_count = 0;
-    }
-}
-
-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;
-}
-
-/*tex
-
-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.
-
-*/
-
-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;
-    }
-}
-
-void append_penalty(void)
-{
-    scan_int();
-    tail_append(new_penalty(cur_val,user_penalty));
-    if (mode == vmode) {
-        checked_page_filter(penalty);
-        build_page();
-    }
-}
-
-/*tex
-
-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.
-
-*/
-
-void delete_last(void)
-{
-    halfword p, q;              /* run through the current list */
-    if ((mode == vmode) && (tail == head)) {
-        /*tex
-            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 {
-        /*tex 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;
-            }
-        }
-    }
-}
-
-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) {
-        /*tex 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);
-    }
-}
-
-/*tex
-
-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.
-
-*/
-
-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;
-    }
-}
-
-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;
-}
-
-/*tex
-
-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.
-
-*/
-
-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 if (cur_chr == automatic_disc) {
-        /*tex As done in hyphenator: */
-        c = get_pre_exhyphen_char(cur_lang_par);
-        if (c <= 0) {
-            c = ex_hyphen_char_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_exhyphen_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));
-        }
-        c = ex_hyphen_char_par;
-        if (c > 0) {
-            vlink(no_break(tail)) = new_char(equiv(cur_font_loc), c);
-            alink(vlink(no_break(tail))) = no_break(tail);
-            tlink(no_break(tail)) = vlink(no_break(tail));
-        }
-        set_automatic_disc_penalty(tail);
-    } else {
-        /*tex |\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;
-        /*tex Already preset: |disc_penalty(tail) = hyphen_penalty_par;| */
-    }
-}
-
-/*tex
-
-The test for |p != null| ensures that empty \.{\\localleftbox} and
-\.{\\localrightbox} commands are not applied.
-
-*/
-
-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) {
-        /*tex Somehow |filtered_hpack| goes beyond the first node so we loose it. */
-        new_hyphenation(p, null);
-        (void) new_ligkern(p, null);
-        p = lua_hpack_filter(p, 0, additional, local_box_group, -1, null);
-    }
-    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) {
-        /*tex 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);
-}
-
-/*tex
-
-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.)
-
-*/
-
-void build_discretionary(void)
-{
-    halfword p, q;              /* for link manipulation */
-    int n;                      /* length of discretionary list */
-    unsave();
-    /*tex
-        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:
-        /*tex
-            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);
-        /*tex There are no other cases. */
-        return;
-        break;
-    }
-    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;
-}
-
-/*tex
-
-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.
-
-*/
-
-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);
-        /*tex real division */
-        s = float_cast(slant(f)) / float_constant(65536);
-        a = glyph_width(p);
-        do_assignments();
-        /*tex
-            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) {
-            /*tex
-                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) {
-                /*tex the accent must be shifted up or down */
-                p = hpack(p, 0, additional, -1);
-                shift_amount(p) = x - h;
-            }
-            /*tex real multiplication */
-            delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s);
-            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;
-    }
-}
-
-/*tex
-
-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.
-
-*/
-
-void align_error(void)
-{
-    if (abs(align_state) > 2) {
-        /*tex
-            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();
-    }
-}
-
-/*tex
-
-The help messages here contain a little white lie, since \.{\\noalign} and
-\.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
-
-*/
-
-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();
-}
-
-/*tex
-
-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.
-
-*/
-
-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)");
-    /*tex interwoven alignment preambles... */
-    if (cur_group == align_group) {
-        end_graf(align_group);
-        if (fin_col())
-            fin_row();
-    } else {
-        off_save();
-    }
-}
-
-/*tex
-
-Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
-
-*/
-
-void cs_error(void)
-{
-    print_err("Extra \\endcsname");
-    help1(
-        "I'm ignoring this, since I wasn't doing a \\csname."
-    );
-    error();
-}
-
-/*tex
-
-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.
-
-*/
-
-/*tex
-
-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.
-
-*/
-
-#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)
-
-/*
-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;
-        /*tex
-            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) {
-            /*tex
-                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();
-    }
-    /*tex
-        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) && (cur_cmd != def_lua_call_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();
-    }
-    /*tex
-        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:
-            /*tex
-                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:
-            /*tex
-                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;
-            switch (n) {
-                case 0:
-                    /*tex |glet| */
-                    if (!is_global(a) && (global_defs_par >= 0)) {
-                        a = a + 4;
-                    }
-                case 1:
-                    /*tex |let| */
-                    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();
-                    }
-                    break;
-                case 2:
-                    /*tex |futurelet| */
-                    get_r_token();
-                    p = cur_cs;
-                    get_token();
-                    q = cur_tok;
-                    get_token();
-                    back_input();
-                    cur_tok = q;
-                    /*tex
-                        We look ahead and then back up. Note that |back_input| doesn't
-                        affect |cur_cmd|, |cur_chr|
-                    */
-                    back_input();
-                    break;
-                case 3:
-                    /*tex |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);
-                    }
-                    break;
-                default:
-                    /*tex We please the compiler. */
-                    p = null;
-                    confusion("let");
-                    break;
-            }
-            if (cur_cmd >= call_cmd)
-                add_token_ref(cur_chr);
-            define(p, cur_cmd, cur_chr);
-            break;
-        case shorthand_def_cmd:
-            /*tex
-                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:
-            /*tex
-                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 {
-                /*tex |p=every_par_loc| or |output_routine_loc| or \dots */
-                p = cur_chr;
-            }
-            scan_optional_equals();
-            /*tex 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) {
-                /*tex
-                    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:
-            /*tex 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_direction_cmd:
-        case assign_dir_cmd:
-            /*tex Assign direction codes. */
-            if (cur_cmd == assign_direction_cmd) {
-                p = cur_chr;
-                scan_optional_equals();
-                scan_int();
-                check_dir_value(cur_val);
-                cur_chr = p;
-            } else {
-                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) {
-                            /*tex |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);
-                                subtype(dirn) = cancel_dir;
-                                couple_nodes(prev,dirn);
-                                couple_nodes(dirn,tail);
-                            } else {
-                                tail_append(new_dir(text_direction_par));
-                                subtype(tail) = cancel_dir;
-                            }
-                        } else {
-                            /*tex 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);
-                    }
-                    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:
-            /*tex 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:
-            /*tex
-                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");
-                help3(
-                    "Sorry, \\setbox is not allowed after \\halign in a display,",
-                    "between \\accent and an accented character, or in immediate",
-                    "assignments."
-                );
-                error();
-            }
-            break;
-        case set_aux_cmd:
-            /*tex
-                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:
-            /*tex
-                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:
-            /*tex
-                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:
-            /*tex Here is where the information for a new font gets loaded. */
-            tex_def_font((small_number) a);
-            break;
-        case def_lua_call_cmd:
-            get_r_token();
-            p = cur_cs;
-            scan_optional_equals();
-            scan_int();
-            if (j != 0) {
-                define(p, lua_call_cmd, cur_val);
-            } else {
-                define(p, lua_expandable_call_cmd, cur_val);
-            }
-            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;
-    }
-    /*tex End of assignments cases. */
-  DONE:
-    /*tex Insert a token saved by \.{\\afterassignment}, if any. */
-    if (after_token != 0) {
-        cur_tok = after_token;
-        back_input();
-        after_token = 0;
-    }
-}
-
-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) {
-        /* 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) {
-            /* Add local dir node. */
-            tail_append(new_dir(text_direction_par));
-            dir_dir(tail) = temporary_dir;
-            subtype(tail) = cancel_dir;
-        }
-        if (temp_no_whatsits != 0) {
-            /*tex Add local paragraph node. */
-            tail_append(make_local_par_node(hmode_par_par_code));
-        }
-    }
-}
-
-/*tex
-
-    This is experimental and needs more checking!
-
-*/
-
-void fixup_directions_only(void)
-{
-    int temp_no_dirs = no_local_dirs_par;
-    int temporary_dir = text_direction_par;
-    if (dir_level(text_dir_ptr) == cur_level) {
-        /* Remove from |text_dir_ptr|. */
-        halfword text_dir_tmp = vlink(text_dir_ptr);
-        flush_node(text_dir_ptr);
-        text_dir_ptr = text_dir_tmp;
-    }
-    if (temp_no_dirs != 0) {
-        /* Add local dir node. */
-        tail_append(new_dir(text_direction_par));
-        dir_dir(tail) = temporary_dir;
-        subtype(tail) = cancel_dir;
-    }
-}
-
-/*tex
-
-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.
-
-*/
-
-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;
-    }
-}
-
-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;
-        }
-        /*tex
-            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)))) {
-            /*tex 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)) {
-        /*tex internal locals */
-        define(p, call_cmd, val);
-    } else {
-        confusion("assign internal value");
-    }
-}
-
-/*tex
-
-We use the fact that |register| $<$ |advance| $<$ |multiply| $<$ |divide|/ We
-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.
-
-*/
-
-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")) {
-        /*tex optional `\.{by}' */
-    }
-    arith_error = false;
-    if (q < multiply_cmd) {
-        /*tex 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 {
-            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 {
-        /*tex 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);
-    }
-}
-
-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;
-            }
-        }
-    }
-}
-
-void alter_prev_graf(void)
-{
-    int p = nest_ptr; /* index into |nest| */
-    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;
-    }
-}
-
-void alter_page_so_far(void)
-{
-    int c = cur_chr;   /* index into |page_so_far| */
-    scan_optional_equals();
-    scan_normal_dimen();
-    page_so_far[c] = cur_val;
-}
-
-/*tex
-    The value of |c| is 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc.
-*/
-
-void alter_integer(void)
-{
-    int 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;
-    }
-}
-
-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;
-}
-
-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);
-}
-
-/*tex
-
-The \.{\\afterassignment} command puts a token into the global variable
-|after_token|. This global variable is examined just after every assignment has
-been performed. It's value is zero, or a saved token.
-
-*/
-
-halfword after_token;
-
-/*tex
-
-    Here is a procedure that might be called `Get the next non-blank non-relax
-    non-call non-assignment token'.
-
-*/
-
-void do_assignments(void)
-{
-    while (true) {
-        /*tex 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;
-    }
-}
-
-/*tex |cur_chr| is 1 for \.{\\openin}, 0 for \.{\\closein}: */
-
-void open_or_close_in(void)
-{
-    int c = cur_chr;
-    int n;
-    char *fn;
-    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) {
-            /*tex Set |cur_name| to desired file name. */
-            scan_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;
-        }
-    }
-}
-
-/*tex
-    Has the long \.{\\errmessage} help been used?
-*/
-
-boolean long_help_seen;
-
-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) {
-        /*tex 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 {
-        /*tex
-            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);
-}
-
-/*tex
-
-The |error| routine calls on |give_err_help| if help is requested from the
-|err_help| parameter.
-
-*/
-
-void give_err_help(void)
-{
-    token_show(err_help_par);
-}
-
-/*tex
-
-The \.{\\uppercase} and \.{\\lowercase} commands are implemented by building a
-token list and then changing the cases of the letters in it.
-
-*/
-
-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) {
-        /*tex
-            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);
-}
-
-/*tex
-
-We come finally to the last pieces missing from |main_control|, namely the
-`\.{\\show}' commands that are useful when debugging.
-
-*/
-
-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:
-        /*tex 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:
-        /*tex 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;
-        /*tex 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:
-        /*tex
-            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;
-    }
-    /*tex 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();
-}
-
-/*tex
-    This procedure gets things started properly:
-*/
-
-void initialize(void)
-{
-    int k; /* index into |mem|, |eqtb|, etc. */
-    /*tex
-        Initialize whatever \TeX\ might access and set initial values of key
-        variables
-    */
-    initialize_errors();
-    initialize_arithmetic();
-    max_used_attr = -1;
-    attr_list_cache = cache_disabled;
-    initialize_nesting();
-    /*tex 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) {
-        /*tex Initialize table entries (done by \.{INITEX} only). */
-        init_node_mem(500);
-        initialize_tokens();
-        /*tex 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;
-        pre_bin_op_penalty_par = inf_penalty;
-        math_script_box_mode_par = 1;
-        math_script_char_mode_par = 1;
-        pre_rel_penalty_par = inf_penalty;
-        compound_hyphen_mode_par = 1;
-        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;
-        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 ;
-        math_flatten_mode_par = 1; /* ord */
-        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);
-        /*tex Bah, this is a bad place do do this. */
-        set_pdf_major_version(1);
-        set_pdf_minor_version(0);
-    }
-    synctexoffset = int_base + synctex_code;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/mathcodes.w
@@ -0,0 +1,367 @@
+% mathnodes.w
+%
+% Copyright 2006-2012 Taco Hoekwater <taco@@luatex.org>
+% Copyright 2012 Khaled Hosny <khaledhosny@@eglug.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"
+
+@ math codes
+@c
+static sa_tree mathcode_head = NULL;
+
+/* the 0xFFFFFFFF is a flag value */
+
+#define MATHCODESTACK 8
+#define MATHCODEDEFAULT 0xFFFFFFFF
+#define MATHCODEACTIVE  0xFFFFFFFE
+
+@ delcodes
+@c
+static sa_tree delcode_head = NULL;
+
+#define DELCODESTACK 4
+#define DELCODEDEFAULT 0xFFFFFFFF
+
+@ some helpers for mathcode printing
+
+@c
+#define print_hex_digit(A) do {                 \
+    if ((A)>=10) print_char('A'+(A)-10);        \
+    else print_char('0'+(A));                   \
+  } while (0)
+
+#define two_hex(A) do {       \
+    print_hex_digit((A)/16);  \
+    print_hex_digit((A)%16);  \
+  } while (0)
+
+#define four_hex(A) do {      \
+    two_hex((A)/256);         \
+    two_hex((A)%256);         \
+  } while (0)
+
+#define six_hex(A) do {       \
+    two_hex((A)/65536);       \
+    two_hex(((A)%65536)/256); \
+    two_hex((A)%256);         \
+  } while (0)
+
+/*
+    At some point we will drop the mathchardef 8 bit storage (c_mathoption_umathcode_meaning_code => 1)
+    and then some of the conversion can go away. Like mathchar_from_integer: only wide characters are
+    possible then.
+*/
+
+
+@ @c
+mathcodeval mathchar_from_integer(int value, int extcode)
+{
+    mathcodeval mval;
+    if (extcode == tex_mathcode) {
+     /* printf("can't happen: tex_mathcode\n"); */
+        mval.class_value = (value / 0x1000);
+        mval.family_value = ((value % 0x1000) / 0x100);
+        mval.character_value = (value % 0x100);
+    } else {
+        int mfam = (value / 0x200000) & 0x7FF;
+        mval.class_value = mfam % 0x08;
+        mval.family_value = mfam / 0x08;
+        mval.character_value = value & 0x1FFFFF;
+    }
+    return mval;
+}
+
+@ @c
+void show_mathcode_value_old(int value)
+{
+    print_char('"');
+    four_hex(value);
+}
+void show_mathcode_value(mathcodeval c)
+{
+    print_char('"');
+    print_hex_digit(c.class_value);
+    print_char('"');
+    two_hex(c.family_value);
+    print_char('"');
+    six_hex(c.character_value);
+}
+
+@ @c
+static void show_mathcode(int n)
+{
+    mathcodeval c = get_math_code(n);
+    tprint_esc("Umathcode");
+    print_int(n);
+    print_char('=');
+    show_mathcode_value(c);
+}
+
+@ @c
+static void unsavemathcode(quarterword gl)
+{
+    sa_stack_item st;
+    if (mathcode_head->stack == NULL)
+        return;
+    while (mathcode_head->stack_ptr > 0 && abs(mathcode_head->stack[mathcode_head->stack_ptr].level) >= gl) {
+        st = mathcode_head->stack[mathcode_head->stack_ptr];
+        if (st.level > 0) {
+            rawset_sa_item(mathcode_head, st.code, st.value);
+            if (tracing_restores_par > 1) {
+                begin_diagnostic();
+                print_char('{');
+                tprint("restoring");
+                print_char(' ');
+                show_mathcode(st.code);
+                print_char('}');
+                end_diagnostic(false);
+            }
+        }
+        (mathcode_head->stack_ptr)--;
+    }
+}
+
+@ @c
+void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword level)
+{
+    sa_tree_item v;
+    if (mathclass == 8 && mathfamily == 0 && mathcharacter == 0) {
+        v.uint_value = MATHCODEACTIVE;
+    } else {
+        v.math_code_value.class_value = mathclass;
+        v.math_code_value.family_value = mathfamily;
+        v.math_code_value.character_value = mathcharacter;
+    }
+    set_sa_item(mathcode_head, n, v, level);
+    if (tracing_assigns_par > 1) {
+        begin_diagnostic();
+        print_char('{');
+        tprint("assigning");
+        print_char(' ');
+        show_mathcode(n);
+        print_char('}');
+        end_diagnostic(false);
+    }
+}
+
+@ @c
+/* we could use two structs ... tex and umath */
+
+mathcodeval get_math_code(int n)
+{
+    mathcodeval d;
+    sa_tree_item v = get_sa_item(mathcode_head, n);
+    if (v.uint_value == MATHCODEDEFAULT) {
+        d.class_value = 0;
+        d.family_value = 0;
+        d.character_value = n;
+    } else if (v.uint_value == MATHCODEACTIVE) {
+        d.class_value = 8;
+        d.family_value = 0;
+        d.character_value = 0;
+    } else {
+        d.class_value = v.math_code_value.class_value;
+        if (d.class_value == 8) {
+            d.family_value = 0;
+            d.character_value = n;
+        } else {
+            d.family_value = v.math_code_value.family_value;
+            d.character_value = v.math_code_value.character_value;
+        }
+    }
+    return d;
+}
+
+@ @c
+int get_math_code_num(int n)
+{
+    mathcodeval d = get_math_code(n);
+    return (d.class_value + (d.family_value * 8)) * (65536 * 32) + d.character_value;
+}
+
+@ @c
+static void initializemathcode(void)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_value.uint_value = MATHCODEDEFAULT;
+    mathcode_head = new_sa_tree(MATHCODESTACK, 1, sa_value);
+}
+
+static void dumpmathcode(void)
+{
+    dump_sa_tree(mathcode_head,"mathcodes");
+}
+
+static void undumpmathcode(void)
+{
+    mathcode_head = undump_sa_tree("mathcodes");
+}
+
+@ @c
+static void show_delcode(int n)
+{
+    delcodeval c;
+    c = get_del_code(n);
+    tprint_esc("Udelcode");
+    print_int(n);
+    print_char('=');
+    if (c.small_family_value < 0) {
+        print_char('-');
+        print_char('1');
+    } else {
+        print_char('"');
+        two_hex(c.small_family_value);
+        six_hex(c.small_character_value);
+    }
+}
+
+@ @c
+static void unsavedelcode(quarterword gl)
+{
+    sa_stack_item st;
+    if (delcode_head->stack == NULL)
+        return;
+    while (delcode_head->stack_ptr > 0 && abs(delcode_head->stack[delcode_head->stack_ptr].level) >= gl) {
+        st = delcode_head->stack[delcode_head->stack_ptr];
+        if (st.level > 0) {
+            rawset_sa_item(delcode_head, st.code, st.value);
+            if (tracing_restores_par > 1) {
+                begin_diagnostic();
+                print_char('{');
+                tprint("restoring");
+                print_char(' ');
+                show_delcode(st.code);
+                print_char('}');
+                end_diagnostic(false);
+            }
+        }
+        (delcode_head->stack_ptr)--;
+    }
+}
+
+@ @c
+void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl)
+{
+    sa_tree_item v;
+    v.del_code_value.class_value = 0;
+    v.del_code_value.small_family_value = smathfamily;
+    v.del_code_value.small_character_value = smathcharacter;
+    v.del_code_value.dummy_value = 0;
+    v.del_code_value.large_family_value = lmathfamily;
+    v.del_code_value.large_character_value = lmathcharacter;
+    set_sa_item(delcode_head, n, v, gl); /* always global */
+    if (tracing_assigns_par > 1) {
+        begin_diagnostic();
+        print_char('{');
+        tprint("assigning");
+        print_char(' ');
+        show_delcode(n);
+        print_char('}');
+        end_diagnostic(false);
+    }
+}
+
+@ @c
+delcodeval get_del_code(int n)
+{
+    delcodeval d;
+    sa_tree_item v = get_sa_item(delcode_head, n);
+    if (v.uint_value == DELCODEDEFAULT) {
+        d.class_value = 0;
+        d.small_family_value = -1;
+        d.small_character_value = 0;
+        d.large_family_value = 0;
+        d.large_character_value = 0;
+    } else {
+        d.class_value = v.del_code_value.class_value;
+        d.small_family_value = v.del_code_value.small_family_value;
+        d.small_character_value = v.del_code_value.small_character_value;
+        d.large_family_value = v.del_code_value.large_family_value;
+        d.large_character_value = v.del_code_value.large_character_value;
+    }
+    return d;
+}
+
+@ this really only works for old-style delcodes!
+
+@c
+int get_del_code_num(int n)
+{
+    delcodeval d = get_del_code(n);
+    if (d.small_family_value < 0) {
+        return -1;
+    } else {
+        return ((d.small_family_value * 256  + d.small_character_value) * 4096 +
+                (d.large_family_value * 256) + d.large_character_value);
+    }
+}
+
+@ @c
+static void initializedelcode(void)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_value.uint_value = DELCODEDEFAULT;
+    delcode_head = new_sa_tree(DELCODESTACK, 2, sa_value);
+}
+
+@ @c
+static void dumpdelcode(void)
+{
+    dump_sa_tree(delcode_head,"delcodes");
+}
+
+static void undumpdelcode(void)
+{
+    delcode_head = undump_sa_tree("delcodes");
+}
+
+@ @c
+void unsave_math_codes(quarterword grouplevel)
+{
+    unsavemathcode(grouplevel);
+    unsavedelcode(grouplevel);
+}
+
+@ @c
+void initialize_math_codes(void)
+{
+    initializemathcode();
+    initializedelcode();
+}
+
+@ @c
+void free_math_codes(void)
+{
+    destroy_sa_tree(mathcode_head);
+    destroy_sa_tree(delcode_head);
+}
+
+@ @c
+void dump_math_codes(void)
+{
+    dumpmathcode();
+    dumpdelcode();
+}
+
+void undump_math_codes(void)
+{
+    undumpmathcode();
+    undumpdelcode();
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/mathcodes.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
-
-mathnodes.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-We support tre traditional math codes as well as larger ones suitable for
-\UNICODE\ input and fonts.
-
-*/
-
-static sa_tree mathcode_head = NULL;
-
-/*tex the |0xFFFFFFFF| is a flag value. */
-
-#define MATHCODESTACK 8
-#define MATHCODEDEFAULT 0xFFFFFFFF
-#define MATHCODEACTIVE  0xFFFFFFFE
-
-/*tex
-
-Delcodes are also went larger.
-
-*/
-
-static sa_tree delcode_head = NULL;
-
-#define DELCODESTACK 4
-#define DELCODEDEFAULT 0xFFFFFFFF
-
-/*tex
-
-We now get lots of helpers for definitions and printing. The storage model
-that we use is different because we can hav emany more so we need to be
-sparse. Therefore we use trees.
-
-*/
-
-
-#define print_hex_digit(A) do { \
-    if ((A)>=10) print_char('A'+(A)-10); \
-    else print_char('0'+(A)); \
-  } while (0)
-
-#define two_hex(A) do { \
-    print_hex_digit((A)/16); \
-    print_hex_digit((A)%16); \
-  } while (0)
-
-#define four_hex(A) do { \
-    two_hex((A)/256); \
-    two_hex((A)%256); \
-  } while (0)
-
-#define six_hex(A) do { \
-    two_hex((A)/65536); \
-    two_hex(((A)%65536)/256); \
-    two_hex((A)%256); \
-  } while (0)
-
-mathcodeval mathchar_from_integer(int value, int extcode)
-{
-    mathcodeval mval;
-    if (extcode == tex_mathcode) {
-        mval.class_value = (value / 0x1000);
-        mval.family_value = ((value % 0x1000) / 0x100);
-        mval.character_value = (value % 0x100);
-    } else {
-        int mfam = (value / 0x200000) & 0x7FF;
-        mval.class_value = mfam % 0x08;
-        mval.family_value = mfam / 0x08;
-        mval.character_value = value & 0x1FFFFF;
-    }
-    return mval;
-}
-
-void show_mathcode_value_old(int value)
-{
-    print_char('"');
-    four_hex(value);
-}
-
-void show_mathcode_value(mathcodeval c)
-{
-    print_char('"');
-    print_hex_digit(c.class_value);
-    print_char('"');
-    two_hex(c.family_value);
-    print_char('"');
-    six_hex(c.character_value);
-}
-
-static void show_mathcode(int n)
-{
-    mathcodeval c = get_math_code(n);
-    tprint_esc("Umathcode");
-    print_int(n);
-    print_char('=');
-    show_mathcode_value(c);
-}
-
-static void unsavemathcode(quarterword gl)
-{
-    sa_stack_item st;
-    if (mathcode_head->stack == NULL)
-        return;
-    while (mathcode_head->stack_ptr > 0 && abs(mathcode_head->stack[mathcode_head->stack_ptr].level) >= gl) {
-        st = mathcode_head->stack[mathcode_head->stack_ptr];
-        if (st.level > 0) {
-            rawset_sa_item(mathcode_head, st.code, st.value);
-            if (tracing_restores_par > 1) {
-                begin_diagnostic();
-                print_char('{');
-                tprint("restoring");
-                print_char(' ');
-                show_mathcode(st.code);
-                print_char('}');
-                end_diagnostic(false);
-            }
-        }
-        (mathcode_head->stack_ptr)--;
-    }
-}
-
-void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword level)
-{
-    sa_tree_item v;
-    if (mathclass == 8 && mathfamily == 0 && mathcharacter == 0) {
-        v.uint_value = MATHCODEACTIVE;
-    } else {
-        v.math_code_value.class_value = mathclass;
-        v.math_code_value.family_value = mathfamily;
-        v.math_code_value.character_value = mathcharacter;
-    }
-    set_sa_item(mathcode_head, n, v, level);
-    if (tracing_assigns_par > 1) {
-        begin_diagnostic();
-        print_char('{');
-        tprint("assigning");
-        print_char(' ');
-        show_mathcode(n);
-        print_char('}');
-        end_diagnostic(false);
-    }
-}
-
-mathcodeval get_math_code(int n)
-{
-    mathcodeval d;
-    sa_tree_item v = get_sa_item(mathcode_head, n);
-    if (v.uint_value == MATHCODEDEFAULT) {
-        d.class_value = 0;
-        d.family_value = 0;
-        d.character_value = n;
-    } else if (v.uint_value == MATHCODEACTIVE) {
-        d.class_value = 8;
-        d.family_value = 0;
-        d.character_value = 0;
-    } else {
-        d.class_value = v.math_code_value.class_value;
-        if (d.class_value == 8) {
-            d.family_value = 0;
-            d.character_value = n;
-        } else {
-            d.family_value = v.math_code_value.family_value;
-            d.character_value = v.math_code_value.character_value;
-        }
-    }
-    return d;
-}
-
-int get_math_code_num(int n)
-{
-    mathcodeval d = get_math_code(n);
-    return (d.class_value + (d.family_value * 8)) * (65536 * 32) + d.character_value;
-}
-
-static void initializemathcode(void)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_value.uint_value = MATHCODEDEFAULT;
-    mathcode_head = new_sa_tree(MATHCODESTACK, 1, sa_value);
-}
-
-static void dumpmathcode(void)
-{
-    dump_sa_tree(mathcode_head,"mathcodes");
-}
-
-static void undumpmathcode(void)
-{
-    mathcode_head = undump_sa_tree("mathcodes");
-}
-
-static void show_delcode(int n)
-{
-    delcodeval c;
-    c = get_del_code(n);
-    tprint_esc("Udelcode");
-    print_int(n);
-    print_char('=');
-    if (c.small_family_value < 0) {
-        print_char('-');
-        print_char('1');
-    } else {
-        print_char('"');
-        two_hex(c.small_family_value);
-        six_hex(c.small_character_value);
-    }
-}
-
-static void unsavedelcode(quarterword gl)
-{
-    sa_stack_item st;
-    if (delcode_head->stack == NULL)
-        return;
-    while (delcode_head->stack_ptr > 0 && abs(delcode_head->stack[delcode_head->stack_ptr].level) >= gl) {
-        st = delcode_head->stack[delcode_head->stack_ptr];
-        if (st.level > 0) {
-            rawset_sa_item(delcode_head, st.code, st.value);
-            if (tracing_restores_par > 1) {
-                begin_diagnostic();
-                print_char('{');
-                tprint("restoring");
-                print_char(' ');
-                show_delcode(st.code);
-                print_char('}');
-                end_diagnostic(false);
-            }
-        }
-        (delcode_head->stack_ptr)--;
-    }
-}
-
-void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl)
-{
-    sa_tree_item v;
-    v.del_code_value.class_value = 0;
-    v.del_code_value.small_family_value = smathfamily;
-    v.del_code_value.small_character_value = smathcharacter;
-    v.del_code_value.dummy_value = 0;
-    v.del_code_value.large_family_value = lmathfamily;
-    v.del_code_value.large_character_value = lmathcharacter;
-    /*tex Always global! */
-    set_sa_item(delcode_head, n, v, gl);
-    if (tracing_assigns_par > 1) {
-        begin_diagnostic();
-        print_char('{');
-        tprint("assigning");
-        print_char(' ');
-        show_delcode(n);
-        print_char('}');
-        end_diagnostic(false);
-    }
-}
-
-delcodeval get_del_code(int n)
-{
-    delcodeval d;
-    sa_tree_item v = get_sa_item(delcode_head, n);
-    if (v.uint_value == DELCODEDEFAULT) {
-        d.class_value = 0;
-        d.small_family_value = -1;
-        d.small_character_value = 0;
-        d.large_family_value = 0;
-        d.large_character_value = 0;
-    } else {
-        d.class_value = v.del_code_value.class_value;
-        d.small_family_value = v.del_code_value.small_family_value;
-        d.small_character_value = v.del_code_value.small_character_value;
-        d.large_family_value = v.del_code_value.large_family_value;
-        d.large_character_value = v.del_code_value.large_character_value;
-    }
-    return d;
-}
-
-/*tex
-
-This really only works for old-style delcodes!
-
-*/
-
-int get_del_code_num(int n)
-{
-    delcodeval d = get_del_code(n);
-    if (d.small_family_value < 0) {
-        return -1;
-    } else {
-        return ((d.small_family_value * 256  + d.small_character_value) * 4096 +
-                (d.large_family_value * 256) + d.large_character_value);
-    }
-}
-
-static void initializedelcode(void)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_value.uint_value = DELCODEDEFAULT;
-    delcode_head = new_sa_tree(DELCODESTACK, 2, sa_value);
-}
-
-static void dumpdelcode(void)
-{
-    dump_sa_tree(delcode_head,"delcodes");
-}
-
-static void undumpdelcode(void)
-{
-    delcode_head = undump_sa_tree("delcodes");
-}
-
-void unsave_math_codes(quarterword grouplevel)
-{
-    unsavemathcode(grouplevel);
-    unsavedelcode(grouplevel);
-}
-
-void initialize_math_codes(void)
-{
-    initializemathcode();
-    initializedelcode();
-}
-
-void free_math_codes(void)
-{
-    destroy_sa_tree(mathcode_head);
-    destroy_sa_tree(delcode_head);
-}
-
-void dump_math_codes(void)
-{
-    dumpmathcode();
-    dumpdelcode();
-}
-
-void undump_math_codes(void)
-{
-    undumpmathcode();
-    undumpdelcode();
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/memoryword.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-memoryword.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-The debug code is no longer present!
-
-*/
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/memoryword.w
@@ -0,0 +1,55 @@
+% memoryword.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"
+
+@ When debugging, we may want to print a |memory_word| without knowing
+what type it is; so we print it in all modes.
+
+@c
+#ifdef DEBUG
+void print_word(memory_word w)
+{
+    /* prints |w| in all ways */
+    print_int(w.cint);
+    print_char(' ');
+    print_scaled(w.cint);
+    print_char(' ');
+    print_scaled(round(unity * float_cast(w.gr)));
+    print_ln();
+    print_int(w.hh.lhfield);
+    print_char('=');
+    print_int(w.hh.b0);
+    print_char(':');
+    print_int(w.hh.b1);
+    print_char(';');
+    print_int(w.hh.rh);
+    print_char(' ');
+    print_int(w.qqqq.b0);
+    print_char(':');
+    print_int(w.qqqq.b1);
+    print_char(':');
+    print_int(w.qqqq.b2);
+    print_char(':');
+    print_int(w.qqqq.b3);
+}
+#endif
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/mlist.w
@@ -0,0 +1,4386 @@
+% mlist.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/>.
+
+% (HH / 0.82+):
+
+@ In traditional \TeX\ the italic correction is added to the width of the glyph. This
+is part of the engine design and related font design. In opentype math this is
+different. There the italic correction had more explicit usage. The 1.7 spec
+says:
+
+italic correction:
+
+  When a run of slanted characters is followed by a straight character (such as
+  an operator or a delimiter), the italics correction of the last glyph is added
+  to its advance width.
+
+  When positioning limits on an N-ary operator (e.g., integral sign), the horizontal
+  position of the upper limit is moved to the right by ½ of the italics correction,
+  while the position of the lower limit is moved to the left by the same distance.
+
+  When positioning superscripts and subscripts, their default horizontal positions are
+  also different by the amount of the italics correction of the preceding glyph.
+
+math kerning:
+
+  Set the default horizontal position for the superscript as shifted relative to the
+  position of the subscript by the italics correction of the base glyph.
+
+Before this was specified we had to gamble a bit and assume that cambria was the font
+benchmark and trust our eyes (and msword) for the logic. I must admit that I have been
+fighting these italics in fonts (and the heuristics that Lua\TeX\ provided) right from
+the start (e.g. using Lua based postprocessing) but by now we know more and have more
+fonts to test with. More fonts are handy because not all fonts are alike when it comes
+to italics. Axis are another area of concern, as it looks like opentype math fonts often
+already apply that shift.
+
+@ @c
+#define is_new_mathfont(A)   ((font_math_params(A) >0) && (math_old_par == 0))
+#define is_old_mathfont(A,B) ((font_math_params(A)==0) && (font_params(A)>=(B)))
+#define do_new_math(A)       ((font_math_params(A) >0) && (font_oldmath(A) == 0) && (math_old_par == 0))
+
+@
+\def\LuaTeX{Lua\TeX}
+
+@ @c
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+@ @c
+#define nDEBUG
+
+#define reset_attributes(p,newatt) do { \
+    delete_attribute_ref(node_attr(p)); \
+    node_attr(p) = newatt;              \
+    if (newatt!=null) {                 \
+      add_node_attr_ref(node_attr(p));  \
+    }                                   \
+  } while (0)
+
+#define DEFINE_MATH_PARAMETERS(A,B,C,D) do {                 \
+    if (B==text_size) {                                      \
+      def_math_param(A, text_style, (C),D);                  \
+      def_math_param(A, cramped_text_style, (C),D);          \
+    } else if (B==script_size) {                             \
+      def_math_param(A, script_style, (C),D);                \
+      def_math_param(A, cramped_script_style, (C),D);        \
+    } else if (B==script_script_size) {                      \
+      def_math_param(A, script_script_style, (C),D);         \
+      def_math_param(A, cramped_script_script_style, (C),D); \
+    }                                                        \
+  } while (0)
+
+#define DEFINE_DMATH_PARAMETERS(A,B,C,D) do {         \
+    if (B==text_size) {                               \
+      def_math_param(A, display_style,(C),D);         \
+      def_math_param(A, cramped_display_style,(C),D); \
+    }                                                 \
+  } while (0)
+
+#define font_MATH_par(a,b) \
+  (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter)
+
+@ here are the math parameters that are font-dependant
+
+@ Before an mlist is converted to an hlist, \TeX\ makes sure that
+the fonts in family~2 have enough parameters to be math-symbol
+fonts, and that the fonts in family~3 have enough parameters to be
+math-extension fonts. The math-symbol parameters are referred to by using the
+following macros, which take a size code as their parameter; for example,
+|num1(cur_size)| gives the value of the |num1| parameter for the current size.
+@^parameters for symbols@>
+@^font parameters@>
+
+@c
+#define total_mathsy_params 22
+#define total_mathex_params 13
+
+#define mathsy(A,B) font_param(fam_fnt(2,A),B)
+
+#define math_x_height(A) mathsy(A,5) /* height of `\.x' */
+#define math_quad(A) mathsy(A,6)     /* \.{18mu} */
+#define num1(A) mathsy(A,8)          /* numerator shift-up in display styles */
+#define num2(A) mathsy(A,9)          /* numerator shift-up in non-display, non-\.{\\atop} */
+#define num3(A) mathsy(A,10)         /* numerator shift-up in non-display \.{\\atop} */
+#define denom1(A) mathsy(A,11)       /* denominator shift-down in display styles */
+#define denom2(A) mathsy(A,12)       /* denominator shift-down in non-display styles */
+#define sup1(A) mathsy(A,13)         /* superscript shift-up in uncramped display style */
+#define sup2(A) mathsy(A,14)         /* superscript shift-up in uncramped non-display */
+#define sup3(A) mathsy(A,15)         /* superscript shift-up in cramped styles */
+#define sub1(A) mathsy(A,16)         /* subscript shift-down if superscript is absent */
+#define sub2(A) mathsy(A,17)         /* subscript shift-down if superscript is present */
+#define sup_drop(A) mathsy(A,18)     /* superscript baseline below top of large box */
+#define sub_drop(A) mathsy(A,19)     /* subscript baseline below bottom of large box */
+#define delim1(A) mathsy(A,20)       /* size of \.{\\atopwithdelims} delimiters in display styles */
+#define delim2(A) mathsy(A,21)       /* size of \.{\\atopwithdelims} delimiters in non-displays */
+#define axis_height(A) mathsy(A,22)  /* height of fraction lines above the baseline */
+
+@ The math-extension parameters have similar macros, but the size code is
+omitted (since it is always |cur_size| when we refer to such parameters).
+@^parameters for symbols@>
+@^font parameters@>
+
+@c
+#define mathex(A,B) font_param(fam_fnt(3,A),B)
+#define default_rule_thickness(A) mathex(A,8)   /* thickness of \.{\\over} bars */
+#define big_op_spacing1(A) mathex(A,9)  /* minimum clearance above a displayed op */
+#define big_op_spacing2(A) mathex(A,10) /* minimum clearance below a displayed op */
+#define big_op_spacing3(A) mathex(A,11) /* minimum baselineskip above displayed op */
+#define big_op_spacing4(A) mathex(A,12) /* minimum baselineskip below displayed op */
+#define big_op_spacing5(A) mathex(A,13) /* padding above and below displayed limits */
+
+@ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of
+the MathConstants values have no matching usage in \LuaTeX\ right now.
+
+ScriptPercentScaleDown,
+ScriptScriptPercentScaleDown:
+  These should be handled by the macro package, on the engine
+  side there are three separate fonts
+
+DelimitedSubFormulaMinHeight:
+  This is perhaps related to word's natural math input? I have
+  no idea what to do about it
+
+MathLeading:
+  LuaTeX does not currently handle multi-line displays, and
+  the parameter does not seem to make much sense elsewhere
+
+FlattenedAccentBaseHeight:
+  This is based on the 'flac' GSUB feature. It would not be hard
+  to support that, but proper math accent placements cf. MATH
+  needs support for MathTopAccentAttachment table to be
+  implemented first
+
+Also still TODO for OpenType Math:
+  * prescripts
+
+@ this is not really a math parameter at all
+
+@c
+static void math_param_error(const char *param, int style)
+{
+    char s[256];
+    const char *hlp[] = {
+        "Sorry, but I can't typeset math unless various parameters have",
+        "been set. This is normally done by loading special math fonts",
+        "into the math family slots. Your font set is lacking at least",
+        "the parameter mentioned earlier.",
+        NULL
+    };
+    snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set",
+             param, math_style_names[style]);
+    tex_error(s, hlp);
+#if 0
+    flush_math();
+#endif
+    return;
+}
+
+@ @c
+static scaled accent_base_height(int f)
+{
+    scaled a;
+    if (do_new_math(f)) {
+        a = font_MATH_par(f, AccentBaseHeight);
+        if (a == undefined_math_parameter)
+            a = x_height(f);
+    } else {
+        a = x_height(f);
+    }
+    return a;
+}
+
+@ The non-staticness of this function is for the benefit of |texmath.w|. Watch out,
+this one uses the style! The style and size numbers don't match because we have
+cramped styles.
+
+@c
+scaled get_math_quad_style(int var)
+{
+    scaled a = get_math_param(math_param_quad, var);
+    if (a == undefined_math_parameter) {
+        math_param_error("quad", var);
+        return 0;
+    } else {
+        return a;
+    }
+}
+
+@ For this reason the next one is different because it is called with a size
+specifier instead of a style specifier.
+
+@c
+static scaled math_axis_size(int b)
+{
+    scaled a;
+    int var;
+    if (b == script_size)
+        var = script_style;
+    else if (b == script_script_size)
+        var = script_script_style;
+    else
+        var = text_style;
+    a = get_math_param(math_param_axis, var);
+    if (a == undefined_math_parameter) {
+        math_param_error("axis", var);
+        return 0;
+    } else {
+        return a;
+    }
+}
+
+@ @c
+scaled get_math_quad_size(int b)
+{
+    int var;
+    if (b == script_size)
+        var = script_style;
+    else if (b == script_script_size)
+        var = script_script_style;
+    else
+        var = text_style;
+    return get_math_param(math_param_quad, var);
+}
+
+@ @c
+static scaled minimum_operator_size(int var)
+{
+    scaled a = get_math_param(math_param_operator_size, var);
+    return a;
+}
+
+@ Old-style fonts do not define the |radical_rule|. This allows |make_radical| to select
+the backward compatibility code, and it means that we can't raise an error here.
+
+@c
+static scaled radical_rule_par(int var)
+{
+    scaled a = get_math_param(math_param_radical_rule, var);
+    return a;
+}
+
+@ now follow all the trivial math parameters
+
+@c
+#define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b)
+#define get_math_param_or_zero(a,b) do_get_math_param_or_zero(a, math_param_##b, #b)
+
+static scaled do_get_math_param_or_error(int var, int param, const char *name)
+{
+    scaled a = get_math_param(param, var);
+    if (a == undefined_math_parameter) {
+        math_param_error(name, var);
+        a = 0;
+    }
+    return a;
+}
+
+static scaled do_get_math_param_or_zero(int var, int param, const char *name)
+{
+    scaled a = get_math_param(param, var);
+    if (a == undefined_math_parameter) {
+        a = 0;
+    }
+    return a;
+}
+
+@ A variant on a suggestion on the list based on analysis by UV.
+
+@c
+static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
+    scaled delta, delta1, delta2;
+    if (axis) {
+        delta2 = max_d + math_axis_size(cur_size);
+    } else {
+        delta2 = max_d;
+    }
+    delta1 = max_h + max_d - delta2;
+    if (delta2 > delta1) {
+        /* |delta1| is max distance from axis */
+        delta1 = delta2;
+    }
+    delta = (delta1 / 500) * delimiter_factor_par;
+    delta2 = delta1 + delta1 - delimiter_shortfall_par;
+    if (delta < delta2) {
+        return delta2;
+    } else {
+        return delta;
+    }
+}
+
+@ @c
+#define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before)
+#define radical_degree_after(a)  get_math_param_or_error(a, radical_degree_after)
+#define radical_degree_raise(a)  get_math_param_or_error(a, radical_degree_raise)
+
+#define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min)
+
+#define overbar_rule(a)          get_math_param_or_error(a, overbar_rule)
+#define overbar_kern(a)          get_math_param_or_error(a, overbar_kern)
+#define overbar_vgap(a)          get_math_param_or_error(a, overbar_vgap)
+
+#define underbar_rule(a)         get_math_param_or_error(a, underbar_rule)
+#define underbar_kern(a)         get_math_param_or_error(a, underbar_kern)
+#define underbar_vgap(a)         get_math_param_or_error(a, underbar_vgap)
+
+#define under_delimiter_vgap(a)  get_math_param_or_error(a, under_delimiter_vgap)
+#define under_delimiter_bgap(a)  get_math_param_or_error(a, under_delimiter_bgap)
+
+#define over_delimiter_vgap(a)   get_math_param_or_error(a, over_delimiter_vgap)
+#define over_delimiter_bgap(a)   get_math_param_or_error(a, over_delimiter_bgap)
+
+#define radical_vgap(a)          get_math_param_or_error(a, radical_vgap)
+#define radical_kern(a)          get_math_param_or_error(a, radical_kern)
+
+#define stack_vgap(a)            get_math_param_or_error(a, stack_vgap)
+#define stack_num_up(a)          get_math_param_or_error(a, stack_num_up)
+#define stack_denom_down(a)      get_math_param_or_error(a, stack_denom_down)
+
+#define fraction_rule(a)         get_math_param_or_error(a, fraction_rule)
+#define fraction_num_vgap(a)     get_math_param_or_error(a, fraction_num_vgap)
+#define fraction_denom_vgap(a)   get_math_param_or_error(a, fraction_denom_vgap)
+#define fraction_num_up(a)       get_math_param_or_error(a, fraction_num_up)
+#define fraction_denom_down(a)   get_math_param_or_error(a, fraction_denom_down)
+#define fraction_del_size_new(a) get_math_param_or_error(a, fraction_del_size)
+/*
+#define fraction_del_size_old(a) get_math_param(a, math_param_fraction_del_size)
+*/
+#define fraction_del_size_old(a) get_math_param_or_error(a, fraction_del_size)
+
+#define skewed_fraction_hgap(a)  get_math_param_or_error(a, skewed_fraction_hgap)
+#define skewed_fraction_vgap(a)  get_math_param_or_error(a, skewed_fraction_vgap)
+
+#define limit_above_vgap(a)      get_math_param_or_error(a, limit_above_vgap)
+#define limit_above_bgap(a)      get_math_param_or_error(a, limit_above_bgap)
+#define limit_above_kern(a)      get_math_param_or_error(a, limit_above_kern)
+
+#define limit_below_vgap(a)      get_math_param_or_error(a, limit_below_vgap)
+#define limit_below_bgap(a)      get_math_param_or_error(a, limit_below_bgap)
+#define limit_below_kern(a)      get_math_param_or_error(a, limit_below_kern)
+
+#define nolimit_sub_factor(a)    get_math_param_or_zero(a, nolimit_sub_factor)
+#define nolimit_sup_factor(a)    get_math_param_or_zero(a, nolimit_sup_factor)
+
+#define sub_shift_drop(a)        get_math_param_or_error(a, sub_shift_drop)
+#define sup_shift_drop(a)        get_math_param_or_error(a, sup_shift_drop)
+#define sub_shift_down(a)        get_math_param_or_error(a, sub_shift_down)
+#define sub_sup_shift_down(a)    get_math_param_or_error(a, sub_sup_shift_down)
+#define sup_shift_up(a)          get_math_param_or_error(a, sup_shift_up)
+#define sub_top_max(a)           get_math_param_or_error(a, sub_top_max)
+#define sup_bottom_min(a)        get_math_param_or_error(a, sup_bottom_min)
+#define sup_sub_bottom_max(a)    get_math_param_or_error(a, sup_sub_bottom_max)
+#define subsup_vgap(a)           get_math_param_or_error(a, subsup_vgap)
+
+#define space_after_script(a)    get_math_param_or_error(a, space_after_script)
+
+@ @c
+void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
+{
+
+    if (is_new_mathfont(f)) {   /* fix all known parameters */
+
+        DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
+            font_size(f), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
+            font_size(f), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
+            font_MATH_par(f, AxisHeight), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
+            font_MATH_par(f, AxisHeight), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
+            font_MATH_par(f, OverbarExtraAscender), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
+            font_MATH_par(f, OverbarExtraAscender), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
+            font_MATH_par(f, OverbarRuleThickness), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
+            font_MATH_par(f, OverbarRuleThickness), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
+            font_MATH_par(f, OverbarVerticalGap), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
+            font_MATH_par(f, OverbarVerticalGap), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
+            font_MATH_par(f, UnderbarExtraDescender), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
+            font_MATH_par(f, UnderbarExtraDescender), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
+            font_MATH_par(f, UnderbarRuleThickness), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
+            font_MATH_par(f, UnderbarRuleThickness), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
+           font_MATH_par(f, UnderbarVerticalGap), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
+            font_MATH_par(f, UnderbarVerticalGap), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
+            font_MATH_par(f, StretchStackGapAboveMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
+            font_MATH_par(f, StretchStackGapAboveMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
+            font_MATH_par(f, StretchStackBottomShiftDown), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
+            font_MATH_par(f, StretchStackBottomShiftDown), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
+            font_MATH_par(f, StretchStackGapBelowMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
+            font_MATH_par(f, StretchStackGapBelowMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
+            font_MATH_par(f, StretchStackTopShiftUp), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
+            font_MATH_par(f, StretchStackTopShiftUp), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
+           font_MATH_par(f, StackTopShiftUp), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
+            font_MATH_par(f, StackTopDisplayStyleShiftUp), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
+            font_MATH_par(f, StackBottomShiftDown), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
+            font_MATH_par(f, StackBottomDisplayStyleShiftDown), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
+            font_MATH_par(f, StackGapMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
+            font_MATH_par(f, StackDisplayStyleGapMin), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
+            font_MATH_par(f, RadicalExtraAscender), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
+            font_MATH_par(f, RadicalExtraAscender), lvl);
+
+        DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id,
+            font_MATH_par(f, DisplayOperatorMinHeight), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id,
+            font_MATH_par(f, RadicalRuleThickness), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id,
+            font_MATH_par(f, RadicalRuleThickness), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
+            font_MATH_par(f, RadicalVerticalGap), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
+            font_MATH_par(f, RadicalDisplayStyleVerticalGap), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
+            font_MATH_par(f, RadicalKernBeforeDegree), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
+            font_MATH_par(f, RadicalKernBeforeDegree), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
+            font_MATH_par(f, RadicalKernAfterDegree), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
+            font_MATH_par(f, RadicalKernAfterDegree), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
+            font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
+            font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
+
+        if (size_id == text_size) {
+            def_math_param(math_param_sup_shift_up, display_style,
+                font_MATH_par(f, SuperscriptShiftUp), lvl);
+            def_math_param(math_param_sup_shift_up, cramped_display_style,
+                font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
+            def_math_param(math_param_sup_shift_up, text_style,
+                font_MATH_par(f, SuperscriptShiftUp), lvl);
+            def_math_param(math_param_sup_shift_up, cramped_text_style,
+                font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
+        } else if (size_id == script_size) {
+            def_math_param(math_param_sup_shift_up, script_style,
+                font_MATH_par(f, SuperscriptShiftUp), lvl);
+            def_math_param(math_param_sup_shift_up, cramped_script_style,
+                font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
+        } else if (size_id == script_script_size) {
+            def_math_param(math_param_sup_shift_up, script_script_style,
+                font_MATH_par(f, SuperscriptShiftUp), lvl);
+            def_math_param(math_param_sup_shift_up, cramped_script_script_style,
+                font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
+        }
+
+        DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id,
+            font_MATH_par(f, SubscriptBaselineDropMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id,
+            font_MATH_par(f, SubscriptBaselineDropMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id,
+            font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id,
+            font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
+            font_MATH_par(f, SubscriptShiftDown), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
+            font_MATH_par(f, SubscriptShiftDown), lvl);
+
+        if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) {
+            DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+                font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
+            DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+                font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
+        } else {
+            DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+                font_MATH_par(f, SubscriptShiftDown), lvl);
+            DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+                font_MATH_par(f, SubscriptShiftDown), lvl);
+        }
+
+        DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
+            font_MATH_par(f, SubscriptTopMax), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
+            font_MATH_par(f, SubscriptTopMax), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
+            font_MATH_par(f, SuperscriptBottomMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
+            font_MATH_par(f, SuperscriptBottomMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
+            font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
+            font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
+            font_MATH_par(f, SubSuperscriptGapMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
+            font_MATH_par(f, SubSuperscriptGapMin), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
+            font_MATH_par(f, UpperLimitGapMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
+            font_MATH_par(f, UpperLimitGapMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
+            font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
+            font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
+            0, lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
+            0, lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
+            font_MATH_par(f, LowerLimitGapMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
+            font_MATH_par(f, LowerLimitGapMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
+            font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
+            font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
+            0, lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
+            0, lvl);
+        DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
+            font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
+        DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
+            font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
+        DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
+            font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
+        DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
+            font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
+
+        DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
+            font_MATH_par(f, FractionRuleThickness), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
+            font_MATH_par(f, FractionRuleThickness), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
+            font_MATH_par(f, FractionNumeratorGapMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
+            font_MATH_par(f, FractionNumeratorDisplayStyleGapMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
+            font_MATH_par(f, FractionNumeratorShiftUp), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
+            font_MATH_par(f, FractionNumeratorDisplayStyleShiftUp), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
+            font_MATH_par(f, FractionDenominatorGapMin), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
+            font_MATH_par(f,FractionDenominatorDisplayStyleGapMin), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
+            font_MATH_par(f, FractionDenominatorShiftDown), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
+            font_MATH_par(f, FractionDenominatorDisplayStyleShiftDown), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
+            font_MATH_par(f, FractionDelimiterSize), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
+            font_MATH_par(f, FractionDelimiterDisplayStyleSize), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
+            font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
+            font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
+            font_MATH_par(f, SkewedFractionVerticalGap), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
+            font_MATH_par(f, SkewedFractionVerticalGap), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id,
+            font_MATH_par(f, SpaceAfterScript), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id,
+            font_MATH_par(f, SpaceAfterScript), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
+            font_MATH_par(f, MinConnectorOverlap), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
+            font_MATH_par(f, MinConnectorOverlap), lvl);
+
+    } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
+
+        /* fix old-style |sy| parameters */
+
+        DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
+            math_quad(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
+            math_quad(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
+            axis_height(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
+            axis_height(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
+            num3(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
+            num1(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
+            denom2(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
+            denom1(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
+            num2(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
+            num1(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
+            denom2(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
+            denom1(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
+            delim2(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
+            delim1(size_id), lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
+            0, lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
+            0, lvl);
+        DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
+            0, lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
+            0, lvl);
+
+        if (size_id == text_size) {
+            def_math_param(math_param_sup_shift_up, display_style,
+                sup1(size_id), lvl);
+            def_math_param(math_param_sup_shift_up, cramped_display_style,
+                sup3(size_id), lvl);
+            def_math_param(math_param_sup_shift_up, text_style,
+                sup2(size_id), lvl);
+            def_math_param(math_param_sup_shift_up, cramped_text_style,
+               sup3(size_id), lvl);
+        } else if (size_id == script_size) {
+            def_math_param(math_param_sub_shift_drop, display_style,
+                sub_drop(size_id), lvl);
+            def_math_param(math_param_sub_shift_drop, cramped_display_style,
+                sub_drop(size_id), lvl);
+            def_math_param(math_param_sub_shift_drop, text_style,
+                sub_drop(size_id), lvl);
+            def_math_param(math_param_sub_shift_drop, cramped_text_style,
+                sub_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_drop, display_style,
+                sup_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_drop, cramped_display_style,
+                sup_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_drop, text_style,
+                sup_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_drop, cramped_text_style,
+                sup_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_up, script_style,
+                sup2(size_id), lvl);
+            def_math_param(math_param_sup_shift_up, cramped_script_style,
+                sup3(size_id), lvl);
+        } else if (size_id == script_script_size) {
+            def_math_param(math_param_sub_shift_drop, script_style,
+                sub_drop(size_id), lvl);
+            def_math_param(math_param_sub_shift_drop, cramped_script_style,
+                sub_drop(size_id), lvl);
+            def_math_param(math_param_sub_shift_drop, script_script_style,
+                sub_drop(size_id), lvl);
+            def_math_param(math_param_sub_shift_drop,
+                cramped_script_script_style, sub_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_drop, script_style,
+                sup_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_drop, cramped_script_style,
+                sup_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_drop, script_script_style,
+                sup_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_drop,
+                cramped_script_script_style, sup_drop(size_id), lvl);
+            def_math_param(math_param_sup_shift_up, script_script_style,
+                sup2(size_id), lvl);
+            def_math_param(math_param_sup_shift_up, cramped_script_script_style,
+                sup3(size_id), lvl);
+        }
+
+        DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
+            sub1(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
+            sub1(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+            sub2(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+            sub2(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
+            (abs(math_x_height(size_id) * 4) / 5), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
+            (abs(math_x_height(size_id) * 4) / 5), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
+            (abs(math_x_height(size_id)) / 4), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
+            (abs(math_x_height(size_id)) / 4), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
+            (abs(math_x_height(size_id) * 4) / 5), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
+            (abs(math_x_height(size_id) * 4) / 5), lvl);
+
+        /*
+            The display-size |radical_vgap| is done twice because it needs
+            values from both the sy and the ex font.
+        */
+
+        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
+            (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
+            60, lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
+            60, lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
+            xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
+            xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
+            (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
+            (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
+
+    } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
+
+        /* fix old-style |ex| parameters */
+
+        DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
+            3 * default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
+            3 * default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
+            3 * default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
+            3 * default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
+           (default_rule_thickness(size_id) + (abs(default_rule_thickness(size_id)) / 4)), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
+            3 * default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
+            7 * default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
+            3 * default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
+            default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
+            3 * default_rule_thickness(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
+            big_op_spacing1(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
+            big_op_spacing1(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
+            big_op_spacing3(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
+            big_op_spacing3(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
+            big_op_spacing5(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
+            big_op_spacing5(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
+            big_op_spacing2(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
+            big_op_spacing2(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
+            big_op_spacing4(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
+            big_op_spacing4(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
+           big_op_spacing5(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
+            big_op_spacing5(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
+            font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
+        DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
+            font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
+        DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
+            font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
+        DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
+            font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
+        DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
+            4 * default_rule_thickness(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
+            4 * default_rule_thickness(size_id), lvl);
+
+        /*
+            All of the |space_after_script|s are done in |finalize_math_parameters|
+            because the \.{\\scriptspace} may have been altered by the user
+        */
+
+        DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
+            0, lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
+            0, lvl);
+
+        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
+            big_op_spacing2(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
+            big_op_spacing2(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
+            big_op_spacing4(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
+            big_op_spacing4(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
+            big_op_spacing1(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
+            big_op_spacing1(size_id), lvl);
+        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
+            big_op_spacing3(size_id), lvl);
+        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
+            big_op_spacing3(size_id), lvl);
+
+        /*
+            The display-size |radical_vgap| is done twice because it needs
+            values from both the sy and the ex font.
+        */
+
+        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
+            (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
+
+    }
+}
+
+@ This needs to be called just at the start of |mlist_to_hlist|, for
+backward compatibility with \.{\\scriptspace}.
+
+@c
+static void finalize_math_parameters(void)
+{
+    int saved_trace = tracing_assigns_par;
+    tracing_assigns_par = 0;
+    if (get_math_param(math_param_space_after_script, display_style) == undefined_math_parameter) {
+        def_math_param(math_param_space_after_script, display_style,
+            script_space_par, level_one);
+        def_math_param(math_param_space_after_script, text_style,
+            script_space_par, level_one);
+        def_math_param(math_param_space_after_script, script_style,
+            script_space_par, level_one);
+        def_math_param(math_param_space_after_script, script_script_style,
+            script_space_par, level_one);
+        def_math_param(math_param_space_after_script, cramped_display_style,
+            script_space_par, level_one);
+        def_math_param(math_param_space_after_script, cramped_text_style,
+            script_space_par, level_one);
+        def_math_param(math_param_space_after_script, cramped_script_style,
+            script_space_par, level_one);
+        def_math_param(math_param_space_after_script, cramped_script_script_style,
+            script_space_par, level_one);
+    }
+    tracing_assigns_par = saved_trace;
+}
+
+@ In order to convert mlists to hlists, i.e., noads to nodes, we need several
+subroutines that are conveniently dealt with now.
+
+Let us first introduce the macros that make it easy to get at the parameters and
+other font information. A size code, which is a multiple of 256, is added to a
+family number to get an index into the table of internal font numbers
+for each combination of family and size.  (Be alert: Size codes get
+larger as the type gets smaller.)
+
+@c
+static const char *math_size_string(int s)
+{
+    if (s == text_size)
+        return "textfont";
+    else if (s == script_size)
+        return "scriptfont";
+    else
+        return "scriptscriptfont";
+}
+
+@ When the style changes, the following piece of program computes associated
+information:
+
+@c
+#define setup_cur_size(a) do { \
+    if (a==script_style || a==cramped_script_style) \
+        cur_size = script_size; \
+    else if (a==script_script_style || a==cramped_script_script_style) \
+        cur_size = script_script_size; \
+    else \
+        cur_size = text_size; \
+} while (0)
+
+
+@ a simple routine that creates a flat copy of a nucleus
+@c
+static pointer math_clone(pointer q)
+{
+    pointer x;
+    if (q == null)
+        return null;
+    x = new_node(type(q), 0);
+    reset_attributes(x, node_attr(q));
+    if (type(q) == math_char_node) {
+        math_fam(x) = math_fam(q);
+        math_character(x) = math_character(q);
+    } else {
+        math_list(x) = math_list(q);
+    }
+    return x;
+}
+
+@ Here is a function that returns a pointer to a rule node having a given
+  thickness |t|. The rule will extend horizontally to the boundary of the vlist
+  that eventually contains it.
+
+@c
+static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfword cur_size, halfword cur_fam)
+{
+    pointer p;                  /* the new node */
+    if (math_rules_mode_par) {
+        p = new_rule(some_rule);
+        rule_math_size(p) = cur_size;
+        rule_math_font(p) = fam_fnt(cur_fam, cur_size);
+    } else {
+        p = new_rule(normal_rule);
+    }
+    rule_dir(p) = math_direction_par;
+    height(p) = t;
+    depth(p) = 0;
+    reset_attributes(p, att);
+    return p;
+}
+
+@ The |overbar| function returns a pointer to a vlist box that consists of
+  a given box |b|, above which has been placed a kern of height |k| under a
+  fraction rule of thickness |t| under additional space of height |ht|.
+
+@c
+static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, halfword index, halfword cur_size, halfword cur_fam)
+{
+    pointer p, q;               /* nodes being constructed */
+    p = new_kern(k);
+    reset_attributes(p, att);
+    couple_nodes(p,b);
+    q = do_fraction_rule(t, att, index, cur_size, cur_fam);
+    couple_nodes(q,p);
+    p = new_kern(ht);
+    reset_attributes(p, att);
+    couple_nodes(p,q);
+    q = vpackage(p, 0, additional, max_dimen, math_direction_par);
+    reset_attributes(q, att);
+    return q;
+}
+
+@ Here is a subroutine that creates a new box, whose list contains a
+single character, and whose width includes the italic correction for
+that character. The height or depth of the box will be negative, if
+the height or depth of the character is negative; thus, this routine
+may deliver a slightly different result than |hpack| would produce.
+
+@c
+static pointer char_box(internal_font_number f, int c, pointer bb)
+{
+    pointer b, p; /* the new box and its character node */
+    b = new_null_box();
+    if (do_new_math(f))
+        width(b) = char_width(f, c);
+    else
+        width(b) = char_width(f, c) + char_italic(f, c);
+    height(b) = char_height(f, c);
+    depth(b) = char_depth(f, c);
+    reset_attributes(b, bb);
+    p = new_char(f, c);
+    reset_attributes(p, bb);
+    list_ptr(b) = p;
+    return b;
+}
+
+@ Another handy subroutine computes the height plus depth of
+  a given character:
+
+@c
+static scaled height_plus_depth(internal_font_number f, int c)
+{
+    return (char_height(f, c) + char_depth(f, c));
+}
+
+@ When we build an extensible character, it's handy to have the
+  following subroutine, which puts a given character on top
+  of the characters already in box |b|:
+
+@c
+static scaled stack_into_box(pointer b, internal_font_number f, int c)
+{
+    pointer p, q; /* new node placed into |b| */
+    p = char_box(f, c, node_attr(b)); /* italic gets added to width */
+    if (type(b) == vlist_node) {
+        try_couple_nodes(p,list_ptr(b));
+        list_ptr(b) = p;
+        height(b) = height(p);
+        if (width(b) < width(p))
+            width(b) = width(p);
+        return height_plus_depth(f, c);
+    } else {
+        q = list_ptr(b);
+        if (q == null) {
+            list_ptr(b) = p;
+        } else {
+            while (vlink(q) != null)
+                q = vlink(q);
+            couple_nodes(q,p);
+        }
+        if (height(b) < height(p))
+            height(b) = height(p);
+        if (depth(b) < depth(p))
+            depth(b) = depth(p);
+        return char_width(f, c);
+    }
+}
+
+static void stack_glue_into_box(pointer b, scaled min, scaled max) {
+    halfword p = new_glue(zero_glue);
+    width(p) = min;
+    stretch(p) = max - min;
+    reset_attributes(p, node_attr(b));
+    if (type(b) == vlist_node) {
+        try_couple_nodes(p,list_ptr(b));
+        list_ptr(b) = p;
+    } else {
+        halfword q = list_ptr(b);
+        if (q == null) {
+            list_ptr(b) = p;
+        } else {
+            while (vlink(q) != null)
+                q = vlink(q);
+            couple_nodes(q,p);
+        }
+    }
+}
+
+@ \TeX's most important routine for dealing with formulas is called
+ |mlist_to_hlist|.  After a formula has been scanned and represented
+ as an mlist, this routine converts it to an hlist that can be placed
+ into a box or incorporated into the text of a paragraph.  The
+ explicit parameter |cur_mlist| points to the first node or noad in
+ the given mlist (and it might be |null|); the parameter |penalties|
+ is |true| if penalty nodes for potential line breaks are to be
+ inserted into the resulting hlist, the parameter |cur_style| is a
+ style code.  After |mlist_to_hlist| has acted, |vlink(temp_head)|
+ points to the translated hlist.
+
+ Since mlists can be inside mlists, the procedure is recursive. And since this
+ is not part of \TeX's inner loop, the program has been written in a manner
+ that stresses compactness over efficiency.
+@^recursion@>
+
+@c
+int cur_size; /* size code corresponding to |cur_style|  */
+
+@ @c
+static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, scaled v,
+                             pointer att, int cur_style, int boxtype)
+{
+    pointer b;                  /* new box */
+    scaled b_max;               /* natural (maximum) size of the stack */
+    scaled s_max;               /* amount of possible shrink in the stack */
+    extinfo *cur;
+    scaled min_overlap, prev_overlap;
+    int i;                      /* a temporary counter number of extensible pieces */
+    int with_extenders;         /* number of times to repeat each repeatable item in |ext| */
+    int num_extenders, num_normal;
+    scaled a, c, d;
+
+    assert(ext != NULL);
+    b = new_null_box();
+    type(b) = (quarterword) boxtype;
+    reset_attributes(b, att);
+    min_overlap = connector_overlap_min(cur_style);
+    assert(min_overlap >= 0);
+    with_extenders = -1;
+    num_extenders = 0;
+    num_normal = 0;
+
+    cur = ext;
+    while (cur != NULL) {
+        if (!char_exists(f, cur->glyph)) {
+            const char *hlp[] = {
+                "Each glyph part in an extensible item should exist in the font.",
+                "I will give up trying to find a suitable size for now. Fix your font!",
+                NULL
+            };
+            tex_error("Variant part doesn't exist.", hlp);
+            width(b) = null_delimiter_space_par;
+            return b;
+        }
+        if (cur->extender > 0)
+            num_extenders++;
+        else
+            num_normal++;
+        /* no negative overlaps or advances are allowed */
+        if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) {
+            const char *hlp[] = {
+                "All measurements in extensible items should be positive.",
+                "To get around this problem, I have changed the font metrics.",
+                "Fix your font!",
+                NULL
+            };
+            tex_error("Extensible recipe has negative fields.", hlp);
+            if (cur->start_overlap < 0)
+                cur->start_overlap = 0;
+            if (cur->end_overlap < 0)
+                cur->end_overlap = 0;
+            if (cur->advance < 0)
+                cur->advance = 0;
+        }
+        cur = cur->next;
+    }
+    if (num_normal == 0) {
+        const char *hlp[] = {
+            "Each extensible recipe should have at least one non-repeatable part.",
+            "To get around this problem, I have changed the first part to be",
+            "non-repeatable. Fix your font!",
+            NULL
+        };
+        tex_error("Extensible recipe has no fixed parts.", hlp);
+        ext->extender = 0;
+        num_normal = 1;
+        num_extenders--;
+    }
+    /*
+        |ext| holds a linked list of numerous items that may or may not be
+        repeatable. For the total height, we have to figure out how many items
+        are needed to create a stack of at least |v|.
+
+        The next |while| loop does  that. It has two goals: it finds out
+        the natural height |b_max|  of the all the parts needed to reach
+        at least |v|,  and it sets |with_extenders| to the number of times
+        each of the repeatable items in |ext| has to be repeated to reach
+        that height.
+
+    */
+    cur = ext;
+    b_max = 0;
+    while (b_max < v && num_extenders > 0) {
+        b_max = 0;
+        prev_overlap = 0;
+        with_extenders++;
+        for (cur = ext; cur != NULL; cur = cur->next) {
+            if (cur->extender == 0) {
+                c = cur->start_overlap;
+                if (min_overlap < c)
+                    c = min_overlap;
+                if (prev_overlap < c)
+                    c = prev_overlap;
+                a = cur->advance;
+                if (a == 0) {
+                    /* for tfm fonts */
+                    if (boxtype == vlist_node)
+                        a = height_plus_depth(f, cur->glyph);
+                    else
+                        a = char_width(f, cur->glyph);
+                    assert(a >= 0);
+                }
+                b_max += a - c;
+                prev_overlap = cur->end_overlap;
+            } else {
+                i = with_extenders;
+                while (i > 0) {
+                    c = cur->start_overlap;
+                    if (min_overlap < c)
+                        c = min_overlap;
+                    if (prev_overlap < c)
+                        c = prev_overlap;
+                    a = cur->advance;
+                    if (a == 0) {
+                        /* for tfm fonts */
+                        if (boxtype == vlist_node)
+                            a = height_plus_depth(f, cur->glyph);
+                        else
+                            a = char_width(f, cur->glyph);
+                        assert(a >= 0);
+                    }
+                    b_max += a - c;
+                    prev_overlap = cur->end_overlap;
+                    i--;
+                }
+            }
+        }
+    }
+
+    /*
+        assemble box using |with_extenders| copies of each extender, with
+        appropriate glue wherever an overlap occurs
+    */
+    prev_overlap = 0;
+    b_max = 0;
+    s_max = 0;
+    for (cur = ext; cur != NULL; cur = cur->next) {
+        if (cur->extender == 0) {
+            c = cur->start_overlap;
+            if (prev_overlap < c)
+                c = prev_overlap;
+            d = c;
+            if (min_overlap < c)
+                c = min_overlap;
+            if (d > 0) {
+                stack_glue_into_box(b, -d, -c);
+                s_max += (-c) - (-d);
+                b_max -= d;
+            }
+            b_max += stack_into_box(b, f, cur->glyph);
+            prev_overlap = cur->end_overlap;
+            i--;
+        } else {
+            i = with_extenders;
+            while (i > 0) {
+                c = cur->start_overlap;
+                if (prev_overlap < c)
+                    c = prev_overlap;
+                d = c;
+                if (min_overlap < c)
+                    c = min_overlap;
+                if (d > 0) {
+                    stack_glue_into_box(b, -d, -c);
+                    s_max += (-c) - (-d);
+                    b_max -= d;
+                }
+                b_max += stack_into_box(b, f, cur->glyph);
+                prev_overlap = cur->end_overlap;
+                i--;
+            }
+        }
+    }
+
+    /* set glue so as to stretch the connections if needed */
+
+    d = 0;
+    if (v > b_max && s_max > 0) {
+        d = v-b_max;
+        /* don't stretch more than |s_max| */
+        if (d > s_max)
+            d = s_max;
+        glue_order(b) = normal;
+        glue_sign(b) = stretching;
+        glue_set(b) = unfloat(d/(float) s_max);
+        b_max += d;
+    }
+
+    if (boxtype == vlist_node) {
+        height(b) = b_max;
+    } else {
+        width(b) = b_max;
+    }
+
+    return b;
+}
+
+@ The |var_delimiter| function, which finds or constructs a sufficiently
+  large delimiter, is the most interesting of the auxiliary functions that
+  currently concern us. Given a pointer |d| to a delimiter field in some noad,
+  together with a size code |s| and a vertical distance |v|, this function
+  returns a pointer to a box that contains the smallest variant of |d| whose
+  height plus depth is |v| or more. (And if no variant is large enough, it
+  returns the largest available variant.) In particular, this routine will
+  construct arbitrarily large delimiters from extensible components, if
+  |d| leads to such characters.
+
+  The value returned is a box whose |shift_amount| has been set so that
+  the box is vertically centered with respect to the axis in the given size.
+  If a built-up symbol is returned, the height of the box before shifting
+  will be the height of its topmost component.
+
+@c
+static void endless_loop_error(internal_font_number g, int y)
+{
+    char s[256];
+    const char *hlp[] = {
+        "You managed to create a seemingly endless charlist chain in the current",
+        "font. I have counted until 10000 already and still have not escaped, so"
+        "I will jump out of the loop all by myself now. Fix your font!",
+        NULL
+    };
+    snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)",
+             (int) y, font_name(g));
+    tex_error(s, hlp);
+}
+
+static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, int cur_style, boolean shift, boolean *stack, scaled *delta, int *same)
+{
+    pointer b;                     /* the box that will be constructed */
+    internal_font_number f, g;     /* best-so-far and tentative font codes */
+    int c, i, x, y;                /* best-so-far and tentative character codes */
+    scaled u;                      /* height-plus-depth of a tentative character */
+    scaled w = 0;                  /* largest height-plus-depth so far */
+    int z;                         /* runs through font family members */
+    boolean large_attempt = false; /* are we trying the ``large'' variant? */
+    pointer att = null;            /* to save the current attribute list */
+    int emas = 0 ;
+    boolean do_parts = false;
+    extinfo *ext;
+    f = null_font;
+    c = 0;
+    if (d == null)
+        goto FOUND;
+    z = small_fam(d);
+    x = small_char(d);
+    i = 0;
+    if (same != NULL) {
+        emas = *same ;
+        same = 0;
+    }
+    while (true) {
+        /*
+            The search process is complicated slightly by the facts that some of the
+            characters might not be present in some of the fonts, and they might not
+            be probed in increasing order of height.
+        */
+        if ((z != 0) || (x != 0)) {
+            g = fam_fnt(z, s);
+            if (g != null_font) {
+                y = x;
+              CONTINUE:
+                i++;
+                if (char_exists(g, y)) {
+                    if (flat)
+                        u = char_width(g, y);
+                    else
+                        u = height_plus_depth(g, y);
+                    if (u > w) {
+                        f = g;
+                        c = y;
+                        w = u;
+                        if (u >= v)
+                            goto FOUND;
+                    }
+                    if (char_tag(g, y) == ext_tag) {
+                        f = g;
+                        c = y;
+                        do_parts = true;
+                        goto FOUND;
+                    }
+                    if (i > 10000) {
+                        /* endless loop */
+                        endless_loop_error(g, y);
+                        goto FOUND;
+                    }
+                    if (char_tag(g, y) == list_tag) {
+                        y = char_remainder(g, y);
+                        goto CONTINUE;
+                    }
+                }
+            }
+        }
+        if (large_attempt)
+            goto FOUND; /* there were none large enough */
+        large_attempt = true;
+        z = large_fam(d);
+        x = large_char(d);
+    }
+  FOUND:
+    if (d != null) {
+        att = node_attr(d);
+        node_attr(d) = null;
+        flush_node(d);
+    }
+    if (f != null_font) {
+        /*
+            When the following code is executed, |do_parts| will be true
+            if a built-up symbol is supposed to be returned.
+        */
+        ext = NULL;
+        if ((do_parts) && ((!flat && (ext = get_charinfo_vert_variants(char_info(f,c))) != NULL)
+                       ||  ( flat && (ext = get_charinfo_hor_variants (char_info(f,c))) != NULL))) {
+            if (flat) {
+                b = get_delim_box(d, ext, f, v, att, cur_style, hlist_node);
+            } else {
+                b = get_delim_box(d, ext, f, v, att, cur_style, vlist_node);
+            }
+            if (delta != NULL) {
+                if (do_new_math(f)) {
+                    *delta = char_vert_italic(f,x);
+                } else {
+                    *delta = char_italic(f,x);
+                }
+            }
+            if (stack != NULL)
+                *stack = true ;
+        } else {
+            if (same != NULL && x == c) {
+                *same = emas;
+            }
+            b = char_box(f, c, att);
+            if (!do_new_math(f)) {
+                /* italic gets added to width */
+                width(b) += char_italic(f, c);
+            }
+            if (delta != NULL) {
+                *delta = char_italic(f, c); /* was historically (f, x) */
+            }
+            if (stack != NULL)
+                *stack = false ;
+        }
+    } else {
+        b = new_null_box();
+        reset_attributes(b, att);
+        if (flat) {
+            width(b) = 0;
+        } else {
+            /* use this width if no delimiter was found */
+            width(b) = null_delimiter_space_par;
+        }
+        if (delta != NULL) {
+            *delta = 0;
+        }
+        if (stack != NULL)
+            *stack = false ;
+    }
+    if (!flat) {
+        if (emas == 0 || ! delimitermodenoshift) {
+            /* vertical variant */
+            shift_amount(b) = half(height(b) - depth(b));
+            if (shift) {
+                shift_amount(b) -= math_axis_size(s);
+            }
+        }
+    }
+    delete_attribute_ref(att);
+    return b;
+}
+
+@ The next subroutine is much simpler; it is used for numerators and
+denominators of fractions as well as for displayed operators and
+their limits above and below. It takes a given box~|b| and
+changes it so that the new box is centered in a box of width~|w|.
+The centering is done by putting \.{\\hss} glue at the left and right
+of the list inside |b|, then packaging the new box; thus, the
+actual box might not really be centered, if it already contains
+infinite glue.
+
+The given box might contain a single character whose italic correction
+has been added to the width of the box; in this case a compensating
+kern is inserted.
+
+@c
+static pointer rebox(pointer b, scaled w)
+{
+    pointer p, q, r, att;       /* temporary registers for list manipulation */
+    internal_font_number f;     /* font in a one-character box */
+    scaled v;                   /* width of a character without italic correction */
+
+    if ((width(b) != w) && (list_ptr(b) != null)) {
+        if (type(b) == vlist_node) {
+            p = hpack(b, 0, additional, -1);
+            reset_attributes(p, node_attr(b));
+            b = p;
+        }
+        p = list_ptr(b);
+        att = node_attr(b);
+        add_node_attr_ref(att);
+        if ((is_char_node(p)) && (vlink(p) == null)) {
+            f = font(p);
+            v = char_width(f, character(p));
+            if (v != width(b)) {
+                q = new_kern(width(b) - v);
+                reset_attributes(q, att);
+                couple_nodes(p,q);
+            }
+        }
+        list_ptr(b) = null;
+        flush_node(b);
+        b = new_glue(ss_glue);
+        reset_attributes(b, att);
+        couple_nodes(b,p);
+        while (vlink(p) != null)
+            p = vlink(p);
+        q = new_glue(ss_glue);
+        reset_attributes(q, att);
+        couple_nodes(p,q);
+        r = hpack(b, w, exactly, -1);
+        reset_attributes(r, att);
+        delete_attribute_ref(att);
+        return r;
+    } else {
+        width(b) = w;
+        return b;
+    }
+}
+
+@ Here is a subroutine that creates a new glue specification from another
+one that is expressed in `\.{mu}', given the value of the math unit.
+
+@c
+#define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen)
+
+static pointer math_glue(pointer g, scaled m)
+{
+    int n = x_over_n(m, unity); /* integer part of |m| */
+    scaled f = tex_remainder;   /* fraction part of |m| */
+    pointer p;                  /* the new glue specification */
+    if (f < 0) {
+        decr(n);
+        f = f + unity;
+    }
+    p = new_node(glue_node, 0);
+    width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */
+    stretch_order(p) = stretch_order(g);
+    if (stretch_order(p) == normal)
+        stretch(p) = mu_mult(stretch(g));
+    else
+        stretch(p) = stretch(g);
+    shrink_order(p) = shrink_order(g);
+    if (shrink_order(p) == normal)
+        shrink(p) = mu_mult(shrink(g));
+    else
+        shrink(p) = shrink(g);
+    return p;
+}
+
+static void math_glue_to_glue(pointer p, scaled m)
+{
+    int n = x_over_n(m, unity); /* integer part of |m| */
+    scaled f = tex_remainder;   /* fraction part of |m| */
+    if (f < 0) {
+        decr(n);
+        f = f + unity;
+    }
+    width(p) = mu_mult(width(p)); /* convert \.{mu} to \.{pt} */
+    if (stretch_order(p) == normal)
+        stretch(p) = mu_mult(stretch(p));
+    if (shrink_order(p) == normal)
+        shrink(p) = mu_mult(shrink(p));
+    subtype(p) = normal;
+}
+
+@ The |math_kern| subroutine removes |mu_glue| from a kern node, given
+the value of the math unit.
+
+@c
+static void math_kern(pointer p, scaled m)
+{
+    int n;    /* integer part of |m| */
+    scaled f; /* fraction part of |m| */
+    if (subtype(p) == mu_glue) {
+        n = x_over_n(m, unity);
+        f = tex_remainder;
+        if (f < 0) {
+            decr(n);
+            f = f + unity;
+        }
+        width(p) = mu_mult(width(p));
+        subtype(p) = italic_kern; /* this is weird, it's not a italic but explicit_kern */
+    }
+}
+
+@ @c
+void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
+{
+    int callback_id;
+    int a, sfix;
+    if (p == null) {
+        vlink(temp_head) = null;
+        return;
+    }
+    finalize_math_parameters();
+    callback_id = callback_defined(mlist_to_hlist_callback);
+    if (callback_id > 0) {
+        sfix = lua_gettop(Luas);
+        if (!get_callback(Luas, callback_id)) {
+            lua_settop(Luas, sfix);
+            return;
+        }
+        alink(p) = null ;
+        nodelist_to_lua(Luas, p);
+        lua_push_math_style_name(Luas, mstyle);
+        lua_pushboolean(Luas, penalties);
+        if (lua_pcall(Luas, 3, 1, 0) != 0) {            /* 3 args, 1 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("mlist to hlist",errmsg); /* to be done */
+            return;
+        }
+        a = nodelist_from_lua(Luas);
+        /* alink(vlink(a)) = null; */
+        lua_settop(Luas, sfix);
+        vlink(temp_head) = a;
+    } else if (callback_id == 0) {
+        mlist_to_hlist(p, penalties, mstyle);
+    } else {
+        vlink(temp_head) = null;
+    }
+}
+
+@ The recursion in |mlist_to_hlist| is due primarily to a subroutine
+called |clean_box| that puts a given noad field into a box using a given
+math style; |mlist_to_hlist| can call |clean_box|, which can call
+|mlist_to_hlist|.
+@^recursion@>
+
+The box returned by |clean_box| is ``clean'' in the
+sense that its |shift_amount| is zero.
+
+@c
+static pointer clean_box(pointer p, int s, int cur_style)
+{
+    pointer q;            /* beginning of a list to be boxed */
+    pointer x;            /* box to be returned */
+    pointer r;            /* temporary pointer */
+    pointer mlist = null; /* beginning of mlist to be translated */
+    switch (type(p)) {
+        case math_char_node:
+            mlist = new_noad();
+            r = math_clone(p);
+            nucleus(mlist) = r;
+            break;
+        case sub_box_node:
+            q = math_list(p);
+            goto FOUND;
+            break;
+        case sub_mlist_node:
+            mlist = math_list(p);
+            break;
+        default:
+            q = new_null_box();
+            goto FOUND;
+    }
+    mlist_to_hlist(mlist, false, s);
+    q = vlink(temp_head); /* recursive call */
+    setup_cur_size(cur_style);
+  FOUND:
+    if (is_char_node(q) || (q == null))
+        x = hpack(q, 0, additional, -1);
+    else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0))
+        x = q; /* it's already clean */
+    else
+        x = hpack(q, 0, additional, -1);
+    if (x != q && q != null)
+        reset_attributes(x, node_attr(q));
+    /* Here we save memory space in a common case. */
+    q = list_ptr(x);
+    if (is_char_node(q)) {
+        r = vlink(q);
+        if (r != null) {
+            if (vlink(r) == null) {
+                if (!is_char_node(r)) {
+                    if (type(r) == kern_node) {
+                        /* unneeded italic correction */
+                        flush_node(r);
+                        vlink(q) = null;
+                    }
+                }
+            }
+        }
+    }
+    return x;
+}
+
+@ It is convenient to have a procedure that converts a |math_char|
+field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c|
+to the font code and character code of a given noad field.
+It also takes care of issuing error messages for
+nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false|
+after |fetch| has acted, and the field will also have been reset to |null|.
+
+The outputs of |fetch| are placed in global variables.
+
+@c
+internal_font_number cur_f; /* the |font| field of a |math_char| */
+int cur_c;                  /* the |character| field of a |math_char| */
+
+@ Here we unpack the |math_char| field |a|.
+
+@c static void fetch(pointer a)
+{
+    cur_c = math_character(a);
+    cur_f = fam_fnt(math_fam(a), cur_size);
+    if (cur_f == null_font) {
+        char *msg;
+        const char *hlp[] = {
+            "Somewhere in the math formula just ended, you used the",
+            "stated character from an undefined font family. For example,",
+            "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
+            "and I'll try to forget that I needed that character.",
+            NULL
+        };
+        msg = xmalloc(256);
+        snprintf(msg, 255, "\\%s%d is undefined (character %d)",
+                 math_size_string(cur_size), (int) math_fam(a), (int) cur_c);
+        tex_error(msg, hlp);
+        free(msg);
+    } else if (!(char_exists(cur_f, cur_c))) {
+        char_warning(cur_f, cur_c);
+    }
+}
+
+@ We need to do a lot of different things, so |mlist_to_hlist| makes two
+passes over the given mlist.
+
+The first pass does most of the processing: It removes ``mu'' spacing from
+glue, it recursively evaluates all subsidiary mlists so that only the
+top-level mlist remains to be handled, it puts fractions and square roots
+and such things into boxes, it attaches subscripts and superscripts, and
+it computes the overall height and depth of the top-level mlist so that
+the size of delimiters for a |fence_noad| will be known.
+The hlist resulting from each noad is recorded in that noad's |new_hlist|
+field, an integer field that replaces the |nucleus| or |thickness|.
+@^recursion@>
+
+The second pass eliminates all noads and inserts the correct glue and
+penalties between nodes.
+
+@c
+static void assign_new_hlist(pointer q, pointer r)
+{
+    switch (type(q)) {
+    case fraction_noad:
+        math_list(numerator(q)) = null;
+        flush_node(numerator(q));
+        numerator(q) = null;
+        math_list(denominator(q)) = null;
+        flush_node(denominator(q));
+        denominator(q) = null;
+        break;
+    case radical_noad:
+    case simple_noad:
+    case accent_noad:
+        if (nucleus(q) != null) {
+            math_list(nucleus(q)) = null;
+            flush_node(nucleus(q));
+            nucleus(q) = null;
+        }
+        break;
+    }
+    new_hlist(q) = r;
+}
+
+@ @c
+#define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
+
+@ Most of the actual construction work of |mlist_to_hlist| is done
+by procedures with names like |make_fraction|, |make_radical|, etc. To
+illustrate the general setup of such procedures, let's begin with a
+couple of simple ones.
+
+@c
+static void make_over(pointer q, int cur_style, int cur_size, int cur_fam)
+{
+    pointer p;
+    p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style),
+                overbar_vgap(cur_style), overbar_rule(cur_style),
+                overbar_kern(cur_style), node_attr(nucleus(q)), math_over_rule, cur_size, cur_fam);
+    math_list(nucleus(q)) = p;
+    type(nucleus(q)) = sub_box_node;
+}
+
+static void make_under(pointer q, int cur_style, int cur_size, int cur_fam)
+{
+    pointer p, x, y, r;         /* temporary registers for box construction */
+    scaled delta;               /* overall height plus depth */
+    x = clean_box(nucleus(q), cur_style, cur_style);
+    p = new_kern(underbar_vgap(cur_style));
+    reset_attributes(p, node_attr(q));
+    couple_nodes(x,p);
+    r = do_fraction_rule(underbar_rule(cur_style), node_attr(q), math_under_rule, cur_size, cur_fam);
+    couple_nodes(p,r);
+    y = vpackage(x, 0, additional, max_dimen, math_direction_par);
+    reset_attributes(y, node_attr(q));
+    delta = height(y) + depth(y) + underbar_kern(cur_style);
+    height(y) = height(x);
+    depth(y) = delta - height(y);
+    math_list(nucleus(q)) = y;
+    type(nucleus(q)) = sub_box_node;
+}
+
+static void make_vcenter(pointer q)
+{
+    pointer v;                  /* the box that should be centered vertically */
+    scaled delta;               /* its height plus depth */
+    v = math_list(nucleus(q));
+    if (type(v) != vlist_node)
+        confusion("vcenter");
+    delta = height(v) + depth(v);
+    height(v) = math_axis_size(cur_size) + half(delta);
+    depth(v) = delta - height(v);
+}
+
+@ According to the rules in the \.{DVI} file specifications, we ensure alignment
+@^square roots@>
+between a square root sign and the rule above its nucleus by assuming that the
+baseline of the square-root symbol is the same as the bottom of the rule. The
+height of the square-root symbol will be the thickness of the rule, and the
+depth of the square-root symbol should exceed or equal the height-plus-depth
+of the nucleus plus a certain minimum clearance~|psi|. The symbol will be
+placed so that the actual clearance is |psi| plus half the excess.
+
+@c
+static void make_hextension(pointer q, int cur_style)
+{
+    pointer e, p;
+    halfword w;
+    boolean stack = false;
+    e = do_delimiter(q, left_delimiter(q), cur_size, radicalwidth(q), true, cur_style, true, &stack, NULL, NULL);
+    w = width(e);
+    if (!stack&& (radicalwidth(q) != 0) && (radicalwidth(q) != width(e))) {
+        if (radicalmiddle(q)) {
+            p = new_kern(half(radicalwidth(q)-w));
+            reset_attributes(p, node_attr(q));
+            couple_nodes(p,e);
+            e = p;
+            w = radicalwidth(q);
+        } else if (radicalexact(q)) {
+            w = radicalwidth(q);
+        }
+    }
+    e = hpack(e, 0, additional, -1);
+    width(e) = w ;
+    reset_attributes(e, node_attr(q));
+    math_list(nucleus(q)) = e;
+    left_delimiter(q) = null;
+}
+
+static void make_radical(pointer q, int cur_style)
+{
+    pointer x, y, p, l1, l2;     /* temporary registers for box construction */
+    scaled delta, clr, theta, h; /* dimensions involved in the calculation */
+    x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
+    clr = radical_vgap(cur_style);
+    theta = radical_rule_par(cur_style);
+    if (theta == undefined_math_parameter) {
+        /* a real radical */
+        theta = fraction_rule(cur_style);
+        y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL);
+        /*
+            If |y| is a composite then set |theta| to the height of its top
+            character, else set it to the height of |y|.
+        */
+        l1 = list_ptr(y);
+        if ((l1 != null) && (type(l1) == hlist_node)) {
+            /* possible composite */
+            l2 = list_ptr(l1);
+            if ((l2 != null) && (type(l2) == glyph_node)) {
+                /* top character */
+                theta = char_height(font(l2), character(l2));
+            } else {
+                theta = height(y);
+            }
+        } else {
+            theta = height(y);
+        }
+    } else {
+        /* not really a radical but we use its node, historical sharing (like in mathml) */
+        y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL);
+    }
+    left_delimiter(q) = null;
+    delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr);
+    if (delta > 0) {
+        /* increase the actual clearance */
+        clr = clr + half(delta);
+    }
+    shift_amount(y) = (height(y) - theta) - (height(x) + clr);
+    h = depth(y) + height(y);
+    p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y), math_radical_rule, cur_size, small_fam(left_delimiter(q)));
+    couple_nodes(y,p);
+    if (degree(q) != null) {
+        scaled wr, br, ar;
+        pointer r = clean_box(degree(q), script_script_style, cur_style);
+        reset_attributes(r, node_attr(degree(q)));
+        wr = width(r);
+        if (wr == 0) {
+            flush_node(r);
+        } else {
+            br = radical_degree_before(cur_style);
+            ar = radical_degree_after(cur_style);
+            if (-ar > (wr + br))
+                ar = -(wr + br);
+            x = new_kern(ar);
+            reset_attributes(x, node_attr(degree(q)));
+            couple_nodes(x,y);
+            shift_amount(r) =
+                -((xn_over_d(h, radical_degree_raise(cur_style), 100)) -
+                  depth(y) - shift_amount(y));
+            couple_nodes(r,x);
+            x = new_kern(br);
+            reset_attributes(x, node_attr(degree(q)));
+            couple_nodes(x,r);
+            y = x;
+        }
+        /* for \.{\\Uroot ..{<list>}{}} : */
+        math_list(degree(q)) = null;
+        flush_node(degree(q));
+    }
+    p = hpack(y, 0, additional, -1);
+    reset_attributes(p, node_attr(q));
+    math_list(nucleus(q)) = p;
+    type(nucleus(q)) = sub_box_node;
+}
+
+@ Construct a vlist box
+
+@c
+static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down)
+{
+    pointer p;  /* temporary register for box construction */
+    pointer v = new_null_box();
+    type(v) = vlist_node;
+    height(v) = shift_up + height(x);
+    depth(v) = depth(y) + shift_down;
+    reset_attributes(v, node_attr(q));
+    p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
+    reset_attributes(p, node_attr(q));
+    couple_nodes(p,y);
+    couple_nodes(x,p);
+    list_ptr(v) = x;
+    return v;
+}
+
+/* when exact use radicalwidth (y is delimiter) */
+
+@ @c
+
+#define fixup_widths(q,x,y) do { \
+    if (width(y) >= width(x)) { \
+        if (radicalwidth(q) != 0) { \
+            shift_amount(x) += half(width(y)-width(x)) ; \
+        } \
+        width(x) = width(y); \
+    } else { \
+        if (radicalwidth(q) != 0) { \
+            shift_amount(y) += half(width(x)-width(y)) ; \
+        } \
+        width(y) = width(x); \
+    } \
+} while (0)
+
+
+#define check_radical(q,stack,r,t) do { \
+    if (!stack && (width(r) >= width(t)) && (radicalwidth(q) != 0) && (radicalwidth(q) != width(r))) { \
+        if (radicalleft(q)) { \
+            halfword p = new_kern(radicalwidth(q)-width(r)); \
+            reset_attributes(p, node_attr(q)); \
+            couple_nodes(p,r); \
+            r = hpack(p, 0, additional, -1); \
+            width(r) = radicalwidth(q); \
+            reset_attributes(r, node_attr(q)); \
+        } else if (radicalmiddle(q)) { \
+            halfword p = new_kern(half(radicalwidth(q)-width(r))); \
+            reset_attributes(p, node_attr(q)); \
+            couple_nodes(p,r); \
+            r = hpack(p, 0, additional, -1); \
+            width(r) = radicalwidth(q); \
+            reset_attributes(r, node_attr(q)); \
+        } else if (radicalright(q)) { \
+            /* also kind of exact compared to vertical */ \
+            r = hpack(r, 0, additional, -1); \
+            width(r) = radicalwidth(q); \
+            reset_attributes(r, node_attr(q)); \
+        } \
+    } \
+} while (0)
+
+#define check_widths(q,p) do { \
+    if (radicalwidth(q) != 0) { \
+        wd = radicalwidth(q); \
+    } else { \
+        wd = width(p); \
+    } \
+} while (0)
+
+@ this has the |nucleus| box |x| as a limit above an extensible delimiter |y|
+
+@c
+static void make_over_delimiter(pointer q, int cur_style)
+{
+    pointer x, y, v; /* temporary registers for box construction */
+    scaled shift_up, shift_down, clr, delta, wd;
+    boolean stack;
+    x = clean_box(nucleus(q), sub_style(cur_style), cur_style);
+    check_widths(q,x);
+    y = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL, NULL);
+    left_delimiter(q) = null;
+    check_radical(q,stack,y,x);
+    fixup_widths(q, x, y);
+    shift_up = over_delimiter_bgap(cur_style);
+    shift_down = 0;
+    clr = over_delimiter_vgap(cur_style);
+    delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
+    if (delta > 0) {
+        shift_up = shift_up + delta;
+    }
+    v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
+    width(v) = width(x); /* this also equals |width(y)| */
+    math_list(nucleus(q)) = v;
+    type(nucleus(q)) = sub_box_node;
+}
+
+@ this has the extensible delimiter |x| as a limit below |nucleus| box |y|
+
+@c
+static void make_under_delimiter(pointer q, int cur_style)
+{
+    pointer x, y, v; /* temporary registers for box construction */
+    scaled shift_up, shift_down, clr, delta, wd;
+    boolean stack;
+    y = clean_box(nucleus(q), sup_style(cur_style), cur_style);
+    check_widths(q,y);
+    x = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL, NULL);
+    left_delimiter(q) = null;
+    check_radical(q,stack,x,y);
+    fixup_widths(q, x, y);
+    shift_up = 0;
+    shift_down = under_delimiter_bgap(cur_style);
+    clr = under_delimiter_vgap(cur_style);
+    delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
+    if (delta > 0) {
+        shift_down = shift_down + delta;
+    }
+    v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
+    width(v) = width(y); /* this also equals |width(y)| */
+    math_list(nucleus(q)) = v;
+    type(nucleus(q)) = sub_box_node;
+}
+
+@ this has the extensible delimiter |x| as a limit above |nucleus| box |y|
+
+@c
+static void make_delimiter_over(pointer q, int cur_style)
+{
+    pointer x, y, v; /* temporary registers for box construction */
+    scaled shift_up, shift_down, clr, actual, wd;
+    boolean stack;
+    y = clean_box(nucleus(q), cur_style, cur_style);
+    check_widths(q,y);
+    x = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL, NULL);
+    left_delimiter(q) = null;
+    check_radical(q,stack,x,y);
+    fixup_widths(q, x, y);
+    shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x);
+    shift_down = 0;
+    clr = over_delimiter_vgap(cur_style);
+    actual = shift_up - height(y);
+    if (actual < clr) {
+        shift_up = shift_up + (clr-actual);
+    }
+    v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
+    width(v) = width(x); /* this also equals |width(y)| */
+    math_list(nucleus(q)) = v;
+    type(nucleus(q)) = sub_box_node;
+}
+
+@ this has the extensible delimiter |y| as a limit below a |nucleus| box |x|
+
+@c
+static void make_delimiter_under(pointer q, int cur_style)
+{
+    pointer x, y, v;            /* temporary registers for box construction */
+    scaled shift_up, shift_down, clr, actual, wd;
+    boolean stack;
+    x = clean_box(nucleus(q), cur_style, cur_style);
+    check_widths(q,x);
+    y = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL, NULL);
+    left_delimiter(q) = null;
+    check_radical(q,stack,y,x);
+    fixup_widths(q, x, y);
+    shift_up = 0;
+    shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y);
+    clr = under_delimiter_vgap(cur_style);
+    actual = shift_down - depth(x);
+    if (actual<clr) {
+       shift_down += (clr-actual);
+    }
+    v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
+    width(v) = width(y); /* this also equals |width(y)| */
+    math_list(nucleus(q)) = v;
+    type(nucleus(q)) = sub_box_node;
+}
+
+@ Slants are not considered when placing accents in math mode. The accenter is
+centered over the accentee, and the accent width is treated as zero with
+respect to the size of the final box.
+
+@c
+#define TOP_CODE            1
+#define BOT_CODE            2
+#define OVERLAY_CODE        4
+#define STRETCH_ACCENT_CODE 8
+
+static boolean compute_accent_skew(pointer q, int flags, scaled *s)
+{
+    pointer p;                     /* temporary register for box construction */
+    boolean s_is_absolute = false; /* will be true if a top-accent is placed in |s| */
+    if (type(nucleus(q)) == math_char_node) {
+        fetch(nucleus(q));
+        if (do_new_math(cur_f)) {
+            /*
+                there is no bot_accent so let's assume similarity
+
+                if (flags & (TOP_CODE | OVERLAY_CODE)) {
+                    *s = char_top_accent(cur_f, cur_c);
+                    if (*s != INT_MIN) {
+                        s_is_absolute = true;
+                    }
+                } else {
+                    *s = char_bot_accent(cur_f, cur_c);
+                    if (*s != INT_MIN) {
+                        s_is_absolute = true;
+                    }
+                }
+            */
+            *s = char_top_accent(cur_f, cur_c);
+            if (*s != INT_MIN) {
+                s_is_absolute = true;
+            }
+        } else {
+            if (flags & TOP_CODE) {
+                *s = get_kern(cur_f, cur_c, skew_char(cur_f));
+            } else {
+                *s = 0;
+            }
+        }
+    } else if (type(nucleus(q)) == sub_mlist_node) {
+        /*
+            if |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we
+
+            * use the positioning of the nucleus of that noad, recursing until
+            * the inner most |accent_noad|. This way multiple stacked accents are
+            * aligned to the inner most one.
+
+            the vlink test was added in version 1.06, so that we only consider a lone
+            noad:
+
+            $
+                \Umathaccent bottom 0 0 "023DF {   \Umathaccent fixed 0 0 "00302 { m } r } \quad
+                \Umathaccent bottom 0 0 "023DF { l \Umathaccent fixed 0 0 "00302 { m } r } \quad
+                \Umathaccent bottom 0 0 "023DF { l \Umathaccent fixed 0 0 "00302 { m }   } \quad
+                \Umathaccent bottom 0 0 "023DF {   \Umathaccent fixed 0 0 "00302 { m }   } \quad
+                \Umathaccent bottom 0 0 "023DF { l                                      r }
+            $
+
+        */
+        p = math_list(nucleus(q));
+        if (type(p) == accent_noad && vlink(p) == null) {
+            s_is_absolute = compute_accent_skew(p, flags, s);
+        }
+    } else {
+    }
+
+    return s_is_absolute;
+}
+
+static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style)
+{
+    pointer p, r, x, y;    /* temporary registers for box construction */
+    scaled s;              /* amount to skew the accent to the right */
+    scaled h;              /* height of character being accented */
+    scaled delta;          /* space to remove between accent and accentee */
+    scaled w;              /* width of the accentee, not including sub/superscripts */
+    boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */
+    scaled fraction ;
+    scaled ic = 0;
+    scaled target ;
+    extinfo *ext;
+    pointer attr_p;
+    attr_p = (flags & TOP_CODE ? top_accent_chr(q) : flags & BOT_CODE ? bot_accent_chr(q) : overlay_accent_chr(q));
+    fraction = accentfraction(q);
+    c = cur_c;
+    f = cur_f;
+    s = 1;
+    if (fraction == 0) {
+        fraction = 1000;
+    }
+    /* Compute the amount of skew, or set |s| to an alignment point */
+    s_is_absolute = compute_accent_skew(q, flags, &s);
+    x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
+    w = width(x);
+    h = height(x);
+    if (do_new_math(cur_f) && !s_is_absolute) {
+        s = half(w);
+        s_is_absolute = true;
+    }
+    /* Switch to a larger accent if available and appropriate */
+    y = null;
+    ext = NULL;
+    if (flags & OVERLAY_CODE) {
+        if (fraction > 0) {
+            target = xn_over_d(h,fraction,1000);
+        } else {
+            target = h;
+        }
+    } else {
+        if (fraction > 0) {
+            target = xn_over_d(w,fraction,1000);
+        } else {
+            target = w;
+        }
+    }
+    if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) {
+      while (1) {
+        if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) {
+            /* a bit weird for an overlay but anyway, here we don't need a factor as we don't step */
+            y = get_delim_box(q, ext, f, w, node_attr(attr_p), cur_style, hlist_node);
+            break;
+        } else if (char_tag(f, c) != list_tag) {
+            break;
+        } else {
+            int yy = char_remainder(f, c);
+            if (!char_exists(f, yy)) {
+                break;
+            } else if (flags & OVERLAY_CODE) {
+                if (char_height(f, yy) > target) {
+                   break;
+                }
+            } else {
+                if (char_width(f, yy) > target)
+                   break;
+            }
+            c = yy;
+        }
+      }
+    }
+    if (y == null) {
+        y = char_box(f, c, node_attr(attr_p)); /* italic gets added to width */
+    }
+    if (flags & TOP_CODE) {
+        if (h < accent_base_height(f)) {
+            delta = h;
+        } else {
+            delta = accent_base_height(f);
+        }
+    } else if (flags & OVERLAY_CODE) {
+        delta = half(height(y) + depth(y) + height(x) + depth(x)); /* center the accent vertically around the accentee */
+    } else {
+        delta = 0; /* hm */
+    }
+    if ((supscr(q) != null) || (subscr(q) != null)) {
+        if (type(nucleus(q)) == math_char_node) {
+            /* swap the subscript and superscript into box |x| */
+            flush_node_list(x);
+            x = new_noad();
+            r = math_clone(nucleus(q));
+            nucleus(x) = r;
+            supscr(x) = supscr(q);
+            supscr(q) = null;
+            subscr(x) = subscr(q);
+            subscr(q) = null;
+            type(nucleus(q)) = sub_mlist_node;
+            math_list(nucleus(q)) = x;
+            x = clean_box(nucleus(q), cur_style, cur_style);
+            delta = delta + height(x) - h;
+            h = height(x);
+        }
+    } else if ((vlink(q) != null) && (type(nucleus(q)) == math_char_node)) {
+        /* only pure math char nodes */
+        internal_font_number f = fam_fnt(math_fam(nucleus(q)),cur_size);
+        if (do_new_math(f)) {
+            ic = char_italic(f,math_character(nucleus(q)));
+        }
+    }
+    /* the top accents of both characters are aligned */
+    if (s_is_absolute) {
+        scaled sa;
+        if (ext != NULL) {
+            /* if the accent is extensible just take the center */
+            sa = half(width(y));
+        } else {
+            /*
+                there is no bot_accent so let's assume similarity
+
+                if (flags & BOT_CODE) {
+                    sa = char_bot_accent(f, c);
+                } else {
+                    sa = char_top_accent(f, c);
+                }
+            */
+            sa = char_top_accent(f, c);
+        }
+        if (sa == INT_MIN) {
+            /* just take the center */
+            sa = half(width(y));
+        }
+        if (math_direction_par == dir_TRT) {
+           shift_amount(y) = s + sa - width(y);
+        } else {
+           shift_amount(y) = s - sa;
+        }
+    } else {
+        if (width(y)== 0) {
+            shift_amount(y) = s + w;
+        } else if (math_direction_par == dir_TRT) {
+            shift_amount(y) = s + width(y); /* ok? */
+        } else {
+            shift_amount(y) = s + half(w - width(y));
+        }
+    }
+    width(y) = 0;
+    if (flags & (TOP_CODE | OVERLAY_CODE)) {
+        p = new_kern(-delta);
+        reset_attributes(p, node_attr(q));
+        couple_nodes(p,x);
+        couple_nodes(y,p);
+    } else {
+        couple_nodes(x,y);
+        y = x;
+    }
+    r = vpackage(y, 0, additional, max_dimen, math_direction_par);
+    reset_attributes(r, node_attr(q));
+    width(r) = width(x);
+    y = r;
+    if (flags & (TOP_CODE | OVERLAY_CODE)) {
+        if (height(y) < h) {
+            /* make the height of box |y| equal to |h| */
+            p = new_kern(h - height(y));
+            reset_attributes(p, node_attr(q));
+            try_couple_nodes(p,list_ptr(y));
+            list_ptr(y) = p;
+            height(y) = h;
+        }
+    } else {
+        shift_amount(y) = -(h - height(y));
+    }
+    if (ic != 0) {
+        /* old font codepath has ic built in, new font code doesn't */
+        width(r) += ic ;
+    }
+    math_list(nucleus(q)) = y;
+    type(nucleus(q)) = sub_box_node;
+}
+
+static void make_math_accent(pointer q, int cur_style)
+{
+    int topstretch = !(subtype(q) % 2);
+    int botstretch = !(subtype(q) / 2);
+
+    if (top_accent_chr(q) != null) {
+        fetch(top_accent_chr(q));
+        if (char_exists(cur_f, cur_c)) {
+          do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
+        }
+        flush_node(top_accent_chr(q));
+        top_accent_chr(q) = null;
+    }
+    if (bot_accent_chr(q) != null) {
+        fetch(bot_accent_chr(q));
+        if (char_exists(cur_f, cur_c)) {
+          do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
+        }
+        flush_node(bot_accent_chr(q));
+        bot_accent_chr(q) = null;
+    }
+    if (overlay_accent_chr(q) != null) {
+        fetch(overlay_accent_chr(q));
+        if (char_exists(cur_f, cur_c)) {
+          do_make_math_accent(q, cur_f, cur_c, OVERLAY_CODE | STRETCH_ACCENT_CODE, cur_style);
+        }
+        flush_node(overlay_accent_chr(q));
+        overlay_accent_chr(q) = null;
+    }
+}
+
+@ The |make_fraction| procedure is a bit different because it sets
+|new_hlist(q)| directly rather than making a sub-box.
+
+@c
+static void make_fraction(pointer q, int cur_style)
+{
+    pointer p, p1, p2, v, x, y, z, l, r, m; /* temporary registers for box construction */
+    scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2;
+    /* dimensions for box calculations */
+    if (thickness(q) == default_code)
+        thickness(q) = fraction_rule(cur_style);
+    /*
+        Create equal-width boxes |x| and |z| for the numerator and denominator,
+        and compute the default amounts |shift_up| and |shift_down| by which they
+        are displaced from the baseline
+    */
+
+    x = clean_box(numerator(q), num_style(cur_style), cur_style);
+    z = clean_box(denominator(q), denom_style(cur_style), cur_style);
+
+    if (middle_delimiter(q) != null) {
+        delta = 0;
+        m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
+        middle_delimiter(q) = null;
+    } else {
+        m = null ;
+        if (width(x) < width(z)) {
+            x = rebox(x, width(z));
+        } else {
+            z = rebox(z, width(x));
+        }
+    }
+
+    if (m != null) {
+        shift_up = 0;
+        shift_down = 0;
+    } else if (thickness(q) == 0) {
+        shift_up = stack_num_up(cur_style);
+        shift_down = stack_denom_down(cur_style);
+        /*
+            the numerator and denominator must be separated by a certain minimum
+            clearance, called |clr| in the following program. The difference between
+            |clr| and the actual clearance is |2delta|.
+        */
+        clr1 = stack_vgap(cur_style);
+        delta = half(clr1 - ((shift_up - depth(x)) - (height(z) - shift_down)));
+        if (delta > 0) {
+            shift_up = shift_up + delta;
+            shift_down = shift_down + delta;
+        }
+    } else {
+        shift_up = fraction_num_up(cur_style);
+        shift_down = fraction_denom_down(cur_style);
+        /*
+            in the case of a fraction line, the minimum clearance depends on the actual
+            thickness of the line.
+        */
+        clr1 = fraction_num_vgap(cur_style);
+        clr2 = fraction_denom_vgap(cur_style);
+        delta = half(thickness(q));
+        if (fractionexact(q)) {
+            delta1 = clr1 - ((shift_up   - depth(x) ) - (math_axis_size(cur_size) + delta));
+            delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta));
+        } else {
+            clr1 = ext_xn_over_d(clr1, thickness(q), fraction_rule(cur_style));
+            clr2 = ext_xn_over_d(clr2, thickness(q), fraction_rule(cur_style));
+            delta1 = clr1 - ((shift_up   - depth(x) ) - (math_axis_size(cur_size) + delta));
+            delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta));
+        }
+        if (delta1 > 0) {
+            shift_up = shift_up + delta1;
+        }
+        if (delta2 > 0) {
+            shift_down = shift_down + delta2;
+        }
+    }
+    if (m != null) {
+        /*
+            construct a hlist box for the fraction, according to |hgap| and |vgap|
+        */
+        shift_up = skewed_fraction_vgap(cur_style);
+
+        if (!fractionnoaxis(q)) {
+            shift_up += half(math_axis_size(cur_size));
+        }
+
+        shift_down = shift_up;
+        v = new_null_box();
+        reset_attributes(v, node_attr(q));
+        type(v) = hlist_node;
+        list_ptr(v) = x;
+        width(v) = width(x);
+        height(v) = height(x) + shift_up;
+        depth(v) = depth(x);
+        shift_amount(v) = - shift_up;
+        x = v;
+
+        v = new_null_box();
+        reset_attributes(v, node_attr(q));
+        type(v) = hlist_node;
+        list_ptr(v) = z;
+        width(v) = width(z);
+        height(v) = height(z);
+        depth(v) = depth(z) + shift_down;
+        shift_amount(v) = shift_down;
+        z = v;
+
+        v = new_null_box();
+        reset_attributes(v, node_attr(q));
+        type(v) = hlist_node;
+        if (height(x) > height(z)) {
+            height(v) = height(x);
+        } else {
+            height(v) = height(z);
+        }
+        if (depth(x) > depth(z)) {
+            depth(v) = depth(x);
+        } else {
+            depth(v) = depth(z);
+        }
+        if (height(m) > height(v)) {
+            height(v) = height(m);
+        }
+        if (depth(m) > depth(v)) {
+            depth(v) = depth(m);
+        }
+
+        if (fractionexact(q)) {
+            delta1 = -half(skewed_fraction_hgap(cur_style));
+            delta2 = delta1;
+            width(v) = width(x) + width(z) + width(m) - skewed_fraction_hgap(cur_style);
+        } else {
+            delta1 = half(skewed_fraction_hgap(cur_style)-width(m));
+            delta2 = half(skewed_fraction_hgap(cur_style)+width(m));
+            width(v) = width(x) + width(z) + skewed_fraction_hgap(cur_style);
+            width(m) = 0;
+        }
+
+        p1 = new_kern(delta1);
+        reset_attributes(p1, node_attr(q));
+        p2 = new_kern(delta2);
+        reset_attributes(p2, node_attr(q));
+
+        couple_nodes(x,p1);
+        couple_nodes(p1,m);
+        couple_nodes(m,p2);
+        couple_nodes(p2,z);
+
+        list_ptr(v) = x;
+    } else {
+        /*
+            construct a vlist box for the fraction, according to |shift_up| and |shift_down|
+        */
+        v = new_null_box();
+        type(v) = vlist_node;
+        height(v) = shift_up + height(x);
+        depth(v) = depth(z) + shift_down;
+        width(v) = width(x); /* this also equals |width(z)| */
+        reset_attributes(v, node_attr(q));
+        if (thickness(q) == 0) {
+            p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
+            couple_nodes(p,z);
+        } else {
+            y = do_fraction_rule(thickness(q), node_attr(q), math_fraction_rule, cur_size, math_rules_fam_par);
+            p = new_kern((math_axis_size(cur_size) - delta) - (height(z) - shift_down));
+            reset_attributes(p, node_attr(q));
+            couple_nodes(y,p);
+            couple_nodes(p,z);
+            p = new_kern((shift_up - depth(x)) - (math_axis_size(cur_size) + delta));
+            couple_nodes(p,y);
+        }
+        reset_attributes(p, node_attr(q));
+        couple_nodes(x,p);
+        list_ptr(v) = x;
+    }
+    /*
+        put the fraction into a box with its delimiters, and make |new_hlist(q)|
+        point to it
+    */
+    if (do_new_math(cur_f)) {
+        if (math_use_old_fraction_scaling_par) {
+            delta = fraction_del_size_old(cur_style);
+        } else {
+            delta = fraction_del_size_new(cur_style);
+        }
+        if (delta == undefined_math_parameter) {
+            delta = get_delimiter_height(depth(v), height(v), true);
+        }
+    } else {
+        delta = fraction_del_size_old(cur_style);
+    }
+    l = do_delimiter(q, left_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
+    left_delimiter(q) = null;
+    r = do_delimiter(q, right_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
+    right_delimiter(q) = null;
+    couple_nodes(l,v);
+    couple_nodes(v,r);
+    y = hpack(l, 0, additional, -1);
+    reset_attributes(y, node_attr(q));
+    assign_new_hlist(q, y);
+}
+
+@ If the nucleus of an |op_noad| is a single character, it is to be
+centered vertically with respect to the axis, after first being enlarged
+(via a character list in the font) if we are in display style.  The normal
+convention for placing displayed limits is to put them above and below the
+operator in display style.
+
+The italic correction is removed from the character if there is a subscript
+and the limits are not being displayed. The |make_op| routine returns the
+value that should be used as an offset between subscript and superscript.
+
+After |make_op| has acted, |subtype(q)| will be |limits| if and only if
+the limits have been set above and below the operator. In that case,
+|new_hlist(q)| will already contain the desired final box.
+
+@c
+static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift);
+static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same);
+
+static scaled make_op(pointer q, int cur_style)
+{
+    scaled delta = 0;            /* offset between subscript and superscript */
+    scaled dummy = 0;
+    pointer p, v, x, y, z, n;    /* temporary registers for box construction */
+    int c;                       /* register for character examination */
+    scaled shift_up, shift_down; /* dimensions for box calculation */
+    boolean axis_shift = false;
+    scaled ok_size;
+    if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) {
+        subtype(q) = op_noad_type_limits;
+    }
+    if (type(nucleus(q)) == math_char_node) {
+        fetch(nucleus(q));
+        if (cur_style < text_style) {
+            /* try to make it larger */
+            ok_size = minimum_operator_size(cur_style);
+            if (ok_size != undefined_math_parameter) {
+                /* creating a temporary delimiter is the cleanest way */
+                y = new_node(delim_node, 0);
+                reset_attributes(y, node_attr(q));
+                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, NULL);
+                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;
+                while ((char_tag(cur_f, cur_c) == list_tag) && height_plus_depth(cur_f, cur_c) < ok_size) {
+                    c = char_remainder(cur_f, cur_c);
+                    if (!char_exists(cur_f, c))
+                        break;
+                    cur_c = c;
+                    math_character(nucleus(q)) = c;
+                }
+                delta = char_italic(cur_f, cur_c);
+                x = clean_box(nucleus(q), cur_style, cur_style);
+                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;
+                    }
+                }
+                axis_shift = true;
+            }
+        } else {
+            /* normal size */
+            delta = char_italic(cur_f, cur_c);
+            x = clean_box(nucleus(q), cur_style, cur_style);
+            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;
+                }
+            }
+            axis_shift = true;
+        }
+        if (axis_shift) {
+            /* center vertically */
+            shift_amount(x) = half(height(x) - depth(x)) - math_axis_size(cur_size);
+        }
+        type(nucleus(q)) = sub_box_node;
+        math_list(nucleus(q)) = x;
+    }
+
+    /* we now handle op_nod_type_no_limits here too */
+
+    if (subtype(q) == op_noad_type_no_limits) {
+        if (do_new_math(cur_f)) {
+            /*
+                if (delta != 0) {
+                    delta = half(delta) ;
+                }
+            */
+            p = check_nucleus_complexity(q, &dummy, cur_style, NULL);
+            if ((subscr(q) == null) && (supscr(q) == null)) {
+                assign_new_hlist(q, p);
+            } else {
+                /*
+                    make_scripts(q, p, 0, cur_style, delta, -delta);
+                */
+                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
+                */
+                switch (mode) {
+                    case 0 :
+                        /* full bottom correction */
+                        make_scripts(q, p, 0, cur_style, 0, -delta);
+                        break;
+                    case 1 :
+                        /* MathConstants driven */
+                        make_scripts(q, p, 0, cur_style,
+                             round_xn_over_d(delta, nolimit_sup_factor(cur_style), 1000),
+                            -round_xn_over_d(delta, nolimit_sub_factor(cur_style), 1000));
+                    case 2 :
+                        /* no correction */
+                        make_scripts(q, p, 0, cur_style, 0, 0);
+                        break ;
+                    case 3 :
+                        /* half bottom correction */
+                        make_scripts(q, p, 0, cur_style, 0, -half(delta));
+                        break;
+                    case 4 :
+                        /* half bottom and top correction */
+                        make_scripts(q, p, 0, cur_style, half(delta), -half(delta));
+                        break;
+                    default :
+                        if (mode > 15) {
+                            /* for quickly testing values */
+                            make_scripts(q, p, 0, cur_style, 0, -round_xn_over_d(delta, mode, 1000));
+                        } else {
+                            make_scripts(q, p, 0, cur_style, 0, 0);
+                        }
+                        break;
+                }
+            }
+            delta = 0;
+        } else {
+            /* similar code then the caller (before CHECK_DIMENSIONS) */
+            p = check_nucleus_complexity(q, &delta, cur_style, NULL);
+            if ((subscr(q) == null) && (supscr(q) == null)) {
+                assign_new_hlist(q, p);
+            } else {
+                make_scripts(q, p, delta, cur_style, 0, 0);
+            }
+        }
+    } else if (subtype(q) == op_noad_type_limits) {
+        /* The following program builds a vlist box |v| for displayed limits. The
+           width of the box is not affected by the fact that the limits may be skewed. */
+        x = clean_box(supscr(q), sup_style(cur_style), cur_style);
+        y = clean_box(nucleus(q), cur_style, cur_style);
+        z = clean_box(subscr(q), sub_style(cur_style), cur_style);
+        v = new_null_box();
+        reset_attributes(v, node_attr(q));
+        type(v) = vlist_node;
+        if (do_new_math(cur_f)) {
+            n = null;
+            if (! math_no_italic_compensation_par) {
+                n = nucleus(q);
+                if (n != null) {
+                    if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) {
+                        n = math_list(n);
+                        if (n != null) {
+                            if (type(n) == hlist_node) {
+                                n = list_ptr(n); /* just a not scaled char */
+                                while (n != null) {
+                                    if (type(n) == glyph_node) {
+                                        delta = char_italic(font(n),character(n));
+                                    }
+                                    n = vlink(n);
+                                }
+                            } else {
+                                while (n != null) {
+                                    if (type(n) == fence_noad) {
+                                        if (delimiteritalic(n) > delta) {
+                                            /* we can have dummies, the period ones */
+                                            delta = delimiteritalic(n);
+                                        }
+                                    }
+                                    n = vlink(n);
+                                }
+                            }
+                        }
+                    } else {
+                        n = nucleus(q);
+                        if (type(n) == math_char_node) {
+                            delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n));
+                        }
+                    }
+                }
+            }
+        }
+        width(v) = width(y);
+        if (width(x) > width(v))
+            width(v) = width(x);
+        if (width(z) > width(v))
+            width(v) = width(z);
+        x = rebox(x, width(v));
+        y = rebox(y, width(v));
+        z = rebox(z, width(v));
+        shift_amount(x) = half(delta);
+        shift_amount(z) = -shift_amount(x);
+        /* v is the still empty target */
+        height(v) = height(y);
+        depth(v) = depth(y);
+        /*
+            attach the limits to |y| and adjust |height(v)|, |depth(v)| to
+            account for their presence
+
+            we use |shift_up| and |shift_down| in the following program for the
+            amount of glue between the displayed operator |y| and its limits |x| and
+            |z|
+
+            the vlist inside box |v| will consist of |x| followed by |y| followed
+            by |z|, with kern nodes for the spaces between and around them
+
+            b: baseline  v: minumum gap
+        */
+
+        if (supscr(q) == null) {
+            list_ptr(x) = null;
+            flush_node(x);
+            list_ptr(v) = y;
+        } else {
+            shift_up = limit_above_bgap(cur_style) - depth(x);
+            if (shift_up < limit_above_vgap(cur_style))
+                shift_up = limit_above_vgap(cur_style);
+            p = new_kern(shift_up);
+            reset_attributes(p, node_attr(q));
+            couple_nodes(p,y);
+            couple_nodes(x,p);
+            p = new_kern(limit_above_kern(cur_style));
+            reset_attributes(p, node_attr(q));
+            couple_nodes(p,x);
+            list_ptr(v) = p;
+            height(v) = height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + shift_up;
+        }
+        if (subscr(q) == null) {
+            list_ptr(z) = null;
+            flush_node(z);
+        } else {
+            shift_down = limit_below_bgap(cur_style) - height(z);
+            if (shift_down < limit_below_vgap(cur_style))
+                shift_down = limit_below_vgap(cur_style);
+            if (shift_down > 0) {
+                p = new_kern(shift_down);
+                reset_attributes(p, node_attr(q));
+                couple_nodes(y,p);
+                couple_nodes(p,z);
+            }
+            p = new_kern(limit_below_kern(cur_style));
+            reset_attributes(p, node_attr(q));
+            couple_nodes(z,p);
+            depth(v) = depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + shift_down;
+        }
+        if (subscr(q) != null) {
+            math_list(subscr(q)) = null;
+            flush_node(subscr(q));
+            subscr(q) = null;
+        }
+        if (supscr(q) != null) {
+            math_list(supscr(q)) = null;
+            flush_node(supscr(q));
+            supscr(q) = null;
+        }
+        assign_new_hlist(q, v);
+        if (do_new_math(cur_f)) {
+            delta = 0;
+        }
+    }
+    return delta;
+}
+
+@ A ligature found in a math formula does not create a ligature, because
+there is no question of hyphenation afterwards; the ligature will simply be
+stored in an ordinary |glyph_node|, after residing in an |ord_noad|.
+
+The |type| is converted to |math_text_char| here if we would not want to
+apply an italic correction to the current character unless it belongs
+to a math font (i.e., a font with |space=0|).
+
+No boundary characters enter into these ligatures.
+
+@c
+#define simple_char_noad(p) (\
+    (p != null) && \
+    (type(p) == simple_noad) && \
+    (subtype(p) <= punct_noad_type) && \
+    (type(nucleus(p)) == math_char_node) \
+)
+
+#define same_nucleus_fam(p,q) \
+    (math_fam(nucleus(p)) == math_fam(nucleus(q)))
+
+static void make_ord(pointer q)
+{
+    int a;           /* the left-side character for lig/kern testing */
+    pointer p, r, s; /* temporary registers for list manipulation */
+    scaled k;        /* a kern */
+    liginfo lig;     /* a ligature */
+  RESTART:
+    if (subscr(q) == null && supscr(q) == null && type(nucleus(q)) == math_char_node) {
+        p = vlink(q);
+        if (simple_char_noad(p) && same_nucleus_fam(p,q)) {
+            type(nucleus(q)) = math_text_char_node;
+            fetch(nucleus(q));
+            a = cur_c;
+            /* add italic correction */
+            if (do_new_math(cur_f) && (char_italic(cur_f,math_character(nucleus(q))) != 0)) {
+                p = new_kern(char_italic(cur_f,math_character(nucleus(q))));
+                subtype(p) = italic_kern;
+                reset_attributes(p, node_attr(q));
+                couple_nodes(p,vlink(q));
+                couple_nodes(q,p);
+                return;
+            }
+            /* construct ligatures, quite unlikely in new math fonts */
+            if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) {
+                cur_c = math_character(nucleus(p));
+                /*
+                    if character |a| has a kern with |cur_c|, attach the kern after~|q|; or if
+                    it has a ligature with |cur_c|, combine noads |q| and~|p| appropriately;
+                    then |return| if the cursor has moved past a noad, or |goto restart|
+
+                    note that a ligature between an |ord_noad| and another kind of noad
+                    is replaced by an |ord_noad|, when the two noads collapse into one
+
+                    we could make a parenthesis (say) change shape when it follows
+                    certain letters. Presumably a font designer will define such
+                    ligatures only when this convention makes sense
+                */
+
+                if (disable_lig_par == 0 && has_lig(cur_f, a)) {
+                    lig = get_ligature(cur_f, a, cur_c);
+                    if (is_valid_ligature(lig)) {
+                        check_interrupt();      /* allow a way out of infinite ligature loop */
+                        switch (lig_type(lig)) {
+                        case 1:
+                            /* \.{=:\char`\|} */
+                        case 5:
+                            /* \.{=:\char`\|>} */
+                            math_character(nucleus(q)) = lig_replacement(lig);
+                            break;
+                        case 2:
+                            /* \.{\char`\|=:} */
+                        case 6:
+                            /* \.{\char`\|=:>} */
+                            math_character(nucleus(p)) = lig_replacement(lig);
+                            break;
+                        case 3:
+                             /* \.{\char`\|=:\char`\|} */
+                        case 7:
+                             /* \.{\char`\|=:\char`\|>} */
+                        case 11:
+                             /* \.{\char`\|=:\char`\|>>} */
+                            r = new_noad();
+                            reset_attributes(r, node_attr(q));
+                            s = new_node(math_char_node, 0);
+                            reset_attributes(s, node_attr(q));
+                            nucleus(r) = s;
+                            math_character(nucleus(r)) = lig_replacement(lig);
+                            math_fam(nucleus(r)) = math_fam(nucleus(q));
+                            couple_nodes(q,r);
+                            couple_nodes(r,p);
+                            if (lig_type(lig) < 11)
+                                type(nucleus(r)) = math_char_node;
+                            else
+                                /* prevent combination */
+                                type(nucleus(r)) = math_text_char_node;
+                            break;
+                        default:
+                            try_couple_nodes(q,vlink(p));
+                            math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */
+                            s = math_clone(subscr(p));
+                            subscr(q) = s;
+                            s = math_clone(supscr(p));
+                            supscr(q) = s;
+                            math_reset(subscr(p)); /* just in case */
+                            math_reset(supscr(p));
+                            flush_node(p);
+                            break;
+                        }
+                        if (lig_type(lig) > 3)
+                            return;
+                        type(nucleus(q)) = math_char_node;
+                        goto RESTART;
+                    }
+                }
+                if (disable_kern_par == 0 && has_kern(cur_f, a)) {
+                    /* todo: should this use mathkerns? */
+                    k = get_kern(cur_f, a, cur_c);
+                    if (k != 0) {
+                        p = new_kern(k);
+                        reset_attributes(p, node_attr(q));
+                        couple_nodes(p,vlink(q));
+                        couple_nodes(q,p);
+                        return;
+                    }
+                }
+            }
+        }
+    }
+}
+
+@ If the fonts for the left and right bits of a mathkern are not
+both new-style fonts, then return a sentinel value meaning:
+please use old-style italic correction placement
+
+@c
+#define MATH_KERN_NOT_FOUND 0x7FFFFFFF
+
+@ This function tries to find the kern needed for proper cut-ins.
+The left side doesn't move, but the right side does, so the first
+order of business is to create a staggered fence line on the
+left side of the right character.
+
+The microsoft spec says that there are four quadrants, but the
+actual images say
+
+@c
+static scaled math_kern_at(internal_font_number f, int c, int side, int v)
+{
+    int h, k, numkerns;
+    scaled *kerns_heights;
+    scaled kern = 0;
+    charinfo *co = char_info(f, c); /* known to exist */
+    numkerns = get_charinfo_math_kerns(co, side);
+#ifdef DEBUG
+    fprintf(stderr, "  entries = %d, height = %d\n", numkerns, v);
+#endif
+    if (numkerns == 0)
+        return kern;
+    if (side == top_left_kern) {
+        kerns_heights = co->top_left_math_kern_array;
+    } else if (side == bottom_left_kern) {
+        kerns_heights = co->bottom_left_math_kern_array;
+    } else if (side == top_right_kern) {
+        kerns_heights = co->top_right_math_kern_array;
+    } else if (side == bottom_right_kern) {
+        kerns_heights = co->bottom_right_math_kern_array;
+    } else {
+        confusion("math_kern_at");
+        kerns_heights = NULL;   /* not reached */
+    }
+#ifdef DEBUG
+    fprintf(stderr, "   entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]);
+#endif
+    if (v < kerns_heights[0])
+        return kerns_heights[1];
+    for (k = 0; k < numkerns; k++) {
+        h = kerns_heights[(k * 2)];
+        kern = kerns_heights[(k * 2) + 1];
+#ifdef DEBUG
+        if (k > 0)
+            fprintf(stderr, "   entry %d: %d,%d\n", k, h, kern);
+#endif
+        if (h > v) {
+            return kern;
+        }
+    }
+    return kern;
+}
+
+@ @c
+static scaled find_math_kern(internal_font_number l_f, int l_c,
+                             internal_font_number r_f, int r_c,
+                             int cmd, scaled shift)
+{
+    scaled corr_height_top = 0, corr_height_bot = 0;
+    scaled krn_l = 0, krn_r = 0, krn = 0;
+//    if ((!do_new_math(l_f)) || (!do_new_math(r_f)) || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c)))
+    if ((!(do_new_math(l_f) || do_new_math(r_f))) || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c)))
+        return MATH_KERN_NOT_FOUND;
+
+    if (cmd == sup_mark_cmd) {
+        corr_height_top = char_height(l_f, l_c);
+        corr_height_bot = -char_depth(r_f, r_c) + shift; /* bottom of superscript */
+        krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top);
+        krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top);
+#ifdef DEBUG
+        fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r, shift);
+#endif
+        krn = (krn_l + krn_r);
+        krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot);
+        krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot);
+#ifdef DEBUG
+        fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r);
+#endif
+        if ((krn_l + krn_r) < krn)
+            krn = (krn_l + krn_r);
+        return (krn);
+
+    } else if (cmd == sub_mark_cmd) {
+        corr_height_top = char_height(r_f, r_c) - shift; /* top of subscript */
+        corr_height_bot = -char_depth(l_f, l_c);
+        krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top);
+        krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top);
+#ifdef DEBUG
+        fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r);
+#endif
+        krn = (krn_l + krn_r);
+        krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot);
+        krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot);
+#ifdef DEBUG
+        fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r);
+#endif
+        if ((krn_l + krn_r) < krn)
+            krn = (krn_l + krn_r);
+        return (krn);
+
+    } else {
+        confusion("find_math_kern");
+    }
+    return 0; /* not reached */
+}
+
+@ just a small helper
+@c
+static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2)
+{
+    pointer y;
+    pointer z = new_kern(delta2);
+    reset_attributes(z, node_attr(q));
+    if (new_hlist(q) == null) {
+        /* this is somewhat weird */
+        new_hlist(q) = z;
+    } else {
+        y = new_hlist(q);
+        while (vlink(y) != null)
+            y = vlink(y);
+        couple_nodes(y,z);
+    }
+    return new_hlist(q);
+}
+
+@
+@c
+#ifdef DEBUG
+void dump_simple_field(pointer q)
+{
+    pointer p;
+    printf("   [%d,  type=%d, vlink=%d] ", q, type(q), vlink(q));
+    switch (type(q)) {
+    case math_char_node:
+        printf("mathchar ");
+        break;
+    case math_text_char_node:
+        printf("texchar ");
+        break;
+    case sub_box_node:
+        printf("box ");
+        break;
+    case sub_mlist_node:
+        printf("mlist ");
+        p = math_list(q);
+        while (p != null) {
+            dump_simple_field(p);
+            p = vlink(p);
+        }
+        break;
+    }
+}
+
+void dump_simple_node(pointer q)
+{
+    printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q));
+    printf("nucleus: ");
+    dump_simple_field(nucleus(q));
+    printf("\n");
+    printf("sub: ");
+    dump_simple_field(subscr(q));
+    printf("\n");
+    printf("sup: ");
+    dump_simple_field(supscr(q));
+    printf("\n\n");
+}
+#endif
+
+@ The purpose of |make_scripts(q,it)| is to attach the subscript and/or
+superscript of noad |q| to the list that starts at |new_hlist(q)|,
+given that subscript and superscript aren't both empty. The superscript
+will be horizontally shifted over |delta1|, the subscript over |delta2|.
+
+We set |shift_down| and |shift_up| to the minimum amounts to shift the
+baseline of subscripts and superscripts based on the given nucleus.
+
+Note: We need to look at a character but also at the first one in a sub list
+and there we ignore leading kerns and glue. Elsewhere is code that removes
+kerns assuming that is italic correction. The heuristics are unreliable for
+the new fonts so eventualy there will be an option to ignore such corrections.
+
+@ @c
+#define analyze_script(init,su_n,su_f,su_c) do { \
+    su_n = init; \
+    if (su_n != null) { \
+        if (math_script_box_mode_par > 0 && type(su_n) == sub_mlist_node) { \
+            su_n = math_list(su_n); \
+            while (su_n != null) { \
+                if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
+                    su_n = vlink(su_n); \
+                } else if (type(su_n) == simple_noad) { \
+                    su_n = nucleus(su_n); \
+                    if (type(su_n) == math_char_node) { \
+                        fetch(su_n); \
+                        if (char_exists(cur_f, cur_c)) { \
+                            su_f = cur_f; \
+                            su_c = cur_c; \
+                        } else { \
+                            su_n = null; \
+                        } \
+                    } else { \
+                        su_n = null; \
+                    } \
+                    break; \
+                } else { \
+                    su_n = null; \
+                    break; \
+                } \
+            } \
+        } else if (type(su_n) == sub_box_node) { \
+            su_n = math_list(su_n); \
+            if (su_n != null) { \
+                if (type(su_n) == hlist_node) { \
+                    su_n = list_ptr(su_n); \
+                } \
+                if (su_n != null) { \
+                    if (math_script_box_mode_par == 2) { \
+                        while (su_n != null) { \
+                            if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
+                                su_n = vlink(su_n); \
+                            } else if (type(su_n) == glyph_node) { \
+                                if (char_exists(font(su_n), character(su_n))) { \
+                                    su_f = font(su_n); \
+                                    su_c = character(su_n); \
+                                } else { \
+                                    su_n = null; \
+                                } \
+                                break ; \
+                            } else { \
+                                su_n = null; \
+                                break; \
+                            } \
+                        } \
+                    } else if (math_script_box_mode_par == 3) { \
+                        int boundary = -1; \
+                        while (su_n != null) { \
+                            if ((type(su_n) == boundary_node) && (subtype(su_n) == user_boundary)) { \
+                                boundary = boundary_value(su_n); \
+                                su_n = vlink(su_n); \
+                            } else if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
+                                su_n = vlink(su_n); \
+                            } else if ((boundary > -1) && (type(su_n) == glyph_node)) { \
+                                if (char_exists(font(su_n), character(su_n))) { \
+                                    su_f = font(su_n); \
+                                    su_c = character(su_n); \
+                                } else { \
+                                    su_n = null; \
+                                } \
+                                break ; \
+                            } else { \
+                                su_n = null; \
+                                break; \
+                            } \
+                        } \
+                    } \
+                } \
+            } else { \
+                su_n = null; \
+            } \
+        } else { \
+            su_n = null; \
+        } \
+    } \
+  } while (0) \
+
+#define x_su_style(n,cur_style,su_style) \
+    (noadoptionnosubscript(n) ? cur_style : su_style(cur_style))
+
+static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift)
+{
+    pointer x, y, z;                  /* temporary registers for box construction */
+    scaled shift_up, shift_down, clr; /* dimensions in the calculation */
+    scaled delta1, delta2;
+    halfword sub_n, sup_n;
+    internal_font_number sub_f, sup_f;
+    int sub_c, sup_c;
+    sub_n = null;
+    sup_n = null;
+    sub_f = 0;
+    sup_f = 0;
+    sub_c = 0;
+    sup_c = 0;
+    delta1 = it;
+    delta2 = 0;
+
+#ifdef DEBUG
+    printf("it: %d\n", it);
+    dump_simple_node(q);
+    printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p));
+#endif
+    switch (type(nucleus(q))) {
+        case math_char_node:
+        case math_text_char_node:
+            if ((subscr(q) == null) && (delta1 != 0)) {
+                /* todo: selective */
+                x = new_kern(delta1); /* italic correction */
+                subtype(x) = italic_kern;
+                reset_attributes(x, node_attr(nucleus(q)));
+                couple_nodes(p,x);
+                delta1 = 0;
+            }
+    }
+    assign_new_hlist(q, p);
+    if (is_char_node(p)) {
+        shift_up = 0;
+        shift_down = 0;
+    } else {
+        z = hpack(p, 0, additional, -1);
+        shift_up = height(z) - sup_shift_drop(cur_style);  /* r18 */
+        shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */
+        list_ptr(z) = null;
+        flush_node(z);
+    }
+
+    if (is_char_node(p)) {
+        /* we look at the subscript character (_i) or first character in a list (_{ij}) */
+        analyze_script(subscr(q),sub_n,sub_f,sub_c);
+        /* we look at the superscript character (^i) or first character in a list (^{ij}) */
+        analyze_script(supscr(q),sup_n,sup_f,sup_c);
+    }
+
+    if (supscr(q) == null) {
+        /*
+            construct a subscript box |x| when there is no superscript
+
+            when there is a subscript without a superscript, the top of the subscript
+            should not exceed the baseline plus four-fifths of the x-height.
+        */
+    /*  x = clean_box(subscr(q), sub_style(cur_style), cur_style); */
+        x = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style);
+        width(x) = width(x) + space_after_script(cur_style);
+        switch (math_scripts_mode_par) {
+            case 1:
+                shift_down = sub_shift_down(cur_style) ;
+                break;
+            case 2:
+                shift_down = sub_sup_shift_down(cur_style) ;
+                break;
+            case 3:
+                shift_down = sub_sup_shift_down(cur_style) ;
+                break;
+            case 4:
+                shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
+                break;
+            case 5:
+                shift_down = sub_shift_down(cur_style) ;
+                break;
+            default:
+                if (shift_down < sub_shift_down(cur_style))
+                    shift_down = sub_shift_down(cur_style);
+                clr = height(x) - sub_top_max(cur_style);
+                if (shift_down < clr)
+                    shift_down = clr;
+                break;
+        }
+        shift_amount(x) = shift_down;
+
+        /* now find and correct for horizontal shift */
+        if (sub_n != null) {
+            delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
+            if (delta2 == MATH_KERN_NOT_FOUND) {
+                delta2 = subshift ;
+            } else {
+                delta2 = delta2 + subshift ;
+            }
+        } else {
+            delta2 = subshift ;
+        }
+        if (delta2 != 0) {
+            p = attach_hkern_to_new_hlist(q, delta2);
+        }
+
+    } else {
+        /*
+            construct a superscript box |x|
+
+            the bottom of a superscript should never descend below the baseline plus
+            one-fourth of the x-height.
+        */
+    /*  x = clean_box(supscr(q), sup_style(cur_style), cur_style); */
+        x = clean_box(supscr(q), (noadoptionnosupscript(q) ? cur_style : sup_style(cur_style)), cur_style);
+        width(x) = width(x) + space_after_script(cur_style);
+        switch (math_scripts_mode_par) {
+            case 1:
+                shift_up = sup_shift_up(cur_style);
+                break;
+            case 2:
+                shift_up = sup_shift_up(cur_style) ;
+                break;
+            case 3:
+                shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style) - sub_shift_down(cur_style) ;
+                break;
+            case 4:
+                shift_up = sup_shift_up(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
+                break;
+            case 5:
+                shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style)-sub_shift_down(cur_style) ;
+                break;
+            default:
+                clr = sup_shift_up(cur_style);
+                if (shift_up < clr)
+                    shift_up = clr;
+                clr = depth(x) + sup_bottom_min(cur_style);
+                if (shift_up < clr)
+                    shift_up = clr;
+                break;
+        }
+        if (subscr(q) == null) {
+            shift_amount(x) = -shift_up;
+            /* now find and correct for horizontal shift */
+            if (sup_n != null) {
+                clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
+                if (clr == MATH_KERN_NOT_FOUND) {
+                    clr = supshift ;
+                } else {
+                    clr = clr + supshift ;
+                }
+            } else {
+                clr = supshift;
+            }
+            if (clr != 0) {
+                p = attach_hkern_to_new_hlist(q, clr);
+            }
+        } else {
+            /*
+                construct a sub/superscript combination box |x|, with the superscript offset
+                by |delta|
+
+                when both subscript and superscript are present, the subscript must be
+                separated from the superscript by at least four times |default_rule_thickness|
+
+                if this condition would be violated, the subscript moves down, after which
+                both subscript and superscript move up so that the bottom of the superscript
+                is at least as high as the baseline plus four-fifths of the x-height
+            */
+        /*  y = clean_box(subscr(q) sub_style(cur_style), cur_style); */
+            y = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style);
+            width(y) = width(y) + space_after_script(cur_style);
+            switch (math_scripts_mode_par) {
+                case 1:
+                    shift_down = sub_shift_down(cur_style) ;
+                    break;
+                case 2:
+                    shift_down = sub_sup_shift_down(cur_style) ;
+                    break;
+                case 3:
+                    shift_down = sub_sup_shift_down(cur_style) ;
+                    break;
+                case 4:
+                    shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
+                    break;
+                case 5:
+                    shift_down = sub_shift_down(cur_style) ;
+                    break;
+                default:
+                    if (shift_down < sub_sup_shift_down(cur_style))
+                        shift_down = sub_sup_shift_down(cur_style);
+                    clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down));
+                    if (clr > 0) {
+                        shift_down = shift_down + clr;
+                        clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x));
+                        if (clr > 0) {
+                            shift_up = shift_up + clr;
+                            shift_down = shift_down - clr;
+                        }
+                    }
+                break;
+            }
+            /* now find and correct for horizontal shift */
+            if (sub_n != null) {
+                delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
+                if (delta2 == MATH_KERN_NOT_FOUND) {
+                    delta2 = subshift ;
+                } else {
+                    delta2 = delta2 + subshift ;
+                }
+            } else {
+                delta2 = subshift ;
+            }
+            if (delta2 != 0) {
+                p = attach_hkern_to_new_hlist(q, delta2);
+            }
+            /*
+                now the horizontal shift for the superscript; the superscript is also to be shifted
+                by |delta1| (the italic correction)
+            */
+            clr = MATH_KERN_NOT_FOUND;
+            if (sup_n != null) {
+                clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
+            }
+
+            /* delta can already have been applied and now be 0 */
+            if (delta2 == MATH_KERN_NOT_FOUND)
+                delta2 = - supshift ;
+            else
+                delta2 = delta2 - supshift ;
+            if (clr != MATH_KERN_NOT_FOUND) {
+                shift_amount(x) = clr + delta1 - delta2;
+            } else {
+                shift_amount(x) = delta1 - delta2;
+            }
+            /* todo: only if kern != 0 */
+            p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
+            reset_attributes(p, node_attr(q));
+            couple_nodes(x,p);
+            couple_nodes(p,y);
+            /* we end up with funny dimensions */
+            x = vpackage(x, 0, additional, max_dimen, math_direction_par);
+            reset_attributes(x, node_attr(q));
+            shift_amount(x) = shift_down;
+        }
+    }
+
+    if (new_hlist(q) == null) {
+        new_hlist(q) = x;
+    } else {
+        p = new_hlist(q);
+        while (vlink(p) != null)
+            p = vlink(p);
+        couple_nodes(p,x);
+    }
+    if (subscr(q) != null) {
+        math_list(subscr(q)) = null;
+        flush_node(subscr(q));
+        subscr(q) = null;
+    }
+    if (supscr(q) != null) {
+        math_list(supscr(q)) = null;
+        flush_node(supscr(q));
+        supscr(q) = null;
+    }
+}
+
+@ The |make_left_right| function constructs a left or right delimiter of
+the required size and returns the value |open_noad| or |close_noad|. The
+|left_noad_side| and |right_noad_side| will both be based on the original |style|,
+so they will have consistent sizes.
+
+@c
+static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h)
+{
+    scaled delta;
+    pointer tmp, lst;
+    scaled ic = 0;
+    boolean stack = false;
+    boolean axis = false;
+    int same = subtype(q);
+
+    setup_cur_size(style);
+
+    if ((delimiterheight(q)!=0) || (delimiterdepth(q)!=0)) {
+
+        delta = delimiterheight(q) + delimiterdepth(q);
+        tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, false, &stack, &ic, &same);
+        delimiteritalic(q) = ic;
+
+        /* beware, a stacked delimiter has a shift but no corrected height/depth (yet) */
+
+        if (stack) {
+            shift_amount(tmp) = delimiterdepth(q);
+        }
+
+        if (delimiterexact(q)) {
+            delimiterheight(q) = height(tmp) - shift_amount(tmp);
+            delimiterdepth(q)  = depth(tmp)  + shift_amount(tmp);
+        }
+        if (delimiteraxis(q)) {
+            delimiterheight(q) += math_axis_size(cur_size);
+            delimiterdepth(q)  -= math_axis_size(cur_size);
+            shift_amount(tmp)  -= math_axis_size(cur_size);
+        }
+        lst = new_node(hlist_node,0);
+        reset_attributes(lst, node_attr(q));
+        box_dir(lst) = dir_TLT ;
+        height(lst) = delimiterheight(q);
+        depth(lst) = delimiterdepth(q);
+        width(lst) = width(tmp);
+        list_ptr(lst) = tmp;
+        tmp = lst ;
+    } else {
+        axis = ! delimiternoaxis(q);
+        delta = get_delimiter_height(max_d,max_h,axis);
+        tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, axis, &stack, &ic, &same);
+        delimiteritalic(q) = ic;
+    }
+    delimiter(q) = null;
+    assign_new_hlist(q, tmp);
+    delimitersamesize(q) = same; /* new */
+    if (delimiterclass(q) >= ord_noad_type) {
+        if (delimiterclass(q) <= inner_noad_type) {
+            return delimiterclass(q);
+        } else {
+            return ord_noad_type;
+        }
+    } else if (subtype(q) == left_noad_side) {
+        return open_noad_type;
+    } else {
+        return close_noad_type;
+    }
+}
+
+@ @c
+#define TEXT_STYLES(A,B) do {					\
+    def_math_param(A,display_style,(B),level_one);		\
+    def_math_param(A,cramped_display_style,(B),level_one);	\
+    def_math_param(A,text_style,(B),level_one);			\
+    def_math_param(A,cramped_text_style,(B),level_one);		\
+  } while (0)
+
+#define SCRIPT_STYLES(A,B) do {						\
+    def_math_param(A,script_style,(B),level_one);			\
+    def_math_param(A,cramped_script_style,(B),level_one);		\
+    def_math_param(A,script_script_style,(B),level_one);		\
+    def_math_param(A,cramped_script_script_style,(B),level_one);	\
+  } while (0)
+
+#define ALL_STYLES(A,B) do {			\
+    TEXT_STYLES(A,(B));				\
+    SCRIPT_STYLES(A,(B));			\
+  } while (0)
+
+#define SPLIT_STYLES(A,B,C) do {		\
+    TEXT_STYLES(A,(B));				\
+    SCRIPT_STYLES(A,(C));			\
+  } while (0)
+
+
+void initialize_math_spacing(void)
+{
+    /* *INDENT-OFF* */
+    ALL_STYLES   (math_param_ord_ord_spacing,     0);
+    ALL_STYLES   (math_param_ord_op_spacing,      thin_mu_skip_code);
+    SPLIT_STYLES (math_param_ord_bin_spacing,     med_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_ord_rel_spacing,     thick_mu_skip_code, 0);
+    ALL_STYLES   (math_param_ord_open_spacing,    0);
+    ALL_STYLES   (math_param_ord_close_spacing,   0);
+    ALL_STYLES   (math_param_ord_punct_spacing,   0);
+    SPLIT_STYLES (math_param_ord_inner_spacing,   thin_mu_skip_code, 0);
+
+    ALL_STYLES   (math_param_op_ord_spacing,      thin_mu_skip_code);
+    ALL_STYLES   (math_param_op_op_spacing,       thin_mu_skip_code);
+    ALL_STYLES   (math_param_op_bin_spacing,      0);
+    SPLIT_STYLES (math_param_op_rel_spacing,      thick_mu_skip_code, 0);
+    ALL_STYLES   (math_param_op_open_spacing,     0);
+    ALL_STYLES   (math_param_op_close_spacing,    0);
+    ALL_STYLES   (math_param_op_punct_spacing,    0);
+    SPLIT_STYLES (math_param_op_inner_spacing,    thin_mu_skip_code, 0);
+
+    SPLIT_STYLES (math_param_bin_ord_spacing,     med_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_bin_op_spacing,      med_mu_skip_code, 0);
+    ALL_STYLES   (math_param_bin_bin_spacing,     0);
+    ALL_STYLES   (math_param_bin_rel_spacing,     0);
+    SPLIT_STYLES (math_param_bin_open_spacing,    med_mu_skip_code, 0);
+    ALL_STYLES   (math_param_bin_close_spacing,   0);
+    ALL_STYLES   (math_param_bin_punct_spacing,   0);
+    SPLIT_STYLES (math_param_bin_inner_spacing,   med_mu_skip_code, 0);
+
+    SPLIT_STYLES (math_param_rel_ord_spacing,     thick_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_rel_op_spacing,      thick_mu_skip_code, 0);
+    ALL_STYLES   (math_param_rel_bin_spacing,     0);
+    ALL_STYLES   (math_param_rel_rel_spacing,     0);
+    SPLIT_STYLES (math_param_rel_open_spacing,    thick_mu_skip_code, 0);
+    ALL_STYLES   (math_param_rel_close_spacing,   0);
+    ALL_STYLES   (math_param_rel_punct_spacing,   0);
+    SPLIT_STYLES (math_param_rel_inner_spacing,   thick_mu_skip_code, 0);
+
+    ALL_STYLES   (math_param_open_ord_spacing,    0);
+    ALL_STYLES   (math_param_open_op_spacing,     0);
+    ALL_STYLES   (math_param_open_bin_spacing,    0);
+    ALL_STYLES   (math_param_open_rel_spacing,    0);
+    ALL_STYLES   (math_param_open_open_spacing,   0);
+    ALL_STYLES   (math_param_open_close_spacing,  0);
+    ALL_STYLES   (math_param_open_punct_spacing,  0);
+    ALL_STYLES   (math_param_open_inner_spacing,  0);
+
+    ALL_STYLES   (math_param_close_ord_spacing,   0);
+    ALL_STYLES   (math_param_close_op_spacing,    thin_mu_skip_code);
+    SPLIT_STYLES (math_param_close_bin_spacing,   med_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_close_rel_spacing,   thick_mu_skip_code, 0);
+    ALL_STYLES   (math_param_close_open_spacing,  0);
+    ALL_STYLES   (math_param_close_close_spacing, 0);
+    ALL_STYLES   (math_param_close_punct_spacing, 0);
+    SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0);
+
+    SPLIT_STYLES (math_param_punct_ord_spacing,   thin_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_punct_op_spacing,    thin_mu_skip_code, 0);
+    ALL_STYLES   (math_param_punct_bin_spacing,   0);
+    SPLIT_STYLES (math_param_punct_rel_spacing,   thin_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_punct_open_spacing,  thin_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0);
+
+    SPLIT_STYLES (math_param_inner_ord_spacing,   thin_mu_skip_code, 0);
+    ALL_STYLES   (math_param_inner_op_spacing,    thin_mu_skip_code);
+    SPLIT_STYLES (math_param_inner_bin_spacing,   med_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_inner_rel_spacing,   thick_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_inner_open_spacing,  thin_mu_skip_code, 0);
+    ALL_STYLES   (math_param_inner_close_spacing, 0);
+    SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0);
+    SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0);
+    /* *INDENT-ON* */
+}
+
+@ @c
+#define both_types(A,B) ((A)*16+(B))
+
+static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
+{
+    int x = -1;
+    pointer z = null;
+    if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits)
+        l_type = op_noad_type_normal;
+    if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits)
+        r_type = op_noad_type_normal;
+    switch (both_types(l_type, r_type)) {
+    /* *INDENT-OFF* */
+    case both_types(ord_noad_type,       ord_noad_type      ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break;
+    case both_types(ord_noad_type,       op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break;
+    case both_types(ord_noad_type,       bin_noad_type      ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break;
+    case both_types(ord_noad_type,       rel_noad_type      ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break;
+    case both_types(ord_noad_type,       open_noad_type     ): x = get_math_param(math_param_ord_open_spacing,mstyle); break;
+    case both_types(ord_noad_type,       close_noad_type    ): x = get_math_param(math_param_ord_close_spacing,mstyle); break;
+    case both_types(ord_noad_type,       punct_noad_type    ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break;
+    case both_types(ord_noad_type,       inner_noad_type    ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break;
+    case both_types(op_noad_type_normal, ord_noad_type      ): x = get_math_param(math_param_op_ord_spacing,mstyle); break;
+    case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break;
+#if 0
+    case both_types(op_noad_type_normal, bin_noad_type      ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;
+#endif
+    case both_types(op_noad_type_normal, rel_noad_type      ): x = get_math_param(math_param_op_rel_spacing,mstyle); break;
+    case both_types(op_noad_type_normal, open_noad_type     ): x = get_math_param(math_param_op_open_spacing,mstyle); break;
+    case both_types(op_noad_type_normal, close_noad_type    ): x = get_math_param(math_param_op_close_spacing,mstyle); break;
+    case both_types(op_noad_type_normal, punct_noad_type    ): x = get_math_param(math_param_op_punct_spacing,mstyle); break;
+    case both_types(op_noad_type_normal, inner_noad_type    ): x = get_math_param(math_param_op_inner_spacing,mstyle); break;
+    case both_types(bin_noad_type,       ord_noad_type      ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break;
+    case both_types(bin_noad_type,       op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break;
+#if 0
+    case both_types(bin_noad_type,       bin_noad_type      ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break;
+    case both_types(bin_noad_type,       rel_noad_type      ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break;
+#endif
+    case both_types(bin_noad_type,       open_noad_type     ): x = get_math_param(math_param_bin_open_spacing,mstyle); break;
+#if 0
+    case both_types(bin_noad_type,       close_noad_type    ): x = get_math_param(math_param_bin_close_spacing,mstyle); break;
+    case both_types(bin_noad_type,       punct_noad_type    ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break;
+#endif
+    case both_types(bin_noad_type,       inner_noad_type    ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break;
+    case both_types(rel_noad_type,       ord_noad_type      ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break;
+    case both_types(rel_noad_type,       op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break;
+#if 0
+    case both_types(rel_noad_type,       bin_noad_type      ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
+#endif
+    case both_types(rel_noad_type,       rel_noad_type      ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break;
+    case both_types(rel_noad_type,       open_noad_type     ): x = get_math_param(math_param_rel_open_spacing,mstyle); break;
+    case both_types(rel_noad_type,       close_noad_type    ): x = get_math_param(math_param_rel_close_spacing,mstyle); break;
+    case both_types(rel_noad_type,       punct_noad_type    ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break;
+    case both_types(rel_noad_type,       inner_noad_type    ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break;
+    case both_types(open_noad_type,      ord_noad_type      ): x = get_math_param(math_param_open_ord_spacing,mstyle); break;
+    case both_types(open_noad_type,      op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break;
+#if 0
+    case both_types(open_noad_type,      bin_noad_type      ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;
+#endif
+    case both_types(open_noad_type,      rel_noad_type      ): x = get_math_param(math_param_open_rel_spacing,mstyle); break;
+    case both_types(open_noad_type,      open_noad_type     ): x = get_math_param(math_param_open_open_spacing,mstyle); break;
+    case both_types(open_noad_type,      close_noad_type    ): x = get_math_param(math_param_open_close_spacing,mstyle); break;
+    case both_types(open_noad_type,      punct_noad_type    ): x = get_math_param(math_param_open_punct_spacing,mstyle); break;
+    case both_types(open_noad_type,      inner_noad_type    ): x = get_math_param(math_param_open_inner_spacing,mstyle); break;
+    case both_types(close_noad_type,     ord_noad_type      ): x = get_math_param(math_param_close_ord_spacing,mstyle); break;
+    case both_types(close_noad_type,     op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break;
+    case both_types(close_noad_type,     bin_noad_type      ): x = get_math_param(math_param_close_bin_spacing,mstyle); break;
+    case both_types(close_noad_type,     rel_noad_type      ): x = get_math_param(math_param_close_rel_spacing,mstyle); break;
+    case both_types(close_noad_type,     open_noad_type     ): x = get_math_param(math_param_close_open_spacing,mstyle); break;
+    case both_types(close_noad_type,     close_noad_type    ): x = get_math_param(math_param_close_close_spacing,mstyle); break;
+    case both_types(close_noad_type,     punct_noad_type    ): x = get_math_param(math_param_close_punct_spacing,mstyle); break;
+    case both_types(close_noad_type,     inner_noad_type    ): x = get_math_param(math_param_close_inner_spacing,mstyle); break;
+    case both_types(punct_noad_type,     ord_noad_type      ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break;
+    case both_types(punct_noad_type,     op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break;
+#if 0
+    case both_types(punct_noad_type,     bin_noad_type      ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
+#endif
+    case both_types(punct_noad_type,     rel_noad_type      ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break;
+    case both_types(punct_noad_type,     open_noad_type     ): x = get_math_param(math_param_punct_open_spacing,mstyle); break;
+    case both_types(punct_noad_type,     close_noad_type    ): x = get_math_param(math_param_punct_close_spacing,mstyle); break;
+    case both_types(punct_noad_type,     punct_noad_type    ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break;
+    case both_types(punct_noad_type,     inner_noad_type    ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break;
+    case both_types(inner_noad_type,     ord_noad_type      ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break;
+    case both_types(inner_noad_type,     op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break;
+    case both_types(inner_noad_type,     bin_noad_type      ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break;
+    case both_types(inner_noad_type,     rel_noad_type      ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break;
+    case both_types(inner_noad_type,     open_noad_type     ): x = get_math_param(math_param_inner_open_spacing,mstyle); break;
+    case both_types(inner_noad_type,     close_noad_type    ): x = get_math_param(math_param_inner_close_spacing,mstyle); break;
+    case both_types(inner_noad_type,     punct_noad_type    ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break;
+    case both_types(inner_noad_type,     inner_noad_type    ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break;
+    /* *INDENT-ON* */
+    }
+    if (x < 0) {
+        confusion("mathspacing");
+    }
+    if (x != 0) {
+        if (x <= thick_mu_skip_code) {
+            /* trap thin/med/thick settings cf. old TeX */
+            z = math_glue(glue_par(x), mmu); /* allocates a glue */
+            /* store a symbolic subtype */
+            subtype(z) = (quarterword) (x + 1);
+        } else {
+            z = math_glue(x, mmu); /* allocates a glue */
+        }
+    }
+    return z;
+}
+
+@ @c
+static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same)
+{
+    pointer p = null;
+    pointer t = null;
+    if (same != NULL) {
+        *same = 0;
+    }
+    switch (type(nucleus(q))) {
+        case math_char_node:
+        case math_text_char_node:
+            fetch(nucleus(q));
+            if (char_exists(cur_f, cur_c)) {
+                /* we could look at neighbours */
+                if (do_new_math(cur_f)) {
+                    *delta = 0 ; /* cf spec only the last one */
+                } else {
+                    *delta = char_italic(cur_f, cur_c);
+                }
+                p = new_glyph(cur_f, cur_c);
+                reset_attributes(p, node_attr(nucleus(q)));
+                if (do_new_math(cur_f)) {
+                    if (! math_no_char_italic_par) {
+                        /* keep italic, but bad with two successive letters */
+                    } else if (get_char_cat_code(cur_c) == 11) {
+                        /* no italic correction in mid-word of text font */
+                        *delta = 0;
+                    }
+                } else {
+                    /* no italic correction in mid-word of text font */
+                    if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) {
+                        *delta = 0;
+                    }
+                }
+                /* so we only add italic correction when we have no scripts */
+                if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) {
+                    pointer x = new_kern(*delta);
+                    subtype(x) = italic_kern;
+                    reset_attributes(x, node_attr(nucleus(q)));
+                    couple_nodes(p,x);
+                    *delta = 0;
+                } else /* needs checking but looks ok */
+                if (do_new_math(cur_f)) {
+                    *delta = char_italic(cur_f, cur_c); /* must be more selective */
+                }
+            }
+            break;
+        case sub_box_node:
+            p = math_list(nucleus(q));
+            break;
+        case sub_mlist_node:
+            t = math_list(nucleus(q));
+            mlist_to_hlist(t, false, cur_style);   /* recursive call */
+if (same != NULL && type(t) == fence_noad && delimitersamesize(t)) {
+    *same = delimitersamesize(t) ;
+}
+            setup_cur_size(cur_style);
+            p = hpack(vlink(temp_head), 0, additional, -1);
+            reset_attributes(p, node_attr(nucleus(q)));
+            break;
+        default:
+            confusion("mlist2");    /* this can't happen mlist2 */
+    }
+    return p;
+}
+
+@ Here is the overall plan of |mlist_to_hlist|, and the list of its
+   local variables.
+
+@c
+void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
+{
+    pointer q = mlist;                    /* runs through the mlist */
+    pointer r = null;                     /* the most recent noad preceding |q| */
+    int style = cur_style;                /* tuck global parameter away as local variable */
+    int r_type = simple_noad;             /* the |type| of noad |r|, or |op_noad| if |r=null| */
+    int r_subtype = op_noad_type_normal;  /* the |subtype| of noad |r| if |r_type| is |fence_noad| */
+    int t;                                /* the effective |type| of noad |q| during the second pass */
+    int t_subtype;                        /* the effective |subtype| of noad |q| during the second pass */
+    pointer p = null;
+    pointer pp = null;
+    pointer z = null;
+    halfword nxt ;
+    int same = 0;
+    int pen;                              /* a penalty to be inserted */
+    int prepen;                           /* 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 */
+    scaled delta;                         /* italic correction offset for subscript and superscript */
+    scaled cur_mu;                        /* the math unit width corresponding to |cur_size| */
+    r_subtype = op_noad_type_normal;
+    setup_cur_size(cur_style);
+    cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
+    if (math_penalties_mode_par) {
+        /* we could do this via the callback but it's nice to have it as primitive too */
+        penalties = 1;
+    }
+    while (q != null) {
+        /*
+            we use the fact that no character nodes appear in an mlist, hence
+            the field |type(q)| is always present.
+
+            one of the things we must do on the first pass is change a |bin_noad| to
+            an |ord_noad| if the |bin_noad| is not in the context of a binary operator
+
+            the values of |r| and |r_type| make this fairly easy
+        */
+      RESWITCH:
+        delta = 0;
+        nxt = vlink(q);
+        switch (type(q)) {
+            case simple_noad:
+                switch (subtype(q)) {
+                case bin_noad_type:
+                    switch (r_type) {
+                    case simple_noad:
+                        switch (r_subtype) {
+                        case bin_noad_type:
+                        case op_noad_type_normal:
+                        case op_noad_type_limits:
+                        case op_noad_type_no_limits:
+                        case rel_noad_type:
+                        case open_noad_type:
+                        case punct_noad_type:
+                            subtype(q) = ord_noad_type;
+                            goto RESWITCH;
+                            break;
+                        }
+                        break;
+                    case fence_noad:
+                        if (r_subtype == left_noad_side) {
+                            subtype(q) = ord_noad_type; /* so these can best be the same size */
+                            goto RESWITCH;
+                        }
+                        break;
+                    }
+                    break;
+                case over_noad_type:
+                    make_over(q, cur_style, cur_size, math_rules_fam_par);
+                    break;
+                case under_noad_type:
+                    make_under(q, cur_style, cur_size, math_rules_fam_par);
+                    break;
+                case vcenter_noad_type:
+                    make_vcenter(q);
+                    break;
+                case rel_noad_type:
+                case close_noad_type:
+                case punct_noad_type:
+                    if (r_type == simple_noad && r_subtype == bin_noad_type) {
+                        type(r) = simple_noad; /* assumes the same size .. can't this go */
+                        subtype(r) = ord_noad_type;
+                    }
+                    break;
+                case op_noad_type_normal:
+                case op_noad_type_limits:
+                case op_noad_type_no_limits:
+                    delta = make_op(q, cur_style);
+                    if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits))
+                        goto CHECK_DIMENSIONS;
+                    break;
+                case ord_noad_type:
+                    make_ord(q);
+                    break;
+                case open_noad_type:
+                case inner_noad_type:
+                    break;
+                }
+                break;
+            case fence_noad:
+                if (subtype(q) != left_noad_side) {
+                    if (r_type == simple_noad && r_subtype == bin_noad_type) {
+                        type(r) = simple_noad; /* assumes the same size  */
+                        subtype(r) = ord_noad_type;
+                    }
+                }
+                goto DONE_WITH_NOAD;
+                break;
+            case fraction_noad:
+                make_fraction(q, cur_style);
+                goto CHECK_DIMENSIONS;
+                break;
+            case radical_noad:
+                if (subtype(q) == 7)
+                    make_hextension(q, cur_style);
+                else if (subtype(q) == 6)
+                    make_delimiter_over(q, cur_style);
+                else if (subtype(q) == 5)
+                    make_delimiter_under(q, cur_style);
+                else if (subtype(q) == 4)
+                    make_over_delimiter(q, cur_style);
+                else if (subtype(q) == 3)
+                    make_under_delimiter(q, cur_style);
+                else
+                    make_radical(q, cur_style);
+                break;
+            case accent_noad:
+                make_math_accent(q, cur_style);
+                break;
+            case style_node:
+                cur_style = subtype(q);
+                setup_cur_size(cur_style);
+                cur_mu = x_over_n(get_math_quad_style(cur_style), 18);
+                goto DONE_WITH_NODE;
+                break;
+            case choice_node:
+                switch (cur_style / 2) {
+                case 0: /* |display_style=0| */
+                    choose_mlist(display_mlist);
+                    break;
+                case 1: /* |text_style=2| */
+                    choose_mlist(text_mlist);
+                    break;
+                case 2: /* |script_style=4| */
+                    choose_mlist(script_mlist);
+                    break;
+                case 3: /* |script_script_style=6| */
+                    choose_mlist(script_script_mlist);
+                    break;
+                }
+                flush_node_list(display_mlist(q));
+                flush_node_list(text_mlist(q));
+                flush_node_list(script_mlist(q));
+                flush_node_list(script_script_mlist(q));
+                type(q) = style_node;
+                subtype(q) = (quarterword) cur_style;
+                if (p != null) {
+                    z = vlink(q);
+                    couple_nodes(q,p);
+                    while (vlink(p) != null)
+                        p = vlink(p);
+                    try_couple_nodes(p,z);
+                }
+                goto DONE_WITH_NODE;
+                break;
+            case ins_node:
+            case mark_node:
+            case adjust_node:
+            case boundary_node:
+            case whatsit_node:
+            case penalty_node:
+            case disc_node:
+                goto DONE_WITH_NODE;
+                break;
+            case rule_node:
+                if (height(q) > max_hl)
+                    max_hl = height(q);
+                if (depth(q) > max_d)
+                    max_d = depth(q);
+                goto DONE_WITH_NODE;
+                break;
+            case glue_node:
+                /*
+                    conditional math glue (`\.{\\nonscript}') results in a |glue_node|
+                    pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case
+                    the node following will be eliminated if it is a glue or kern node and if the
+                    current size is different from |text_size|
+
+                    unconditional math glue (`\.{\\muskip}') is converted to normal glue by
+                    multiplying the dimensions by |cur_mu|
+
+                */
+                if (subtype(q) == mu_glue) {
+                    math_glue_to_glue(q, cur_mu);
+                } else if ((cur_size != text_size) && (subtype(q) == cond_math_glue)) {
+                    p = vlink(q);
+                    if (p != null)
+                        if ((type(p) == glue_node) || (type(p) == kern_node)) {
+                            if (vlink(p) != null) {
+                                couple_nodes(q,vlink(p));
+                                vlink(p) = null;
+                            } else {
+                                vlink(q) = null;
+                            }
+                            flush_node_list(p);
+                        }
+                }
+                goto DONE_WITH_NODE;
+                break;
+            case kern_node:
+                math_kern(q, cur_mu);
+                goto DONE_WITH_NODE;
+                break;
+            default:
+                confusion("mlist1");
+        }
+        /*
+            When we get to the following part of the program, we have ``fallen through''
+            from cases that did not lead to |check_dimensions| or |done_with_noad| or
+            |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
+            converted to an hlist, and whose subscripts and superscripts need to be
+            appended if they are present.
+
+            If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
+            by which a superscript should be moved right with respect to a subscript
+            when both are present.
+
+        */
+same = 0 ;
+        p = check_nucleus_complexity(q, &delta, cur_style, &same);
+if (same) {
+    noadextra4(q) = same ;
+}
+        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:
+                            delta = 0;
+                            break;
+                        case inner_noad_type:
+                            if (! delimitermodeitalics) {
+                                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);
+                    subtype(d) = italic_kern;
+                    reset_attributes(d, node_attr(q));
+                    couple_nodes(p,d);
+                }
+            }
+            assign_new_hlist(q, p);
+        } else {
+            /* top, bottom */
+            make_scripts(q, p, delta, cur_style, 0, 0);
+        }
+      CHECK_DIMENSIONS:
+        z = hpack(new_hlist(q), 0, additional, -1);
+        if (height(z) > max_hl)
+            max_hl = height(z);
+        if (depth(z) > max_d)
+            max_d = depth(z);
+        list_ptr(z) = null;
+        /* only drop the \.{\\hbox} */
+        flush_node(z);
+      DONE_WITH_NOAD:
+        r = q;
+        r_type = type(r);
+        r_subtype = subtype(r);
+        if (r_type == fence_noad) {
+            r_subtype = left_noad_side;
+            cur_style = style;
+            setup_cur_size(cur_style);
+            cur_mu = x_over_n(get_math_quad_size(cur_size), 18); /* style */
+        }
+      DONE_WITH_NODE:
+        q = vlink(q);
+    }
+    if (r_type == simple_noad && r_subtype == bin_noad_type) {
+        type(r) = simple_noad;
+        subtype(r) = ord_noad_type;
+    }
+    /*
+        Make a second pass over the mlist, removing all noads and inserting the
+        proper spacing and penalties.
+
+        We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
+        The second pass simply goes through and hooks everything together with the
+        proper glue and penalties. It also handles the |fence_noad|s that
+        might be present, since |max_hl| and |max_d| are now known. Variable |p| points
+        to a node at the current end of the final hlist.
+    */
+    p = temp_head;
+    vlink(p) = null;
+    q = mlist;
+    r_type = 0;
+    r_subtype = 0;
+    cur_style = style;
+    setup_cur_size(cur_style);
+    cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
+  NEXT_NODE:
+    while (q != null) {
+        /*
+            If node |q| is a style node, change the style and |goto delete_q|;
+            otherwise if it is not a noad, put it into the hlist,
+            advance |q|, and |goto done|; otherwise set |s| to the size
+            of noad |q|, set |t| to the associated type (|ord_noad..
+            inner_noad|), and set |pen| to the associated penalty
+
+            Just before doing the big |case| switch in the second pass, the program
+            sets up default values so that most of the branches are short.
+        */
+        t = simple_noad;
+        t_subtype = ord_noad_type;
+        pen = inf_penalty;
+        prepen = inf_penalty;
+        switch (type(q)) {
+        case simple_noad:
+            t_subtype = subtype(q);
+            switch (t_subtype) {
+            case bin_noad_type:
+                pen = bin_op_penalty_par;
+                prepen = pre_bin_op_penalty_par;
+                break;
+            case rel_noad_type:
+                pen = rel_penalty_par;
+                prepen = pre_rel_penalty_par;
+                break;
+            case vcenter_noad_type:
+            case over_noad_type:
+            case under_noad_type:
+                t_subtype = ord_noad_type;
+                break;
+            }
+        case radical_noad:
+            break;
+        case accent_noad:
+            break;
+        case fraction_noad:
+            t = simple_noad;
+            t_subtype = inner_noad_type;
+            break;
+        case fence_noad:
+            t_subtype = make_left_right(q, style, max_d, max_hl);
+            break;
+        case style_node:
+            /* Change the current style and |goto delete_q| */
+            cur_style = subtype(q);
+            setup_cur_size(cur_style);
+            cur_mu = x_over_n(get_math_quad_style(cur_style), 18);
+            goto DELETE_Q;
+            break;
+        case whatsit_node:
+        case penalty_node:
+        case rule_node:
+        case disc_node:
+        case adjust_node:
+        case ins_node:
+        case mark_node:
+        case glue_node:
+        case kern_node:
+            couple_nodes(p,q);
+            p = q;
+            q = vlink(q);
+            vlink(p) = null;
+            goto NEXT_NODE;
+            break;
+        default:
+            confusion("mlist3");
+        }
+        /* Append inter-element spacing based on |r_type| and |t| */
+        if (r_type > 0) {
+            /* not the first noad */
+            pp = p;
+if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1) {
+    z = math_spacing_glue(r_subtype, ord_noad_type, cur_style, cur_mu);
+} else {
+            z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu);
+}
+            if (z != null) {
+                reset_attributes(z, node_attr(p));
+                couple_nodes(p,z);
+                p = z;
+            }
+            if (penalties && prepen < inf_penalty && type(pp) != penalty_node) {
+                /* no checking of prev node type */
+                z = new_penalty(prepen,noad_penalty);
+                reset_attributes(z, node_attr(p));
+                couple_nodes(p,z);
+                p = z;
+            }
+        }
+        /*
+            Append any |new_hlist| entries for |q|, and any appropriate penalties
+
+            We insert a penalty node after the hlist entries of noad |q| if |pen|
+            is not an ``infinite'' penalty, and if the node immediately following |q|
+            is not a penalty node or a |rel_noad| or absent entirely.
+        */
+        if (new_hlist(q) != null) {
+            couple_nodes(p,new_hlist(q));
+            do {
+                p = vlink(p);
+            } while (vlink(p) != null);
+        }
+        if (penalties && vlink(q) != null && pen < inf_penalty) {
+            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,noad_penalty);
+                reset_attributes(z, node_attr(q));
+                couple_nodes(p,z);
+                p = z;
+            }
+        }
+        if (type(q) == fence_noad && subtype(q) == right_noad_side) {
+            t = simple_noad;
+            t_subtype = open_noad_type;
+        }
+        r_type = t;
+        r_subtype = t_subtype;
+      DELETE_Q:
+        r = q;
+        q = vlink(q);
+        /*
+            The m-to-hlist conversion takes place in-place, so the various dependant
+            fields may not be freed (as would happen if |flush_node| was called).
+
+            A low-level |free_node| is easier than attempting to nullify such dependant
+            fields for all possible node and noad types.
+        */
+        if (nodetype_has_attributes(type(r))) {
+            delete_attribute_ref(node_attr(r));
+        }
+        reset_node_properties(r);
+        free_node(r, get_node_size(type(r), subtype(r)));
+    }
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/mlist.c
+++ /dev/null
@@ -1,4781 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-/*tex
-
-    In traditional \TeX\ the italic correction is added to the width of the
-    glyph. This is part of the engine design and related font design. In opentype
-    math this is different. There the italic correction had more explicit usage.
-    The 1.7 spec says:
-
-    \startitemize
-
-    \startitem
-        {\em italic correction:} When a run of slanted characters is followed by
-        a straight character (such as an operator or a delimiter), the italics
-        correction of the last glyph is added to its advance width.
-
-        When positioning limits on an N-ary operator (e.g., integral sign), the
-        horizontal position of the upper limit is moved to the right by ½ of the
-        italics correction, while the position of the lower limit is moved to the
-        left by the same distance.
-
-        When positioning superscripts and subscripts, their default horizontal
-        positions are also different by the amount of the italics correction of
-        the preceding glyph.
-    \stopitem
-
-    \startitem
-        {\em math kerning:} Set the default horizontal position for the
-        superscript as shifted relative to the position of the subscript by the
-        italics correction of the base glyph.
-    \stopitem
-
-    \stopitemize
-
-    Before this was specified we had to gamble a bit and assume that cambria was
-    the font benchmark and trust our eyes (and msword) for the logic. I must
-    admit that I have been fighting these italics in fonts (and the heuristics
-    that Lua\TeX\ provided) right from the start (e.g. using Lua based
-    postprocessing) but by now we know more and have more fonts to test with.
-    More fonts are handy because not all fonts are alike when it comes to
-    italics. Axis are another area of concern, as it looks like opentype math
-    fonts often already apply that shift.
-
-*/
-
-#define is_new_mathfont(A)   ((font_math_params(A) >0) && (math_old_par == 0))
-#define is_old_mathfont(A,B) ((font_math_params(A)==0) && (font_params(A)>=(B)))
-#define do_new_math(A)       ((font_math_params(A) >0) && (font_oldmath(A) == 0) && (math_old_par == 0))
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-#define reset_attributes(p,newatt) do { \
-    delete_attribute_ref(node_attr(p)); \
-    node_attr(p) = newatt;              \
-    if (newatt!=null) {                 \
-      add_node_attr_ref(node_attr(p));  \
-    }                                   \
-  } while (0)
-
-#define DEFINE_MATH_PARAMETERS(A,B,C,D) do {                 \
-    if (B==text_size) {                                      \
-      def_math_param(A, text_style, (C),D);                  \
-      def_math_param(A, cramped_text_style, (C),D);          \
-    } else if (B==script_size) {                             \
-      def_math_param(A, script_style, (C),D);                \
-      def_math_param(A, cramped_script_style, (C),D);        \
-    } else if (B==script_script_size) {                      \
-      def_math_param(A, script_script_style, (C),D);         \
-      def_math_param(A, cramped_script_script_style, (C),D); \
-    }                                                        \
-  } while (0)
-
-#define DEFINE_DMATH_PARAMETERS(A,B,C,D) do {         \
-    if (B==text_size) {                               \
-      def_math_param(A, display_style,(C),D);         \
-      def_math_param(A, cramped_display_style,(C),D); \
-    }                                                 \
-  } while (0)
-
-#define font_MATH_par(a,b) \
-  (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter)
-
-/*tex
-
-    Here are the math parameters that are font-dependant.
-
-    Before an mlist is converted to an hlist, \TeX\ makes sure that the fonts in
-    family~2 have enough parameters to be math-symbol fonts, and that the fonts
-    in family~3 have enough parameters to be math-extension fonts. The
-    math-symbol parameters are referred to by using the following macros, which
-    take a size code as their parameter; for example, |num1(cur_size)| gives the
-    value of the |num1| parameter for the current size.
-
-*/
-
-#define total_mathsy_params 22
-#define total_mathex_params 13
-
-#define mathsy(A,B) font_param(fam_fnt(2,A),B)
-
-/*tex height of `\.x' */
-
-#define math_x_height(A) mathsy(A,5)
-
-/*tex \.{18mu} */
-
-#define math_quad(A) mathsy(A,6)
-
-/*tex numerator shift-up in display styles */
-
-#define num1(A) mathsy(A,8)
-
-/*tex numerator shift-up in non-display, non-\.{\\atop} */
-
-#define num2(A) mathsy(A,9)
-
-/*tex numerator shift-up in non-display \.{\\atop} */
-
-#define num3(A) mathsy(A,10)
-
-/*tex denominator shift-down in display styles */
-
-#define denom1(A) mathsy(A,11)
-
-/*tex denominator shift-down in non-display styles */
-
-#define denom2(A) mathsy(A,12)
-
-/*tex superscript shift-up in uncramped display style */
-
-#define sup1(A) mathsy(A,13)
-
-/*tex superscript shift-up in uncramped non-display */
-
-#define sup2(A) mathsy(A,14)
-
-/*tex superscript shift-up in cramped styles */
-
-#define sup3(A) mathsy(A,15)
-
-/*tex subscript shift-down if superscript is absent */
-
-#define sub1(A) mathsy(A,16)
-
-/*tex subscript shift-down if superscript is present */
-
-#define sub2(A) mathsy(A,17)
-
-/*tex superscript baseline below top of large box */
-
-#define sup_drop(A) mathsy(A,18)
-
-/*tex subscript baseline below bottom of large box */
-
-#define sub_drop(A) mathsy(A,19)
-
-/*tex size of \.{\\atopwithdelims} delimiters in display styles */
-
-#define delim1(A) mathsy(A,20)
-
-/*tex size of \.{\\atopwithdelims} delimiters in non-displays */
-
-#define delim2(A) mathsy(A,21)
-
-/*tex height of fraction lines above the baseline */
-
-#define axis_height(A) mathsy(A,22)
-
-/*tex
-
-    The math-extension parameters have similar macros, but the size code is
-    omitted (since it is always |cur_size| when we refer to such parameters).
-
-*/
-
-#define mathex(A,B) font_param(fam_fnt(3,A),B)
-
-/*tex thickness of \.{\\over} bars */
-
-#define default_rule_thickness(A) mathex(A,8)
-
-/*tex minimum clearance above a displayed op */
-
-#define big_op_spacing1(A) mathex(A,9)
-
-/*tex minimum clearance below a displayed op */
-
-#define big_op_spacing2(A) mathex(A,10)
-
-/*tex minimum baselineskip above displayed op */
-
-#define big_op_spacing3(A) mathex(A,11)
-
-/*tex minimum baselineskip below displayed op */
-
-#define big_op_spacing4(A) mathex(A,12)
-
-/*tex padding above and below displayed limits */
-
-#define big_op_spacing5(A) mathex(A,13)
-
-/*tex
-
-    \LUATEX makes a bunch of extensions cf. the |MATH| table in \OPENTYPE, but
-    some of the |MathConstants| values have no matching usage in \LUATEX\ right
-    now.
-
-    \startitemize
-
-        \startitem
-            |ScriptPercentScaleDown| |ScriptScriptPercentScaleDown|: These should
-            be handled by the macro package, on the engine side there are three
-            separate fonts.
-        \stopitem
-
-        \startitem
-            |DelimitedSubFormulaMinHeight|: This is perhaps related to word's
-            natural math input? We have no idea what to do about it.
-        \stopitem
-
-        \startitem
-            |MathLeading|: \LUATEX does not currently handle multi-line displays,
-            and the parameter does not seem to make much sense elsewhere.
-        \stopitem
-
-        \startitem
-            |FlattenedAccentBaseHeight|: This is based on the |flac| |GSUB|
-            feature. It would not be hard to support that, but proper math accent
-            placements cf.\ |MATH| needs support for |MathTopAccentAttachment|
-            table to be implemented first.
-        \stopitem
-
-    \stopitemize
-
-*/
-
-static void math_param_error(const char *param, int style)
-{
-    char s[256];
-    const char *hlp[] = {
-        "Sorry, but I can't typeset math unless various parameters have",
-        "been set. This is normally done by loading special math fonts",
-        "into the math family slots. Your font set is lacking at least",
-        "the parameter mentioned earlier.",
-        NULL
-    };
-    snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", param, math_style_names[style]);
-    tex_error(s, hlp);
-    return;
-}
-
-static scaled accent_base_height(int f)
-{
-    scaled a;
-    if (do_new_math(f)) {
-        a = font_MATH_par(f, AccentBaseHeight);
-        if (a == undefined_math_parameter)
-            a = x_height(f);
-    } else {
-        a = x_height(f);
-    }
-    return a;
-}
-
-/*tex
-
-        The non-staticness of this function is for the benefit of |texmath.w|.
-        Watch out, this one uses the style! The style and size numbers don't
-        match because we have cramped styles.
-
-*/
-
-scaled get_math_quad_style(int var)
-{
-    scaled a = get_math_param(math_param_quad, var);
-    if (a == undefined_math_parameter) {
-        math_param_error("quad", var);
-        return 0;
-    } else {
-        return a;
-    }
-}
-
-/*tex
-
-    For this reason the next one is different because it is called with a size
-    specifier instead of a style specifier.
-
-*/
-
-static scaled math_axis_size(int b)
-{
-    scaled a;
-    int var;
-    if (b == script_size)
-        var = script_style;
-    else if (b == script_script_size)
-        var = script_script_style;
-    else
-        var = text_style;
-    a = get_math_param(math_param_axis, var);
-    if (a == undefined_math_parameter) {
-        math_param_error("axis", var);
-        return 0;
-    } else {
-        return a;
-    }
-}
-
-scaled get_math_quad_size(int b)
-{
-    int var;
-    if (b == script_size)
-        var = script_style;
-    else if (b == script_script_size)
-        var = script_script_style;
-    else
-        var = text_style;
-    return get_math_param(math_param_quad, var);
-}
-
-static scaled minimum_operator_size(int var)
-{
-    scaled a = get_math_param(math_param_operator_size, var);
-    return a;
-}
-
-/*tex
-
-    Old-style fonts do not define the |radical_rule|. This allows |make_radical|
-    to select the backward compatibility code, and it means that we can't raise
-    an error here.
-
-*/
-
-static scaled radical_rule_par(int var)
-{
-    scaled a = get_math_param(math_param_radical_rule, var);
-    return a;
-}
-
-/*tex
-
-    Now follow all the trivial math parameters.
-
-*/
-
-#define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b)
-#define get_math_param_or_zero(a,b) do_get_math_param_or_zero(a, math_param_##b, #b)
-
-static scaled do_get_math_param_or_error(int var, int param, const char *name)
-{
-    scaled a = get_math_param(param, var);
-    if (a == undefined_math_parameter) {
-        math_param_error(name, var);
-        a = 0;
-    }
-    return a;
-}
-
-static scaled do_get_math_param_or_zero(int var, int param, const char *name)
-{
-    scaled a = get_math_param(param, var);
-    if (a == undefined_math_parameter) {
-        a = 0;
-    }
-    return a;
-}
-
-/*tex
-
-    A variant on a suggestion on the list based on analysis by UV.
-
-*/
-
-static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
-    scaled delta, delta1, delta2;
-    if (axis) {
-        delta2 = max_d + math_axis_size(cur_size);
-    } else {
-        delta2 = max_d;
-    }
-    delta1 = max_h + max_d - delta2;
-    if (delta2 > delta1) {
-        /*tex |delta1| is max distance from axis */
-        delta1 = delta2;
-    }
-    delta = (delta1 / 500) * delimiter_factor_par;
-    delta2 = delta1 + delta1 - delimiter_shortfall_par;
-    if (delta < delta2) {
-        return delta2;
-    } else {
-        return delta;
-    }
-}
-
-#define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before)
-#define radical_degree_after(a)  get_math_param_or_error(a, radical_degree_after)
-#define radical_degree_raise(a)  get_math_param_or_error(a, radical_degree_raise)
-
-#define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min)
-
-#define overbar_rule(a)          get_math_param_or_error(a, overbar_rule)
-#define overbar_kern(a)          get_math_param_or_error(a, overbar_kern)
-#define overbar_vgap(a)          get_math_param_or_error(a, overbar_vgap)
-
-#define underbar_rule(a)         get_math_param_or_error(a, underbar_rule)
-#define underbar_kern(a)         get_math_param_or_error(a, underbar_kern)
-#define underbar_vgap(a)         get_math_param_or_error(a, underbar_vgap)
-
-#define under_delimiter_vgap(a)  get_math_param_or_error(a, under_delimiter_vgap)
-#define under_delimiter_bgap(a)  get_math_param_or_error(a, under_delimiter_bgap)
-
-#define over_delimiter_vgap(a)   get_math_param_or_error(a, over_delimiter_vgap)
-#define over_delimiter_bgap(a)   get_math_param_or_error(a, over_delimiter_bgap)
-
-#define radical_vgap(a)          get_math_param_or_error(a, radical_vgap)
-#define radical_kern(a)          get_math_param_or_error(a, radical_kern)
-
-#define stack_vgap(a)            get_math_param_or_error(a, stack_vgap)
-#define stack_num_up(a)          get_math_param_or_error(a, stack_num_up)
-#define stack_denom_down(a)      get_math_param_or_error(a, stack_denom_down)
-
-#define fraction_rule(a)         get_math_param_or_error(a, fraction_rule)
-#define fraction_num_vgap(a)     get_math_param_or_error(a, fraction_num_vgap)
-#define fraction_denom_vgap(a)   get_math_param_or_error(a, fraction_denom_vgap)
-#define fraction_num_up(a)       get_math_param_or_error(a, fraction_num_up)
-#define fraction_denom_down(a)   get_math_param_or_error(a, fraction_denom_down)
-#define fraction_del_size_new(a) get_math_param_or_error(a, fraction_del_size)
-/*      fraction_del_size_old(a) get_math_param         (a, math_param_fraction_del_size) */
-#define fraction_del_size_old(a) get_math_param_or_error(a, fraction_del_size)
-
-#define skewed_fraction_hgap(a)  get_math_param_or_error(a, skewed_fraction_hgap)
-#define skewed_fraction_vgap(a)  get_math_param_or_error(a, skewed_fraction_vgap)
-
-#define limit_above_vgap(a)      get_math_param_or_error(a, limit_above_vgap)
-#define limit_above_bgap(a)      get_math_param_or_error(a, limit_above_bgap)
-#define limit_above_kern(a)      get_math_param_or_error(a, limit_above_kern)
-
-#define limit_below_vgap(a)      get_math_param_or_error(a, limit_below_vgap)
-#define limit_below_bgap(a)      get_math_param_or_error(a, limit_below_bgap)
-#define limit_below_kern(a)      get_math_param_or_error(a, limit_below_kern)
-
-#define nolimit_sub_factor(a)    get_math_param_or_zero(a, nolimit_sub_factor)
-#define nolimit_sup_factor(a)    get_math_param_or_zero(a, nolimit_sup_factor)
-
-#define sub_shift_drop(a)        get_math_param_or_error(a, sub_shift_drop)
-#define sup_shift_drop(a)        get_math_param_or_error(a, sup_shift_drop)
-#define sub_shift_down(a)        get_math_param_or_error(a, sub_shift_down)
-#define sub_sup_shift_down(a)    get_math_param_or_error(a, sub_sup_shift_down)
-#define sup_shift_up(a)          get_math_param_or_error(a, sup_shift_up)
-#define sub_top_max(a)           get_math_param_or_error(a, sub_top_max)
-#define sup_bottom_min(a)        get_math_param_or_error(a, sup_bottom_min)
-#define sup_sub_bottom_max(a)    get_math_param_or_error(a, sup_sub_bottom_max)
-#define subsup_vgap(a)           get_math_param_or_error(a, subsup_vgap)
-
-#define space_after_script(a)    get_math_param_or_error(a, space_after_script)
-
-void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
-{
-    if (is_new_mathfont(f)) {
-
-        /*tex Fix all known parameters. */
-
-        DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
-            font_size(f), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
-            font_size(f), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
-            font_MATH_par(f, AxisHeight), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
-            font_MATH_par(f, AxisHeight), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
-            font_MATH_par(f, OverbarExtraAscender), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
-            font_MATH_par(f, OverbarExtraAscender), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
-            font_MATH_par(f, OverbarRuleThickness), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
-            font_MATH_par(f, OverbarRuleThickness), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
-            font_MATH_par(f, OverbarVerticalGap), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
-            font_MATH_par(f, OverbarVerticalGap), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
-            font_MATH_par(f, UnderbarExtraDescender), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
-            font_MATH_par(f, UnderbarExtraDescender), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
-            font_MATH_par(f, UnderbarRuleThickness), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
-            font_MATH_par(f, UnderbarRuleThickness), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
-           font_MATH_par(f, UnderbarVerticalGap), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
-            font_MATH_par(f, UnderbarVerticalGap), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
-            font_MATH_par(f, StretchStackGapAboveMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
-            font_MATH_par(f, StretchStackGapAboveMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
-            font_MATH_par(f, StretchStackBottomShiftDown), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
-            font_MATH_par(f, StretchStackBottomShiftDown), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
-            font_MATH_par(f, StretchStackGapBelowMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
-            font_MATH_par(f, StretchStackGapBelowMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
-            font_MATH_par(f, StretchStackTopShiftUp), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
-            font_MATH_par(f, StretchStackTopShiftUp), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
-           font_MATH_par(f, StackTopShiftUp), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
-            font_MATH_par(f, StackTopDisplayStyleShiftUp), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
-            font_MATH_par(f, StackBottomShiftDown), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
-            font_MATH_par(f, StackBottomDisplayStyleShiftDown), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
-            font_MATH_par(f, StackGapMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
-            font_MATH_par(f, StackDisplayStyleGapMin), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
-            font_MATH_par(f, RadicalExtraAscender), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
-            font_MATH_par(f, RadicalExtraAscender), lvl);
-
-        DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id,
-            font_MATH_par(f, DisplayOperatorMinHeight), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id,
-            font_MATH_par(f, RadicalRuleThickness), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id,
-            font_MATH_par(f, RadicalRuleThickness), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
-            font_MATH_par(f, RadicalVerticalGap), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
-            font_MATH_par(f, RadicalDisplayStyleVerticalGap), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
-            font_MATH_par(f, RadicalKernBeforeDegree), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
-            font_MATH_par(f, RadicalKernBeforeDegree), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
-            font_MATH_par(f, RadicalKernAfterDegree), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
-            font_MATH_par(f, RadicalKernAfterDegree), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
-            font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
-            font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
-
-        if (size_id == text_size) {
-            def_math_param(math_param_sup_shift_up, display_style,
-                font_MATH_par(f, SuperscriptShiftUp), lvl);
-            def_math_param(math_param_sup_shift_up, cramped_display_style,
-                font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
-            def_math_param(math_param_sup_shift_up, text_style,
-                font_MATH_par(f, SuperscriptShiftUp), lvl);
-            def_math_param(math_param_sup_shift_up, cramped_text_style,
-                font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
-        } else if (size_id == script_size) {
-            def_math_param(math_param_sup_shift_up, script_style,
-                font_MATH_par(f, SuperscriptShiftUp), lvl);
-            def_math_param(math_param_sup_shift_up, cramped_script_style,
-                font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
-        } else if (size_id == script_script_size) {
-            def_math_param(math_param_sup_shift_up, script_script_style,
-                font_MATH_par(f, SuperscriptShiftUp), lvl);
-            def_math_param(math_param_sup_shift_up, cramped_script_script_style,
-                font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
-        }
-
-        DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id,
-            font_MATH_par(f, SubscriptBaselineDropMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id,
-            font_MATH_par(f, SubscriptBaselineDropMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id,
-            font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id,
-            font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
-            font_MATH_par(f, SubscriptShiftDown), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
-            font_MATH_par(f, SubscriptShiftDown), lvl);
-
-        if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) {
-            DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
-                font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
-            DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
-                font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
-        } else {
-            DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
-                font_MATH_par(f, SubscriptShiftDown), lvl);
-            DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
-                font_MATH_par(f, SubscriptShiftDown), lvl);
-        }
-
-        DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
-            font_MATH_par(f, SubscriptTopMax), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
-            font_MATH_par(f, SubscriptTopMax), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
-            font_MATH_par(f, SuperscriptBottomMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
-            font_MATH_par(f, SuperscriptBottomMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
-            font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
-            font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
-            font_MATH_par(f, SubSuperscriptGapMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
-            font_MATH_par(f, SubSuperscriptGapMin), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
-            font_MATH_par(f, UpperLimitGapMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
-            font_MATH_par(f, UpperLimitGapMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
-            font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
-            font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
-            0, lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
-            0, lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
-            font_MATH_par(f, LowerLimitGapMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
-            font_MATH_par(f, LowerLimitGapMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
-            font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
-            font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
-            0, lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
-            0, lvl);
-        DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
-            font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
-        DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
-            font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
-        DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
-            font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
-        DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
-            font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
-
-        DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
-            font_MATH_par(f, FractionRuleThickness), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
-            font_MATH_par(f, FractionRuleThickness), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
-            font_MATH_par(f, FractionNumeratorGapMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
-            font_MATH_par(f, FractionNumeratorDisplayStyleGapMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
-            font_MATH_par(f, FractionNumeratorShiftUp), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
-            font_MATH_par(f, FractionNumeratorDisplayStyleShiftUp), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
-            font_MATH_par(f, FractionDenominatorGapMin), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
-            font_MATH_par(f,FractionDenominatorDisplayStyleGapMin), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
-            font_MATH_par(f, FractionDenominatorShiftDown), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
-            font_MATH_par(f, FractionDenominatorDisplayStyleShiftDown), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
-            font_MATH_par(f, FractionDelimiterSize), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
-            font_MATH_par(f, FractionDelimiterDisplayStyleSize), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
-            font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
-            font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
-            font_MATH_par(f, SkewedFractionVerticalGap), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
-            font_MATH_par(f, SkewedFractionVerticalGap), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id,
-            font_MATH_par(f, SpaceAfterScript), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id,
-            font_MATH_par(f, SpaceAfterScript), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
-            font_MATH_par(f, MinConnectorOverlap), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
-            font_MATH_par(f, MinConnectorOverlap), lvl);
-
-    } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
-
-        /*tex Fix old-style |sy| parameters. */
-
-        DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
-            math_quad(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
-            math_quad(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
-            axis_height(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
-            axis_height(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
-            num3(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
-            num1(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
-            denom2(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
-            denom1(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
-            num2(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
-            num1(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
-            denom2(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
-            denom1(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
-            delim2(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
-            delim1(size_id), lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
-            0, lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
-            0, lvl);
-        DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
-            0, lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
-            0, lvl);
-
-        if (size_id == text_size) {
-            def_math_param(math_param_sup_shift_up, display_style,
-                sup1(size_id), lvl);
-            def_math_param(math_param_sup_shift_up, cramped_display_style,
-                sup3(size_id), lvl);
-            def_math_param(math_param_sup_shift_up, text_style,
-                sup2(size_id), lvl);
-            def_math_param(math_param_sup_shift_up, cramped_text_style,
-               sup3(size_id), lvl);
-        } else if (size_id == script_size) {
-            def_math_param(math_param_sub_shift_drop, display_style,
-                sub_drop(size_id), lvl);
-            def_math_param(math_param_sub_shift_drop, cramped_display_style,
-                sub_drop(size_id), lvl);
-            def_math_param(math_param_sub_shift_drop, text_style,
-                sub_drop(size_id), lvl);
-            def_math_param(math_param_sub_shift_drop, cramped_text_style,
-                sub_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_drop, display_style,
-                sup_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_drop, cramped_display_style,
-                sup_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_drop, text_style,
-                sup_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_drop, cramped_text_style,
-                sup_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_up, script_style,
-                sup2(size_id), lvl);
-            def_math_param(math_param_sup_shift_up, cramped_script_style,
-                sup3(size_id), lvl);
-        } else if (size_id == script_script_size) {
-            def_math_param(math_param_sub_shift_drop, script_style,
-                sub_drop(size_id), lvl);
-            def_math_param(math_param_sub_shift_drop, cramped_script_style,
-                sub_drop(size_id), lvl);
-            def_math_param(math_param_sub_shift_drop, script_script_style,
-                sub_drop(size_id), lvl);
-            def_math_param(math_param_sub_shift_drop,
-                cramped_script_script_style, sub_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_drop, script_style,
-                sup_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_drop, cramped_script_style,
-                sup_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_drop, script_script_style,
-                sup_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_drop,
-                cramped_script_script_style, sup_drop(size_id), lvl);
-            def_math_param(math_param_sup_shift_up, script_script_style,
-                sup2(size_id), lvl);
-            def_math_param(math_param_sup_shift_up, cramped_script_script_style,
-                sup3(size_id), lvl);
-        }
-
-        DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
-            sub1(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
-            sub1(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
-            sub2(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
-            sub2(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
-            (abs(math_x_height(size_id) * 4) / 5), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
-            (abs(math_x_height(size_id) * 4) / 5), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
-            (abs(math_x_height(size_id)) / 4), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
-            (abs(math_x_height(size_id)) / 4), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
-            (abs(math_x_height(size_id) * 4) / 5), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
-            (abs(math_x_height(size_id) * 4) / 5), lvl);
-
-        /*tex
-
-            The display-size |radical_vgap| is done twice because it needs values
-            from both the sy and the ex font.
-
-        */
-
-        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
-            (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
-            60, lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
-            60, lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
-            xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
-            xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
-            (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
-            (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
-
-    } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
-
-        /*tex Fix old-style |ex| parameters. */
-
-        DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
-            3 * default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
-            3 * default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
-            3 * default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
-            3 * default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
-           (default_rule_thickness(size_id) + (abs(default_rule_thickness(size_id)) / 4)), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
-            3 * default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
-            7 * default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
-            3 * default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
-            default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
-            3 * default_rule_thickness(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
-            big_op_spacing1(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
-            big_op_spacing1(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
-            big_op_spacing3(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
-            big_op_spacing3(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
-            big_op_spacing5(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
-            big_op_spacing5(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
-            big_op_spacing2(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
-            big_op_spacing2(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
-            big_op_spacing4(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
-            big_op_spacing4(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
-           big_op_spacing5(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
-            big_op_spacing5(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
-            font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
-        DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
-            font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
-        DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
-            font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
-        DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
-            font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
-        DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
-            4 * default_rule_thickness(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
-            4 * default_rule_thickness(size_id), lvl);
-
-        /*tex
-
-            All of the |space_after_script|s are done in
-            |finalize_math_parameters| because the \.{\\scriptspace} may have
-            been altered by the user.
-
-        */
-
-        DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
-            0, lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
-            0, lvl);
-
-        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
-            big_op_spacing2(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
-            big_op_spacing2(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
-            big_op_spacing4(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
-            big_op_spacing4(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
-            big_op_spacing1(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
-            big_op_spacing1(size_id), lvl);
-        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
-            big_op_spacing3(size_id), lvl);
-        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
-            big_op_spacing3(size_id), lvl);
-
-        /*tex
-
-            The display-size |radical_vgap| is done twice because it needs values
-            from both the sy and the ex font.
-
-        */
-
-        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
-            (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
-
-    }
-}
-
-/*tex
-
-    This needs to be called just at the start of |mlist_to_hlist|, for backward
-    compatibility with \.{\\scriptspace}.
-
-*/
-
-static void finalize_math_parameters(void)
-{
-    int saved_trace = tracing_assigns_par;
-    tracing_assigns_par = 0;
-    if (get_math_param(math_param_space_after_script, display_style) == undefined_math_parameter) {
-        def_math_param(math_param_space_after_script, display_style,
-            script_space_par, level_one);
-        def_math_param(math_param_space_after_script, text_style,
-            script_space_par, level_one);
-        def_math_param(math_param_space_after_script, script_style,
-            script_space_par, level_one);
-        def_math_param(math_param_space_after_script, script_script_style,
-            script_space_par, level_one);
-        def_math_param(math_param_space_after_script, cramped_display_style,
-            script_space_par, level_one);
-        def_math_param(math_param_space_after_script, cramped_text_style,
-            script_space_par, level_one);
-        def_math_param(math_param_space_after_script, cramped_script_style,
-            script_space_par, level_one);
-        def_math_param(math_param_space_after_script, cramped_script_script_style,
-            script_space_par, level_one);
-    }
-    tracing_assigns_par = saved_trace;
-}
-
-/*tex
-
-    In order to convert mlists to hlists, i.e., noads to nodes, we need several
-    subroutines that are conveniently dealt with now.
-
-    Let us first introduce the macros that make it easy to get at the parameters
-    and other font information. A size code, which is a multiple of 256, is added
-    to a family number to get an index into the table of internal font numbers
-    for each combination of family and size. (Be alert: Size codes get larger as
-    the type gets smaller.)
-
-*/
-
-static const char *math_size_string(int s)
-{
-    if (s == text_size)
-        return "textfont";
-    else if (s == script_size)
-        return "scriptfont";
-    else
-        return "scriptscriptfont";
-}
-
-/*tex
-
-    When the style changes, the following piece of program computes associated
-    information:
-
-*/
-
-#define setup_cur_size(a) do { \
-    if (a==script_style || a==cramped_script_style) \
-        cur_size = script_size; \
-    else if (a==script_script_style || a==cramped_script_script_style) \
-        cur_size = script_script_size; \
-    else \
-        cur_size = text_size; \
-} while (0)
-
-
-/*tex
-
-    A simple routine that creates a flat copy of a nucleus.
-
-*/
-
-static pointer math_clone(pointer q)
-{
-    pointer x;
-    if (q == null)
-        return null;
-    x = new_node(type(q), 0);
-    reset_attributes(x, node_attr(q));
-    if (type(q) == math_char_node) {
-        math_fam(x) = math_fam(q);
-        math_character(x) = math_character(q);
-    } else {
-        math_list(x) = math_list(q);
-    }
-    return x;
-}
-
-/*tex
-
-    Here is a function that returns a pointer to a rule node having a given
-    thickness |t|. The rule will extend horizontally to the boundary of the vlist
-    that eventually contains it.
-
-*/
-
-static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfword cur_size, halfword cur_fam)
-{
-    pointer p;
-    if (math_rules_mode_par) {
-        p = new_rule(some_rule);
-        rule_math_size(p) = cur_size;
-        rule_math_font(p) = fam_fnt(cur_fam, cur_size);
-    } else {
-        p = new_rule(normal_rule);
-    }
-    rule_dir(p) = math_direction_par;
-    height(p) = t;
-    depth(p) = 0;
-    reset_attributes(p, att);
-    return p;
-}
-
-/*tex
-
-    The |overbar| function returns a pointer to a vlist box that consists of a
-    given box |b|, above which has been placed a kern of height |k| under a
-    fraction rule of thickness |t| under additional space of height |ht|.
-
-*/
-
-static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, halfword index, halfword cur_size, halfword cur_fam)
-{
-    pointer p, q;
-    p = new_kern(k);
-    reset_attributes(p, att);
-    couple_nodes(p,b);
-    q = do_fraction_rule(t, att, index, cur_size, cur_fam);
-    couple_nodes(q,p);
-    p = new_kern(ht);
-    reset_attributes(p, att);
-    couple_nodes(p,q);
-    q = vpackage(p, 0, additional, max_dimen, math_direction_par);
-    reset_attributes(q, att);
-    return q;
-}
-
-/*tex
-
-    Here is a subroutine that creates a new box, whose list contains a single
-    character, and whose width includes the italic correction for that character.
-    The height or depth of the box will be negative, if the height or depth of
-    the character is negative; thus, this routine may deliver a slightly
-    different result than |hpack| would produce.
-
-*/
-
-static pointer char_box(internal_font_number f, int c, pointer bb)
-{
-    /*tex The new box and its character node. */
-    pointer b, p;
-    b = new_null_box();
-    if (do_new_math(f))
-        width(b) = char_width(f, c);
-    else
-        width(b) = char_width(f, c) + char_italic(f, c);
-    height(b) = char_height(f, c);
-    depth(b) = char_depth(f, c);
-    reset_attributes(b, bb);
-    p = new_char(f, c);
-    reset_attributes(p, bb);
-    list_ptr(b) = p;
-    return b;
-}
-
-/*tex
-
-    Another handy subroutine computes the height plus depth of a given character:
-
-*/
-
-static scaled height_plus_depth(internal_font_number f, int c)
-{
-    return (char_height(f, c) + char_depth(f, c));
-}
-
-/*tex
-
-    When we build an extensible character, it's handy to have the following
-    subroutine, which puts a given character on top of the characters already in
-    box |b|:
-
-*/
-
-static scaled stack_into_box(pointer b, internal_font_number f, int c)
-{
-    /*tex New node placed into |b|: */
-    pointer p, q;
-    /*tex Italic gets added to width. */
-    p = char_box(f, c, node_attr(b));
-    if (type(b) == vlist_node) {
-        try_couple_nodes(p,list_ptr(b));
-        list_ptr(b) = p;
-        height(b) = height(p);
-        if (width(b) < width(p))
-            width(b) = width(p);
-        return height_plus_depth(f, c);
-    } else {
-        q = list_ptr(b);
-        if (q == null) {
-            list_ptr(b) = p;
-        } else {
-            while (vlink(q) != null)
-                q = vlink(q);
-            couple_nodes(q,p);
-        }
-        if (height(b) < height(p))
-            height(b) = height(p);
-        if (depth(b) < depth(p))
-            depth(b) = depth(p);
-        return char_width(f, c);
-    }
-}
-
-static void stack_glue_into_box(pointer b, scaled min, scaled max) {
-    halfword p = new_glue(zero_glue);
-    width(p) = min;
-    stretch(p) = max - min;
-    if (node_attr(b) != null) {
-        reset_attributes(p, node_attr(b));
-    }
-    if (type(b) == vlist_node) {
-        try_couple_nodes(p,list_ptr(b));
-        list_ptr(b) = p;
-    } else {
-        halfword q = list_ptr(b);
-        if (q == null) {
-            list_ptr(b) = p;
-        } else {
-            while (vlink(q) != null) {
-                q = vlink(q);
-            }
-            couple_nodes(q,p);
-        }
-    }
-}
-
-/*tex
-
-    \TeX's most important routine for dealing with formulas is called
-    |mlist_to_hlist|. After a formula has been scanned and represented as an
-    mlist, this routine converts it to an hlist that can be placed into a box or
-    incorporated into the text of a paragraph. The explicit parameter |cur_mlist|
-    points to the first node or noad in the given mlist (and it might be |null|);
-    the parameter |penalties| is |true| if penalty nodes for potential line
-    breaks are to be inserted into the resulting hlist, the parameter |cur_style|
-    is a style code. After |mlist_to_hlist| has acted, |vlink(temp_head)| points
-    to the translated hlist.
-
-    Since mlists can be inside mlists, the procedure is recursive. And since this
-    is not part of \TeX's inner loop, the program has been written in a manner
-    that stresses compactness over efficiency.
-
-*/
-
-/*tex Size code corresponding to |cur_style|:  */
-
-int cur_size;
-
-static pointer get_delim_box(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att)
-{
-    int callback_id = callback_defined(make_extensible_callback);
-    if (callback_id > 0) {
-        /*tex
-            This call is not optimized as it hardly makes sense to use it ... special
-            and a it of feature creep too.
-        */
-        halfword b = null;
-        run_callback(callback_id, "ddddbN->N",fnt,chr,v,min_overlap,horizontal,att,&b);
-        if (b == null) {
-            /*tex
-                We see this as a signal to do it the \TEX\ way.
-            */
-        } else if (type(b) == hlist_node || type(b) == vlist_node) {
-            return b;
-        } else {
-            formatted_error("fonts","invalid extensible character %i created for font %i, [h|v]list expected",chr,fnt);
-        }
-    }
-    return make_extensible(fnt, chr, v, min_overlap, horizontal, att);
-}
-
-pointer make_extensible(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att)
-{
-    /*tex new box */
-    pointer b;
-    /*tex natural (maximum) size of the stack */
-    scaled b_max;
-    /*tex amount of possible shrink in the stack */
-    scaled s_max;
-    extinfo *cur;
-    extinfo *ext;
-    scaled prev_overlap;
-    /*tex a temporary counter number of extensible pieces */
-    int i;
-    /*tex number of times to repeat each repeatable item in |ext| */
-    int with_extenders;
-    int num_extenders, num_normal;
-    scaled a, c, d;
-    b = new_null_box();
-    with_extenders = -1;
-    num_extenders = 0;
-    num_normal = 0;
-    if (min_overlap < 0) {
-        min_overlap = 0;
-    }
-    if (horizontal) {
-        type(b) = (quarterword) hlist_node;
-        ext = get_charinfo_hor_variants(char_info(fnt,chr));
-    } else {
-        type(b) = (quarterword) vlist_node;
-        ext = get_charinfo_vert_variants(char_info(fnt,chr));
-    }
-    if (att != null) {
-        reset_attributes(b,att);
-    }
-    cur = ext;
-    while (cur != NULL) {
-        if (!char_exists(fnt, cur->glyph)) {
-            const char *hlp[] = {
-                "Each glyph part in an extensible item should exist in the font.",
-                "I will give up trying to find a suitable size for now. Fix your font!",
-                NULL
-            };
-            tex_error("Variant part doesn't exist.", hlp);
-            width(b) = null_delimiter_space_par;
-            return b;
-        }
-        if (cur->extender > 0)
-            num_extenders++;
-        else
-            num_normal++;
-        /*tex No negative overlaps or advances are allowed. */
-        if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) {
-            const char *hlp[] = {
-                "All measurements in extensible items should be positive.",
-                "To get around this problem, I have changed the font metrics.",
-                "Fix your font!",
-                NULL
-            };
-            tex_error("Extensible recipe has negative fields.", hlp);
-            if (cur->start_overlap < 0)
-                cur->start_overlap = 0;
-            if (cur->end_overlap < 0)
-                cur->end_overlap = 0;
-            if (cur->advance < 0)
-                cur->advance = 0;
-        }
-        cur = cur->next;
-    }
-    if (num_normal == 0) {
-        const char *hlp[] = {
-            "Each extensible recipe should have at least one non-repeatable part.",
-            "To get around this problem, I have changed the first part to be",
-            "non-repeatable. Fix your font!",
-            NULL
-        };
-        tex_error("Extensible recipe has no fixed parts.", hlp);
-        ext->extender = 0;
-        num_normal = 1;
-        num_extenders--;
-    }
-    /*tex
-
-        |ext| holds a linked list of numerous items that may or may not be
-        repeatable. For the total height, we have to figure out how many items
-        are needed to create a stack of at least |v|.
-
-        The next |while| loop does that. It has two goals: it finds out the
-        natural height |b_max| of the all the parts needed to reach at least |v|,
-        and it sets |with_extenders| to the number of times each of the
-        repeatable items in |ext| has to be repeated to reach that height.
-
-    */
-    cur = ext;
-    b_max = 0;
-    while (b_max < v && num_extenders > 0) {
-        b_max = 0;
-        prev_overlap = 0;
-        with_extenders++;
-        for (cur = ext; cur != NULL; cur = cur->next) {
-            if (cur->extender == 0) {
-                c = cur->start_overlap;
-                if (min_overlap < c)
-                    c = min_overlap;
-                if (prev_overlap < c)
-                    c = prev_overlap;
-                a = cur->advance;
-                if (a == 0) {
-                    /*tex for tfm fonts */
-                    if (horizontal) {
-                        a = char_width(fnt, cur->glyph);
-                    } else {
-                        a = height_plus_depth(fnt, cur->glyph);
-                    }
-                    if (a < 0) {
-                        formatted_error("fonts","bad extensible character %i in font %i",chr,fnt);
-                    }
-                }
-                b_max += a - c;
-                prev_overlap = cur->end_overlap;
-            } else {
-                i = with_extenders;
-                while (i > 0) {
-                    c = cur->start_overlap;
-                    if (min_overlap < c)
-                        c = min_overlap;
-                    if (prev_overlap < c)
-                        c = prev_overlap;
-                    a = cur->advance;
-                    if (a == 0) {
-                        /*tex for tfm fonts */
-                        if (horizontal) {
-                            a = char_width(fnt, cur->glyph);
-                        } else {
-                            a = height_plus_depth(fnt, cur->glyph);
-                        }
-                        if (a < 0) {
-                            formatted_error("fonts","bad extensible character %i in font %i",chr,fnt);
-                        }
-                    }
-                    b_max += a - c;
-                    prev_overlap = cur->end_overlap;
-                    i--;
-                }
-            }
-        }
-    }
-    /*tex
-
-        Assemble box using |with_extenders| copies of each extender, with
-        appropriate glue wherever an overlap occurs.
-
-    */
-    prev_overlap = 0;
-    b_max = 0;
-    s_max = 0;
-    for (cur = ext; cur != NULL; cur = cur->next) {
-        if (cur->extender == 0) {
-            c = cur->start_overlap;
-            if (prev_overlap < c)
-                c = prev_overlap;
-            d = c;
-            if (min_overlap < c)
-                c = min_overlap;
-            if (d > 0) {
-                stack_glue_into_box(b, -d, -c);
-                s_max += (-c) - (-d);
-                b_max -= d;
-            }
-            b_max += stack_into_box(b, fnt, cur->glyph);
-            prev_overlap = cur->end_overlap;
-            i--;
-        } else {
-            i = with_extenders;
-            while (i > 0) {
-                c = cur->start_overlap;
-                if (prev_overlap < c)
-                    c = prev_overlap;
-                d = c;
-                if (min_overlap < c)
-                    c = min_overlap;
-                if (d > 0) {
-                    stack_glue_into_box(b, -d, -c);
-                    s_max += (-c) - (-d);
-                    b_max -= d;
-                }
-                b_max += stack_into_box(b, fnt, cur->glyph);
-                prev_overlap = cur->end_overlap;
-                i--;
-            }
-        }
-    }
-    /*tex Set glue so as to stretch the connections if needed. */
-    d = 0;
-    if (v > b_max && s_max > 0) {
-        d = v-b_max;
-        /*tex Don't stretch more than |s_max|. */
-        if (d > s_max)
-            d = s_max;
-        glue_order(b) = normal;
-        glue_sign(b) = stretching;
-        glue_set(b) = unfloat(d/(float) s_max);
-        b_max += d;
-    }
-    if (horizontal) {
-        width(b) = b_max;
-    } else {
-        height(b) = b_max;
-    }
-    return b;
-}
-
-/*tex
-
-    The |var_delimiter| function, which finds or constructs a sufficiently large
-    delimiter, is the most interesting of the auxiliary functions that currently
-    concern us. Given a pointer |d| to a delimiter field in some noad, together
-    with a size code |s| and a vertical distance |v|, this function returns a
-    pointer to a box that contains the smallest variant of |d| whose height plus
-    depth is |v| or more. (And if no variant is large enough, it returns the
-    largest available variant.) In particular, this routine will construct
-    arbitrarily large delimiters from extensible components, if |d| leads to such
-    characters.
-
-    The value returned is a box whose |shift_amount| has been set so that the box
-    is vertically centered with respect to the axis in the given size. If a
-    built-up symbol is returned, the height of the box before shifting will be
-    the height of its topmost component.
-
-*/
-
-static void endless_loop_error(internal_font_number g, int y)
-{
-    char s[256];
-    const char *hlp[] = {
-        "You managed to create a seemingly endless charlist chain in the current",
-        "font. I have counted until 10000 already and still have not escaped, so"
-        "I will jump out of the loop all by myself now. Fix your font!",
-        NULL
-    };
-    snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)", (int) y, font_name(g));
-    tex_error(s, hlp);
-}
-
-static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, int cur_style, boolean shift, boolean *stack, scaled *delta, int *same)
-{
-    /*tex the box that will be constructed */
-    pointer b;
-    /*tex best-so-far and tentative font codes */
-    internal_font_number f, g;
-    /*tex best-so-far and tentative character codes */
-    int c, i, x, y;
-    /*tex height-plus-depth of a tentative character */
-    scaled u;
-    /*tex largest height-plus-depth so far */
-    scaled w = 0;
-    /*tex runs through font family members */
-    int z;
-    /*tex are we trying the ``large'' variant? */
-    boolean large_attempt = false;
-    /*tex to save the current attribute list */
-    pointer att = null;
-    int emas = 0 ;
-    boolean do_parts = false;
-    boolean parts_done = false;
-    extinfo *ext;
-    f = null_font;
-    c = 0;
-    if (d == null)
-        goto FOUND;
-    z = small_fam(d);
-    x = small_char(d);
-    i = 0;
-    if (same != NULL) {
-        emas = *same ;
-        same = 0;
-    }
-    while (true) {
-        /*tex
-
-            The search process is complicated slightly by the facts that some of
-            the characters might not be present in some of the fonts, and they
-            might not be probed in increasing order of height.
-
-        */
-        if ((z != 0) || (x != 0)) {
-            g = fam_fnt(z, s);
-            if (g != null_font) {
-                y = x;
-              CONTINUE:
-                i++;
-                if (char_exists(g, y)) {
-                    if (flat)
-                        u = char_width(g, y);
-                    else
-                        u = height_plus_depth(g, y);
-                    if (u > w) {
-                        f = g;
-                        c = y;
-                        w = u;
-                        if (u >= v)
-                            goto FOUND;
-                    }
-                    if (char_tag(g, y) == ext_tag) {
-                        f = g;
-                        c = y;
-                        do_parts = true;
-                        goto FOUND;
-                    }
-                    if (i > 10000) {
-                        endless_loop_error(g, y);
-                        goto FOUND;
-                    }
-                    if (char_tag(g, y) == list_tag) {
-                        y = char_remainder(g, y);
-                        goto CONTINUE;
-                    }
-                }
-            }
-        }
-        if (large_attempt) {
-            /*tex There were none large enough. */
-            goto FOUND;
-        }
-        large_attempt = true;
-        z = large_fam(d);
-        x = large_char(d);
-    }
-  FOUND:
-    if (d != null) {
-        att = node_attr(d);
-        node_attr(d) = null;
-        flush_node(d);
-    }
-    if (f != null_font) {
-        /*tex
-
-            When the following code is executed, |do_parts| will be true if a
-            built-up symbol is supposed to be returned.
-
-        */
-        ext = NULL;
-        if ((do_parts) && ((!flat && (ext = get_charinfo_vert_variants(char_info(f,c))) != NULL)
-                       ||  ( flat && (ext = get_charinfo_hor_variants (char_info(f,c))) != NULL))) {
-            parts_done = true;
-            if (flat) {
-                b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 1, att);
-            } else {
-                b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 0, att);
-            }
-            if (delta != NULL) {
-                if (do_new_math(f)) {
-                    *delta = char_vert_italic(f,x);
-                } else {
-                    *delta = char_italic(f,x);
-                }
-            }
-            if (stack != NULL)
-                *stack = true ;
-        } else {
-            parts_done = false;
-            if (same != NULL && x == c) {
-                *same = emas;
-            }
-            b = char_box(f, c, att);
-            if (!do_new_math(f)) {
-                /*tex Italic gets added to width. */
-                width(b) += char_italic(f, c);
-            }
-            if (delta != NULL) {
-                /*tex This used to be (f, x). */
-                *delta = char_italic(f, c);
-            }
-            if (stack != NULL)
-                *stack = false ;
-        }
-    } else {
-        b = new_null_box();
-        reset_attributes(b, att);
-        if (flat) {
-            width(b) = 0;
-        } else {
-            /*tex Use this width if no delimiter was found. */
-            width(b) = null_delimiter_space_par;
-        }
-        if (delta != NULL) {
-            *delta = 0;
-        }
-        if (stack != NULL)
-            *stack = false ;
-    }
-    if (!flat) {
-        /*tex when emas ~= 0 then we have a non scaled character */
-        if (emas != 0 && delimitermodesamenos) {
-            /*tex same character and no shift when same forced */
-            goto DONE;
-        }
-        if (! parts_done && delimitermodecharnos) {
-            /*tex same character and no shift when same forced */
-            goto DONE;
-        }
-        if (delimitermodenoshift) {
-            /*tex no shift forced */
-            goto DONE;
-        }
-        /*tex vertical variant */
-        shift_amount(b) = half(height(b) - depth(b));
-        if (shift) {
-            shift_amount(b) -= math_axis_size(s);
-        }
-    }
-    DONE:
-    delete_attribute_ref(att);
-    return b;
-}
-
-/*tex
-
-    The next subroutine is much simpler; it is used for numerators and
-    denominators of fractions as well as for displayed operators and their limits
-    above and below. It takes a given box~|b| and changes it so that the new box
-    is centered in a box of width~|w|. The centering is done by putting \.{\\hss}
-    glue at the left and right of the list inside |b|, then packaging the new
-    box; thus, the actual box might not really be centered, if it already
-    contains infinite glue.
-
-    The given box might contain a single character whose italic correction has
-    been added to the width of the box; in this case a compensating kern is
-    inserted.
-
-*/
-
-static pointer rebox(pointer b, scaled w)
-{
-    /*tex temporary registers for list manipulation */
-    pointer p, q, r, att;
-    /*tex font in a one-character box */
-    internal_font_number f;
-    /*tex width of a character without italic correction */
-    scaled v;
-    if ((width(b) != w) && (list_ptr(b) != null)) {
-        if (type(b) == vlist_node) {
-            p = hpack(b, 0, additional, -1);
-            reset_attributes(p, node_attr(b));
-            b = p;
-        }
-        p = list_ptr(b);
-        att = node_attr(b);
-        add_node_attr_ref(att);
-        if ((is_char_node(p)) && (vlink(p) == null)) {
-            f = font(p);
-            v = char_width(f, character(p));
-            if (v != width(b)) {
-                q = new_kern(width(b) - v);
-                reset_attributes(q, att);
-                couple_nodes(p,q);
-            }
-        }
-        list_ptr(b) = null;
-        flush_node(b);
-        b = new_glue(ss_glue);
-        reset_attributes(b, att);
-        couple_nodes(b,p);
-        while (vlink(p) != null)
-            p = vlink(p);
-        q = new_glue(ss_glue);
-        reset_attributes(q, att);
-        couple_nodes(p,q);
-        r = hpack(b, w, exactly, -1);
-        reset_attributes(r, att);
-        delete_attribute_ref(att);
-        return r;
-    } else {
-        width(b) = w;
-        return b;
-    }
-}
-
-/*tex
-
-    Here is a subroutine that creates a new glue specification from another one
-    that is expressed in `\.{mu}', given the value of the math unit.
-
-*/
-
-#define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen)
-
-static pointer math_glue(pointer g, scaled m)
-{
-    /*tex integer part of |m| */
-    int n = x_over_n(m, unity);
-    /*tex fraction part of |m| */
-    scaled f = tex_remainder;
-    /*tex the new glue specification */
-    pointer p;
-    if (f < 0) {
-        decr(n);
-        f = f + unity;
-    }
-    p = new_node(glue_node, 0);
-    /* convert \.{mu} to \.{pt} */
-    width(p) = mu_mult(width(g));
-    stretch_order(p) = stretch_order(g);
-    if (stretch_order(p) == normal)
-        stretch(p) = mu_mult(stretch(g));
-    else
-        stretch(p) = stretch(g);
-    shrink_order(p) = shrink_order(g);
-    if (shrink_order(p) == normal)
-        shrink(p) = mu_mult(shrink(g));
-    else
-        shrink(p) = shrink(g);
-    return p;
-}
-
-static void math_glue_to_glue(pointer p, scaled m)
-{
-    /*tex integer part of |m| */
-    int n = x_over_n(m, unity);
-    /*tex fraction part of |m| */
-    scaled f = tex_remainder;
-    if (f < 0) {
-        decr(n);
-        f = f + unity;
-    }
-    /* convert \.{mu} to \.{pt} */
-    width(p) = mu_mult(width(p));
-    if (stretch_order(p) == normal)
-        stretch(p) = mu_mult(stretch(p));
-    if (shrink_order(p) == normal)
-        shrink(p) = mu_mult(shrink(p));
-    subtype(p) = normal;
-}
-
-/*tex
-
-    The |math_kern| subroutine removes |mu_glue| from a kern node, given the
-    value of the math unit.
-
-*/
-static void math_kern(pointer p, scaled m)
-{
-    /*tex integer part of |m| */
-    int n;
-    /*tex fraction part of |m| */
-    scaled f;
-    if (subtype(p) == mu_glue) {
-        n = x_over_n(m, unity);
-        f = tex_remainder;
-        if (f < 0) {
-            decr(n);
-            f = f + unity;
-        }
-        width(p) = mu_mult(width(p));
-        /* this is weird, it's not a italic but explicit_kern */
-        subtype(p) = italic_kern;
-    }
-}
-
-void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
-{
-    int callback_id;
-    int a, sfix, i;
-    if (p == null) {
-        vlink(temp_head) = null;
-        return;
-    }
-    finalize_math_parameters();
-    callback_id = callback_defined(mlist_to_hlist_callback);
-    if (callback_id > 0) {
-        sfix = lua_gettop(Luas);
-        if (!get_callback(Luas, callback_id)) {
-            lua_settop(Luas, sfix);
-            return;
-        }
-        alink(p) = null ;
-        nodelist_to_lua(Luas, p);
-        lua_push_math_style_name(Luas, mstyle);
-        lua_pushboolean(Luas, penalties);
-        if ((i=lua_pcall(Luas, 3, 1, 0)) != 0) {
-            formatted_warning("mlist to hlist","error: %s",lua_tostring(Luas, -1));
-            lua_settop(Luas, sfix);
-            luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-            return;
-        }
-        a = nodelist_from_lua(Luas,-1);
-        /* alink(vlink(a)) = null; */
-        vlink(temp_head) = a;
-        lua_settop(Luas, sfix);
-    } else if (callback_id == 0) {
-        mlist_to_hlist(p, penalties, mstyle);
-    } else {
-        vlink(temp_head) = null;
-    }
-}
-
-/*tex
-
-    The recursion in |mlist_to_hlist| is due primarily to a subroutine called
-    |clean_box| that puts a given noad field into a box using a given math style;
-    |mlist_to_hlist| can call |clean_box|, which can call |mlist_to_hlist|.
-
-    The box returned by |clean_box| is ``clean'' in the sense that its
-    |shift_amount| is zero.
-
-*/
-
-static pointer clean_box(pointer p, int s, int cur_style)
-{
-    /*tex beginning of a list to be boxed */
-    pointer q;
-    /*tex box to be returned */
-    pointer x;
-    /*tex temporary pointer */
-    pointer r;
-    /*tex beginning of mlist to be translated */
-    pointer mlist = null;
-    switch (type(p)) {
-        case math_char_node:
-            mlist = new_noad();
-            r = math_clone(p);
-            nucleus(mlist) = r;
-            break;
-        case sub_box_node:
-            q = math_list(p);
-            goto FOUND;
-            break;
-        case sub_mlist_node:
-            mlist = math_list(p);
-            break;
-        default:
-            q = new_null_box();
-            goto FOUND;
-    }
-    mlist_to_hlist(mlist, false, s);
-    /*tex recursive call */
-    q = vlink(temp_head);
-    setup_cur_size(cur_style);
-  FOUND:
-    if (is_char_node(q) || (q == null))
-        x = hpack(q, 0, additional, -1);
-    else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0))
-        /*tex It's already clean. */
-        x = q;
-    else
-        x = hpack(q, 0, additional, -1);
-    if (x != q && q != null)
-        reset_attributes(x, node_attr(q));
-    /*tex Here we save memory space in a common case. */
-    q = list_ptr(x);
-    if (is_char_node(q)) {
-        r = vlink(q);
-        if (r != null) {
-            if (vlink(r) == null) {
-                if (!is_char_node(r)) {
-                    if (type(r) == kern_node) {
-                        /*tex Unneeded italic correction. */
-                        flush_node(r);
-                        vlink(q) = null;
-                    }
-                }
-            }
-        }
-    }
-    return x;
-}
-
-/*tex
-
-    It is convenient to have a procedure that converts a |math_char| field to an
-    ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c| to the font
-    code and character code of a given noad field. It also takes care of issuing
-    error messages for nonexistent characters; in such cases,
-    |char_exists(cur_f,cur_c)| will be |false| after |fetch| has acted, and the
-    field will also have been reset to |null|.
-
-    The outputs of |fetch| are placed in global variables.
-
-*/
-
-/*tex the |font| field of a |math_char| */
-
-internal_font_number cur_f;
-
-/*tex the |character| field of a |math_char| */
-
-int cur_c;
-
-/*tex Here we unpack the |math_char| field |a|. */
-
-static void fetch(pointer a)
-{
-    cur_c = math_character(a);
-    cur_f = fam_fnt(math_fam(a), cur_size);
-    if (cur_f == null_font) {
-        char *msg;
-        const char *hlp[] = {
-            "Somewhere in the math formula just ended, you used the",
-            "stated character from an undefined font family. For example,",
-            "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
-            "and I'll try to forget that I needed that character.",
-            NULL
-        };
-        msg = xmalloc(256);
-        snprintf(msg, 255, "\\%s%d is undefined (character %d)",
-                 math_size_string(cur_size), (int) math_fam(a), (int) cur_c);
-        tex_error(msg, hlp);
-        free(msg);
-    } else if (!(char_exists(cur_f, cur_c))) {
-        char_warning(cur_f, cur_c);
-    }
-}
-
-/*tex
-
-    We need to do a lot of different things, so |mlist_to_hlist| makes two passes
-    over the given mlist.
-
-    The first pass does most of the processing: It removes ``mu'' spacing from
-    glue, it recursively evaluates all subsidiary mlists so that only the
-    top-level mlist remains to be handled, it puts fractions and square roots and
-    such things into boxes, it attaches subscripts and superscripts, and it
-    computes the overall height and depth of the top-level mlist so that the size
-    of delimiters for a |fence_noad| will be known. The hlist resulting from each
-    noad is recorded in that noad's |new_hlist| field, an integer field that
-    replaces the |nucleus| or |thickness|.
-
-    The second pass eliminates all noads and inserts the correct glue and
-    penalties between nodes.
-
-*/
-
-static void assign_new_hlist(pointer q, pointer r)
-{
-    switch (type(q)) {
-        case fraction_noad:
-            math_list(numerator(q)) = null;
-            flush_node(numerator(q));
-            numerator(q) = null;
-            math_list(denominator(q)) = null;
-            flush_node(denominator(q));
-            denominator(q) = null;
-            break;
-        case radical_noad:
-        case simple_noad:
-        case accent_noad:
-            if (nucleus(q) != null) {
-                math_list(nucleus(q)) = null;
-                flush_node(nucleus(q));
-                nucleus(q) = null;
-            }
-            break;
-    }
-    new_hlist(q) = r;
-}
-
-#define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
-
-/*tex
-
-    Most of the actual construction work of |mlist_to_hlist| is done by
-    procedures with names like |make_fraction|, |make_radical|, etc. To
-    illustrate the general setup of such procedures, let's begin with a couple of
-    simple ones.
-
-*/
-
-static void make_over(pointer q, int cur_style, int cur_size, int cur_fam)
-{
-    /*tex
-
-        No rule adaption yet, maybe never as overbars should be proper
-        extensibles.
-
-    */
-    pointer p;
-    scaled f, t;
-    scaled used_thickness = overbar_rule(cur_style);
-    scaled used_fam = cur_fam;
-    if (math_rule_thickness_mode_par > 0) {
-        f = noad_fam(q);
-        if (f >= 0) {
-            t = fam_fnt(f,cur_size);
-            if (do_new_math(t)) {
-                t = font_MATH_par(t, OverbarRuleThickness);
-                if (t != undefined_math_parameter) {
-                    used_thickness = t;
-                    used_fam = f;
-               }
-            }
-        }
-    }
-    p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style),
-                overbar_vgap(cur_style), used_thickness, overbar_kern(cur_style),
-                node_attr(nucleus(q)), math_over_rule, cur_size, used_fam);
-    math_list(nucleus(q)) = p;
-    type(nucleus(q)) = sub_box_node;
-}
-
-static void make_under(pointer q, int cur_style, int cur_size, int cur_fam)
-{
-    /*tex
-
-        No rule adaption yet, maybe never as underbars should be proper
-        extensibles.
-
-    */
-    /*tex temporary registers for box construction */
-    pointer p, x, y, r;
-    /*tex overall height plus depth */
-    scaled delta;
-    scaled f, t;
-    scaled used_thickness = underbar_rule(cur_style);
-    scaled used_fam = cur_fam;
-    x = clean_box(nucleus(q), cur_style, cur_style);
-    p = new_kern(underbar_vgap(cur_style));
-    reset_attributes(p, node_attr(q));
-    couple_nodes(x,p);
-    if (math_rule_thickness_mode_par > 0) {
-        f = noad_fam(q);
-        if (f >= 0) {
-            t = fam_fnt(f,cur_size);
-            if (do_new_math(t)) {
-                t = font_MATH_par(t, UnderbarRuleThickness);
-                if (t != undefined_math_parameter) {
-                    used_thickness = t;
-                    used_fam = f;
-               }
-            }
-        }
-    }
-    r = do_fraction_rule(used_thickness, node_attr(q), math_under_rule, cur_size, used_fam);
-    couple_nodes(p,r);
-    y = vpackage(x, 0, additional, max_dimen, math_direction_par);
-    reset_attributes(y, node_attr(q));
-    delta = height(y) + depth(y) + underbar_kern(cur_style);
-    height(y) = height(x);
-    depth(y) = delta - height(y);
-    math_list(nucleus(q)) = y;
-    type(nucleus(q)) = sub_box_node;
-}
-
-static void make_vcenter(pointer q)
-{
-    /*tex the box that should be centered vertically */
-    pointer v;
-    /*tex its height plus depth */
-    scaled delta;
-    v = math_list(nucleus(q));
-    if (type(v) != vlist_node)
-        confusion("vcenter");
-    delta = height(v) + depth(v);
-    height(v) = math_axis_size(cur_size) + half(delta);
-    depth(v) = delta - height(v);
-}
-
-/*tex
-
-    According to the rules in the \.{DVI} file specifications, we ensure
-    alignment between a square root sign and the rule above its nucleus by
-    assuming that the baseline of the square-root symbol is the same as the
-    bottom of the rule. The height of the square-root symbol will be the
-    thickness of the rule, and the depth of the square-root symbol should exceed
-    or equal the height-plus-depth of the nucleus plus a certain minimum
-    clearance~|psi|. The symbol will be placed so that the actual clearance is
-    |psi| plus half the excess.
-
-*/
-
-static void make_hextension(pointer q, int cur_style)
-{
-    pointer e, p;
-    halfword w;
-    boolean stack = false;
-    e = do_delimiter(q, left_delimiter(q), cur_size, radicalwidth(q), true, cur_style, true, &stack, NULL, NULL);
-    w = width(e);
-    if (!stack&& (radicalwidth(q) != 0) && (radicalwidth(q) != width(e))) {
-        if (radicalmiddle(q)) {
-            p = new_kern(half(radicalwidth(q)-w));
-            reset_attributes(p, node_attr(q));
-            couple_nodes(p,e);
-            e = p;
-            w = radicalwidth(q);
-        } else if (radicalexact(q)) {
-            w = radicalwidth(q);
-        }
-    }
-    e = hpack(e, 0, additional, -1);
-    width(e) = w ;
-    reset_attributes(e, node_attr(q));
-    math_list(nucleus(q)) = e;
-    left_delimiter(q) = null;
-}
-
-static void make_radical(pointer q, int cur_style)
-{
-    /*tex temporary registers for box construction */
-    pointer x, y, p, l1, l2;
-    /*tex dimensions involved in the calculation */
-    scaled delta, clr, theta, h, f;
-    scaled t, used_fam ;
-    x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
-    clr = radical_vgap(cur_style);
-    theta = radical_rule_par(cur_style);
-    used_fam = small_fam(left_delimiter(q));
-    /*tex
-
-        We can take the rule width from the fam/style of the delimiter or use the
-        most recent math parameters value.
-
-    */
-    if (math_rule_thickness_mode_par > 0) {
-        f = small_fam(left_delimiter(q));
-        if (f >= 0) {
-            t = fam_fnt(f,cur_size);
-            if (do_new_math(t)) {
-                t = font_MATH_par(t, RadicalRuleThickness);
-                if (t != undefined_math_parameter) {
-                    theta = t;
-                    used_fam = f;
-                }
-            }
-        }
-    }
-    if (theta == undefined_math_parameter) {
-        /*tex a real radical */
-        theta = fraction_rule(cur_style);
-        y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL);
-        /*tex
-
-            If |y| is a composite then set |theta| to the height of its top
-            character, else set it to the height of |y|.
-
-        */
-        l1 = list_ptr(y);
-        if ((l1 != null) && (type(l1) == hlist_node)) {
-            /*tex possible composite */
-            l2 = list_ptr(l1);
-            if ((l2 != null) && (type(l2) == glyph_node)) {
-                /*tex top character */
-                theta = char_height(font(l2), character(l2));
-            } else {
-                theta = height(y);
-            }
-        } else {
-            theta = height(y);
-        }
-    } else {
-        /*tex
-
-            Not really a radical but we use its node, historical sharing (like in
-            mathml).
-
-        */
-        y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL);
-    }
-    /*tex
-
-        Weird hack, in overbar we use small_fam(left_delimiter(q)) so actually
-        small_fam(0).
-
-    */
-    left_delimiter(q) = null;
-    delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr);
-    if (delta > 0) {
-        /*tex increase the actual clearance */
-        clr = clr + half(delta);
-    }
-    shift_amount(y) = (height(y) - theta) - (height(x) + clr);
-    h = depth(y) + height(y);
-    p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y), math_radical_rule, cur_size, used_fam);
-    couple_nodes(y,p);
-    if (degree(q) != null) {
-        scaled wr, br, ar;
-        pointer r = clean_box(degree(q), script_script_style, cur_style);
-        reset_attributes(r, node_attr(degree(q)));
-        wr = width(r);
-        if (wr == 0) {
-            flush_node(r);
-        } else {
-            br = radical_degree_before(cur_style);
-            ar = radical_degree_after(cur_style);
-            if (-ar > (wr + br))
-                ar = -(wr + br);
-            x = new_kern(ar);
-            reset_attributes(x, node_attr(degree(q)));
-            couple_nodes(x,y);
-            shift_amount(r) =
-                -((xn_over_d(h, radical_degree_raise(cur_style), 100)) -
-                  depth(y) - shift_amount(y));
-            couple_nodes(r,x);
-            x = new_kern(br);
-            reset_attributes(x, node_attr(degree(q)));
-            couple_nodes(x,r);
-            y = x;
-        }
-        /*tex for \.{\\Uroot ..{<list>}{}} : */
-        math_list(degree(q)) = null;
-        flush_node(degree(q));
-    }
-    p = hpack(y, 0, additional, -1);
-    reset_attributes(p, node_attr(q));
-    math_list(nucleus(q)) = p;
-    type(nucleus(q)) = sub_box_node;
-}
-
-/*tex Construct a vlist box: */
-
-static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down)
-{
-    pointer p;
-    pointer v = new_null_box();
-    type(v) = vlist_node;
-    height(v) = shift_up + height(x);
-    depth(v) = depth(y) + shift_down;
-    reset_attributes(v, node_attr(q));
-    p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
-    reset_attributes(p, node_attr(q));
-    couple_nodes(p,y);
-    couple_nodes(x,p);
-    list_ptr(v) = x;
-    return v;
-}
-
-/*tex When |exact| use radicalwidth (|y| is delimiter). */
-
-#define fixup_widths(q,x,y) do { \
-    if (width(y) >= width(x)) { \
-        if (radicalwidth(q) != 0) { \
-            shift_amount(x) += half(width(y)-width(x)) ; \
-        } \
-        width(x) = width(y); \
-    } else { \
-        if (radicalwidth(q) != 0) { \
-            shift_amount(y) += half(width(x)-width(y)) ; \
-        } \
-        width(y) = width(x); \
-    } \
-} while (0)
-
-
-#define check_radical(q,stack,r,t) do { \
-    if (!stack && (width(r) >= width(t)) && (radicalwidth(q) != 0) && (radicalwidth(q) != width(r))) { \
-        if (radicalleft(q)) { \
-            halfword p = new_kern(radicalwidth(q)-width(r)); \
-            reset_attributes(p, node_attr(q)); \
-            couple_nodes(p,r); \
-            r = hpack(p, 0, additional, -1); \
-            width(r) = radicalwidth(q); \
-            reset_attributes(r, node_attr(q)); \
-        } else if (radicalmiddle(q)) { \
-            halfword p = new_kern(half(radicalwidth(q)-width(r))); \
-            reset_attributes(p, node_attr(q)); \
-            couple_nodes(p,r); \
-            r = hpack(p, 0, additional, -1); \
-            width(r) = radicalwidth(q); \
-            reset_attributes(r, node_attr(q)); \
-        } else if (radicalright(q)) { \
-            /*tex also kind of exact compared to vertical */ \
-            r = hpack(r, 0, additional, -1); \
-            width(r) = radicalwidth(q); \
-            reset_attributes(r, node_attr(q)); \
-        } \
-    } \
-} while (0)
-
-#define check_widths(q,p) do { \
-    if (radicalwidth(q) != 0) { \
-        wd = radicalwidth(q); \
-    } else { \
-        wd = width(p); \
-    } \
-} while (0)
-
-/*tex
-
-    This has the |nucleus| box |x| as a limit above an extensible delimiter |y|.
-
-*/
-
-static void make_over_delimiter(pointer q, int cur_style)
-{
-    pointer x, y, v;
-    scaled shift_up, shift_down, clr, delta, wd;
-    boolean stack;
-    x = clean_box(nucleus(q), sub_style(cur_style), cur_style);
-    check_widths(q,x);
-    y = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL, NULL);
-    left_delimiter(q) = null;
-    check_radical(q,stack,y,x);
-    fixup_widths(q, x, y);
-    shift_up = over_delimiter_bgap(cur_style);
-    shift_down = 0;
-    clr = over_delimiter_vgap(cur_style);
-    delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
-    if (delta > 0) {
-        shift_up = shift_up + delta;
-    }
-    v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
-    /*tex This also equals |width(y)|: */
-    width(v) = width(x);
-    math_list(nucleus(q)) = v;
-    type(nucleus(q)) = sub_box_node;
-}
-
-/*tex
-
-    This has the extensible delimiter |x| as a limit below |nucleus| box |y|.
-
-*/
-
-static void make_under_delimiter(pointer q, int cur_style)
-{
-    pointer x, y, v;
-    scaled shift_up, shift_down, clr, delta, wd;
-    boolean stack;
-    y = clean_box(nucleus(q), sup_style(cur_style), cur_style);
-    check_widths(q,y);
-    x = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL, NULL);
-    left_delimiter(q) = null;
-    check_radical(q,stack,x,y);
-    fixup_widths(q, x, y);
-    shift_up = 0;
-    shift_down = under_delimiter_bgap(cur_style);
-    clr = under_delimiter_vgap(cur_style);
-    delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
-    if (delta > 0) {
-        shift_down = shift_down + delta;
-    }
-    v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
-    /*tex This also equals |width(y)|: */
-    width(v) = width(y);
-    math_list(nucleus(q)) = v;
-    type(nucleus(q)) = sub_box_node;
-}
-
-/*tex
-
-    This has the extensible delimiter |x| as a limit above |nucleus| box |y|.
-
-*/
-
-static void make_delimiter_over(pointer q, int cur_style)
-{
-    pointer x, y, v;
-    scaled shift_up, shift_down, clr, actual, wd;
-    boolean stack;
-    y = clean_box(nucleus(q), cur_style, cur_style);
-    check_widths(q,y);
-    x = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL, NULL);
-    left_delimiter(q) = null;
-    check_radical(q,stack,x,y);
-    fixup_widths(q, x, y);
-    shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x);
-    shift_down = 0;
-    clr = over_delimiter_vgap(cur_style);
-    actual = shift_up - height(y);
-    if (actual < clr) {
-        shift_up = shift_up + (clr-actual);
-    }
-    v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
-    /*tex This also equals |width(y)|: */
-    width(v) = width(x);
-    math_list(nucleus(q)) = v;
-    type(nucleus(q)) = sub_box_node;
-}
-
-/*tex
-
-    This has the extensible delimiter |y| as a limit below a |nucleus| box |x|.
-
-*/
-
-static void make_delimiter_under(pointer q, int cur_style)
-{
-    pointer x, y, v;
-    scaled shift_up, shift_down, clr, actual, wd;
-    boolean stack;
-    x = clean_box(nucleus(q), cur_style, cur_style);
-    check_widths(q,x);
-    y = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL, NULL);
-    left_delimiter(q) = null;
-    check_radical(q,stack,y,x);
-    fixup_widths(q, x, y);
-    shift_up = 0;
-    shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y);
-    clr = under_delimiter_vgap(cur_style);
-    actual = shift_down - depth(x);
-    if (actual<clr) {
-       shift_down += (clr-actual);
-    }
-    v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
-    /*tex This also equals |width(y)|: */
-    width(v) = width(y);
-    math_list(nucleus(q)) = v;
-    type(nucleus(q)) = sub_box_node;
-}
-
-/*tex
-
-    Slants are not considered when placing accents in math mode. The accenter is
-    centered over the accentee, and the accent width is treated as zero with
-    respect to the size of the final box.
-
-*/
-
-#define TOP_CODE            1
-#define BOT_CODE            2
-#define OVERLAY_CODE        4
-#define STRETCH_ACCENT_CODE 8
-
-static boolean compute_accent_skew(pointer q, int flags, scaled *s)
-{
-    /*tex temporary register for box construction */
-    pointer p;
-    /*tex will be true if a top-accent is placed in |s| */
-    boolean s_is_absolute = false;
-    if (type(nucleus(q)) == math_char_node) {
-        fetch(nucleus(q));
-        if (do_new_math(cur_f)) {
-            /*tex
-                There is no bot_accent so let's assume similarity
-
-                \starttyping
-                if (flags & (TOP_CODE | OVERLAY_CODE)) {
-                    *s = char_top_accent(cur_f, cur_c);
-                    if (*s != INT_MIN) {
-                        s_is_absolute = true;
-                    }
-                } else {
-                    *s = char_bot_accent(cur_f, cur_c);
-                    if (*s != INT_MIN) {
-                        s_is_absolute = true;
-                    }
-                }
-                \stoptyping
-            */
-            *s = char_top_accent(cur_f, cur_c);
-            if (*s != INT_MIN) {
-                s_is_absolute = true;
-            }
-        } else {
-            if (flags & TOP_CODE) {
-                *s = get_kern(cur_f, cur_c, skew_char(cur_f));
-            } else {
-                *s = 0;
-            }
-        }
-    } else if (type(nucleus(q)) == sub_mlist_node) {
-        /*tex
-            If |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we:
-
-            \startitemize
-            \startitem
-                use the positioning of the nucleus of that noad, recursing until
-            \stopitem
-            \startitem
-                the inner most |accent_noad|. This way multiple stacked accents
-                are
-            \stopitem
-            \startitem
-                aligned to the inner most one.
-            \stopitem
-            \stoptitemize
-
-            The vlink test was added in version 1.06, so that we only consider a
-            lone noad:
-
-            $
-                \Umathaccent bottom 0 0 "023DF {   \Umathaccent fixed 0 0 "00302 { m } r } \quad
-                \Umathaccent bottom 0 0 "023DF { l \Umathaccent fixed 0 0 "00302 { m } r } \quad
-                \Umathaccent bottom 0 0 "023DF { l \Umathaccent fixed 0 0 "00302 { m }   } \quad
-                \Umathaccent bottom 0 0 "023DF {   \Umathaccent fixed 0 0 "00302 { m }   } \quad
-                \Umathaccent bottom 0 0 "023DF { l                                      r }
-            $
-
-        */
-        p = math_list(nucleus(q));
-        if (type(p) == accent_noad && vlink(p) == null) {
-            s_is_absolute = compute_accent_skew(p, flags, s);
-        }
-    } else {
-    }
-
-    return s_is_absolute;
-}
-
-static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style)
-{
-    /*tex temporary registers for box construction */
-    pointer p, r, x, y;
-    /*tex amount to skew the accent to the right */
-    scaled s;
-    /*tex height of character being accented */
-    scaled h;
-    /*tex space to remove between accent and accentee */
-    scaled delta;
-    /*tex width of the accentee, not including sub/superscripts */
-    scaled w;
-    /*tex will be true if a top-accent is placed in |s| */
-    boolean s_is_absolute;
-    scaled fraction ;
-    scaled ic = 0;
-    scaled target ;
-    extinfo *ext;
-    pointer attr_p;
-    attr_p = (flags & TOP_CODE ? top_accent_chr(q) : flags & BOT_CODE ? bot_accent_chr(q) : overlay_accent_chr(q));
-    fraction = accentfraction(q);
-    c = cur_c;
-    f = cur_f;
-    s = 1;
-    if (fraction == 0) {
-        fraction = 1000;
-    }
-    /*tex Compute the amount of skew, or set |s| to an alignment point */
-    s_is_absolute = compute_accent_skew(q, flags, &s);
-    x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
-    w = width(x);
-    h = height(x);
-    if (do_new_math(cur_f) && !s_is_absolute) {
-        s = half(w);
-        s_is_absolute = true;
-    }
-    /*tex Switch to a larger accent if available and appropriate */
-    y = null;
-    ext = NULL;
-    if (flags & OVERLAY_CODE) {
-        if (fraction > 0) {
-            target = xn_over_d(h,fraction,1000);
-        } else {
-            target = h;
-        }
-    } else {
-        if (fraction > 0) {
-            target = xn_over_d(w,fraction,1000);
-        } else {
-            target = w;
-        }
-    }
-    if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) {
-        while (1) {
-            if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) {
-                /*tex a bit weird for an overlay but anyway, here we don't need a factor as we don't step */
-                y = get_delim_box(f, c, w, connector_overlap_min(cur_style), 1, node_attr(attr_p));
-                break;
-            } else if (char_tag(f, c) != list_tag) {
-                break;
-            } else {
-                int yy = char_remainder(f, c);
-                if (!char_exists(f, yy)) {
-                    break;
-                } else if (flags & OVERLAY_CODE) {
-                    if (char_height(f, yy) > target) {
-                        break;
-                    }
-                } else {
-                    if (char_width(f, yy) > target)
-                    break;
-                }
-                c = yy;
-            }
-        }
-    }
-    if (y == null) {
-        /*tex italic gets added to width */
-        y = char_box(f, c, node_attr(attr_p));
-    }
-    if (flags & TOP_CODE) {
-        if (h < accent_base_height(f)) {
-            delta = h;
-        } else {
-            delta = accent_base_height(f);
-        }
-    } else if (flags & OVERLAY_CODE) {
-        /*tex center the accent vertically around the accentee */
-        delta = half(height(y) + depth(y) + height(x) + depth(x));
-    } else {
-        delta = 0; /* hm */
-    }
-    if ((supscr(q) != null) || (subscr(q) != null)) {
-        if (type(nucleus(q)) == math_char_node) {
-            /*tex swap the subscript and superscript into box |x| */
-            flush_node_list(x);
-            x = new_noad();
-            r = math_clone(nucleus(q));
-            nucleus(x) = r;
-            supscr(x) = supscr(q);
-            supscr(q) = null;
-            subscr(x) = subscr(q);
-            subscr(q) = null;
-            type(nucleus(q)) = sub_mlist_node;
-            math_list(nucleus(q)) = x;
-            x = clean_box(nucleus(q), cur_style, cur_style);
-            delta = delta + height(x) - h;
-            h = height(x);
-        }
-    } else if ((vlink(q) != null) && (type(nucleus(q)) == math_char_node)) {
-        /*tex only pure math char nodes */
-        internal_font_number f = fam_fnt(math_fam(nucleus(q)),cur_size);
-        if (do_new_math(f)) {
-            ic = char_italic(f,math_character(nucleus(q)));
-        }
-    }
-    /*tex the top accents of both characters are aligned */
-    if (s_is_absolute) {
-        scaled sa;
-        if (ext != NULL) {
-            /*tex if the accent is extensible just take the center */
-            sa = half(width(y));
-        } else {
-            /*tex
-                There is no bot_accent so let's assume similarity
-
-                \starttyping
-                if (flags & BOT_CODE) {
-                    sa = char_bot_accent(f, c);
-                } else {
-                    sa = char_top_accent(f, c);
-                }
-                \stoptyping
-            */
-            sa = char_top_accent(f, c);
-        }
-        if (sa == INT_MIN) {
-            /*tex just take the center */
-            sa = half(width(y));
-        }
-        if (math_direction_par == dir_TRT) {
-           shift_amount(y) = s + sa - width(y);
-        } else {
-           shift_amount(y) = s - sa;
-        }
-    } else {
-        if (width(y)== 0) {
-            shift_amount(y) = s + w;
-        } else if (math_direction_par == dir_TRT) {
-            shift_amount(y) = s + width(y); /* ok? */
-        } else {
-            shift_amount(y) = s + half(w - width(y));
-        }
-    }
-    width(y) = 0;
-    if (flags & (TOP_CODE | OVERLAY_CODE)) {
-        p = new_kern(-delta);
-        reset_attributes(p, node_attr(q));
-        couple_nodes(p,x);
-        couple_nodes(y,p);
-    } else {
-        couple_nodes(x,y);
-        y = x;
-    }
-    r = vpackage(y, 0, additional, max_dimen, math_direction_par);
-    reset_attributes(r, node_attr(q));
-    width(r) = width(x);
-    y = r;
-    if (flags & (TOP_CODE | OVERLAY_CODE)) {
-        if (height(y) < h) {
-            /*tex make the height of box |y| equal to |h| */
-            p = new_kern(h - height(y));
-            reset_attributes(p, node_attr(q));
-            try_couple_nodes(p,list_ptr(y));
-            list_ptr(y) = p;
-            height(y) = h;
-        }
-    } else {
-        shift_amount(y) = -(h - height(y));
-    }
-    if (ic != 0) {
-        /*tex old font codepath has ic built in, new font code doesn't */
-        width(r) += ic ;
-    }
-    math_list(nucleus(q)) = y;
-    type(nucleus(q)) = sub_box_node;
-}
-
-static void make_math_accent(pointer q, int cur_style)
-{
-    int topstretch = !(subtype(q) % 2);
-    int botstretch = !(subtype(q) / 2);
-
-    if (top_accent_chr(q) != null) {
-        fetch(top_accent_chr(q));
-        if (char_exists(cur_f, cur_c)) {
-          do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
-        }
-        flush_node(top_accent_chr(q));
-        top_accent_chr(q) = null;
-    }
-    if (bot_accent_chr(q) != null) {
-        fetch(bot_accent_chr(q));
-        if (char_exists(cur_f, cur_c)) {
-          do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
-        }
-        flush_node(bot_accent_chr(q));
-        bot_accent_chr(q) = null;
-    }
-    if (overlay_accent_chr(q) != null) {
-        fetch(overlay_accent_chr(q));
-        if (char_exists(cur_f, cur_c)) {
-          do_make_math_accent(q, cur_f, cur_c, OVERLAY_CODE | STRETCH_ACCENT_CODE, cur_style);
-        }
-        flush_node(overlay_accent_chr(q));
-        overlay_accent_chr(q) = null;
-    }
-}
-
-/*tex
-
-    The |make_fraction| procedure is a bit different because it sets
-    |new_hlist(q)| directly rather than making a sub-box.
-
-*/
-
-static void make_fraction(pointer q, int cur_style)
-{
-    pointer p, p1, p2, v, x, y, z, l, r, m;
-    scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2, f, t;\
-    /*tex
-
-        We can take the rule width from an explicitly set fam, even if a fraction
-        itself has no character, otherwise we just use the math parameter.
-
-    */
-    scaled used_fam = math_rules_fam_par;
-    if (math_rule_thickness_mode_par > 0 && thickness(q) != 0) {
-        f = fraction_fam(q);
-        if (f >= 0) {
-            t = fam_fnt(f,cur_size);
-            if (do_new_math(t)) {
-                t = font_MATH_par(t, FractionRuleThickness);
-                if (t != undefined_math_parameter) {
-                    thickness(q) = t;
-                    used_fam = f;
-               }
-            }
-        }
-    }
-    if (thickness(q) == default_code)
-        thickness(q) = fraction_rule(cur_style);
-    /*tex
-
-        Create equal-width boxes |x| and |z| for the numerator and denominator,
-        and compute the default amounts |shift_up| and |shift_down| by which they
-        are displaced from the baseline.
-
-    */
-    x = clean_box(numerator(q), num_style(cur_style), cur_style);
-    z = clean_box(denominator(q), denom_style(cur_style), cur_style);
-    if (middle_delimiter(q) != null) {
-        delta = 0;
-        m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
-        middle_delimiter(q) = null;
-    } else {
-        m = null ;
-        if (width(x) < width(z)) {
-            x = rebox(x, width(z));
-        } else {
-            z = rebox(z, width(x));
-        }
-    }
-    if (m != null) {
-        shift_up = 0;
-        shift_down = 0;
-    } else if (thickness(q) == 0) {
-        shift_up = stack_num_up(cur_style);
-        shift_down = stack_denom_down(cur_style);
-        /*tex
-
-            The numerator and denominator must be separated by a certain minimum
-            clearance, called |clr| in the following program. The difference
-            between |clr| and the actual clearance is |2delta|.
-
-        */
-        clr1 = stack_vgap(cur_style);
-        delta = half(clr1 - ((shift_up - depth(x)) - (height(z) - shift_down)));
-        if (delta > 0) {
-            shift_up = shift_up + delta;
-            shift_down = shift_down + delta;
-        }
-    } else {
-        shift_up = fraction_num_up(cur_style);
-        shift_down = fraction_denom_down(cur_style);
-        /*tex
-
-            In the case of a fraction line, the minimum clearance depends on the
-            actual thickness of the line.
-
-        */
-        clr1 = fraction_num_vgap(cur_style);
-        clr2 = fraction_denom_vgap(cur_style);
-        delta = half(thickness(q));
-        if (fractionexact(q)) {
-            delta1 = clr1 - ((shift_up   - depth(x) ) - (math_axis_size(cur_size) + delta));
-            delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta));
-        } else {
-            clr1 = ext_xn_over_d(clr1, thickness(q), fraction_rule(cur_style));
-            clr2 = ext_xn_over_d(clr2, thickness(q), fraction_rule(cur_style));
-            delta1 = clr1 - ((shift_up   - depth(x) ) - (math_axis_size(cur_size) + delta));
-            delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta));
-        }
-        if (delta1 > 0) {
-            shift_up = shift_up + delta1;
-        }
-        if (delta2 > 0) {
-            shift_down = shift_down + delta2;
-        }
-    }
-    if (m != null) {
-        /*tex
-
-            Construct a hlist box for the fraction, according to |hgap| and
-            |vgap|.
-
-        */
-        shift_up = skewed_fraction_vgap(cur_style);
-
-        if (!fractionnoaxis(q)) {
-            shift_up += half(math_axis_size(cur_size));
-        }
-        shift_down = shift_up;
-        v = new_null_box();
-        reset_attributes(v, node_attr(q));
-        type(v) = hlist_node;
-        list_ptr(v) = x;
-        width(v) = width(x);
-        height(v) = height(x) + shift_up;
-        depth(v) = depth(x);
-        shift_amount(v) = - shift_up;
-        x = v;
-        v = new_null_box();
-        reset_attributes(v, node_attr(q));
-        type(v) = hlist_node;
-        list_ptr(v) = z;
-        width(v) = width(z);
-        height(v) = height(z);
-        depth(v) = depth(z) + shift_down;
-        shift_amount(v) = shift_down;
-        z = v;
-        v = new_null_box();
-        reset_attributes(v, node_attr(q));
-        type(v) = hlist_node;
-        if (height(x) > height(z)) {
-            height(v) = height(x);
-        } else {
-            height(v) = height(z);
-        }
-        if (depth(x) > depth(z)) {
-            depth(v) = depth(x);
-        } else {
-            depth(v) = depth(z);
-        }
-        if (height(m) > height(v)) {
-            height(v) = height(m);
-        }
-        if (depth(m) > depth(v)) {
-            depth(v) = depth(m);
-        }
-        if (fractionexact(q)) {
-            delta1 = -half(skewed_fraction_hgap(cur_style));
-            delta2 = delta1;
-            width(v) = width(x) + width(z) + width(m) - skewed_fraction_hgap(cur_style);
-        } else {
-            delta1 = half(skewed_fraction_hgap(cur_style)-width(m));
-            delta2 = half(skewed_fraction_hgap(cur_style)+width(m));
-            width(v) = width(x) + width(z) + skewed_fraction_hgap(cur_style);
-            width(m) = 0;
-        }
-        p1 = new_kern(delta1);
-        reset_attributes(p1, node_attr(q));
-        p2 = new_kern(delta2);
-        reset_attributes(p2, node_attr(q));
-        couple_nodes(x,p1);
-        couple_nodes(p1,m);
-        couple_nodes(m,p2);
-        couple_nodes(p2,z);
-        list_ptr(v) = x;
-    } else {
-        /*tex
-
-            Construct a vlist box for the fraction, according to |shift_up| and
-            |shift_down|.
-
-        */
-        v = new_null_box();
-        type(v) = vlist_node;
-        height(v) = shift_up + height(x);
-        depth(v) = depth(z) + shift_down;
-        /*tex This also equals |width(z)|. */
-        width(v) = width(x);
-        reset_attributes(v, node_attr(q));
-        if (thickness(q) == 0) {
-            p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
-            couple_nodes(p,z);
-        } else {
-            y = do_fraction_rule(thickness(q), node_attr(q), math_fraction_rule, cur_size, used_fam);
-            p = new_kern((math_axis_size(cur_size) - delta) - (height(z) - shift_down));
-            reset_attributes(p, node_attr(q));
-            couple_nodes(y,p);
-            couple_nodes(p,z);
-            p = new_kern((shift_up - depth(x)) - (math_axis_size(cur_size) + delta));
-            couple_nodes(p,y);
-        }
-        reset_attributes(p, node_attr(q));
-        couple_nodes(x,p);
-        list_ptr(v) = x;
-    }
-    /*tex
-
-        Put the fraction into a box with its delimiters, and make |new_hlist(q)|
-        point to it.
-
-    */
-    if (do_new_math(cur_f)) {
-        delta = fraction_del_size_new(cur_style);
-        if (delta == undefined_math_parameter) {
-            delta = get_delimiter_height(depth(v), height(v), true);
-        }
-    } else {
-        delta = fraction_del_size_old(cur_style);
-    }
-    l = do_delimiter(q, left_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
-    left_delimiter(q) = null;
-    r = do_delimiter(q, right_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
-    right_delimiter(q) = null;
-    couple_nodes(l,v);
-    couple_nodes(v,r);
-    y = hpack(l, 0, additional, -1);
-    reset_attributes(y, node_attr(q));
-    assign_new_hlist(q, y);
-}
-
-/*tex
-
-    If the nucleus of an |op_noad| is a single character, it is to be centered
-    vertically with respect to the axis, after first being enlarged (via a
-    character list in the font) if we are in display style. The normal convention
-    for placing displayed limits is to put them above and below the operator in
-    display style.
-
-    The italic correction is removed from the character if there is a subscript
-    and the limits are not being displayed. The |make_op| routine returns the
-    value that should be used as an offset between subscript and superscript.
-
-    After |make_op| has acted, |subtype(q)| will be |limits| if and only if the
-    limits have been set above and below the operator. In that case,
-    |new_hlist(q)| will already contain the desired final box.
-
-*/
-
-static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift);
-static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same);
-
-static scaled make_op(pointer q, int cur_style)
-{
-    /*tex offset between subscript and superscript */
-    scaled delta = 0;
-    scaled dummy = 0;
-    /*tex temporary registers for box construction */
-    pointer p, v, x, y, z, n;
-    /*tex register for character examination */
-    int c;
-    /*tex dimensions for box calculation */
-    scaled shift_up, shift_down;
-    boolean axis_shift = false;
-    scaled ok_size;
-    if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) {
-        subtype(q) = op_noad_type_limits;
-    }
-    if (type(nucleus(q)) == math_char_node) {
-        fetch(nucleus(q));
-        if (cur_style < text_style) {
-            /*tex try to make it larger */
-            ok_size = minimum_operator_size(cur_style);
-            if (ok_size != undefined_math_parameter) {
-                /*tex creating a temporary delimiter is the cleanest way */
-                y = new_node(delim_node, 0);
-                reset_attributes(y, node_attr(q));
-                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, NULL);
-                if (delta != 0) {
-                    if (do_new_math(cur_f)) {
-                        /*tex we never added italic correction */
-                    } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
-                        /*tex remove italic correction */
-                        width(x) -= delta;
-                    }
-                }
-            } else {
-                ok_size = height_plus_depth(cur_f, cur_c) + 1;
-                while ((char_tag(cur_f, cur_c) == list_tag) && height_plus_depth(cur_f, cur_c) < ok_size) {
-                    c = char_remainder(cur_f, cur_c);
-                    if (!char_exists(cur_f, c))
-                        break;
-                    cur_c = c;
-                    math_character(nucleus(q)) = c;
-                }
-                delta = char_italic(cur_f, cur_c);
-                x = clean_box(nucleus(q), cur_style, cur_style);
-                if (delta != 0) {
-                    if (do_new_math(cur_f)) {
-                        /*tex we never added italic correction */
-                    } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
-                        /*tex remove italic correction */
-                        width(x) -= delta;
-                    }
-                }
-                axis_shift = true;
-            }
-        } else {
-            /*tex normal size */
-            delta = char_italic(cur_f, cur_c);
-            x = clean_box(nucleus(q), cur_style, cur_style);
-            if (delta != 0) {
-                if (do_new_math(cur_f)) {
-                    /*tex we never added italic correction */
-                } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
-                    /*tex remove italic correction */
-                    width(x) -= delta;
-                }
-            }
-            axis_shift = true;
-        }
-        if (axis_shift) {
-            /*tex center vertically */
-            shift_amount(x) = half(height(x) - depth(x)) - math_axis_size(cur_size);
-        }
-        type(nucleus(q)) = sub_box_node;
-        math_list(nucleus(q)) = x;
-    }
-    /*tex we now handle op_nod_type_no_limits here too */
-    if (subtype(q) == op_noad_type_no_limits) {
-        if (do_new_math(cur_f)) {
-            /*tex
-                Not:
-
-                \starttyping
-                if (delta != 0) {
-                    delta = half(delta) ;
-                }
-                \stoptyping
-            */
-            p = check_nucleus_complexity(q, &dummy, cur_style, NULL);
-            if ((subscr(q) == null) && (supscr(q) == null)) {
-                assign_new_hlist(q, p);
-            } else {
-                /*tex
-                    Not:
-
-                    \starttyping
-                    make_scripts(q, p, 0, cur_style, delta, -delta);
-                    \stoptyping
-                */
-                int mode = math_nolimits_mode_par; /* wins */
-                /*tex
-
-                    For easy configuration ... fonts are somewhat inconsistent
-                    and the values for italic correction run from 30 to 60\% of.
-                    the width.
-
-                */
-                switch (mode) {
-                    case 0 :
-                        /*tex full bottom correction */
-                        make_scripts(q, p, 0, cur_style, 0, -delta);
-                        break;
-                    case 1 :
-                        /*tex |MathConstants| driven */
-                        make_scripts(q, p, 0, cur_style,
-                             round_xn_over_d(delta, nolimit_sup_factor(cur_style), 1000),
-                            -round_xn_over_d(delta, nolimit_sub_factor(cur_style), 1000));
-                    case 2 :
-                        /*tex no correction */
-                        make_scripts(q, p, 0, cur_style, 0, 0);
-                        break ;
-                    case 3 :
-                        /*tex half bottom correction */
-                        make_scripts(q, p, 0, cur_style, 0, -half(delta));
-                        break;
-                    case 4 :
-                        /*tex half bottom and top correction */
-                        make_scripts(q, p, 0, cur_style, half(delta), -half(delta));
-                        break;
-                    default :
-                        if (mode > 15) {
-                            /*tex for quickly testing values */
-                            make_scripts(q, p, 0, cur_style, 0, -round_xn_over_d(delta, mode, 1000));
-                        } else {
-                            make_scripts(q, p, 0, cur_style, 0, 0);
-                        }
-                        break;
-                }
-            }
-            delta = 0;
-        } else {
-            /*tex similar code then the caller (before CHECK_DIMENSIONS) */
-            p = check_nucleus_complexity(q, &delta, cur_style, NULL);
-            if ((subscr(q) == null) && (supscr(q) == null)) {
-                assign_new_hlist(q, p);
-            } else {
-                make_scripts(q, p, delta, cur_style, 0, 0);
-            }
-        }
-    } else if (subtype(q) == op_noad_type_limits) {
-        /*tex
-
-            The following program builds a vlist box |v| for displayed limits.
-            The width of the box is not affected by the fact that the limits may
-            be skewed.
-
-        */
-        x = clean_box(supscr(q), sup_style(cur_style), cur_style);
-        y = clean_box(nucleus(q), cur_style, cur_style);
-        z = clean_box(subscr(q), sub_style(cur_style), cur_style);
-        v = new_null_box();
-        reset_attributes(v, node_attr(q));
-        type(v) = vlist_node;
-        if (do_new_math(cur_f)) {
-            n = nucleus(q);
-            if (n != null) {
-                if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) {
-                    n = math_list(n);
-                    if (n != null) {
-                        if (type(n) == hlist_node) {
-                            /*tex just a not scaled char */
-                            n = list_ptr(n);
-                            while (n != null) {
-                                if (type(n) == glyph_node) {
-                                    delta = char_italic(font(n),character(n));
-                                }
-                                n = vlink(n);
-                            }
-                        } else {
-                            while (n != null) {
-                                if (type(n) == fence_noad) {
-                                    if (delimiteritalic(n) > delta) {
-                                        /*tex we can have dummies, the period ones */
-                                        delta = delimiteritalic(n);
-                                    }
-                                }
-                                n = vlink(n);
-                            }
-                        }
-                    }
-                } else {
-                    n = nucleus(q);
-                    if (type(n) == math_char_node) {
-                        delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n));
-                    }
-                }
-            }
-        }
-        width(v) = width(y);
-        if (width(x) > width(v))
-            width(v) = width(x);
-        if (width(z) > width(v))
-            width(v) = width(z);
-        x = rebox(x, width(v));
-        y = rebox(y, width(v));
-        z = rebox(z, width(v));
-        shift_amount(x) = half(delta);
-        shift_amount(z) = -shift_amount(x);
-        /*tex v is the still empty target */
-        height(v) = height(y);
-        depth(v) = depth(y);
-        /*tex
-
-            Attach the limits to |y| and adjust |height(v)|, |depth(v)| to
-            account for their presence.
-
-            We use |shift_up| and |shift_down| in the following program for the
-            amount of glue between the displayed operator |y| and its limits |x|
-            and |z|.
-
-            The vlist inside box |v| will consist of |x| followed by |y| followed
-            by |z|, with kern nodes for the spaces between and around them;
-            |b| is baseline and |v| is the minumum gap.
-
-        */
-        if (supscr(q) == null) {
-            list_ptr(x) = null;
-            flush_node(x);
-            list_ptr(v) = y;
-        } else {
-            shift_up = limit_above_bgap(cur_style) - depth(x);
-            if (shift_up < limit_above_vgap(cur_style))
-                shift_up = limit_above_vgap(cur_style);
-            p = new_kern(shift_up);
-            reset_attributes(p, node_attr(q));
-            couple_nodes(p,y);
-            couple_nodes(x,p);
-            p = new_kern(limit_above_kern(cur_style));
-            reset_attributes(p, node_attr(q));
-            couple_nodes(p,x);
-            list_ptr(v) = p;
-            height(v) = height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + shift_up;
-        }
-        if (subscr(q) == null) {
-            list_ptr(z) = null;
-            flush_node(z);
-        } else {
-            shift_down = limit_below_bgap(cur_style) - height(z);
-            if (shift_down < limit_below_vgap(cur_style))
-                shift_down = limit_below_vgap(cur_style);
-            if (shift_down > 0) {
-                p = new_kern(shift_down);
-                reset_attributes(p, node_attr(q));
-                couple_nodes(y,p);
-                couple_nodes(p,z);
-            }
-            p = new_kern(limit_below_kern(cur_style));
-            reset_attributes(p, node_attr(q));
-            couple_nodes(z,p);
-            depth(v) = depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + shift_down;
-        }
-        if (subscr(q) != null) {
-            math_list(subscr(q)) = null;
-            flush_node(subscr(q));
-            subscr(q) = null;
-        }
-        if (supscr(q) != null) {
-            math_list(supscr(q)) = null;
-            flush_node(supscr(q));
-            supscr(q) = null;
-        }
-        assign_new_hlist(q, v);
-        if (do_new_math(cur_f)) {
-            delta = 0;
-        }
-    }
-    return delta;
-}
-
-/*tex
-
-    A ligature found in a math formula does not create a ligature, because there
-    is no question of hyphenation afterwards; the ligature will simply be stored
-    in an ordinary |glyph_node|, after residing in an |ord_noad|.
-
-    The |type| is converted to |math_text_char| here if we would not want to
-    apply an italic correction to the current character unless it belongs to a
-    math font (i.e., a font with |space=0|).
-
-    No boundary characters enter into these ligatures.
-
-*/
-
-#define simple_char_noad(p) (\
-    (p != null) && \
-    (type(p) == simple_noad) && \
-    (subtype(p) <= punct_noad_type) && \
-    (type(nucleus(p)) == math_char_node) \
-)
-
-#define same_nucleus_fam(p,q) \
-    (math_fam(nucleus(p)) == math_fam(nucleus(q)))
-
-static void make_ord(pointer q)
-{
-    /*tex the left-side character for lig/kern testing */
-    int a;
-    /*tex temporary registers for list manipulation */
-    pointer p, r, s;
-    /*tex a kern */
-    scaled k;
-    /*tex a ligature */
-    liginfo lig;
-  RESTART:
-    if (subscr(q) == null && supscr(q) == null && type(nucleus(q)) == math_char_node) {
-        p = vlink(q);
-        if (simple_char_noad(p) && same_nucleus_fam(p,q)) {
-            type(nucleus(q)) = math_text_char_node;
-            fetch(nucleus(q));
-            a = cur_c;
-            /*tex add italic correction */
-            if (do_new_math(cur_f) && (char_italic(cur_f,math_character(nucleus(q))) != 0)) {
-                p = new_kern(char_italic(cur_f,math_character(nucleus(q))));
-                subtype(p) = italic_kern;
-                reset_attributes(p, node_attr(q));
-                couple_nodes(p,vlink(q));
-                couple_nodes(q,p);
-                return;
-            }
-            /*tex construct ligatures, quite unlikely in new math fonts */
-            if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) {
-                cur_c = math_character(nucleus(p));
-                /*tex
-
-                    If character |a| has a kern with |cur_c|, attach the kern
-                    after~|q|; or if it has a ligature with |cur_c|, combine
-                    noads |q| and~|p| appropriately; then |return| if the cursor
-                    has moved past a noad, or |goto restart|.
-
-                    Note that a ligature between an |ord_noad| and another kind
-                    of noad is replaced by an |ord_noad|, when the two noads
-                    collapse into one.
-
-                    We could make a parenthesis (say) change shape when it
-                    follows certain letters. Presumably a font designer will
-                    define such ligatures only when this convention makes sense.
-
-                */
-                if (disable_lig_par == 0 && has_lig(cur_f, a)) {
-                    lig = get_ligature(cur_f, a, cur_c);
-                    if (is_valid_ligature(lig)) {
-                        /*tex allow a way out of infinite ligature loop */
-                        check_interrupt();
-                        switch (lig_type(lig)) {
-                        case 1:
-                            /*tex \.{=:\char`\|} */
-                        case 5:
-                            /*tex \.{=:\char`\|>} */
-                            math_character(nucleus(q)) = lig_replacement(lig);
-                            break;
-                        case 2:
-                            /*tex \.{\char`\|=:} */
-                        case 6:
-                            /*tex \.{\char`\|=:>} */
-                            math_character(nucleus(p)) = lig_replacement(lig);
-                            break;
-                        case 3:
-                            /*tex \.{\char`\|=:\char`\|} */
-                        case 7:
-                            /*tex \.{\char`\|=:\char`\|>} */
-                        case 11:
-                            /*tex \.{\char`\|=:\char`\|>>} */
-                            r = new_noad();
-                            reset_attributes(r, node_attr(q));
-                            s = new_node(math_char_node, 0);
-                            reset_attributes(s, node_attr(q));
-                            nucleus(r) = s;
-                            math_character(nucleus(r)) = lig_replacement(lig);
-                            math_fam(nucleus(r)) = math_fam(nucleus(q));
-                            couple_nodes(q,r);
-                            couple_nodes(r,p);
-                            if (lig_type(lig) < 11) {
-                                type(nucleus(r)) = math_char_node;
-                            } else {
-                                /*tex prevent combination */
-                                type(nucleus(r)) = math_text_char_node;
-                            }
-                            break;
-                        default:
-                            try_couple_nodes(q,vlink(p));
-                            math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */
-                            subscr(q) = subscr(p);
-                            supscr(q) = supscr(p);
-                            subscr(p) = null ;
-                            supscr(p) = null ;
-                            flush_node(p);
-                            break;
-                        }
-                        if (lig_type(lig) > 3)
-                            return;
-                        type(nucleus(q)) = math_char_node;
-                        goto RESTART;
-                    }
-                }
-                if (disable_kern_par == 0 && has_kern(cur_f, a)) {
-                    /*tex todo: should this use mathkerns? */
-                    k = get_kern(cur_f, a, cur_c);
-                    if (k != 0) {
-                        p = new_kern(k);
-                        reset_attributes(p, node_attr(q));
-                        couple_nodes(p,vlink(q));
-                        couple_nodes(q,p);
-                        return;
-                    }
-                }
-            }
-        }
-    }
-}
-
-/*tex
-
-    If the fonts for the left and right bits of a mathkern are not both new-style
-    fonts, then return a sentinel value meaning: please use old-style italic
-    correction placement
-
-*/
-
-#define MATH_KERN_NOT_FOUND 0x7FFFFFFF
-
-/*tex
-
-    This function tries to find the kern needed for proper cut-ins. The left side
-    doesn't move, but the right side does, so the first order of business is to
-    create a staggered fence line on the left side of the right character.
-
-    The microsoft spec says that there are four quadrants, but the actual images
-    say.
-
-*/
-
-static scaled math_kern_at(internal_font_number f, int c, int side, int v)
-{
-    int h, k, numkerns;
-    scaled *kerns_heights;
-    scaled kern = 0;
-    /*tex Known to exist: */
-    charinfo *co = char_info(f, c);
-    numkerns = get_charinfo_math_kerns(co, side);
-    if (numkerns == 0)
-        return kern;
-    if (side == top_left_kern) {
-        kerns_heights = co->top_left_math_kern_array;
-    } else if (side == bottom_left_kern) {
-        kerns_heights = co->bottom_left_math_kern_array;
-    } else if (side == top_right_kern) {
-        kerns_heights = co->top_right_math_kern_array;
-    } else if (side == bottom_right_kern) {
-        kerns_heights = co->bottom_right_math_kern_array;
-    } else {
-        /*tex Not reached: */
-        confusion("math_kern_at");
-        kerns_heights = NULL;
-    }
-    if (v < kerns_heights[0])
-        return kerns_heights[1];
-    for (k = 0; k < numkerns; k++) {
-        h = kerns_heights[(k * 2)];
-        kern = kerns_heights[(k * 2) + 1];
-        if (h > v) {
-            return kern;
-        }
-    }
-    return kern;
-}
-
-static scaled find_math_kern(internal_font_number l_f, int l_c, internal_font_number r_f, int r_c, int cmd, scaled shift)
-{
-    scaled corr_height_top = 0, corr_height_bot = 0;
-    scaled krn_l = 0, krn_r = 0, krn = 0;
-    if ((!do_new_math(l_f)) || (!do_new_math(r_f)) || (!char_exists(l_f,l_c)) || (!char_exists(r_f,r_c)))
-        return MATH_KERN_NOT_FOUND;
-    if (cmd == sup_mark_cmd) {
-        corr_height_top = char_height(l_f, l_c);
-        /*tex bottom of superscript */
-        corr_height_bot = -char_depth(r_f, r_c) + shift;
-        krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top);
-        krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top);
-        krn = (krn_l + krn_r);
-        krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot);
-        krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot);
-        if ((krn_l + krn_r) < krn)
-            krn = (krn_l + krn_r);
-        return (krn);
-    } else if (cmd == sub_mark_cmd) {
-        /*tex top of subscript */
-        corr_height_top = char_height(r_f, r_c) - shift;
-        corr_height_bot = -char_depth(l_f, l_c);
-        krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top);
-        krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top);
-        krn = (krn_l + krn_r);
-        krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot);
-        krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot);
-        if ((krn_l + krn_r) < krn)
-            krn = (krn_l + krn_r);
-        return (krn);
-    } else {
-        confusion("find_math_kern");
-    }
-    /*tex Not reached: */
-    return 0;
-}
-
-/*tex Just a small helper: */
-
-static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2, halfword subtyp)
-{
-    pointer y;
-    pointer z = new_kern(delta2);
-    if (subtyp != 0) {
-        subtype(z) = subtyp;
-    }
-    reset_attributes(z, node_attr(q));
-    if (new_hlist(q) == null) {
-        /*tex this is somewhat weird */
-        new_hlist(q) = z;
-    } else {
-        y = new_hlist(q);
-        while (vlink(y) != null)
-            y = vlink(y);
-        couple_nodes(y,z);
-    }
-    return new_hlist(q);
-}
-
-/*tex
-
-    The purpose of |make_scripts(q,it)| is to attach the subscript and/or
-    superscript of noad |q| to the list that starts at |new_hlist(q)|, given that
-    subscript and superscript aren't both empty. The superscript will be
-    horizontally shifted over |delta1|, the subscript over |delta2|.
-
-    We set |shift_down| and |shift_up| to the minimum amounts to shift the
-    baseline of subscripts and superscripts based on the given nucleus.
-
-    Note: We need to look at a character but also at the first one in a sub list
-    and there we ignore leading kerns and glue. Elsewhere is code that removes
-    kerns assuming that is italic correction. The heuristics are unreliable for
-    the new fonts so eventualy there will be an option to ignore such
-    corrections.
-
-*/
-
-#define analyze_script(init,su_n,su_f,su_c) do { \
-    su_n = init; \
-    if (su_n != null) { \
-        if (math_script_char_mode_par > 0 && type(su_n) == math_char_node) { \
-            fetch(su_n); \
-            if (char_exists(cur_f, cur_c)) { \
-                su_f = cur_f; \
-                su_c = cur_c; \
-            } else { \
-                su_n = null; \
-            } \
-        } else if (math_script_box_mode_par > 0 && type(su_n) == sub_mlist_node) { \
-            su_n = math_list(su_n); \
-            while (su_n != null) { \
-                if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
-                    su_n = vlink(su_n); \
-                } else if (type(su_n) == simple_noad) { \
-                    su_n = nucleus(su_n); \
-                    if (type(su_n) == math_char_node) { \
-                        fetch(su_n); \
-                        if (char_exists(cur_f, cur_c)) { \
-                            su_f = cur_f; \
-                            su_c = cur_c; \
-                        } else { \
-                            su_n = null; \
-                        } \
-                    } else { \
-                        su_n = null; \
-                    } \
-                    break; \
-                } else { \
-                    su_n = null; \
-                    break; \
-                } \
-            } \
-        } else if (type(su_n) == sub_box_node) { \
-            su_n = math_list(su_n); \
-            if (su_n != null) { \
-                if (type(su_n) == hlist_node) { \
-                    su_n = list_ptr(su_n); \
-                } \
-                if (su_n != null) { \
-                    if (math_script_box_mode_par == 2) { \
-                        while (su_n != null) { \
-                            if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
-                                su_n = vlink(su_n); \
-                            } else if (type(su_n) == glyph_node) { \
-                                if (char_exists(font(su_n), character(su_n))) { \
-                                    su_f = font(su_n); \
-                                    su_c = character(su_n); \
-                                } else { \
-                                    su_n = null; \
-                                } \
-                                break ; \
-                            } else { \
-                                su_n = null; \
-                                break; \
-                            } \
-                        } \
-                    } else if (math_script_box_mode_par == 3) { \
-                        int boundary = -1; \
-                        while (su_n != null) { \
-                            if ((type(su_n) == boundary_node) && (subtype(su_n) == user_boundary)) { \
-                                boundary = boundary_value(su_n); \
-                                su_n = vlink(su_n); \
-                            } else if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
-                                su_n = vlink(su_n); \
-                            } else if ((boundary > -1) && (type(su_n) == glyph_node)) { \
-                                if (char_exists(font(su_n), character(su_n))) { \
-                                    su_f = font(su_n); \
-                                    su_c = character(su_n); \
-                                } else { \
-                                    su_n = null; \
-                                } \
-                                break ; \
-                            } else { \
-                                su_n = null; \
-                                break; \
-                            } \
-                        } \
-                    } \
-                } \
-            } else { \
-                su_n = null; \
-            } \
-        } else { \
-            su_n = null; \
-        } \
-    } \
-  } while (0) \
-
-#define x_su_style(n,cur_style,su_style) \
-    (noadoptionnosubscript(n) ? cur_style : su_style(cur_style))
-
-static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift)
-{
-    pointer x, y, z;
-    scaled shift_up, shift_down, clr;
-    scaled delta1, delta2;
-    halfword sub_n, sup_n, subtyp;
-    internal_font_number sub_f, sup_f;
-    int sub_c, sup_c;
-    sub_n = null;
-    sup_n = null;
-    sub_f = 0;
-    sup_f = 0;
-    sub_c = 0;
-    sup_c = 0;
-    delta1 = it;
-    delta2 = 0;
-    subtyp = 0;
-    switch (type(nucleus(q))) {
-        case math_char_node:
-        case math_text_char_node:
-            if ((subscr(q) == null) && (delta1 != 0)) {
-                /*tex todo: selective italic correction */
-                x = new_kern(delta1);
-                subtype(x) = italic_kern;
-                reset_attributes(x, node_attr(nucleus(q)));
-                couple_nodes(p,x);
-                delta1 = 0;
-            }
-    }
-    assign_new_hlist(q, p);
-    if (is_char_node(p)) {
-        shift_up = 0;
-        shift_down = 0;
-    } else {
-        z = hpack(p, 0, additional, -1);
-        shift_up = height(z) - sup_shift_drop(cur_style);  /* r18 */
-        shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */
-        list_ptr(z) = null;
-        flush_node(z);
-    }
-    if (is_char_node(p)) {
-        /*tex We look at the subscript character (_i) or first character in a list (_{ij}). */
-        analyze_script(subscr(q),sub_n,sub_f,sub_c);
-        /*tex We look at the superscript character (^i) or first character in a list (^{ij}). */
-        analyze_script(supscr(q),sup_n,sup_f,sup_c);
-    }
-    if (supscr(q) == null) {
-        /*tex
-
-            Construct a subscript box |x| when there is no superscript. When
-            there is a subscript without a superscript, the top of the subscript
-            should not exceed the baseline plus four-fifths of the x-height.
-
-        */
-        x = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style);
-        width(x) = width(x) + space_after_script(cur_style);
-        switch (math_scripts_mode_par) {
-            case 1:
-                shift_down = sub_shift_down(cur_style) ;
-                break;
-            case 2:
-                shift_down = sub_sup_shift_down(cur_style) ;
-                break;
-            case 3:
-                shift_down = sub_sup_shift_down(cur_style) ;
-                break;
-            case 4:
-                shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
-                break;
-            case 5:
-                shift_down = sub_shift_down(cur_style) ;
-                break;
-            default:
-                if (shift_down < sub_shift_down(cur_style))
-                    shift_down = sub_shift_down(cur_style);
-                clr = height(x) - sub_top_max(cur_style);
-                if (shift_down < clr)
-                    shift_down = clr;
-                break;
-        }
-        shift_amount(x) = shift_down;
-        /*tex Now find and correct for horizontal shift. */
-        subtyp = 0;
-        if (sub_n != null) {
-            delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
-            if (delta2 == MATH_KERN_NOT_FOUND) {
-                delta2 = subshift ;
-            } else {
-                delta2 = delta2 + subshift ;
-                subtyp = font_kern;
-            }
-        } else {
-            delta2 = subshift ;
-        }
-        if (delta2 != 0) {
-            p = attach_hkern_to_new_hlist(q, delta2, subtyp);
-        }
-    } else {
-        /*tex
-
-            Construct a superscript box |x|. The bottom of a superscript should
-            never descend below the baseline plus one-fourth of the x-height.
-
-        */
-        x = clean_box(supscr(q), (noadoptionnosupscript(q) ? cur_style : sup_style(cur_style)), cur_style);
-        width(x) = width(x) + space_after_script(cur_style);
-        switch (math_scripts_mode_par) {
-            case 1:
-                shift_up = sup_shift_up(cur_style);
-                break;
-            case 2:
-                shift_up = sup_shift_up(cur_style) ;
-                break;
-            case 3:
-                shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style) - sub_shift_down(cur_style) ;
-                break;
-            case 4:
-                shift_up = sup_shift_up(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
-                break;
-            case 5:
-                shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style)-sub_shift_down(cur_style) ;
-                break;
-            default:
-                clr = sup_shift_up(cur_style);
-                if (shift_up < clr)
-                    shift_up = clr;
-                clr = depth(x) + sup_bottom_min(cur_style);
-                if (shift_up < clr)
-                    shift_up = clr;
-                break;
-        }
-        if (subscr(q) == null) {
-            shift_amount(x) = -shift_up;
-            /*tex Now find and correct for horizontal shift. */
-            subtyp = 0;
-            if (sup_n != null) {
-                clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
-                if (clr == MATH_KERN_NOT_FOUND) {
-                    clr = supshift ;
-                } else {
-                    clr = clr + supshift ;
-                    subtyp = font_kern;
-                }
-            } else {
-                clr = supshift;
-            }
-            if (clr != 0) {
-                p = attach_hkern_to_new_hlist(q, clr, subtyp);
-            }
-        } else {
-            /*tex
-
-                Construct a sub/superscript combination box |x|, with the
-                superscript offset by |delta|. When both subscript and
-                superscript are present, the subscript must be separated from the
-                superscript by at least four times |default_rule_thickness| If
-                this condition would be violated, the subscript moves down, after
-                which both subscript and superscript move up so that the bottom
-                of the superscript is at least as high as the baseline plus
-                four-fifths of the x-height.
-
-            */
-            y = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style);
-            width(y) = width(y) + space_after_script(cur_style);
-            switch (math_scripts_mode_par) {
-                case 1:
-                    shift_down = sub_shift_down(cur_style) ;
-                    break;
-                case 2:
-                    shift_down = sub_sup_shift_down(cur_style) ;
-                    break;
-                case 3:
-                    shift_down = sub_sup_shift_down(cur_style) ;
-                    break;
-                case 4:
-                    shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
-                    break;
-                case 5:
-                    shift_down = sub_shift_down(cur_style) ;
-                    break;
-                default:
-                    if (shift_down < sub_sup_shift_down(cur_style))
-                        shift_down = sub_sup_shift_down(cur_style);
-                    clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down));
-                    if (clr > 0) {
-                        shift_down = shift_down + clr;
-                        clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x));
-                        if (clr > 0) {
-                            shift_up = shift_up + clr;
-                            shift_down = shift_down - clr;
-                        }
-                    }
-                break;
-            }
-            /*tex Now find and correct for horizontal shift. */
-            subtyp = 0;
-            if (sub_n != null) {
-                delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
-                if (delta2 == MATH_KERN_NOT_FOUND) {
-                    delta2 = subshift ;
-                } else {
-                    delta2 = delta2 + subshift ;
-                    subtyp = font_kern;
-                }
-            } else {
-                delta2 = subshift ;
-            }
-            if (delta2 != 0) {
-                p = attach_hkern_to_new_hlist(q, delta2, subtyp);
-            }
-            /*tex
-
-                Now the horizontal shift for the superscript; the superscript is
-                also to be shifted by |delta1| (the italic correction).
-
-            */
-            clr = MATH_KERN_NOT_FOUND;
-            if (sup_n != null) {
-                clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
-            }
-            /*tex
-
-                The delta can already have been applied and now be 0.
-
-            */
-            if (delta2 == MATH_KERN_NOT_FOUND)
-                delta2 = - supshift ;
-            else
-                delta2 = delta2 - supshift ;
-            if (clr != MATH_KERN_NOT_FOUND) {
-                shift_amount(x) = clr + delta1 - delta2;
-            } else {
-                shift_amount(x) = delta1 - delta2;
-            }
-            /*tex todo: only if kern != 0 */
-            p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
-            reset_attributes(p, node_attr(q));
-            couple_nodes(x,p);
-            couple_nodes(p,y);
-            /*tex We end up with funny dimensions. */
-            x = vpackage(x, 0, additional, max_dimen, math_direction_par);
-            reset_attributes(x, node_attr(q));
-            shift_amount(x) = shift_down;
-        }
-    }
-    if (new_hlist(q) == null) {
-        new_hlist(q) = x;
-    } else {
-        p = new_hlist(q);
-        while (vlink(p) != null)
-            p = vlink(p);
-        couple_nodes(p,x);
-    }
-    if (subscr(q) != null) {
-        math_list(subscr(q)) = null;
-        flush_node(subscr(q));
-        subscr(q) = null;
-    }
-    if (supscr(q) != null) {
-        math_list(supscr(q)) = null;
-        flush_node(supscr(q));
-        supscr(q) = null;
-    }
-}
-
-/*tex
-
-    The |make_left_right| function constructs a left or right delimiter of the
-    required size and returns the value |open_noad| or |close_noad|. The
-    |left_noad_side| and |right_noad_side| will both be based on the original
-    |style|, so they will have consistent sizes.
-
-*/
-
-static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h)
-{
-    scaled delta;
-    pointer tmp, lst;
-    scaled ic = 0;
-    boolean stack = false;
-    boolean axis = false;
-    int same = subtype(q);
-    setup_cur_size(style);
-    if ((delimiterheight(q)!=0) || (delimiterdepth(q)!=0)) {
-
-        delta = delimiterheight(q) + delimiterdepth(q);
-        tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, false, &stack, &ic, &same);
-        delimiteritalic(q) = ic;
-        /*tex
-
-            Beware, a stacked delimiter has a shift but no corrected height/depth
-            (yet).
-
-        */
-        if (stack) {
-            shift_amount(tmp) = delimiterdepth(q);
-        }
-        if (delimiterexact(q)) {
-            delimiterheight(q) = height(tmp) - shift_amount(tmp);
-            delimiterdepth(q)  = depth(tmp)  + shift_amount(tmp);
-        }
-        if (delimiteraxis(q)) {
-            delimiterheight(q) += math_axis_size(cur_size);
-            delimiterdepth(q)  -= math_axis_size(cur_size);
-            shift_amount(tmp)  -= math_axis_size(cur_size);
-        }
-        lst = new_node(hlist_node,0);
-        reset_attributes(lst, node_attr(q));
-        box_dir(lst) = dir_TLT ;
-        height(lst) = delimiterheight(q);
-        depth(lst) = delimiterdepth(q);
-        width(lst) = width(tmp);
-        list_ptr(lst) = tmp;
-        tmp = lst ;
-    } else {
-        axis = ! delimiternoaxis(q);
-        delta = get_delimiter_height(max_d,max_h,axis);
-        tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, axis, &stack, &ic, &same);
-        delimiteritalic(q) = ic;
-    }
-    delimiter(q) = null;
-    assign_new_hlist(q, tmp);
-    delimitersamesize(q) = same;
-    if (delimiterclass(q) >= ord_noad_type) {
-        if (delimiterclass(q) <= inner_noad_type) {
-            return delimiterclass(q);
-        } else {
-            return ord_noad_type;
-        }
-    } else if (subtype(q) == left_noad_side) {
-        return open_noad_type;
-    } else {
-        return close_noad_type;
-    }
-}
-
-#define TEXT_STYLES(A,B) do {                              \
-    def_math_param(A,display_style,(B),level_one);         \
-    def_math_param(A,cramped_display_style,(B),level_one); \
-    def_math_param(A,text_style,(B),level_one);            \
-    def_math_param(A,cramped_text_style,(B),level_one);    \
-  } while (0)
-
-#define SCRIPT_STYLES(A,B) do {                                  \
-    def_math_param(A,script_style,(B),level_one);                \
-    def_math_param(A,cramped_script_style,(B),level_one);        \
-    def_math_param(A,script_script_style,(B),level_one);         \
-    def_math_param(A,cramped_script_script_style,(B),level_one); \
-  } while (0)
-
-#define ALL_STYLES(A,B) do { \
-    TEXT_STYLES(A,(B));      \
-    SCRIPT_STYLES(A,(B));    \
-  } while (0)
-
-#define SPLIT_STYLES(A,B,C) do { \
-    TEXT_STYLES(A,(B));          \
-    SCRIPT_STYLES(A,(C));        \
-  } while (0)
-
-void initialize_math_spacing(void)
-{
-    ALL_STYLES   (math_param_ord_ord_spacing,     0);
-    ALL_STYLES   (math_param_ord_op_spacing,      thin_mu_skip_code);
-    SPLIT_STYLES (math_param_ord_bin_spacing,     med_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_ord_rel_spacing,     thick_mu_skip_code, 0);
-    ALL_STYLES   (math_param_ord_open_spacing,    0);
-    ALL_STYLES   (math_param_ord_close_spacing,   0);
-    ALL_STYLES   (math_param_ord_punct_spacing,   0);
-    SPLIT_STYLES (math_param_ord_inner_spacing,   thin_mu_skip_code, 0);
-
-    ALL_STYLES   (math_param_op_ord_spacing,      thin_mu_skip_code);
-    ALL_STYLES   (math_param_op_op_spacing,       thin_mu_skip_code);
-    ALL_STYLES   (math_param_op_bin_spacing,      0);
-    SPLIT_STYLES (math_param_op_rel_spacing,      thick_mu_skip_code, 0);
-    ALL_STYLES   (math_param_op_open_spacing,     0);
-    ALL_STYLES   (math_param_op_close_spacing,    0);
-    ALL_STYLES   (math_param_op_punct_spacing,    0);
-    SPLIT_STYLES (math_param_op_inner_spacing,    thin_mu_skip_code, 0);
-
-    SPLIT_STYLES (math_param_bin_ord_spacing,     med_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_bin_op_spacing,      med_mu_skip_code, 0);
-    ALL_STYLES   (math_param_bin_bin_spacing,     0);
-    ALL_STYLES   (math_param_bin_rel_spacing,     0);
-    SPLIT_STYLES (math_param_bin_open_spacing,    med_mu_skip_code, 0);
-    ALL_STYLES   (math_param_bin_close_spacing,   0);
-    ALL_STYLES   (math_param_bin_punct_spacing,   0);
-    SPLIT_STYLES (math_param_bin_inner_spacing,   med_mu_skip_code, 0);
-
-    SPLIT_STYLES (math_param_rel_ord_spacing,     thick_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_rel_op_spacing,      thick_mu_skip_code, 0);
-    ALL_STYLES   (math_param_rel_bin_spacing,     0);
-    ALL_STYLES   (math_param_rel_rel_spacing,     0);
-    SPLIT_STYLES (math_param_rel_open_spacing,    thick_mu_skip_code, 0);
-    ALL_STYLES   (math_param_rel_close_spacing,   0);
-    ALL_STYLES   (math_param_rel_punct_spacing,   0);
-    SPLIT_STYLES (math_param_rel_inner_spacing,   thick_mu_skip_code, 0);
-
-    ALL_STYLES   (math_param_open_ord_spacing,    0);
-    ALL_STYLES   (math_param_open_op_spacing,     0);
-    ALL_STYLES   (math_param_open_bin_spacing,    0);
-    ALL_STYLES   (math_param_open_rel_spacing,    0);
-    ALL_STYLES   (math_param_open_open_spacing,   0);
-    ALL_STYLES   (math_param_open_close_spacing,  0);
-    ALL_STYLES   (math_param_open_punct_spacing,  0);
-    ALL_STYLES   (math_param_open_inner_spacing,  0);
-
-    ALL_STYLES   (math_param_close_ord_spacing,   0);
-    ALL_STYLES   (math_param_close_op_spacing,    thin_mu_skip_code);
-    SPLIT_STYLES (math_param_close_bin_spacing,   med_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_close_rel_spacing,   thick_mu_skip_code, 0);
-    ALL_STYLES   (math_param_close_open_spacing,  0);
-    ALL_STYLES   (math_param_close_close_spacing, 0);
-    ALL_STYLES   (math_param_close_punct_spacing, 0);
-    SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0);
-
-    SPLIT_STYLES (math_param_punct_ord_spacing,   thin_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_punct_op_spacing,    thin_mu_skip_code, 0);
-    ALL_STYLES   (math_param_punct_bin_spacing,   0);
-    SPLIT_STYLES (math_param_punct_rel_spacing,   thin_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_punct_open_spacing,  thin_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0);
-
-    SPLIT_STYLES (math_param_inner_ord_spacing,   thin_mu_skip_code, 0);
-    ALL_STYLES   (math_param_inner_op_spacing,    thin_mu_skip_code);
-    SPLIT_STYLES (math_param_inner_bin_spacing,   med_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_inner_rel_spacing,   thick_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_inner_open_spacing,  thin_mu_skip_code, 0);
-    ALL_STYLES   (math_param_inner_close_spacing, 0);
-    SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0);
-    SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0);
-}
-
-#define both_types(A,B) ((A)*16+(B))
-
-static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
-{
-    int x = -1;
-    pointer z = null;
-    if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits)
-        l_type = op_noad_type_normal;
-    if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits)
-        r_type = op_noad_type_normal;
-    switch (both_types(l_type, r_type)) {
-        case both_types(ord_noad_type,       ord_noad_type      ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break;
-        case both_types(ord_noad_type,       op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break;
-        case both_types(ord_noad_type,       bin_noad_type      ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break;
-        case both_types(ord_noad_type,       rel_noad_type      ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break;
-        case both_types(ord_noad_type,       open_noad_type     ): x = get_math_param(math_param_ord_open_spacing,mstyle); break;
-        case both_types(ord_noad_type,       close_noad_type    ): x = get_math_param(math_param_ord_close_spacing,mstyle); break;
-        case both_types(ord_noad_type,       punct_noad_type    ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break;
-        case both_types(ord_noad_type,       inner_noad_type    ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break;
-        case both_types(op_noad_type_normal, ord_noad_type      ): x = get_math_param(math_param_op_ord_spacing,mstyle); break;
-        case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break;
-        /* shouldn't happen */
-        case both_types(op_noad_type_normal, bin_noad_type      ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;
-        /* */
-        case both_types(op_noad_type_normal, rel_noad_type      ): x = get_math_param(math_param_op_rel_spacing,mstyle); break;
-        case both_types(op_noad_type_normal, open_noad_type     ): x = get_math_param(math_param_op_open_spacing,mstyle); break;
-        case both_types(op_noad_type_normal, close_noad_type    ): x = get_math_param(math_param_op_close_spacing,mstyle); break;
-        case both_types(op_noad_type_normal, punct_noad_type    ): x = get_math_param(math_param_op_punct_spacing,mstyle); break;
-        case both_types(op_noad_type_normal, inner_noad_type    ): x = get_math_param(math_param_op_inner_spacing,mstyle); break;
-        case both_types(bin_noad_type,       ord_noad_type      ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break;
-        case both_types(bin_noad_type,       op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break;
-        /* shouldn't happen */
-        case both_types(bin_noad_type,       bin_noad_type      ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break;
-        case both_types(bin_noad_type,       rel_noad_type      ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break;
-        /* */
-        case both_types(bin_noad_type,       open_noad_type     ): x = get_math_param(math_param_bin_open_spacing,mstyle); break;
-        /* shouldn't happen */
-        case both_types(bin_noad_type,       close_noad_type    ): x = get_math_param(math_param_bin_close_spacing,mstyle); break;
-        case both_types(bin_noad_type,       punct_noad_type    ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break;
-        /* */
-        case both_types(bin_noad_type,       inner_noad_type    ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break;
-        case both_types(rel_noad_type,       ord_noad_type      ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break;
-        case both_types(rel_noad_type,       op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break;
-        /* shouldn't happen */
-        case both_types(rel_noad_type,       bin_noad_type      ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
-        /* */
-        case both_types(rel_noad_type,       rel_noad_type      ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break;
-        case both_types(rel_noad_type,       open_noad_type     ): x = get_math_param(math_param_rel_open_spacing,mstyle); break;
-        case both_types(rel_noad_type,       close_noad_type    ): x = get_math_param(math_param_rel_close_spacing,mstyle); break;
-        case both_types(rel_noad_type,       punct_noad_type    ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break;
-        case both_types(rel_noad_type,       inner_noad_type    ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break;
-        case both_types(open_noad_type,      ord_noad_type      ): x = get_math_param(math_param_open_ord_spacing,mstyle); break;
-        case both_types(open_noad_type,      op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break;
-        /* shouldn't happen */
-        case both_types(open_noad_type,      bin_noad_type      ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;
-        /* */
-        case both_types(open_noad_type,      rel_noad_type      ): x = get_math_param(math_param_open_rel_spacing,mstyle); break;
-        case both_types(open_noad_type,      open_noad_type     ): x = get_math_param(math_param_open_open_spacing,mstyle); break;
-        case both_types(open_noad_type,      close_noad_type    ): x = get_math_param(math_param_open_close_spacing,mstyle); break;
-        case both_types(open_noad_type,      punct_noad_type    ): x = get_math_param(math_param_open_punct_spacing,mstyle); break;
-        case both_types(open_noad_type,      inner_noad_type    ): x = get_math_param(math_param_open_inner_spacing,mstyle); break;
-        case both_types(close_noad_type,     ord_noad_type      ): x = get_math_param(math_param_close_ord_spacing,mstyle); break;
-        case both_types(close_noad_type,     op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break;
-        case both_types(close_noad_type,     bin_noad_type      ): x = get_math_param(math_param_close_bin_spacing,mstyle); break;
-        case both_types(close_noad_type,     rel_noad_type      ): x = get_math_param(math_param_close_rel_spacing,mstyle); break;
-        case both_types(close_noad_type,     open_noad_type     ): x = get_math_param(math_param_close_open_spacing,mstyle); break;
-        case both_types(close_noad_type,     close_noad_type    ): x = get_math_param(math_param_close_close_spacing,mstyle); break;
-        case both_types(close_noad_type,     punct_noad_type    ): x = get_math_param(math_param_close_punct_spacing,mstyle); break;
-        case both_types(close_noad_type,     inner_noad_type    ): x = get_math_param(math_param_close_inner_spacing,mstyle); break;
-        case both_types(punct_noad_type,     ord_noad_type      ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break;
-        case both_types(punct_noad_type,     op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break;
-        /* shouldn't happen */
-        case both_types(punct_noad_type,     bin_noad_type      ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
-        /* */
-        case both_types(punct_noad_type,     rel_noad_type      ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break;
-        case both_types(punct_noad_type,     open_noad_type     ): x = get_math_param(math_param_punct_open_spacing,mstyle); break;
-        case both_types(punct_noad_type,     close_noad_type    ): x = get_math_param(math_param_punct_close_spacing,mstyle); break;
-        case both_types(punct_noad_type,     punct_noad_type    ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break;
-        case both_types(punct_noad_type,     inner_noad_type    ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break;
-        case both_types(inner_noad_type,     ord_noad_type      ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break;
-        case both_types(inner_noad_type,     op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break;
-        case both_types(inner_noad_type,     bin_noad_type      ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break;
-        case both_types(inner_noad_type,     rel_noad_type      ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break;
-        case both_types(inner_noad_type,     open_noad_type     ): x = get_math_param(math_param_inner_open_spacing,mstyle); break;
-        case both_types(inner_noad_type,     close_noad_type    ): x = get_math_param(math_param_inner_close_spacing,mstyle); break;
-        case both_types(inner_noad_type,     punct_noad_type    ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break;
-        case both_types(inner_noad_type,     inner_noad_type    ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break;
-    }
-    if (x < 0) {
-        confusion("mathspacing");
-    }
-    if (x != 0) {
-        if (x <= thick_mu_skip_code) {
-            /*tex trap thin/med/thick settings cf.\ old \TeX */
-            z = math_glue(glue_par(x), mmu);
-            /*tex store a symbolic subtype */
-            subtype(z) = (quarterword) (x + 1);
-        } else {
-            z = math_glue(x, mmu);
-        }
-    }
-    return z;
-}
-
-static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same)
-{
-    pointer p = null;
-    pointer t = null;
-    if (same != NULL) {
-        *same = 0;
-    }
-    switch (type(nucleus(q))) {
-        case math_char_node:
-        case math_text_char_node:
-            fetch(nucleus(q));
-            if (char_exists(cur_f, cur_c)) {
-                /*tex we could look at neighbours */
-                if (do_new_math(cur_f)) {
-                    /*tex cf spec only the last one */
-                    *delta = 0 ;
-                } else {
-                    *delta = char_italic(cur_f, cur_c);
-                }
-                p = new_glyph(cur_f, cur_c);
-                reset_attributes(p, node_attr(nucleus(q)));
-                if (do_new_math(cur_f)) {
-                    if (get_char_cat_code(cur_c) == 11) {
-                        /*tex no italic correction in mid-word of text font */
-                        *delta = 0;
-                    }
-                } else {
-                    /*tex no italic correction in mid-word of text font */
-                    if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) {
-                        *delta = 0;
-                    }
-                }
-                /*tex so we only add italic correction when we have no scripts */
-                if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) {
-                    pointer x = new_kern(*delta);
-                    subtype(x) = italic_kern;
-                    reset_attributes(x, node_attr(nucleus(q)));
-                    couple_nodes(p,x);
-                    *delta = 0;
-                } else if (do_new_math(cur_f)) {
-                    /*tex Needs checking but looks ok. It must be more selective. */
-                    *delta = char_italic(cur_f, cur_c);
-                }
-            }
-            break;
-        case sub_box_node:
-            p = math_list(nucleus(q));
-            break;
-        case sub_mlist_node:
-            t = math_list(nucleus(q));
-            /*tex Recursive call: */
-            mlist_to_hlist(t, false, cur_style);
-            if (same != NULL && type(t) == fence_noad && delimitersamesize(t)) {
-                *same = delimitersamesize(t) ;
-            }
-            setup_cur_size(cur_style);
-            p = hpack(vlink(temp_head), 0, additional, -1);
-            reset_attributes(p, node_attr(nucleus(q)));
-            break;
-        default:
-            confusion("mlist2");
-    }
-    return p;
-}
-
-/*tex
-
-    Here is the overall plan of |mlist_to_hlist|, and the list of its local
-    variables.
-
-*/
-
-void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
-{
-    /*tex runs through the mlist */
-    pointer q = mlist;
-    /*tex the most recent noad preceding |q| */
-    pointer r = null;
-    /*tex tuck global parameter away as local variable */
-    int style = cur_style;
-    /*tex the |type| of noad |r|, or |op_noad| if |r=null| */
-    int r_type = simple_noad;
-    /*tex the |subtype| of noad |r| if |r_type| is |fence_noad| */
-    int r_subtype = op_noad_type_normal;
-    /*tex the effective |type| of noad |q| during the second pass */
-    int t;
-    /*tex the effective |subtype| of noad |q| during the second pass */
-    int t_subtype;
-    pointer p = null;
-    pointer pp = null;
-    pointer z = null;
-    halfword nxt ;
-    int same = 0;
-    /*tex a penalty to be inserted */
-    int pen;
-    /*tex a penalty to be inserted */
-    int prepen;
-    /*tex maximum height of the list translated so far */
-    scaled max_hl = 0;
-    /*tex maximum depth of the list translated so far */
-    scaled max_d = 0;
-    /*tex italic correction offset for subscript and superscript */
-    scaled delta;
-    /*tex the math unit width corresponding to |cur_size| */
-    scaled cur_mu;
-    r_subtype = op_noad_type_normal;
-    setup_cur_size(cur_style);
-    cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
-    if (math_penalties_mode_par) {
-        /*tex
-            We could do this via the callback but it's nice to have it as
-            primitive too.
-        */
-        penalties = 1;
-    }
-    while (q != null) {
-        /*tex
-
-            We use the fact that no character nodes appear in an mlist, hence the
-            field |type(q)| is always present.One of the things we must do on the
-            first pass is change a |bin_noad| to an |ord_noad| if the |bin_noad|
-            is not in the context of a binary operator. The values of |r| and
-            |r_type| make this fairly easy.
-
-        */
-      RESWITCH:
-        delta = 0;
-        nxt = vlink(q);
-        switch (type(q)) {
-            case simple_noad:
-                switch (subtype(q)) {
-                    case bin_noad_type:
-                        switch (r_type) {
-                            case simple_noad:
-                                switch (r_subtype) {
-                                case bin_noad_type:
-                                case op_noad_type_normal:
-                                case op_noad_type_limits:
-                                case op_noad_type_no_limits:
-                                case rel_noad_type:
-                                case open_noad_type:
-                                case punct_noad_type:
-                                    subtype(q) = ord_noad_type;
-                                    goto RESWITCH;
-                                    break;
-                                }
-                                break;
-                            case fence_noad:
-                                if (r_subtype == left_noad_side) {
-                                    /*tex So these can best be the same size. */
-                                    subtype(q) = ord_noad_type;
-                                    goto RESWITCH;
-                                }
-                                break;
-                        }
-                        break;
-                    case over_noad_type:
-                        make_over(q, cur_style, cur_size, math_rules_fam_par);
-                        break;
-                    case under_noad_type:
-                        make_under(q, cur_style, cur_size, math_rules_fam_par);
-                        break;
-                    case vcenter_noad_type:
-                        make_vcenter(q);
-                        break;
-                    case rel_noad_type:
-                    case close_noad_type:
-                    case punct_noad_type:
-                        if (r_type == simple_noad && r_subtype == bin_noad_type) {
-                            /*tex Assumes the same size; can't this go. */
-                            type(r) = simple_noad;
-                            subtype(r) = ord_noad_type;
-                        }
-                        break;
-                    case op_noad_type_normal:
-                    case op_noad_type_limits:
-                    case op_noad_type_no_limits:
-                        delta = make_op(q, cur_style);
-                        if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits))
-                            goto CHECK_DIMENSIONS;
-                        break;
-                    case ord_noad_type:
-                        make_ord(q);
-                        break;
-                    case open_noad_type:
-                    case inner_noad_type:
-                        break;
-                }
-                break;
-            case fence_noad:
-                if (subtype(q) != left_noad_side) {
-                    if (r_type == simple_noad && r_subtype == bin_noad_type) {
-                        /*tex Assumes the same size. */
-                        type(r) = simple_noad;
-                        subtype(r) = ord_noad_type;
-                    }
-                }
-                goto DONE_WITH_NOAD;
-                break;
-            case fraction_noad:
-                make_fraction(q, cur_style);
-                goto CHECK_DIMENSIONS;
-                break;
-            case radical_noad:
-                if (subtype(q) == 7)
-                    make_hextension(q, cur_style);
-                else if (subtype(q) == 6)
-                    make_delimiter_over(q, cur_style);
-                else if (subtype(q) == 5)
-                    make_delimiter_under(q, cur_style);
-                else if (subtype(q) == 4)
-                    make_over_delimiter(q, cur_style);
-                else if (subtype(q) == 3)
-                    make_under_delimiter(q, cur_style);
-                else
-                    make_radical(q, cur_style);
-                break;
-            case accent_noad:
-                make_math_accent(q, cur_style);
-                break;
-            case style_node:
-                cur_style = subtype(q);
-                setup_cur_size(cur_style);
-                cur_mu = x_over_n(get_math_quad_style(cur_style), 18);
-                goto DONE_WITH_NODE;
-                break;
-            case choice_node:
-                switch (cur_style / 2) {
-                case 0:
-                    /*tex |display_style=0| */
-                    choose_mlist(display_mlist);
-                    break;
-                case 1:
-                    /*tex |text_style=2| */
-                    choose_mlist(text_mlist);
-                    break;
-                case 2:
-                    /*tex |script_style=4| */
-                    choose_mlist(script_mlist);
-                    break;
-                case 3:
-                    /*tex |script_script_style=6| */
-                    choose_mlist(script_script_mlist);
-                    break;
-                }
-                flush_node_list(display_mlist(q));
-                flush_node_list(text_mlist(q));
-                flush_node_list(script_mlist(q));
-                flush_node_list(script_script_mlist(q));
-                type(q) = style_node;
-                subtype(q) = (quarterword) cur_style;
-                if (p != null) {
-                    z = vlink(q);
-                    couple_nodes(q,p);
-                    while (vlink(p) != null)
-                        p = vlink(p);
-                    try_couple_nodes(p,z);
-                }
-                goto DONE_WITH_NODE;
-                break;
-            case ins_node:
-            case mark_node:
-            case adjust_node:
-            case boundary_node:
-            case whatsit_node:
-            case penalty_node:
-            case disc_node:
-                goto DONE_WITH_NODE;
-                break;
-            case rule_node:
-                if (height(q) > max_hl)
-                    max_hl = height(q);
-                if (depth(q) > max_d)
-                    max_d = depth(q);
-                goto DONE_WITH_NODE;
-                break;
-            case glue_node:
-                /*tex
-
-                    Conditional math glue (`\.{\\nonscript}') results in a
-                    |glue_node| pointing to |zero_glue|, with
-                    |subtype(q)=cond_math_glue|; in such a case the node
-                    following will be eliminated if it is a glue or kern node and
-                    if the current size is different from |text_size|.
-
-                    Unconditional math glue (`\.{\\muskip}') is converted to
-                    normal glue by multiplying the dimensions by |cur_mu|.
-
-                */
-                if (subtype(q) == mu_glue) {
-                    math_glue_to_glue(q, cur_mu);
-                } else if ((cur_size != text_size) && (subtype(q) == cond_math_glue)) {
-                    p = vlink(q);
-                    if (p != null)
-                        if ((type(p) == glue_node) || (type(p) == kern_node)) {
-                            if (vlink(p) != null) {
-                                couple_nodes(q,vlink(p));
-                                vlink(p) = null;
-                            } else {
-                                vlink(q) = null;
-                            }
-                            flush_node_list(p);
-                        }
-                }
-                goto DONE_WITH_NODE;
-                break;
-            case kern_node:
-                math_kern(q, cur_mu);
-                goto DONE_WITH_NODE;
-                break;
-            default:
-                confusion("mlist1");
-        }
-        /*tex
-
-            When we get to the following part of the program, we have ``fallen
-            through'' from cases that did not lead to |check_dimensions| or
-            |done_with_noad| or |done_with_node|. Thus, |q|~points to a noad
-            whose nucleus may need to be converted to an hlist, and whose
-            subscripts and superscripts need to be appended if they are present.
-
-            If |nucleus(q)| is not a |math_char|, the variable |delta| is the
-            amount by which a superscript should be moved right with respect to a
-            subscript when both are present.
-
-        */
-        same = 0 ;
-        p = check_nucleus_complexity(q, &delta, cur_style, &same);
-        if (same) {
-            noadextra4(q) = same ;
-        }
-        if ((subscr(q) == null) && (supscr(q) == null)) {
-            /*tex
-
-                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:
-                            delta = 0;
-                            break;
-                        case inner_noad_type:
-                            if (! delimitermodeitalics) {
-                                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);
-                    subtype(d) = italic_kern;
-                    reset_attributes(d, node_attr(q));
-                    couple_nodes(p,d);
-                }
-            }
-            assign_new_hlist(q, p);
-        } else {
-            /*tex top, bottom */
-            make_scripts(q, p, delta, cur_style, 0, 0);
-        }
-      CHECK_DIMENSIONS:
-        z = hpack(new_hlist(q), 0, additional, -1);
-        if (height(z) > max_hl)
-            max_hl = height(z);
-        if (depth(z) > max_d)
-            max_d = depth(z);
-        list_ptr(z) = null;
-        /*tex only drop the \.{\\hbox} */
-        flush_node(z);
-      DONE_WITH_NOAD:
-        r = q;
-        r_type = type(r);
-        r_subtype = subtype(r);
-        if (r_type == fence_noad) {
-            r_subtype = left_noad_side;
-            cur_style = style;
-            setup_cur_size(cur_style);
-            /*tex style */
-            cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
-        }
-      DONE_WITH_NODE:
-        q = vlink(q);
-    }
-    if (r_type == simple_noad && r_subtype == bin_noad_type) {
-        type(r) = simple_noad;
-        subtype(r) = ord_noad_type;
-    }
-    /*tex
-
-        Make a second pass over the mlist, removing all noads and inserting the
-        proper spacing and penalties.
-
-        We have now tied up all the loose ends of the first pass of
-        |mlist_to_hlist|. The second pass simply goes through and hooks
-        everything together with the proper glue and penalties. It also handles
-        the |fence_noad|s that might be present, since |max_hl| and |max_d| are
-        now known. Variable |p| points to a node at the current end of the final
-        hlist.
-
-    */
-    p = temp_head;
-    vlink(p) = null;
-    q = mlist;
-    r_type = 0;
-    r_subtype = 0;
-    cur_style = style;
-    setup_cur_size(cur_style);
-    cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
-  NEXT_NODE:
-    while (q != null) {
-        /*tex
-
-            If node |q| is a style node, change the style and |goto delete_q|;
-            otherwise if it is not a noad, put it into the hlist, advance |q|,
-            and |goto done|; otherwise set |s| to the size of noad |q|, set |t|
-            to the associated type (|ord_noad.. inner_noad|), and set |pen| to
-            the associated penalty.
-
-            Just before doing the big |case| switch in the second pass, the
-            program sets up default values so that most of the branches are
-            short.
-
-        */
-        t = simple_noad;
-        t_subtype = ord_noad_type;
-        pen = inf_penalty;
-        prepen = inf_penalty;
-        switch (type(q)) {
-        case simple_noad:
-            t_subtype = subtype(q);
-            switch (t_subtype) {
-            case bin_noad_type:
-                pen = bin_op_penalty_par;
-                prepen = pre_bin_op_penalty_par;
-                break;
-            case rel_noad_type:
-                pen = rel_penalty_par;
-                prepen = pre_rel_penalty_par;
-                break;
-            case vcenter_noad_type:
-            case over_noad_type:
-            case under_noad_type:
-                t_subtype = ord_noad_type;
-                break;
-            }
-        case radical_noad:
-            break;
-        case accent_noad:
-            break;
-        case fraction_noad:
-            t = simple_noad;
-            t_subtype = inner_noad_type;
-            break;
-        case fence_noad:
-            t_subtype = make_left_right(q, style, max_d, max_hl);
-            break;
-        case style_node:
-            /*tex Change the current style and |goto delete_q| */
-            cur_style = subtype(q);
-            setup_cur_size(cur_style);
-            cur_mu = x_over_n(get_math_quad_style(cur_style), 18);
-            goto DELETE_Q;
-            break;
-        case whatsit_node:
-        case penalty_node:
-        case rule_node:
-        case disc_node:
-        case adjust_node:
-        case ins_node:
-        case mark_node:
-        case glue_node:
-        case kern_node:
-            couple_nodes(p,q);
-            p = q;
-            q = vlink(q);
-            vlink(p) = null;
-            goto NEXT_NODE;
-            break;
-        default:
-            confusion("mlist3");
-        }
-        /*tex Append inter-element spacing based on |r_type| and |t| */
-        if (r_type > 0) {
-            /*tex not the first noad */
-            pp = p;
-            if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1) {
-                z = math_spacing_glue(r_subtype, ord_noad_type, cur_style, cur_mu);
-            } else {
-                z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu);
-            }
-            if (z != null) {
-                reset_attributes(z, node_attr(p));
-                couple_nodes(p,z);
-                p = z;
-            }
-            if (penalties && prepen < inf_penalty && type(pp) != penalty_node) {
-                /*tex no checking of prev node type */
-                z = new_penalty(prepen,noad_penalty);
-                reset_attributes(z, node_attr(p));
-                couple_nodes(p,z);
-                p = z;
-            }
-        }
-        /*tex
-
-            Append any |new_hlist| entries for |q|, and any appropriate
-            penalties. We insert a penalty node after the hlist entries of noad
-            |q| if |pen| is not an ``infinite'' penalty, and if the node
-            immediately following |q| is not a penalty node or a |rel_noad| or
-            absent entirely.
-
-        */
-        if (new_hlist(q) != null) {
-            couple_nodes(p,new_hlist(q));
-            do {
-                p = vlink(p);
-            } while (vlink(p) != null);
-        }
-        if (penalties && vlink(q) != null && pen < inf_penalty) {
-            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,noad_penalty);
-                reset_attributes(z, node_attr(q));
-                couple_nodes(p,z);
-                p = z;
-            }
-        }
-        if (type(q) == fence_noad && subtype(q) == right_noad_side) {
-            t = simple_noad;
-            t_subtype = open_noad_type;
-        }
-        r_type = t;
-        r_subtype = t_subtype;
-      DELETE_Q:
-        r = q;
-        q = vlink(q);
-        /*tex
-
-            The m-to-hlist conversion takes place in-place, so the various
-            dependant fields may not be freed (as would happen if |flush_node|
-            was called). A low-level |free_node| is easier than attempting to
-            nullify such dependant fields for all possible node and noad types.
-
-        */
-        if (nodetype_has_attributes(type(r))) {
-            delete_attribute_ref(node_attr(r));
-        }
-        reset_node_properties(r);
-        free_node(r, get_node_size(type(r), subtype(r)));
-    }
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/nesting.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
-
-nesting.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex These are for |show_activities|: */
-
-#define page_goal page_so_far[0]
-
-/*tex
-
-\TeX\ is typically in the midst of building many lists at once. For example, when
-a math formula is being processed, \TeX\ is in math mode and working on an mlist;
-this formula has temporarily interrupted \TeX\ from being in horizontal mode and
-building the hlist of a paragraph; and this paragraph has temporarily interrupted
-\TeX\ from being in vertical mode and building the vlist for the next page of a
-document. Similarly, when a \.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is
-temporarily interrupted from working in restricted horizontal mode, and it enters
-internal vertical mode. The ``semantic nest'' is a stack that keeps track of what
-lists and modes are currently suspended.
-
-At each level of processing we are in one of six modes:
-
-|vmode| stands for vertical mode (the page builder);
-
-|hmode| stands for horizontal mode (the paragraph builder);
-
-|mmode| stands for displayed formula mode;
-
-|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox});
-
-|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox});
-
-|-mmode| stands for math formula mode (not displayed).
-
-The mode is temporarily set to zero while processing \.{\\write} texts in the
-|ship_out| routine.
-
-Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that \TeX's ``big
-semantic switch'' can select the appropriate thing to do by computing the value
-|abs(mode)+cur_cmd|, where |mode| is the current mode and |cur_cmd| is the
-current command code.
-
-*/
-
-static const char *string_mode(int m)
-{
-    if (m > 0) {
-        switch (m / (max_command_cmd + 1)) {
-            case 0:
-                return "vertical mode";
-                break;
-            case 1:
-                return "horizontal mode";
-                break;
-            case 2:
-                return "display math mode";
-                break;
-            default:
-                break;
-        }
-    } else if (m == 0) {
-        return "no mode";
-    } else {
-        switch ((-m) / (max_command_cmd + 1)) {
-            case 0:
-                return "internal vertical mode";
-                break;
-            case 1:
-                return "restricted horizontal mode";
-                break;
-            case 2:
-                return "math mode";
-                break;
-            default:
-                break;
-        }
-    }
-    return "unknown mode";
-}
-
-void print_mode(int m)
-{
-    tprint(string_mode(m));
-}
-
-void print_in_mode(int m)
-{
-    tprint("' in ");
-    tprint(string_mode(m));
-}
-
-int get_mode_id(void)
-{
-    int m = cur_list.mode_field;
-    if (m > 0) {
-        switch (m / (max_command_cmd + 1)) {
-            case 0:
-                return 'v';
-                break;
-            case 1:
-                return 'h';
-                break;
-            case 2:
-                return 'm';
-                break;
-            default:
-                return '\0';
-                break;
-        }
-    } else if (m == 0) {
-        return 'n';;
-    } else {
-        switch ((-m) / (max_command_cmd + 1)) {
-            case 0:
-                return 'V';
-                break;
-            case 1:
-                return 'H';
-                break;
-            case 2:
-                return 'M';
-                break;
-            default:
-                return '\0';
-                break;
-        }
-    }
-}
-
-/*tex
-
-The state of affairs at any semantic level can be represented by five values:
-
-|mode| is the number representing the semantic mode, as just explained.
-
-|head| is a |pointer| to a list head for the list being built; |link(head)|
-therefore points to the first element of the list, or to |null| if the list is
-empty.
-
-|tail| is a |pointer| to the final node of the list being built; thus,
-|tail=head| if and only if the list is empty.
-
-|prev_graf| is the number of lines of the current paragraph that have already
-been put into the present vertical list.
-
-|aux| is an auxiliary |memory_word| that gives further information that is needed
-to characterize the situation.
-
-In vertical mode, |aux| is also known as |prev_depth|; it is the scaled value
-representing the depth of the previous box, for use in baseline calculations, or
-it is |<=-1000|pt if the next box on the vertical list is to be exempt from
-baseline calculations. In horizontal mode, |aux| is also known as |space_factor|;
-it holds the current space factor used in spacing calculations. In math mode,
-|aux| is also known as |incompleat_noad|; if not |null|, it points to a record
-that represents the numerator of a generalized fraction for which the denominator
-is currently being formed in the current list.
-
-There is also a sixth quantity, |mode_line|, which correlates the semantic nest
-with the user's input; |mode_line| contains the source line number at which the
-current level of nesting was entered. The negative of this line number is the
-|mode_line| at the level of the user's output routine.
-
-A seventh quantity, |eTeX_aux|, is used by the extended features eTeX. In math
-mode it is known as |delim_ptr| and points to the most recent |fence_noad| of a
-|math_left_group|.
-
-In horizontal mode, the |prev_graf| field is used for initial language data.
-
-The semantic nest is an array called |nest| that holds the |mode|, |head|,
-|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels below
-the currently active one. Information about the currently active level is kept in
-the global quantities |mode|, |head|, |tail|, |prev_graf|, |aux|, and
-|mode_line|, which live in a struct that is ready to be pushed onto |nest| if
-necessary.
-
-The math field is used by various bits and pieces in |texmath.w|
-
-This implementation of \TeX\ uses two different conventions for representing
-sequential stacks. @^stack conventions@>@^conventions for representing stacks@>
-
-1) If there is frequent access to the top entry, and if the stack is essentially
-never empty, then the top entry is kept in a global variable (even better would
-be a machine register), and the other entries appear in the array
-$\\{stack}[0\to(\\{ptr}-1)]$. The semantic stack is handled this way.
-
-2) If there is infrequent top access, the entire stack contents are in the array
-$\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| is treated this way,
-as we have seen.
-
-In |nest_ptr| we have the first unused location of |nest|, and |max_nest_stack|
-has the maximum of |nest_ptr| when pushing. In |shown_mode| we store the most
-recent mode shown by \.{\\tracingcommands} and with |save_tail| we can examine
-whether we have an auto kern before a glue.
-
-*/
-
-list_state_record *nest;
-int nest_ptr, max_nest_stack, shown_mode;
-halfword save_tail;
-
-/*tex
-
-We will see later that the vertical list at the bottom semantic level is split
-into two parts; the ``current page'' runs from |page_head| to |page_tail|, and
-the ``contribution list'' runs from |contrib_head| to |tail| of semantic level
-zero. The idea is that contributions are first formed in vertical mode, then
-``contributed'' to the current page (during which time the page-breaking
-decisions are made). For now, we don't need to know any more details about the
-page-building process.
-
-*/
-
-void initialize_nesting(void)
-{
-    nest_ptr = 0;
-    max_nest_stack = 0;
-    shown_mode = 0;
-    cur_list.mode_field = vmode;
-    cur_list.head_field = contrib_head;
-    cur_list.tail_field = contrib_head;
-    cur_list.eTeX_aux_field = null;
-    cur_list.prev_depth_field = ignore_depth;
-    cur_list.space_factor_field = 1000;
-    cur_list.incompleat_noad_field = null;
-    cur_list.ml_field = 0;
-    cur_list.pg_field = 0;
-    cur_list.dirs_field = null;
-    init_math_fields();
-}
-
-/*tex
-
-Here is a common way to make the current list grow:
-
-*/
-
-void tail_append(halfword p)
-{
-    couple_nodes(cur_list.tail_field, p);
-    cur_list.tail_field = vlink(cur_list.tail_field);
-}
-
-halfword pop_tail(void)
-{
-    halfword n, r;
-    if (cur_list.tail_field != cur_list.head_field) {
-        r = cur_list.tail_field;
-        if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) {
-            n = alink(cur_list.tail_field);
-        } else {
-            n = cur_list.head_field;
-            while (vlink(n) != cur_list.tail_field)
-                n = vlink(n);
-        }
-        flush_node(cur_list.tail_field);
-        cur_list.tail_field = n;
-        vlink(n) = null;
-        return r;
-    } else {
-        return null;
-    }
-}
-
-/*tex
-
-When \TeX's work on one level is interrupted, the state is saved by calling
-|push_nest|. This routine changes |head| and |tail| so that a new (empty) list is
-begun; it does not change |mode| or |aux|.
-
-*/
-
-void push_nest(void)
-{
-    if (nest_ptr > max_nest_stack) {
-        max_nest_stack = nest_ptr;
-        if (nest_ptr == nest_size)
-            overflow("semantic nest size", (unsigned) nest_size);
-    }
-    incr(nest_ptr);
-    cur_list.mode_field = nest[nest_ptr - 1].mode_field;
-    cur_list.head_field = new_node(temp_node, 0);
-    cur_list.tail_field = cur_list.head_field;
-    cur_list.eTeX_aux_field = null;
-    cur_list.ml_field = line;
-    cur_list.pg_field = 0;
-    cur_list.dirs_field = null;
-    cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field;
-    cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field;
-    cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field;
-    init_math_fields();
-}
-
-/*tex
-
-Conversely, when \TeX\ is finished on the current level, the former state is
-restored by calling |pop_nest|. This routine will never be called at the lowest
-semantic level, nor will it be called unless |head| is a node that should be
-returned to free memory.
-
-*/
-
-void pop_nest(void)
-{
-    flush_node(cur_list.head_field);
-    decr(nest_ptr);
-}
-
-/*tex
-
-Here is a procedure that displays what \TeX\ is working on, at all levels.
-
-*/
-
-void show_activities(void)
-{
-    /*tex Index into |nest|: */
-    int p;
-    /*tex The mode: */
-    int m;
-    /*tex For showing the current page: */
-    halfword q, r;
-    /*tex Ditto: */
-    int t;
-    tprint_nl("");
-    print_ln();
-    for (p = nest_ptr; p >= 0; p--) {
-        m = nest[p].mode_field;
-        tprint_nl("### ");
-        print_mode(m);
-        tprint(" entered at line ");
-        print_int(abs(nest[p].ml_field));
-        if (nest[p].ml_field < 0)
-            tprint(" (\\output routine)");
-        if (p == 0) {
-            /*tex Show the status of the current page */
-            if (page_head != page_tail) {
-                tprint_nl("### current page:");
-                if (output_active)
-                    tprint(" (held over for next output)");
-                show_box(vlink(page_head));
-                if (page_contents > empty) {
-                    tprint_nl("total height ");
-                    print_totals();
-                    tprint_nl(" goal height ");
-                    print_scaled(page_goal);
-                    r = vlink(page_ins_head);
-                    while (r != page_ins_head) {
-                        print_ln();
-                        tprint_esc("insert");
-                        t = subtype(r);
-                        print_int(t);
-                        tprint(" adds ");
-                        if (count(t) == 1000)
-                            t = height(r);
-                        else
-                            t = x_over_n(height(r), 1000) * count(t);
-                        print_scaled(t);
-                        if (type(r) == split_up_node) {
-                            q = page_head;
-                            t = 0;
-                            do {
-                                q = vlink(q);
-                                if ((type(q) == ins_node)
-                                    && (subtype(q) == subtype(r)))
-                                    incr(t);
-                            } while (q != broken_ins(r));
-                            tprint(", #");
-                            print_int(t);
-                            tprint(" might split");
-                        }
-                        r = vlink(r);
-                    }
-                }
-            }
-            if (vlink(contrib_head) != null)
-                tprint_nl("### recent contributions:");
-        }
-        show_box(vlink(nest[p].head_field));
-        /*tex Show the auxiliary field, |a|. */
-        switch (abs(m) / (max_command_cmd + 1)) {
-            case 0:
-                tprint_nl("prevdepth ");
-                if (nest[p].prev_depth_field <= ignore_depth)
-                    tprint("ignored");
-                else
-                    print_scaled(nest[p].prev_depth_field);
-                if (nest[p].pg_field != 0) {
-                    tprint(", prevgraf ");
-                    print_int(nest[p].pg_field);
-                    if (nest[p].pg_field != 1)
-                        tprint(" lines");
-                    else
-                        tprint(" line");
-                }
-                break;
-            case 1:
-                tprint_nl("spacefactor ");
-                print_int(nest[p].space_factor_field);
-                break;
-            case 2:
-                if (nest[p].incompleat_noad_field != null) {
-                    tprint("this will be denominator of:");
-                    show_box(nest[p].incompleat_noad_field);
-                }
-                break;
-        }
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/nesting.w
@@ -0,0 +1,436 @@
+% nesting.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"
+
+@ these are for |show_activities|
+@c
+#define page_goal page_so_far[0]
+
+@ \TeX\ is typically in the midst of building many lists at once. For example,
+when a math formula is being processed, \TeX\ is in math mode and
+working on an mlist; this formula has temporarily interrupted \TeX\ from
+being in horizontal mode and building the hlist of a paragraph; and this
+paragraph has temporarily interrupted \TeX\ from being in vertical mode
+and building the vlist for the next page of a document. Similarly, when a
+\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily
+interrupted from working in restricted horizontal mode, and it enters
+internal vertical mode.  The ``semantic nest'' is a stack that
+keeps track of what lists and modes are currently suspended.
+
+At each level of processing we are in one of six modes:
+
+\yskip\hang|vmode| stands for vertical mode (the page builder);
+
+\hang|hmode| stands for horizontal mode (the paragraph builder);
+
+\hang|mmode| stands for displayed formula mode;
+
+\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox});
+
+\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox});
+
+\hang|-mmode| stands for math formula mode (not displayed).
+
+\yskip\noindent The mode is temporarily set to zero while processing \.{\\write}
+texts in the |ship_out| routine.
+
+Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that
+\TeX's ``big semantic switch'' can select the appropriate thing to
+do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current
+mode and |cur_cmd| is the current command code.
+
+@c
+static const char *string_mode(int m)
+{                               /* prints the mode represented by |m| */
+    if (m > 0) {
+        switch (m / (max_command_cmd + 1)) {
+        case 0:
+            return "vertical mode";
+            break;
+        case 1:
+            return "horizontal mode";
+            break;
+        case 2:
+            return "display math mode";
+            break;
+        default:
+            break;
+        }
+    } else if (m == 0) {
+        return "no mode";
+    } else {
+        switch ((-m) / (max_command_cmd + 1)) {
+        case 0:
+            return "internal vertical mode";
+            break;
+        case 1:
+            return "restricted horizontal mode";
+            break;
+        case 2:
+            return "math mode";
+            break;
+        default:
+            break;
+        }
+    }
+    return "unknown mode";
+}
+
+@ @c
+void print_mode(int m)
+{                               /* prints the mode represented by |m| */
+    tprint(string_mode(m));
+}
+
+@ @c
+void print_in_mode(int m)
+{                               /* prints the mode represented by |m| */
+    tprint("' in ");
+    tprint(string_mode(m));
+}
+
+@ @c
+int get_mode_id(void)
+{                               /* returns the mode represented by |m| */
+    int m = cur_list.mode_field;
+    if (m > 0) {
+        switch (m / (max_command_cmd + 1)) {
+        case 0:
+            return 'v';
+            break;
+        case 1:
+            return 'h';
+            break;
+        case 2:
+            return 'm';
+            break;
+        default:
+            return '\0';
+            break;
+        }
+    } else if (m == 0) {
+        return 'n';;
+    } else {
+        switch ((-m) / (max_command_cmd + 1)) {
+        case 0:
+            return 'V';
+            break;
+        case 1:
+            return 'H';
+            break;
+        case 2:
+            return 'M';
+            break;
+        default:
+            return '\0';
+            break;
+        }
+    }
+}
+
+
+@ The state of affairs at any semantic level can be represented by
+five values:
+
+\yskip\hang|mode| is the number representing the semantic mode, as
+just explained.
+
+\yskip\hang|head| is a |pointer| to a list head for the list being built;
+|link(head)| therefore points to the first element of the list, or
+to |null| if the list is empty.
+
+\yskip\hang|tail| is a |pointer| to the final node of the list being
+built; thus, |tail=head| if and only if the list is empty.
+
+\yskip\hang|prev_graf| is the number of lines of the current paragraph that
+have already been put into the present vertical list.
+
+\yskip\hang|aux| is an auxiliary |memory_word| that gives further information
+that is needed to characterize the situation.
+
+\yskip\noindent
+In vertical mode, |aux| is also known as |prev_depth|; it is the scaled
+value representing the depth of the previous box, for use in baseline
+calculations, or it is |<=-1000|pt if the next box on the vertical list is to
+be exempt from baseline calculations.  In horizontal mode, |aux| is also
+known as |space_factor|; it holds the current space factor used in
+spacing calculations. In math mode, |aux| is also known as |incompleat_noad|;
+if not |null|, it points to a record that represents the numerator of a
+generalized fraction for which the denominator is currently being formed
+in the current list.
+
+There is also a sixth quantity, |mode_line|, which correlates
+the semantic nest with the user's input; |mode_line| contains the source
+line number at which the current level of nesting was entered. The negative
+of this line number is the |mode_line| at the level of the
+user's output routine.
+
+A seventh quantity, |eTeX_aux|, is used by the extended features eTeX.
+In math mode it is known as |delim_ptr| and points to the most
+recent |fence_noad|  of a |math_left_group|.
+
+In horizontal mode, the |prev_graf| field is used for initial language data.
+
+The semantic nest is an array called |nest| that holds the |mode|, |head|,
+|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels
+below the currently active one. Information about the currently active
+level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|,
+|aux|, and |mode_line|, which live in a struct that is ready to
+be pushed onto |nest| if necessary.
+
+The math field is used by various bits and pieces in |texmath.w|
+
+@ This implementation of
+\TeX\ uses two different conventions for representing sequential stacks.
+@^stack conventions@>@^conventions for representing stacks@>
+
+\yskip\hang 1) If there is frequent access to the top entry, and if the
+stack is essentially never empty, then the top entry is kept in a global
+variable (even better would be a machine register), and the other entries
+appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. The
+semantic stack is handled this way.
+
+\yskip\hang 2) If there is infrequent top access, the entire stack contents
+are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack|
+is treated this way, as we have seen.
+
+@c
+list_state_record *nest;
+int nest_ptr;                   /* first unused location of |nest| */
+int max_nest_stack;             /* maximum of |nest_ptr| when pushing */
+int shown_mode;                 /* most recent mode shown by \.{\\tracingcommands} */
+halfword save_tail;             /* save |tail| so we can examine whether we have an auto
+                                   kern before a glue */
+
+@ We will see later that the vertical list at the bottom semantic level is split
+into two parts; the ``current page'' runs from |page_head| to |page_tail|,
+and the ``contribution list'' runs from |contrib_head| to |tail| of
+semantic level zero. The idea is that contributions are first formed in
+vertical mode, then ``contributed'' to the current page (during which time
+the page-breaking decisions are made). For now, we don't need to know
+any more details about the page-building process.
+
+@c
+void initialize_nesting(void)
+{
+    nest_ptr = 0;
+    max_nest_stack = 0;
+    shown_mode = 0;
+    cur_list.mode_field = vmode;
+    cur_list.head_field = contrib_head;
+    cur_list.tail_field = contrib_head;
+    cur_list.eTeX_aux_field = null;
+    cur_list.prev_depth_field = ignore_depth;
+    cur_list.space_factor_field = 1000;
+    cur_list.incompleat_noad_field = null;
+    cur_list.ml_field = 0;
+    cur_list.pg_field = 0;
+    cur_list.dirs_field = null;
+    init_math_fields();
+}
+
+@ Here is a common way to make the current list grow:
+
+@c
+void tail_append(halfword p)
+{
+    couple_nodes(cur_list.tail_field, p);
+    cur_list.tail_field = vlink(cur_list.tail_field);
+}
+
+@ @c
+halfword pop_tail(void)
+{
+    halfword n, r;
+    if (cur_list.tail_field != cur_list.head_field) {
+        r = cur_list.tail_field;
+        if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) {
+            n = alink(cur_list.tail_field);
+        } else {
+            n = cur_list.head_field;
+            while (vlink(n) != cur_list.tail_field)
+                n = vlink(n);
+        }
+        flush_node(cur_list.tail_field);
+        cur_list.tail_field = n;
+        vlink(n) = null;
+        return r;
+    } else {
+        return null;
+    }
+}
+
+@ When \TeX's work on one level is interrupted, the state is saved by
+calling |push_nest|. This routine changes |head| and |tail| so that
+a new (empty) list is begun; it does not change |mode| or |aux|.
+
+@c
+void push_nest(void)
+{                               /* enter a new semantic level, save the old */
+    if (nest_ptr > max_nest_stack) {
+        max_nest_stack = nest_ptr;
+        if (nest_ptr == nest_size)
+            overflow("semantic nest size", (unsigned) nest_size);
+    }
+    incr(nest_ptr);
+    cur_list.mode_field = nest[nest_ptr - 1].mode_field;
+    cur_list.head_field = new_node(temp_node, 0);
+    cur_list.tail_field = cur_list.head_field;
+    cur_list.eTeX_aux_field = null;
+    cur_list.ml_field = line;
+    cur_list.pg_field = 0;
+    cur_list.dirs_field = null;
+    cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field;
+    cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field;
+    cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field;
+    init_math_fields();
+}
+
+@ Conversely, when \TeX\ is finished on the current level, the former
+state is restored by calling |pop_nest|. This routine will never be
+called at the lowest semantic level, nor will it be called unless |head|
+is a node that should be returned to free memory.
+
+@c
+void pop_nest(void)
+{                               /* leave a semantic level, re-enter the old */
+    flush_node(cur_list.head_field);
+    decr(nest_ptr);
+}
+
+@ Here is a procedure that displays what \TeX\ is working on, at all levels.
+
+@c
+void show_activities(void)
+{
+    int p;                      /* index into |nest| */
+    int m;                      /* mode */
+    halfword q, r;              /* for showing the current page */
+    int t;                      /* ditto */
+    tprint_nl("");
+    print_ln();
+    for (p = nest_ptr; p >= 0; p--) {
+        m = nest[p].mode_field;
+        tprint_nl("### ");
+        print_mode(m);
+        tprint(" entered at line ");
+        print_int(abs(nest[p].ml_field));
+        /* we dont do this any more */
+#if 0
+
+           if (m == hmode)
+           if (nest[p].pg_field != 040600000) {
+           tprint(" (language");
+           print_int(nest[p].pg_field % 0200000);
+           tprint(":hyphenmin");
+           print_int(nest[p].pg_field / 020000000);
+           print_char(',');
+           print_int((nest[p].pg_field / 0200000) % 0100);
+           print_char(')');
+           }
+#endif
+        if (nest[p].ml_field < 0)
+            tprint(" (\\output routine)");
+        if (p == 0) {
+            /* Show the status of the current page */
+            if (page_head != page_tail) {
+                tprint_nl("### current page:");
+                if (output_active)
+                    tprint(" (held over for next output)");
+                show_box(vlink(page_head));
+                if (page_contents > empty) {
+                    tprint_nl("total height ");
+                    print_totals();
+                    tprint_nl(" goal height ");
+                    print_scaled(page_goal);
+                    r = vlink(page_ins_head);
+                    while (r != page_ins_head) {
+                        print_ln();
+                        tprint_esc("insert");
+                        t = subtype(r);
+                        print_int(t);
+                        tprint(" adds ");
+                        if (count(t) == 1000)
+                            t = height(r);
+                        else
+                            t = x_over_n(height(r), 1000) * count(t);
+                        print_scaled(t);
+                        if (type(r) == split_up_node) {
+                            q = page_head;
+                            t = 0;
+                            do {
+                                q = vlink(q);
+                                if ((type(q) == ins_node)
+                                    && (subtype(q) == subtype(r)))
+                                    incr(t);
+                            } while (q != broken_ins(r));
+                            tprint(", #");
+                            print_int(t);
+                            tprint(" might split");
+                        }
+                        r = vlink(r);
+                    }
+                }
+            }
+            if (vlink(contrib_head) != null)
+                tprint_nl("### recent contributions:");
+        }
+        show_box(vlink(nest[p].head_field));
+        /* Show the auxiliary field, |a| */
+        switch (abs(m) / (max_command_cmd + 1)) {
+        case 0:
+            tprint_nl("prevdepth ");
+            if (nest[p].prev_depth_field <= ignore_depth)
+                tprint("ignored");
+            else
+                print_scaled(nest[p].prev_depth_field);
+            if (nest[p].pg_field != 0) {
+                tprint(", prevgraf ");
+                print_int(nest[p].pg_field);
+                if (nest[p].pg_field != 1)
+                    tprint(" lines");
+                else
+                    tprint(" line");
+            }
+            break;
+        case 1:
+            tprint_nl("spacefactor ");
+            print_int(nest[p].space_factor_field);
+            /* we dont do this any more, this was aux.rh originally */
+#if 0
+               if (m > 0) {
+               if (nest[p].current_language_field > 0) {
+               tprint(", current language ");
+               print_int(nest[p].current_language_field);
+               }
+               }
+#endif
+            break;
+        case 2:
+            if (nest[p].incompleat_noad_field != null) {
+                tprint("this will be denominator of:");
+                show_box(nest[p].incompleat_noad_field);
+            }
+        }                       /* there are no other cases */
+
+    }
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/packaging.w
@@ -0,0 +1,2170 @@
+% packaging.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're essentially done with the parts of \TeX\ that are concerned with the
+input (|get_next|) and the output (|ship_out|). So it's time to get heavily into
+the remaining part, which does the real work of typesetting.
+
+After lists are constructed, \TeX\ wraps them up and puts them into boxes. Two
+major subroutines are given the responsibility for this task: |hpack| applies to
+horizontal lists (hlists) and |vpack| applies to vertical lists (vlists). The
+main duty of |hpack| and |vpack| is to compute the dimensions of the resulting
+boxes, and to adjust the glue if one of those dimensions is pre-specified. The
+computed sizes normally enclose all of the material inside the new box; but some
+items may stick out if negative glue is used, if the box is overfull, or if a
+\.{\\vbox} includes other boxes that have been shifted left.
+
+The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node| for a box
+containing the hlist that starts at |p|. Parameter |w| specifies a width; and
+parameter |m| is either `|exactly|' or `|additional|'. Thus, |hpack(p,w,exactly)|
+produces a box whose width is exactly |w|, while |hpack(p,w,additional)| yields a
+box whose width is the natural width plus |w|. It is convenient to define a macro
+called `|natural|' to cover the most common case, so that we can say
+|hpack(p,natural)| to get a box that has the natural width of list |p|.
+
+Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a box
+containing the vlist that starts at |p|. In this case |w| represents a height
+instead of a width; the parameter |m| is interpreted as in |hpack|.
+
+@ The parameters to |hpack| and |vpack| correspond to \TeX's primitives like
+`\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note that
+`\.{\\hbox}' with no dimension following it is equivalent to `\.{\\hbox}
+\.{spread} \.{0pt}'. The |scan_spec| subroutine scans such constructions in the
+user's input, including the mandatory left brace that follows them, and it puts
+the specification onto |save_stack| so that the desired box can later be obtained
+by executing the following code: $$\vbox{\halign{#\hfil\cr
+|save_ptr:=save_ptr-1;|\cr |hpack(p,saved_value(0),saved_level(0)).|\cr}}$$
+
+@c
+/*
+    void scan_spec(group_code c)
+    {
+        int spec_code;
+        if (scan_keyword("to")) {
+            spec_code = exactly;
+            scan_normal_dimen();
+        } else if (scan_keyword("spread")) {
+            spec_code = additional;
+            scan_normal_dimen();
+        } else {
+            spec_code = additional;
+            cur_val = 0;
+        }
+        set_saved_record(0, saved_boxspec, spec_code, cur_val);
+        save_ptr++;
+        new_save_level(c);
+        scan_left_brace();
+    }
+*/
+
+void scan_spec(group_code c)
+{                               /* scans a box specification and left brace */
+    int spec_code;
+    boolean done = false ;
+    do {
+        get_x_token();
+    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+    if (cur_cmd == left_brace_cmd) {
+        spec_code = additional;
+        cur_val = 0;
+        done = true;
+    } else {
+        /* todo: attr */
+        back_input();
+        if (scan_keyword("to")) {
+            spec_code = exactly;
+            scan_normal_dimen();
+        } else if (scan_keyword("spread")) {
+            spec_code = additional;
+            scan_normal_dimen();
+        } else {
+            spec_code = additional;
+            cur_val = 0;
+        }
+    }
+    set_saved_record(0, saved_boxspec, spec_code, cur_val);
+    save_ptr++;
+    new_save_level(c);
+    if (!done) {
+        scan_left_brace();
+    }
+}
+
+@ When scanning, special care is necessary to ensure that the special
+|save_stack| codes are placed just below the new group code, because scanning can
+change |save_stack| when \.{\\csname} appears.
+
+This coincides with the text on |dir| and |attr| keywords, as these are exaclty
+the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the input stream (the
+others are \.{\\vcenter}, \.{\\valign}, and \.{\\halign}).
+
+@c
+/*
+    void scan_full_spec(group_code c, int spec_direction)
+    {
+        int s;
+        int i;
+        int v;
+        int spec_code;
+        halfword attr_list;
+        if (attr_list_cache == cache_disabled)
+            update_attribute_cache();
+        attr_list = attr_list_cache;
+        s = saved_value(0);
+      CONTINUE:
+        while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
+            get_x_token();
+            if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd)
+                back_input();
+        }
+        if (scan_keyword("attr")) {
+            scan_register_num();
+            i = cur_val;
+            scan_optional_equals();
+            scan_int();
+            v = cur_val;
+            if ((attr_list != null) && (attr_list == attr_list_cache)) {
+                attr_list = copy_attribute_list(attr_list_cache);
+                add_node_attr_ref(attr_list);
+            }
+            attr_list = do_set_attribute(attr_list, i, v);
+            goto CONTINUE;
+        }
+        if (scan_keyword("dir")) {
+            scan_direction();
+            spec_direction = cur_val;
+            goto CONTINUE;
+        }
+        if (attr_list == attr_list_cache) {
+            add_node_attr_ref(attr_list);
+        }
+        if (scan_keyword("to")) {
+            spec_code = exactly;
+        } else if (scan_keyword("spread")) {
+            spec_code = additional;
+        } else {
+            spec_code = additional;
+            cur_val = 0;
+            goto FOUND;
+        }
+        scan_normal_dimen();
+      FOUND:
+        set_saved_record(0, saved_boxcontext, 0, s);
+        set_saved_record(1, saved_boxspec, spec_code, cur_val);
+        if (spec_direction != -1) {
+            set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr);
+            text_dir_ptr = new_dir(spec_direction);
+        } else {
+            set_saved_record(2, saved_boxdir, spec_direction, null);
+        }
+        set_saved_record(3, saved_boxattr, 0, attr_list);
+        save_ptr += 4;
+        new_save_level(c);
+        scan_left_brace();
+        eq_word_define(int_base + body_direction_code, spec_direction);
+        eq_word_define(int_base + par_direction_code, spec_direction);
+        eq_word_define(int_base + text_direction_code, spec_direction);
+    }
+*/
+
+/* scans a box specification and left brace */
+
+void scan_full_spec(group_code c, int spec_direction, int just_pack)
+{
+    int s, i, v, spec_code;
+    boolean done = false ;
+    halfword attr_list;
+    boolean attr_done = false ;
+    if (attr_list_cache == cache_disabled)
+        update_attribute_cache();
+    attr_list = attr_list_cache;
+    s = saved_value(0); /* the box context */
+    do {
+        get_x_token();
+    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+    if (cur_cmd == left_brace_cmd) {
+        goto QUICK;
+    } else {
+        back_input();
+        goto KEYWORDS;
+    }
+  CONTINUE:
+    while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
+        get_x_token();
+        if (cur_cmd == left_brace_cmd) {
+            goto QUICK;
+        } else if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd) {
+            back_input();
+            break;
+        }
+    }
+  KEYWORDS:
+    if (scan_keyword("attr")) {
+        scan_register_num();
+        i = cur_val;
+        scan_optional_equals();
+        scan_int();
+        v = cur_val;
+        if (! attr_done) {
+            attr_list = copy_attribute_list(attr_list_cache);
+            attr_done = true;
+        }
+        attr_list = do_set_attribute(attr_list, i, v);
+        goto CONTINUE;
+    }
+    if (scan_keyword("dir")) {
+        scan_direction();
+        spec_direction = cur_val;
+        goto CONTINUE;
+    }
+    if (scan_keyword("to")) {
+        spec_code = exactly;
+    } else if (scan_keyword("spread")) {
+        spec_code = additional;
+    } else {
+        spec_code = additional;
+        cur_val = 0;
+        goto FOUND;
+    }
+    scan_normal_dimen();
+    goto FOUND;
+  QUICK:
+    spec_code = additional;
+    cur_val = 0;
+    done = true;
+  FOUND:
+    add_node_attr_ref(attr_list);
+    set_saved_record(0, saved_boxcontext, 0, s);
+    set_saved_record(1, saved_boxspec, spec_code, cur_val);
+    /* DIR: Adjust |text_dir_ptr| for |scan_spec| */
+    if (spec_direction != -1) {
+        set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr);
+        text_dir_ptr = new_dir(spec_direction);
+    } else {
+        set_saved_record(2, saved_boxdir, spec_direction, null);
+    }
+    set_saved_record(3, saved_boxattr, 0, attr_list);
+    set_saved_record(4, saved_boxpack, 0, just_pack);
+    save_ptr += 5;
+    new_save_level(c);
+    if (! done) {
+        scan_left_brace();
+    }
+    /* no gain: if (body_direction_par != spec_direction) etc */
+    eq_word_define(int_base + body_direction_code, spec_direction);
+    eq_word_define(int_base + par_direction_code, spec_direction);
+    eq_word_define(int_base + text_direction_code, spec_direction);
+}
+
+
+@ To figure out the glue setting, |hpack| and |vpack| determine how much
+stretchability and shrinkability are present, considering all four orders of
+infinity. The highest order of infinity that has a nonzero coefficient is then
+used as if no other orders were present.
+
+For example, suppose that the given list contains six glue nodes with the
+respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill. Then the
+total is essentially 2fil; and if a total additional space of 6pt is to be
+achieved by stretching, the actual amounts of stretch will be 0pt, 0pt, 15pt,
+0pt, $-9$pt, and 0pt, since only `fil' glue will be considered. (The `fill' glue
+is therefore not really stretching infinitely with respect to `fil'; nobody would
+actually want that to happen.)
+
+The arrays |total_stretch| and |total_shrink| are used to determine how much glue
+of each kind is present. A global variable |last_badness| is used to implement
+\.{\\badness}.
+
+@c
+scaled total_stretch[5];
+scaled total_shrink[5];         /* glue found by |hpack| or |vpack| */
+int last_badness;               /* badness of the most recently packaged box */
+
+@ If the global variable |adjust_tail| is non-null, the |hpack| routine also
+removes all occurrences of |ins_node|, |mark_node|, and |adjust_node| items and
+appends the resulting material onto the list that ends at location |adjust_tail|.
+
+@c
+halfword adjust_tail;           /* tail of adjustment list */
+
+
+@ Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to
+|pre_adjust_tail| instead of |adjust_tail|.
+
+@c
+halfword pre_adjust_tail;
+
+halfword last_leftmost_char;
+halfword last_rightmost_char;
+
+halfword next_char_p;           /* pointer to the next char of an implicit kern */
+halfword prev_char_p;           /* pointer to the previous char of an implicit kern */
+
+@ This procedure is called repeatedly from inside the line break algorithm.
+@c
+void set_prev_char_p(halfword p)
+{
+    prev_char_p = p;
+}
+
+/*
+    the kern stretch / shrink code was (or had become) rather weird ... the width field
+    is set, and then used in a second calculation, repeatedly, so why is that ... maybe some
+    some weird left-over ... anyway, the values are so small that in practice they are not
+    significant at all when the backend sees them because a few hundred sp positive or
+    negative are just noise there (so adjustlevel 3 has hardly any consequence for the
+    result but is more efficient)
+*/
+
+
+@ @c
+scaled char_stretch(halfword p)
+{
+    internal_font_number f = font(p);
+    int m = font_max_stretch(f);
+    if (m > 0) {
+        int c = character(p);
+        int ef = get_ef_code(f, c);
+        if (ef > 0) {
+            scaled dw = calc_char_width(f, c, m) - char_width(f, c) - x_advance(p);
+            if (dw > 0) {
+                return round_xn_over_d(dw, ef, 1000);
+            }
+        }
+    }
+    return 0;
+}
+
+@ @c
+scaled char_shrink(halfword p)
+{
+    internal_font_number f = font(p);
+    int m = font_max_shrink(f);
+    if (m > 0) {
+        int c = character(p);
+        int ef = get_ef_code(f, c);
+        if (ef > 0) {
+            scaled dw = char_width(f, c) + x_advance(p) - calc_char_width(f, c, -m);
+            if (dw > 0) {
+                return round_xn_over_d(dw, ef, 1000);
+            }
+        }
+    }
+    return 0;
+}
+
+@ @c
+/*
+scaled kern_stretch(halfword p)
+{
+    halfword l, r;
+    scaled d;
+    int m;
+    if ((prev_char_p == null) || (vlink(prev_char_p) != p) || (vlink(p) == null))
+        return 0;
+    l = prev_char_p;
+    // we need a left char
+    if (!is_char_node(l))
+        return 0;
+    r = vlink(p);
+    // and a right char
+    if (!is_char_node(r))
+        return 0;
+    // and a reason to kern
+    if ((font(l) != font(r)) || (font_max_stretch(font(l)) == 0))
+        return 0;
+    m = font_max_stretch(font(l));
+    d = get_kern(font(l), character(l), character(r)); // real kern, so what is width(p) then; the messed up one
+    d = round_xn_over_d(d, 1000 + m, 1000);
+    return round_xn_over_d(d - width(p), get_ef_code(font(l), character(l)), 1000);
+}
+*/
+
+scaled kern_stretch(halfword p)
+{
+    int m;
+    scaled d, e, x;
+    scaled w = width(p) ;
+    halfword l;
+    halfword r;
+    if (w == 0)  {
+        /* why bother about zero kerns */
+        return 0;
+    }
+    l = prev_char_p ;
+    if ((l == null) || (vlink(l) != p)) {
+        /* we only care about kerns following a char*/
+        return 0;
+    }
+    r = vlink(p);
+    if (r == null) {
+        /* we only care about kerns between a char and something else */
+    }
+    if (!(is_char_node(l) && is_char_node(r))) {
+        /* we want two chars (but but don't care about the fonts) */
+        return 0;
+    }
+    /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
+    m = (font_max_stretch(font(l)) + font_max_stretch(font(r)))/2;
+    if (m == 0) {
+        /* nothing to kern */
+        return 0;
+    }
+    d = round_xn_over_d(w, 1000 + m, 1000);
+    /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
+    e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ;
+    if (e == 1000) {
+        x = d - w;
+    } else {
+        x = round_xn_over_d(d - w, e, 1000);
+    }
+    /*
+        printf("STRETCH w=%i s=%i x=%i\n",w,e+m,x);
+    */
+    return x;
+}
+
+@ @c
+/*
+scaled kern_shrink(halfword p)
+{
+    halfword l, r;
+    scaled d;
+    int m;
+    if ((prev_char_p == null) || (vlink(prev_char_p) != p) || (vlink(p) == null))
+        return 0;
+    l = prev_char_p;
+    // we need a left char
+    if (!is_char_node(l))
+        return 0;
+    r = vlink(p);
+    // and a right char
+    if (!is_char_node(r))
+        return 0;
+    // and a reason to kern
+    if ((font(l) != font(r)) || (font_max_shrink(font(l)) == 0))
+        return 0;
+    m = font_max_shrink(font(l));
+    d = get_kern(font(l), character(l), character(r)); // real kern, so what is width(p) then; the messed up one
+    d = round_xn_over_d(d, 1000 - m, 1000);
+    return round_xn_over_d(width(p) - d, get_ef_code(font(l), character(l)), 1000);
+}
+*/
+
+scaled kern_shrink(halfword p)
+{
+    int m;
+    scaled d, e, x;
+    scaled w = width(p) ;
+    halfword l;
+    halfword r;
+    if (w == 0)  {
+        /* why bother about zero kerns */
+        return 0;
+    }
+    l = prev_char_p ;
+    if ((l == null) || (vlink(l) != p)) {
+        /* we only care about kerns following a char*/
+        return 0;
+    }
+    r = vlink(p);
+    if (r == null) {
+        /* we only care about kerns between a char and something else */
+    }
+    if (!(is_char_node(l) && is_char_node(r))) {
+        /* we want two chars (but but don't care about the fonts) */
+        return 0;
+    }
+    /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
+    m = (font_max_shrink(font(l)) + font_max_shrink(font(r)))/2;
+    if (m == 0) {
+        /* nothing to kern */
+        return 0;
+    }
+    d = round_xn_over_d(w, 1000 - m, 1000);
+    e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ;
+    if (e == 1000) {
+         x = w - d ;
+    } else {
+        x = round_xn_over_d(w - d, e, 1000);
+    }
+    /*
+    printf("SHRINK w=%i s=%i x=%i\n",w,e+m,x);
+    */
+    return x;
+}
+
+@ @c
+void do_subst_font(halfword p, int ex_ratio)
+{
+    if (type(p) == disc_node) {
+        halfword r = vlink(pre_break(p));
+        while (r != null) {
+            if (is_char_node(r))
+                do_subst_font(r, ex_ratio);
+            r = vlink(r);
+        }
+        r = vlink(post_break(p));
+        while (r != null) {
+            if (is_char_node(r))
+                do_subst_font(r, ex_ratio);
+            r = vlink(r);
+        }
+        r = vlink(no_break(p));
+        while (r != null) {
+            if (is_char_node(r))
+                do_subst_font(r, ex_ratio);
+            r = vlink(r);
+        }
+        return;
+    }
+    if (! is_char_node(p)) {
+        normal_error("font expansion", "invalid node type");
+        return;
+    } else {
+        internal_font_number f = font(p);
+        int ef = get_ef_code(f, character(p));
+        if (ef == 0)
+            return;
+        if ((font_max_stretch(f) > 0) && (ex_ratio > 0)) {
+            int ex_stretch = ext_xn_over_d(ex_ratio * ef, font_max_stretch(f), 1000000);
+            ex_glyph(p) = fix_expand_value(f, ex_stretch)*1000;
+        } else if ((font_max_shrink(f) > 0) && (ex_ratio < 0)) {
+            int ex_shrink = ext_xn_over_d(ex_ratio * ef, font_max_shrink(f), 1000000);
+            ex_glyph(p) = fix_expand_value(f, ex_shrink)*1000;
+        }
+    }
+}
+
+@ @c
+scaled char_pw(halfword p, int side)
+{
+    internal_font_number f;
+    int c, w;
+    if (side == left_side)
+        last_leftmost_char = null;
+    else
+        last_rightmost_char = null;
+    if (p == null)
+        return 0;
+    if (!is_char_node(p))
+        return 0;
+    f = font(p);
+    if (side == left_side) {
+        c = get_lp_code(f, character(p));
+        last_leftmost_char = p;
+    } else {
+        c = get_rp_code(f, character(p));
+        last_rightmost_char = p;
+    }
+    if (c == 0)
+        return 0;
+    w = quad(f);
+    return round_xn_over_d(w, c, 1000);
+}
+
+@ @c
+halfword new_margin_kern(scaled w, halfword p, int side)
+{
+    halfword k, q;
+    k = new_node(margin_kern_node, side);
+    width(k) = w;
+    if (p == null)
+        normal_error("margin kerning", "invalid pointer to marginal char node");
+    q = new_char(font(p), character(p));
+    margin_char(k) = q;
+    return k;
+}
+
+@ Here is |hpack|, which is place where we do font substituting when font
+expansion is being used.
+
+@c
+int font_expand_ratio = 0;  /* current expansion ratio, needed for recursive call */
+
+int ignore_math_skip(halfword p)
+{
+    if (math_skip_mode == 6) {
+        if (subtype(p) == after) {
+            if (math_skip_boundary(vlink(p))) {
+                return 0;
+            }
+        } else {
+            if (math_skip_boundary(alink(p))) {
+                return 0;
+            }
+        }
+    } else if (math_skip_mode == 7) {
+        if (subtype(p) == after) {
+            if (! math_skip_boundary(vlink(p))) {
+                return 0;
+            }
+        } else {
+            if (! math_skip_boundary(alink(p))) {
+                return 0;
+            }
+        }
+    } else {
+        return 0;
+    }
+    reset_glue_to_zero(p);
+    return 1;
+}
+
+halfword hpack(halfword p, scaled w, int m, int pack_direction)
+{
+    halfword r;                 /* the box node that will be returned */
+    halfword q;                 /* trails behind |p| */
+    scaled h = 0;               /* height */
+    scaled d = 0;               /* depth */
+    scaled x = 0;               /* natural width */
+    scaled_whd whd;
+    scaled s;                   /* shift amount */
+    int o;                      /* order of infinity */
+    halfword dir_ptr1 = null;   /* for managing the direction stack */
+    int hpack_dir;              /* the current direction */
+    int disc_level = 0;
+    halfword pack_interrupt[8];
+    scaled font_stretch = 0;
+    scaled font_shrink = 0;
+    int adjust_spacing = adjust_spacing_par;
+
+/*
+    int font_expand_ratio = 0;
+*/
+    last_badness = 0;
+    r = new_node(hlist_node, min_quarterword); /* the box node that will be returned */
+    if (pack_direction == -1) {
+        hpack_dir = text_direction_par;
+    } else {
+        hpack_dir = pack_direction;
+    }
+    box_dir(r) = hpack_dir;
+    /*
+        potential optimimization, save a little but neglectable in practice (not so
+        many empty boxes are used)
+
+        if (p == null) {
+            width(r) = w;
+            return r;
+        }
+    */
+    push_dir(dir_ptr1,hpack_dir); /* push null */
+    q = r + list_offset; /* hm, adding something to a node address? */
+    vlink(q) = p;
+    if (m == cal_expand_ratio) {
+        prev_char_p = null; /* why not always */
+    }
+    if (adjust_spacing > 2) {
+        adjust_spacing = 0;
+    }
+    total_stretch[normal] = 0;
+    total_shrink[normal] = 0;
+    total_stretch[sfi] = 0;
+    total_shrink[sfi] = 0;
+    total_stretch[fil] = 0;
+    total_shrink[fil] = 0;
+    total_stretch[fill] = 0;
+    total_shrink[fill] = 0;
+    total_stretch[filll] = 0;
+    total_shrink[filll] = 0;
+
+  RESWITCH:
+    while ((p != null) || (disc_level > 0)) {
+        if (p == null) {
+            decr(disc_level);
+            p = pack_interrupt[disc_level];
+            goto RESWITCH;
+        }
+        /*
+            Examine node |p| in the hlist, taking account of its effect
+            on the dimensions of the new box, or moving it to the adjustment list;
+            then advance |p| to the next node
+        */
+        while (is_char_node(p)) {
+            /*
+                Incorporate character dimensions into the dimensions of the hbox
+                that will contain~it, then move to the next node.
+
+                The following code is part of \TeX's inner loop; i.e., adding
+                another character of text to the user's input will cause each of
+                these instructions to be exercised one more time.
+             */
+            if (m >= cal_expand_ratio) {
+                prev_char_p = p;
+                if (m == cal_expand_ratio) {
+                    font_stretch += char_stretch(p);
+                    font_shrink += char_shrink(p);
+                } else if (m == subst_ex_font) {
+                    do_subst_font(p, font_expand_ratio);
+                }
+            }
+            whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
+            x += whd.wd;
+            if (whd.ht > h)
+                h = whd.ht;
+            if (whd.dp > d)
+                d = whd.dp;
+            p = vlink(p);
+        }
+        if (p != null) {
+            switch (type(p)) {
+                case hlist_node:
+                case vlist_node:
+                    /*
+                        Incorporate box dimensions into the dimensions of the hbox
+                        that will contain~it
+
+                        The code here implicitly uses the fact that running dimensions are
+                        indicated by |null_flag|, which will be ignored in the calculations
+                        because it is a highly negative number.
+                    */
+                    s = shift_amount(p);
+                    whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
+                    x += whd.wd;
+                    if (whd.ht - s > h)
+                        h = whd.ht - s;
+                    if (whd.dp + s > d)
+                        d = whd.dp + s;
+                    break;
+                /*
+                case rule_node:
+                case unset_node:
+                    x += width(p);
+                    if (type(p) >= rule_node) // always
+                        s = 0;
+                    else
+                        s = shift_amount(p);
+                    if (height(p) - s > h)
+                        h = height(p) - s;
+                    if (depth(p) + s > d)
+                        d = depth(p) + s;
+                    break;
+                */
+                case rule_node:
+                case unset_node:
+                    x += width(p);
+                    if (height(p) > h)
+                        h = height(p);
+                    if (depth(p) > d)
+                        d = depth(p);
+                    break;
+                /* */
+                case math_node:
+                    /* begin mathskip code */
+                    if (glue_is_zero(p) || ignore_math_skip(p)) {
+                        x += surround(p);
+                        break;
+                    } else {
+                        /* fall through */
+                    }
+                    /* end mathskip code */
+                case glue_node:
+                    /* Incorporate glue into the horizontal totals */
+                    x += width(p);
+                    o = stretch_order(p);
+                    total_stretch[o] = total_stretch[o] + stretch(p);
+                    o = shrink_order(p);
+                    total_shrink[o] = total_shrink[o] + shrink(p);
+                    if (subtype(p) >= a_leaders) {
+                        halfword g = leader_ptr(p);
+                        if (height(g) > h)
+                            h = height(g);
+                        if (depth(g) > d)
+                            d = depth(g);
+                    }
+                    break;
+                case kern_node:
+                    x += width(p);
+                    if (subtype(p) == font_kern && adjust_spacing) {
+                        /* so only when 1 or 2 */
+                        if (m == cal_expand_ratio) {
+                            font_stretch = font_stretch + kern_stretch(p);
+                            font_shrink = font_shrink + kern_shrink(p);
+                        } else if (m == subst_ex_font) {
+                            /* this is the finalizer */
+                            int k = 0;
+                            if (font_expand_ratio > 0) {
+                                k = kern_stretch(p);
+                            } else if (font_expand_ratio < 0) {
+                                k = kern_shrink(p);
+                            }
+                            ex_kern(p) = k;
+                            x += k;
+                            /*
+                                if (x!=0) printf("SET %i %i %i\n",font_expand_ratio,k,x);
+                            */
+                        }
+                    }
+                    break;
+                case disc_node:
+                    if (m == subst_ex_font)
+                        do_subst_font(p, font_expand_ratio);
+                    if ((subtype(p) != select_disc) && (vlink(no_break(p)) != null)) {
+                        pack_interrupt[disc_level] = vlink(p);
+                        incr(disc_level);
+                        p = no_break(p);
+                    }
+                    break;
+                case dir_node:
+                    /* Adjust the dir stack for the |hpack| routine */
+                    if (dir_dir(p) >= 0) {
+                        hpack_dir = dir_dir(p);
+                        push_dir_node(dir_ptr1,p);
+                    } else {
+                        pop_dir_node(dir_ptr1);
+                        if (dir_ptr1 != null)
+                            hpack_dir = dir_dir(dir_ptr1);
+                    }
+                    break;
+                case margin_kern_node:
+                    if (m == cal_expand_ratio) {
+                        int f = font(margin_char(p));
+                        do_subst_font(margin_char(p), 1000);
+                        if (f != font(margin_char(p)))
+                            font_stretch = font_stretch - width(p) - char_pw(margin_char(p), subtype(p));
+                        font(margin_char(p)) = f;
+                        do_subst_font(margin_char(p), -1000);
+                        if (f != font(margin_char(p)))
+                            font_shrink = font_shrink - width(p) - char_pw(margin_char(p), subtype(p));
+                        font(margin_char(p)) = f;
+                    } else if (m == subst_ex_font) {
+                        do_subst_font(margin_char(p), font_expand_ratio);
+                        width(p) = -char_pw(margin_char(p), subtype(p));
+                    }
+                    x += width(p);
+                    break;
+                case ins_node:
+                case mark_node:
+                case adjust_node:
+                    /*
+                        Transfer node |p| to the adjustment list.
+
+                        Although node |q| is not necessarily the immediate predecessor of node |p|,
+                        it always points to some node in the list preceding |p|. Thus, we can delete
+                        nodes by moving |q| when necessary. The algorithm takes linear time, and the
+                        extra computation does not intrude on the inner loop unless it is necessary
+                        to make a deletion.
+                     */
+                    if (adjust_tail != null || pre_adjust_tail != null) {
+                        while (vlink(q) != p)
+                            q = vlink(q);
+                        if (type(p) == adjust_node) {
+                            if (adjust_pre(p) != 0)
+                                update_adjust_list(pre_adjust_tail);
+                            else
+                                update_adjust_list(adjust_tail);
+                            p = vlink(p);
+                            adjust_ptr(vlink(q)) = null;
+                            flush_node(vlink(q));
+                        } else {
+                            vlink(adjust_tail) = p;
+                            adjust_tail = p;
+                            p = vlink(p);
+                        }
+                        vlink(q) = p;
+                        p = q;
+                    }
+                    break;
+                /* */
+                default:
+                    break;
+            }
+            p = vlink(p);
+        }
+
+    }
+
+    if (adjust_tail != null)
+        vlink(adjust_tail) = null;
+    if (pre_adjust_tail != null)
+        vlink(pre_adjust_tail) = null;
+    height(r) = h;
+    depth(r) = d;
+    /*
+        Determine the value of |width(r)| and the appropriate glue setting; then
+        |return| or |goto common_ending|.
+
+        When we get to the present part of the program, |x| is the natural width
+        of the box being packaged.
+    */
+    if (m == additional)
+        w = x + w;
+    width(r) = w;
+    x = w - x;
+    /* now |x| is the excess to be made up */
+    if (x == 0) {
+        glue_sign(r) = normal;
+        glue_order(r) = normal;
+        set_glue_ratio_zero(glue_set(r));
+        goto EXIT;
+    } else if (x > 0) {
+        /*
+            Determine horizontal glue stretch setting, then |return|
+            or \hbox{|goto common_ending|}.
+
+            If |hpack| is called with |m=cal_expand_ratio| we calculate
+            |font_expand_ratio| and return without checking for overfull or
+            underfull box.
+        */
+        if (total_stretch[filll] != 0)
+            o = filll;
+        else if (total_stretch[fill] != 0)
+            o = fill;
+        else if (total_stretch[fil] != 0)
+            o = fil;
+        else if (total_stretch[sfi] != 0)
+            o = sfi;
+        else
+            o = normal;
+
+        if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) {
+            font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0);
+            goto EXIT;
+        }
+        glue_order(r) = (quarterword) o;
+        glue_sign(r) = stretching;
+        if (total_stretch[o] != 0) {
+            glue_set(r) = unfloat((double) x / total_stretch[o]);
+        } else {
+            /* there's nothing to stretch */
+            glue_sign(r) = normal;
+            set_glue_ratio_zero(glue_set(r));
+        }
+        if (o == normal) {
+            if (list_ptr(r) != null) {
+                /*
+                    Report an underfull hbox and |goto common_ending|, if this box
+                    is sufficiently bad.
+                */
+                last_badness = badness(x, total_stretch[normal]);
+                if (last_badness > hbadness_par) {
+                    int callback_id = callback_defined(hpack_quality_callback);
+                    if (callback_id > 0) {
+                        halfword rule = null;
+                        if (last_badness > 100) {
+                            run_callback(callback_id, "SdNdd->N","underfull",last_badness,r,abs(pack_begin_line),line,&rule);
+                        } else {
+                            run_callback(callback_id, "SdNdd->N","loose",last_badness,r,abs(pack_begin_line),line,&rule);
+                        }
+                        if (rule != null) {
+                            while (vlink(q) != null) {
+                                q = vlink(q);
+                            }
+                            couple_nodes(q,rule);
+                        }
+                    } else {
+                        print_ln();
+                        if (last_badness > 100) {
+                            tprint_nl("Underfull \\hbox (badness ");
+                        } else {
+                            tprint_nl("Loose \\hbox (badness ");
+                        }
+                        print_int(last_badness);
+                        goto COMMON_ENDING;
+                    }
+                }
+            }
+        }
+        goto EXIT;
+    } else {
+        /*
+            Determine horizontal glue shrink setting, then |return|
+            or \hbox{|goto common_ending|},
+        */
+        if (total_shrink[filll] != 0)
+            o = filll;
+        else if (total_shrink[fill] != 0)
+            o = fill;
+        else if (total_shrink[fil] != 0)
+            o = fil;
+        else if (total_shrink[sfi] != 0)
+            o = sfi;
+        else
+            o = normal;
+
+        if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) {
+            font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0);
+            goto EXIT;
+        }
+        glue_order(r) = (quarterword) o;
+        glue_sign(r) = shrinking;
+        if (total_shrink[o] != 0) {
+            glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]);
+        } else {
+            /* there's nothing to shrink */
+            glue_sign(r) = normal;
+            set_glue_ratio_zero(glue_set(r));
+        }
+        if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
+            int overshoot = -x - total_shrink[normal] ;
+            last_badness = 1000000;
+            /* use the maximum shrinkage */
+            set_glue_ratio_one(glue_set(r));
+            /*
+                Report an overfull hbox and |goto common_ending|, if this box
+                is sufficiently bad.
+            */
+            if ((overshoot > hfuzz_par) || (hbadness_par < 100)) {
+                int callback_id = callback_defined(hpack_quality_callback);
+                halfword rule = null;
+                if (callback_id > 0) {
+                    run_callback(callback_id, "SdNdd->N","overfull",overshoot,r,abs(pack_begin_line),line,&rule);
+                } else if (overfull_rule_par > 0) {
+                    rule = new_rule(normal_rule);
+                    rule_dir(rule) = box_dir(r);
+                    width(rule) = overfull_rule_par;
+                }
+                if (rule != null) {
+                    while (vlink(q) != null) {
+                        q = vlink(q);
+                    }
+                    couple_nodes(q,rule);
+                }
+                if (callback_id == 0) {
+                    print_ln();
+                    tprint_nl("Overfull \\hbox (");
+                    print_scaled(overshoot);
+                    tprint("pt too wide");
+                    goto COMMON_ENDING;
+                }
+            }
+        } else if (o == normal) {
+            if (list_ptr(r) != null) {
+                /*
+                    Report a tight hbox and |goto common_ending|, if this box is
+                    sufficiently bad.
+                */
+                last_badness = badness(-x, total_shrink[normal]);
+                if (last_badness > hbadness_par) {
+                    int callback_id = callback_defined(hpack_quality_callback);
+                    if (callback_id > 0) {
+                        halfword rule = null;
+                        run_callback(callback_id, "SdNdd->N","tight",last_badness,r,abs(pack_begin_line),line,&rule);
+                        if (rule != null) {
+                            while (vlink(q) != null) {
+                                q = vlink(q);
+                            }
+                            couple_nodes(q,rule);
+                        }
+                    } else {
+                        print_ln();
+                        tprint_nl("Tight \\hbox (badness ");
+                        print_int(last_badness);
+                        goto COMMON_ENDING;
+                    }
+                }
+            }
+        }
+        goto EXIT;
+    }
+
+  COMMON_ENDING:
+    /*
+        Finish issuing a diagnostic message for an overfull or underfull
+        hbox.
+    */
+    if (output_active) {
+        tprint(") has occurred while \\output is active");
+    } else {
+        if (pack_begin_line != 0) {
+            if (pack_begin_line > 0) {
+                tprint(") in paragraph at lines ");
+            } else {
+                tprint(") in alignment at lines ");
+            }
+            print_int(abs(pack_begin_line));
+            tprint("--");
+        } else {
+            tprint(") detected at line ");
+        }
+        print_int(line);
+    }
+
+    print_ln();
+    font_in_short_display = null_font;
+    short_display(list_ptr(r));
+    print_ln();
+    begin_diagnostic();
+    show_box(r);
+    end_diagnostic(true);
+  EXIT:
+    if ((m == cal_expand_ratio) && (font_expand_ratio != 0)) {
+        font_expand_ratio = fix_int(font_expand_ratio, -1000, 1000);
+        q = list_ptr(r);
+        list_ptr(r) = null;
+        flush_node(r);
+        /* this nested call uses the more or less global font_expand_ratio */
+        r = hpack(q, w, subst_ex_font, hpack_dir);
+    }
+    while (dir_ptr1 != null)
+        pop_dir_node(dir_ptr1);
+    /* here we reset the font_expan_ratio */
+    font_expand_ratio = 0;
+    return r;
+}
+
+@ @c
+halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int pac, int just_pack, halfword attr)
+{
+    halfword q;
+    if (just_pack) {
+        q = vlink(p);
+    } else if (type(p) == temp_node && vlink(p) == null) {
+        q = vlink(p);
+        /*
+            q = new_node(hlist_node, min_quarterword);
+            box_dir(q) = (pac == -1) ? text_direction_par : pac;
+            width(q) = w;
+            return q;
+        */
+    } else {
+        new_hyphenation(p, qt);
+        (void) new_ligkern(p, qt);  /* we don't care about the tail in this case */
+        q = vlink(p);
+        /* maybe here: alink(p) = null */
+        q = lua_hpack_filter(q, w, m, grp, pac, attr); /* ignores empty anyway */ /* maybe also pass tail */
+    }
+    return hpack(q, w, m, pac);
+}
+
+@ here is a function to calculate the natural whd of a (horizontal) node list
+
+@c
+scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult,
+                         int g_sign, int g_order, int pack_direction)
+{
+    scaled s;      /* shift amount */
+    halfword g;    /* points to a glue specification */
+    int hpack_dir;
+    scaled_whd xx; /* for recursion */
+    scaled_whd whd, siz = { 0, 0, 0 };
+    scaled gp = 0;
+    scaled gm = 0;
+    if (pack_direction == -1) {
+        hpack_dir = text_direction_par;
+    } else {
+        hpack_dir = pack_direction;
+    }
+    while (p != pp && p != null) {
+        while (is_char_node(p) && p != pp) {
+            whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
+            siz.wd += whd.wd;
+            if (whd.ht > siz.ht)
+                siz.ht = whd.ht;
+            if (whd.dp > siz.dp)
+                siz.dp = whd.dp;
+            p = vlink(p);
+        }
+        if (p != pp && p != null) {
+            switch (type(p)) {
+                case hlist_node:
+                case vlist_node:
+                    s = shift_amount(p);
+                    whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
+                    siz.wd += whd.wd;
+                    if (whd.ht - s > siz.ht)
+                        siz.ht = whd.ht - s;
+                    if (whd.dp + s > siz.dp)
+                        siz.dp = whd.dp + s;
+                    break;
+                /*
+                case rule_node:
+                case unset_node:
+                    siz.wd += width(p);
+                    if (type(p) >= rule_node) // always true
+                        s = 0;
+                    else
+                        s = shift_amount(p);
+                    if (height(p) - s > siz.ht)
+                        siz.ht = height(p) - s;
+                    if (depth(p) + s > siz.dp)
+                        siz.dp = depth(p) + s;
+                    break;
+                */
+                case rule_node:
+                case unset_node:
+                    siz.wd += width(p);
+                    if (height(p) > siz.ht)
+                        siz.ht = height(p);
+                    if (depth(p) > siz.dp)
+                        siz.dp = depth(p);
+                    break;
+                /* */
+                case math_node:
+                    /* begin mathskip code */
+                    if (glue_is_zero(p) || ignore_math_skip(p)) {
+                        siz.wd += surround(p);
+                        break;
+                    } else {
+                        /* fall through */
+                    }
+                    /* end mathskip code */
+                case glue_node:
+                    siz.wd += width(p);
+                    if (g_sign != normal) {
+                        if (g_sign == stretching) {
+                            if (stretch_order(p) == g_order) {
+                                /*
+                                    siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p)));
+                                */
+                                gp += stretch(p);
+                            }
+                        } else if (shrink_order(p) == g_order) {
+                            /*
+                                siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));
+                            */
+                            gm += shrink(p);
+                        }
+                    }
+                    if (subtype(p) >= a_leaders) {
+                        g = leader_ptr(p);
+                        if (height(g) > siz.ht)
+                            siz.ht = height(g);
+                        if (depth(g) > siz.dp)
+                            siz.dp = depth(g);
+                    }
+                    break;
+                case margin_kern_node:
+                    siz.wd += width(p);
+                    break;
+                case kern_node:
+                    siz.wd += width(p) + ex_kern(p);
+                    break;
+                case disc_node:
+                    xx = natural_sizes(no_break(p), null, g_mult, g_sign, g_order, hpack_dir);
+                    siz.wd += xx.wd;
+                    if (xx.ht > siz.ht)
+                        siz.ht = xx.ht;
+                    if (xx.dp > siz.dp)
+                        siz.dp = xx.dp;
+                    break;
+                default:
+                    break;
+            }
+            p = vlink(p);
+        }
+
+    }
+    if (g_sign != normal) {
+        if (g_sign == stretching) {
+            siz.wd += float_round(float_cast(g_mult) * float_cast(gp));
+        } else {
+            siz.wd -= float_round(float_cast(g_mult) * float_cast(gm));
+        }
+    }
+    return siz;
+}
+
+@ In order to provide a decent indication of where an overfull or underfull box
+originated, we use a global variable |pack_begin_line| that is set nonzero only
+when |hpack| is being called by the paragraph builder or the alignment finishing
+routine.
+
+@ The source file line where the current paragraph or alignment began; a negative
+value denotes alignment:
+
+@c
+int pack_begin_line;
+
+@ The |vpack| subroutine is actually a special case of a slightly more general
+routine called |vpackage|, which has four parameters. The fourth parameter, which
+is |max_dimen| in the case of |vpack|, specifies the maximum depth of the page
+box that is constructed. The depth is first computed by the normal rules; if it
+exceeds this limit, the reference point is simply moved down until the limiting
+depth is attained.
+
+@c
+halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
+{
+    halfword r;                 /* the box node that will be returned */
+    scaled w = 0;               /* width */
+    scaled d = 0;               /* depth */
+    scaled x = 0;               /* natural height */
+    scaled_whd whd;
+    scaled s;                   /* shift amount */
+    int o;                      /* order of infinity */
+    last_badness = 0;
+    r = new_node(vlist_node, 0);
+    if (pack_direction == -1) {
+        box_dir(r) = body_direction_par;
+    } else {
+        box_dir(r) = pack_direction;
+    }
+    subtype(r) = min_quarterword;
+    shift_amount(r) = 0;
+    list_ptr(r) = p;
+    total_stretch[normal] = 0;
+    total_shrink[normal] = 0;
+    total_stretch[sfi] = 0;
+    total_shrink[sfi] = 0;
+    total_stretch[fil] = 0;
+    total_shrink[fil] = 0;
+    total_stretch[fill] = 0;
+    total_shrink[fill] = 0;
+    total_stretch[filll] = 0;
+    total_shrink[filll] = 0;
+
+    while (p != null) {
+        /*
+            Examine node |p| in the vlist, taking account of its effect
+            on the dimensions of the new box; then advance |p| to the next
+            node.
+        */
+        if (is_char_node(p)) {
+            confusion("vpack");
+        } else {
+            switch (type(p)) {
+            case hlist_node:
+            case vlist_node:
+                /*
+                    Incorporate box dimensions into the dimensions of
+                    the vbox that will contain it.
+                */
+                s = shift_amount(p);
+                whd = pack_width_height_depth(box_dir(r), box_dir(p), p, false);
+                if (whd.wd + s > w)
+                    w = whd.wd + s;
+                x += d + whd.ht;
+                d = whd.dp;
+                break;
+            /*
+            case rule_node:
+            case unset_node:
+                x += d + height(p);
+                d = depth(p);
+                if (type(p) >= rule_node) // always
+                    s = 0;
+                else
+                    s = shift_amount(p);
+                if (width(p) + s > w)
+                    w = width(p) + s;
+                break;
+            */
+            case rule_node:
+            case unset_node:
+                x += d + height(p);
+                d = depth(p);
+                if (width(p) > w)
+                    w = width(p);
+                break;
+            /* */
+            case glue_node:
+                /* Incorporate glue into the vertical totals */
+                x += d;
+                d = 0;
+                x += width(p);
+                o = stretch_order(p);
+                total_stretch[o] = total_stretch[o] + stretch(p);
+                o = shrink_order(p);
+                total_shrink[o] = total_shrink[o] + shrink(p);
+                if (subtype(p) >= a_leaders) {
+                    halfword g = leader_ptr(p);
+                    if (width(g) > w)
+                        w = width(g);
+                }
+                break;
+            case kern_node:
+                x += d + width(p);
+                d = 0;
+                break;
+            default:
+                break;
+            }
+        }
+        p = vlink(p);
+    }
+    width(r) = w;
+    if (d > l) {
+        x += d - l;
+        depth(r) = l;
+    } else {
+        depth(r) = d;
+    }
+    /*
+        Determine the value of |height(r)| and the appropriate glue setting;
+        then |return| or |goto common_ending|.
+
+        When we get to the present part of the program, |x| is the natural
+        height of the box being packaged.
+    */
+    if (m == additional)
+        h = x + h;
+    height(r) = h;
+    x = h - x;
+    /* now |x| is the excess to be made up */
+    if (x == 0) {
+        glue_sign(r) = normal;
+        glue_order(r) = normal;
+        set_glue_ratio_zero(glue_set(r));
+        return r;
+    } else if (x > 0) {
+        /*
+            Determine vertical glue stretch setting, then |return|
+            or \hbox{|goto common_ending|}.
+        */
+        if (total_stretch[filll] != 0)
+            o = filll;
+        else if (total_stretch[fill] != 0)
+            o = fill;
+        else if (total_stretch[fil] != 0)
+            o = fil;
+        else if (total_stretch[sfi] != 0)
+            o = sfi;
+        else
+            o = normal;
+
+        glue_order(r) = (quarterword) o;
+        glue_sign(r) = stretching;
+        if (total_stretch[o] != 0) {
+            glue_set(r) = unfloat((double) x / total_stretch[o]);
+        } else {
+            glue_sign(r) = normal;
+            set_glue_ratio_zero(glue_set(r));   /* there's nothing to stretch */
+        }
+        if (o == normal) {
+            if (list_ptr(r) != null) {
+                /*
+                    Report an underfull vbox and |goto common_ending|, if this box
+                    is sufficiently bad.
+                */
+                last_badness = badness(x, total_stretch[normal]);
+                if (last_badness > vbadness_par) {
+                    int callback_id = callback_defined(vpack_quality_callback);
+                    if (callback_id > 0) {
+                        if (last_badness > 100) {
+                            run_callback(callback_id, "SdNdd->","underfull",last_badness,r,abs(pack_begin_line),line);
+                        } else {
+                            run_callback(callback_id, "SdNdd->","loose",last_badness,r,abs(pack_begin_line),line);
+                        }
+                        goto EXIT;
+                    } else {
+                        print_ln();
+                        if (last_badness > 100) {
+                            tprint_nl("Underfull \\vbox (badness ");
+                        } else {
+                            tprint_nl("Loose \\vbox (badness ");
+                        }
+                        print_int(last_badness);
+                        goto COMMON_ENDING;
+                    }
+                }
+            }
+        }
+        return r;
+
+    } else {
+        /*
+            Determine vertical glue shrink setting, then |return|
+            or \hbox{|goto common_ending|}.
+        */
+        if (total_shrink[filll] != 0)
+            o = filll;
+        else if (total_shrink[fill] != 0)
+            o = fill;
+        else if (total_shrink[fil] != 0)
+            o = fil;
+        else if (total_shrink[sfi] != 0)
+            o = sfi;
+        else
+            o = normal;
+
+        glue_order(r) = (quarterword) o;
+        glue_sign(r) = shrinking;
+        if (total_shrink[o] != 0) {
+            glue_set(r) = unfloat((double) (-x) / total_shrink[o]);
+        } else {
+            /* there's nothing to shrink */
+            glue_sign(r) = normal;
+            set_glue_ratio_zero(glue_set(r));
+        }
+        if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
+            int overshoot = -x - total_shrink[normal];
+            last_badness = 1000000;
+            /* use the maximum shrinkage */
+            set_glue_ratio_one(glue_set(r));
+            /*
+                Report an overfull vbox and |goto common_ending|, if this box
+                is sufficiently bad.
+            */
+            if ((overshoot > vfuzz_par) || (vbadness_par < 100)) {
+                int callback_id = callback_defined(vpack_quality_callback);
+                if (callback_id > 0) {
+                    run_callback(callback_id, "SdNdd->","overfull",overshoot,r,abs(pack_begin_line),line);
+                    goto EXIT;
+                } else {
+                    print_ln();
+                    tprint_nl("Overfull \\vbox (");
+                    print_scaled(-x - total_shrink[normal]);
+                    tprint("pt too high");
+                    goto COMMON_ENDING;
+                }
+            }
+        } else if (o == normal) {
+            if (list_ptr(r) != null) {
+                /*
+                    Report a tight vbox and |goto common_ending|, if this box is
+                    sufficiently bad.
+                */
+                last_badness = badness(-x, total_shrink[normal]);
+                if (last_badness > vbadness_par) {
+                    int callback_id = callback_defined(vpack_quality_callback);
+                    if (callback_id > 0) {
+                        run_callback(callback_id, "SdNdd->","tight",last_badness,r,abs(pack_begin_line),line);
+                        goto EXIT;
+                    } else {
+                        print_ln();
+                        tprint_nl("Tight \\vbox (badness ");
+                        print_int(last_badness);
+                        goto COMMON_ENDING;
+                    }
+                }
+            }
+        }
+        return r;
+    }
+
+  COMMON_ENDING:
+    /* Finish issuing a diagnostic message or an overfull or underfull vbox */
+    if (output_active) {
+        tprint(") has occurred while \\output is active");
+    } else {
+        if (pack_begin_line != 0) {
+            /* it's actually negative */
+            tprint(") in alignment at lines ");
+            print_int(abs(pack_begin_line));
+            tprint("--");
+        } else {
+            tprint(") detected at line ");
+        }
+        print_int(line);
+        print_ln();
+    }
+    begin_diagnostic();
+    show_box(r);
+    end_diagnostic(true);
+  EXIT:
+    return r;
+}
+
+@ @c
+halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, int pack_direction, int just_pack, halfword attr)
+{
+    halfword q = p;
+    if (!just_pack)
+ /* if (q != null) */
+        q = lua_vpack_filter(q, h, m, l, grp, pack_direction, attr);
+    return vpackage(q, h, m, l, pack_direction);
+}
+
+@ @c
+void finish_vcenter(void)
+{
+    halfword p;
+    unsave();
+    save_ptr--;
+    p = vpack(vlink(cur_list.head_field), saved_value(0), saved_level(0), -1);
+    pop_nest();
+    p = math_vcenter_group(p);
+    tail_append(p);
+}
+
+@ @c
+void package(int c)
+{
+    halfword saved0, saved2, saved3, saved4;
+    int grp = cur_group;
+    scaled d = box_max_depth_par; /* max depth */
+    unsave();
+    save_ptr -= 5;
+    saved0 = saved_value(0);
+    saved2 = saved_value(2);
+    saved3 = saved_value(3);
+    saved4 = saved_value(4);
+    if (cur_list.mode_field == -hmode) {
+        cur_box = filtered_hpack(cur_list.head_field, cur_list.tail_field,
+            saved_value(1), saved_level(1), grp, saved_level(2), saved4, saved3);
+        subtype(cur_box) = hbox_list;
+    } else {
+        cur_box = filtered_vpackage(vlink(cur_list.head_field),
+            saved_value(1), saved_level(1), d, grp, saved_level(2), saved4, saved3);
+        if (c == vtop_code) {
+            /*
+                Read just the height and depth of |cur_box|, for \.{\\vtop}. The
+                height of a `\.{\\vtop}' box is inherited from the first item on
+                its list, if that item is an |hlist_node|, |vlist_node|, or
+                |rule_node|; otherwise the \.{\\vtop} height is zero.
+            */
+            scaled h = 0;
+            halfword p = list_ptr(cur_box);
+            if ((p != null) && (type(p) <= rule_node)) {
+                /* hlist, vlist, rule */
+                h = height(p);
+            }
+            depth(cur_box) = depth(cur_box) - h + height(cur_box);
+            height(cur_box) = h;
+        }
+    }
+    if (saved2 != null) {
+        /* DIR: Adjust back |text_dir_ptr| for |scan_spec| */
+        flush_node_list(text_dir_ptr);
+        text_dir_ptr = saved2;
+
+    }
+    replace_attribute_list(cur_box, saved3);
+    pop_nest();
+    box_end(saved0);
+}
+
+@ When a box is being appended to the current vertical list, the baselineskip
+calculation is handled by the |append_to_vlist| routine.
+
+@c
+void append_to_vlist(halfword b, int location)
+{
+    scaled d;   /* deficiency of space between baselines */
+    halfword p; /* a new glue node */
+    boolean mirrored = (type(b) == hlist_node) && is_mirrored(box_dir(b)) ;
+    halfword result = null;
+    halfword next_depth = ignore_depth;
+    boolean prev_set = false ;
+    if (lua_appendtovlist_callback(b,location,prev_depth_par,mirrored,&result,&next_depth,&prev_set)) {
+        while (result != null) {
+            couple_nodes(cur_list.tail_field, result);
+            cur_list.tail_field = result;
+            result = vlink(result);
+        }
+        if (prev_set) {
+            prev_depth_par = next_depth;
+        }
+    } else {
+        if (prev_depth_par > ignore_depth) {
+            if (mirrored) {
+                d = width(baseline_skip_par) - prev_depth_par - depth(b);
+            } else {
+                d = width(baseline_skip_par) - prev_depth_par - height(b);
+            }
+            if (d < line_skip_limit_par) {
+                p = new_param_glue(line_skip_code);
+            } else {
+                p = new_skip_param(baseline_skip_code);
+                width(p) = d;
+            }
+            couple_nodes(cur_list.tail_field, p);
+            cur_list.tail_field = p;
+        }
+        couple_nodes(cur_list.tail_field, b);
+        cur_list.tail_field = b;
+        if (mirrored) {
+            prev_depth_par = height(b);
+        } else {
+            prev_depth_par = depth(b);
+        }
+    }
+}
+
+@ When |saving_vdiscards| is positive then the glue, kern, and penalty nodes
+removed by the page builder or by \.{\\vsplit} from the top of a vertical list
+are saved in special lists instead of being discarded.
+
+@c
+#define tail_page_disc disc_ptr[copy_code]  /* last item removed by page builder */
+#define page_disc disc_ptr[last_box_code]   /* first item removed by page builder */
+#define split_disc disc_ptr[vsplit_code]    /* first item removed by \.{\\vsplit} */
+
+halfword disc_ptr[(vsplit_code + 1)];       /* list pointers */
+
+@ The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation, is
+considerably simpler than |line_break| because it doesn't have to worry about
+hyphenation, and because its mission is to discover a single break instead of an
+optimum sequence of breakpoints. But before we get into the details of |vsplit|,
+we need to consider a few more basic things.
+
+A subroutine called |prune_page_top| takes a pointer to a vlist and returns a
+pointer to a modified vlist in which all glue, kern, and penalty nodes have been
+deleted before the first box or rule node. However, the first box or rule is
+actually preceded by a newly created glue node designed so that the topmost
+baseline will be at distance |split_top_skip| from the top, whenever this is
+possible without backspacing.
+
+When the second argument |s| is |false| the deleted nodes are destroyed,
+otherwise they are collected in a list starting at |split_disc|.
+
+@c
+halfword prune_page_top(halfword p, boolean s)
+{
+    halfword q;
+    halfword prev_p = temp_head; /* lags one step behind |p| */
+    halfword r = null;
+    vlink(temp_head) = p;
+    while (p != null) {
+        switch (type(p)) {
+        case hlist_node:
+        case vlist_node:
+        case rule_node:
+            /* Insert glue for |split_top_skip| and set~|p:=null| */
+            q = new_skip_param(split_top_skip_code);
+            vlink(prev_p) = q;
+            vlink(q) = p;
+            if (width(q) > height(p))
+                width(q) = width(q) - height(p);
+            else
+                width(q) = 0;
+            p = null;
+            break;
+        case boundary_node:
+        case whatsit_node:
+        case mark_node:
+        case ins_node:
+            prev_p = p;
+            p = vlink(prev_p);
+            break;
+        case glue_node:
+        case kern_node:
+        case penalty_node:
+            q = p;
+            p = vlink(q);
+            vlink(q) = null;
+            vlink(prev_p) = p;
+            if (s) {
+                if (split_disc == null)
+                    split_disc = q;
+                else
+                    vlink(r) = q;
+                r = q;
+            } else {
+                flush_node_list(q);
+            }
+            break;
+        default:
+            confusion("pruning");
+            break;
+        }
+    }
+    return vlink(temp_head);
+}
+
+@ The next subroutine finds the best place to break a given vertical list so as
+to obtain a box of height~|h|, with maximum depth~|d|. A pointer to the beginning
+of the vertical list is given, and a pointer to the optimum breakpoint is
+returned. The list is effectively followed by a forced break, i.e., a penalty
+node with the |eject_penalty|; if the best break occurs at this artificial node,
+the value |null| is returned.
+
+@c
+scaled active_height[10]; /* distance from first active node to~|cur_p| */
+
+@ An array of six |scaled| distances is used to keep track of the height from the
+beginning of the list to the current place, just as in |line_break|. In fact, we
+use one of the same arrays, only changing its name to reflect its new
+significance.
+
+@c
+#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
+#define set_height_zero(A) active_height[A]=0   /* initialize the height to zero */
+
+@ A global variable |best_height_plus_depth| will be set to the natural size of
+the box that corresponds to the optimum breakpoint found by |vert_break|. (This
+value is used by the insertion-splitting algorithm of the page builder.)
+
+@ height of the best box, without stretching or shrinking
+
+@c
+scaled best_height_plus_depth;
+
+/* finds optimum page break */
+
+halfword vert_break(halfword p, scaled h, scaled d)
+{
+    halfword prev_p = p;        /* if |p| is a glue node, |type(prev_p)| determines whether |p| is a
+                                   legal breakpoint, an initial glue node is not a legal breakpoint */
+    int pi = 0;                 /* penalty value */
+    int b;                      /* badness at a trial breakpoint */
+    int t;                      /* |type| of the node following a kern */
+    int least_cost;             /* the smallest badness plus penalties found so far */
+    halfword best_place = null; /* the most recent break that leads to |least_cost| */
+    scaled prev_dp = 0;         /* depth of previous box in the list */
+    least_cost = awful_bad;
+    do_all_six(set_height_zero);
+    while (1) {
+        /* If node |p| is a legal breakpoint, check if this break is
+           the best known, and |goto done| if |p| is null or
+           if the page-so-far is already too full to accept more stuff */
+        /* A subtle point to be noted here is that the maximum depth~|d| might be
+           negative, so |cur_height| and |prev_dp| might need to be corrected even
+           after a glue or kern node. */
+
+        if (p == null) {
+            pi = eject_penalty;
+        } else {
+            /* Use node |p| to update the current height and depth measurements;
+               if this node is not a legal breakpoint, |goto not_found|
+               or |update_heights|,
+               otherwise set |pi| to the associated penalty at the break */
+            switch (type(p)) {
+            case hlist_node:
+            case vlist_node:
+            case rule_node:
+                cur_height = cur_height + prev_dp + height(p);
+                prev_dp = depth(p);
+                goto NOT_FOUND;
+                break;
+            case boundary_node:
+            case whatsit_node:
+                goto NOT_FOUND;
+                break;
+            case glue_node:
+                if (precedes_break(prev_p))
+                    pi = 0;
+                else
+                    goto UPDATE_HEIGHTS;
+                break;
+            case kern_node:
+                if (vlink(p) == null)
+                    t = penalty_node;
+                else
+                    t = type(vlink(p));
+                if (t == glue_node)
+                    pi = 0;
+                else
+                    goto UPDATE_HEIGHTS;
+                break;
+            case penalty_node:
+                pi = penalty(p);
+                break;
+            case mark_node:
+            case ins_node:
+                goto NOT_FOUND;
+                break;
+            default:
+                confusion("vertbreak");
+                break;
+            }
+        }
+        /* Check if node |p| is a new champion breakpoint; then |goto done|
+           if |p| is a forced break or if the page-so-far is already too full */
+        if (pi < inf_penalty) {
+            /* Compute the badness, |b|, using |awful_bad| if the box is too full */
+            if (cur_height < h) {
+                if ((active_height[3] != 0) || (active_height[4] != 0) ||
+                    (active_height[5] != 0) || (active_height[6] != 0))
+                    b = 0;
+                else
+                    b = badness(h - cur_height, active_height[2]);
+            } else if (cur_height - h > active_height[7]) {
+                b = awful_bad;
+            } else {
+                b = badness(cur_height - h, active_height[7]);
+            }
+
+            if (b < awful_bad) {
+                if (pi <= eject_penalty)
+                    b = pi;
+                else if (b < inf_bad)
+                    b = b + pi;
+                else
+                    b = deplorable;
+            }
+            if (b <= least_cost) {
+                best_place = p;
+                least_cost = b;
+                best_height_plus_depth = cur_height + prev_dp;
+            }
+            if ((b == awful_bad) || (pi <= eject_penalty))
+                goto DONE;
+        }
+
+        if ((type(p) < glue_node) || (type(p) > kern_node))
+            goto NOT_FOUND;
+      UPDATE_HEIGHTS:
+        /* Update the current height and depth measurements with
+           respect to a glue or kern node~|p| */
+        /* Vertical lists that are subject to the |vert_break| procedure should not
+           contain infinite shrinkability, since that would permit any amount of
+           information to ``fit'' on one page. */
+
+        if (type(p) != kern_node) {
+            active_height[2 + stretch_order(p)] += stretch(p);
+            active_height[7] += shrink(p);
+            if ((shrink_order(p) != normal) && (shrink(p) != 0)) {
+                print_err("Infinite glue shrinkage found in box being split");
+                help4("The box you are \\vsplitting contains some infinitely",
+                      "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
+                      "Such glue doesn't belong there; but you can safely proceed,",
+                      "since the offensive shrinkability has been made finite.");
+                error();
+                shrink_order(p) = normal;
+            }
+        }
+        cur_height = cur_height + prev_dp + width(p);
+        prev_dp = 0;
+      NOT_FOUND:
+        if (prev_dp > d) {
+            cur_height = cur_height + prev_dp - d;
+            prev_dp = d;
+        }
+        prev_p = p;
+        p = vlink(prev_p);
+    }
+  DONE:
+    return best_place;
+}
+
+@ Now we are ready to consider |vsplit| itself. Most of its work is accomplished
+by the two subroutines that we have just considered.
+
+Given the number of a vlist box |n|, and given a desired page height |h|, the
+|vsplit| function finds the best initial segment of the vlist and returns a box
+for a page of height~|h|. The remainder of the vlist, if any, replaces the
+original box, after removing glue and penalties and adjusting for
+|split_top_skip|. Mark nodes in the split-off box are used to set the values of
+|split_first_mark| and |split_bot_mark|; we use the fact that
+|split_first_mark(x)=null| if and only if |split_bot_mark(x)=null|.
+
+The original box becomes ``void'' if and only if it has been entirely extracted.
+The extracted box is ``void'' if and only if the original box was void (or if it
+was, erroneously, an hlist box).
+
+@c
+/* extracts a page of height |h| from box |n| */
+
+halfword vsplit(halfword n, scaled h, int m)
+{
+    halfword v;  /* the box to be split */
+    int vdir;    /* the direction of the box to be split */
+    halfword p;  /* runs through the vlist */
+    halfword q;  /* points to where the break occurs */
+    halfword i;  /* for traversing marks lists */
+    v = box(n);
+    vdir = box_dir(v);
+    flush_node_list(split_disc);
+    split_disc = null;
+    for (i = 0; i <= biggest_used_mark; i++) {
+        delete_split_first_mark(i);
+        delete_split_bot_mark(i);
+    }
+    /* Dispense with trivial cases of void or bad boxes */
+    if (v == null) {
+        return null;
+    }
+    if (type(v) != vlist_node) {
+        print_err("\\vsplit needs a \\vbox");
+        help2("The box you are trying to split is an \\hbox.",
+              "i can't split such a box, so I''ll leave it alone.");
+        error();
+        return null;
+    }
+    q = vert_break(list_ptr(v), h, split_max_depth_par);
+    /*
+        Look at all the marks in nodes before the break, and set the final
+        link to |null| at the break. It's possible that the box begins with
+        a penalty node that is the ``best'' break, so we must be careful to
+        handle this special case correctly.
+    */
+    p = list_ptr(v);
+    if (p == q) {
+        list_ptr(v) = null;
+    } else {
+        while (1) {
+            if (type(p) == mark_node) {
+                if (split_first_mark(mark_class(p)) == null) {
+                    set_split_first_mark(mark_class(p), mark_ptr(p));
+                    set_split_bot_mark(mark_class(p), split_first_mark(mark_class(p)));
+                    set_token_ref_count(split_first_mark(mark_class(p)),
+                        token_ref_count(split_first_mark(mark_class(p))) + 2);
+                } else {
+                    delete_token_ref(split_bot_mark(mark_class(p)));
+                    set_split_bot_mark(mark_class(p), mark_ptr(p));
+                    add_token_ref(split_bot_mark(mark_class(p)));
+                }
+            }
+            if (vlink(p) == q) {
+                vlink(p) = null;
+                break;
+            }
+            p = vlink(p);
+        }
+    }
+    q = prune_page_top(q, saving_vdiscards_par > 0);
+    p = list_ptr(v);
+    list_ptr(v) = null;
+    flush_node(v);
+    if (q == null) {
+        /* the |eq_level| of the box stays the same */
+        box(n) = null;
+    } else {
+        box(n) = filtered_vpackage(q, 0, additional, max_depth_par, split_keep_group, vdir, 0, 0);
+    }
+    if (m == exactly) {
+        return filtered_vpackage(p, h, exactly, split_max_depth_par, split_off_group, vdir, 0, 0);
+    } else {
+        return filtered_vpackage(p, 0, additional, max_depth_par, split_off_group, vdir, 0, 0);
+    }
+}
+
+@ Now that we can see what eventually happens to boxes, we can consider the first
+steps in their creation. The |begin_box| routine is called when |box_context| is
+a context specification, |cur_chr| specifies the type of box desired, and
+|cur_cmd=make_box|.
+
+@c
+void begin_box(int box_context)
+{
+    halfword q; /* run through the current list */
+    halfword k; /* 0 or |vmode| or |hmode| */
+    int n;      /* a box number */
+    int spec_direction = -1;
+    int just_pack = 0;
+    int split_mode = exactly ;
+    switch (cur_chr) {
+        case box_code:
+            scan_register_num();
+            cur_box = box(cur_val);
+            /* the box becomes void, at the same level */
+            box(cur_val) = null;
+            break;
+        case copy_code:
+            scan_register_num();
+            cur_box = copy_node_list(box(cur_val));
+            break;
+        case last_box_code:
+            /*
+                If the current list ends with a box node, delete it from
+                the list and make |cur_box| point to it; otherwise set
+                |cur_box:=null|.
+            */
+            cur_box = null;
+            if (abs(cur_list.mode_field) == mmode) {
+                you_cant();
+                help1("Sorry; this \\lastbox will be void.");
+                error();
+            } else if ((cur_list.mode_field == vmode) && (cur_list.head_field == cur_list.tail_field)) {
+                you_cant();
+                help2("Sorry...I usually can't take things from the current page.",
+                      "This \\lastbox will therefore be void.");
+                error();
+            } else {
+                if (cur_list.head_field != cur_list.tail_field) {
+                    /* todo: new code,  needs testing */
+
+                    /* maybe: ((type(cur_list.tail_field) == hlist_node) < rule_node) */
+
+                    if ((type(cur_list.tail_field) == hlist_node) || (type(cur_list.tail_field) == vlist_node)) {
+                        /* Remove the last box ... */
+                        q = alink(cur_list.tail_field);
+                        if (q == null || vlink(q) != cur_list.tail_field) {
+                            q = cur_list.head_field;
+                            while (vlink(q) != cur_list.tail_field)
+                                q = vlink(q);
+                        }
+                        uncouple_node(cur_list.tail_field);
+                        cur_box = cur_list.tail_field;
+                        shift_amount(cur_box) = 0;
+                        cur_list.tail_field = q;
+                        vlink(cur_list.tail_field) = null;
+                    }
+                }
+            }
+            break;
+        case vsplit_code:
+            /*
+                Split off part of a vertical box, make |cur_box| point to it. Here we
+                deal with things like `\.{\\vsplit 13 to 100pt}'.
+            */
+            scan_register_num();
+            n = cur_val;
+            if (scan_keyword("upto")) {
+                split_mode = additional ;
+            } else if (!scan_keyword("to")) {
+                print_err("Missing `to' inserted");
+                help2("I'm working on `\\vsplit<box number> to <dimen>';",
+                      "will look for the <dimen> next.");
+                error();
+            }
+            scan_normal_dimen();
+            cur_box = vsplit(n, cur_val, split_mode);
+         break;
+        default:
+            /*
+                Initiate the construction of an hbox or vbox, then |return|. Here is
+                where we enter restricted horizontal mode or internal vertical mode,
+                in order to make a box.
+            */
+            switch (cur_chr) {
+                case tpack_code:
+                    cur_chr = vtop_code;
+                    just_pack = 1;
+                    break;
+                case vpack_code:
+                    cur_chr = vtop_code + vmode;
+                    just_pack = 1;
+                    break;
+                case hpack_code:
+                    cur_chr = vtop_code + hmode;
+                    just_pack = 1;
+                    break;
+            }
+            /* */
+            k = cur_chr - vtop_code;
+            set_saved_record(0, saved_boxcontext, 0, box_context);
+            switch (abs(cur_list.mode_field)) {
+                case vmode:
+                    spec_direction = body_direction_par;
+                    break;
+                case hmode:
+                    spec_direction = text_direction_par;
+                    break;
+                case mmode:
+                    spec_direction = math_direction_par;
+                    break;
+            }
+            if (k == hmode) {
+                if ((box_context < box_flag) && (abs(cur_list.mode_field) == vmode))
+                    scan_full_spec(adjusted_hbox_group, spec_direction,just_pack);
+                else
+                    scan_full_spec(hbox_group, spec_direction,just_pack);
+            } else {
+                if (k == vmode) {
+                    scan_full_spec(vbox_group, spec_direction,just_pack);
+                } else {
+                    scan_full_spec(vtop_group, spec_direction,just_pack);
+                    k = vmode;
+                }
+                normal_paragraph();
+            }
+            push_nest();
+            cur_list.mode_field = -k;
+            if (k == vmode) {
+                prev_depth_par = ignore_depth;
+                if (every_vbox_par != null)
+                    begin_token_list(every_vbox_par, every_vbox_text);
+            } else {
+                space_factor_par = 1000;
+                if (every_hbox_par != null)
+                    begin_token_list(every_hbox_par, every_hbox_text);
+            }
+            return;
+            break;
+    }
+    /* in simple cases, we use the box immediately */
+    box_end(box_context);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/packaging.c
+++ /dev/null
@@ -1,2142 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    We're essentially done with the parts of \TeX\ that are concerned with the
-    input (|get_next|) and the output (|ship_out|). So it's time to get heavily
-    into the remaining part, which does the real work of typesetting.
-
-    After lists are constructed, \TeX\ wraps them up and puts them into boxes.
-    Two major subroutines are given the responsibility for this task: |hpack|
-    applies to horizontal lists (hlists) and |vpack| applies to vertical lists
-    (vlists). The main duty of |hpack| and |vpack| is to compute the dimensions
-    of the resulting boxes, and to adjust the glue if one of those dimensions is
-    pre-specified. The computed sizes normally enclose all of the material inside
-    the new box; but some items may stick out if negative glue is used, if the
-    box is overfull, or if a \.{\\vbox} includes other boxes that have been
-    shifted left.
-
-    The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node| for a
-    box containing the hlist that starts at |p|. Parameter |w| specifies a width;
-    and parameter |m| is either `|exactly|' or `|additional|'. Thus,
-    |hpack(p,w,exactly)| produces a box whose width is exactly |w|, while
-    |hpack(p,w,additional)| yields a box whose width is the natural width plus
-    |w|. It is convenient to define a macro called `|natural|' to cover the most
-    common case, so that we can say |hpack(p,natural)| to get a box that has the
-    natural width of list |p|.
-
-    Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a box
-    containing the vlist that starts at |p|. In this case |w| represents a height
-    instead of a width; the parameter |m| is interpreted as in |hpack|.
-
-    The parameters to |hpack| and |vpack| correspond to \TeX's primitives like
-    `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note that
-    `\.{\\hbox}' with no dimension following it is equivalent to `\.{\\hbox}
-    \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such constructions in
-    the user's input, including the mandatory left brace that follows them, and
-    it puts the specification onto |save_stack| so that the desired box can later
-    be obtained by executing the following code: $$\vbox{\halign{#\hfil\cr
-    |save_ptr:=save_ptr-1;|\cr |hpack(p,saved_value(0),saved_level(0)).|\cr}}$$
-
-*/
-
-/*tex Scan a box specification and left brace: */
-
-void scan_spec(group_code c)
-{
-    int spec_code;
-    boolean done = false ;
-    do {
-        get_x_token();
-    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
-    if (cur_cmd == left_brace_cmd) {
-        spec_code = additional;
-        cur_val = 0;
-        done = true;
-    } else {
-        /*tex todo: attr */
-        back_input();
-        if (scan_keyword("to")) {
-            spec_code = exactly;
-            scan_normal_dimen();
-        } else if (scan_keyword("spread")) {
-            spec_code = additional;
-            scan_normal_dimen();
-        } else {
-            spec_code = additional;
-            cur_val = 0;
-        }
-    }
-    set_saved_record(0, saved_boxspec, spec_code, cur_val);
-    save_ptr++;
-    new_save_level(c);
-    if (!done) {
-        scan_left_brace();
-    }
-}
-
-/*tex
-
-    When scanning, special care is necessary to ensure that the special
-    |save_stack| codes are placed just below the new group code, because scanning
-    can change |save_stack| when \.{\\csname} appears.
-
-    This coincides with the text on |dir| and |attr| keywords, as these are
-    exaclty the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the input
-    stream (the others are \.{\\vcenter}, \.{\\valign}, and \.{\\halign}).
-
-*/
-
-/*tex Scan a box specification and left brace: */
-
-#define first_char_is(a,A) (cur_cs == 0 && (cur_chr == a || cur_chr == A))
-
-void scan_full_spec(group_code c, int spec_direction, int just_pack)
-{
-    int s, i, v, spec_code;
-    boolean done = false ;
-    halfword attr_list;
-    boolean attr_done = false ;
-    boolean dir_done = false ;
-    if (attr_list_cache == cache_disabled)
-        update_attribute_cache();
-    attr_list = attr_list_cache;
-    /*tex The box context: */
-    s = saved_value(0);
-    do {
-        get_x_token();
-    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
-    if (cur_cmd == left_brace_cmd) {
-        goto QUICK;
-    } else {
-        back_input();
-        goto KEYWORDS;
-    }
-  CONTINUE:
-    while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
-        get_x_token();
-        if (cur_cmd == left_brace_cmd) {
-            goto QUICK;
-        } else if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd) {
-            back_input();
-            break;
-        }
-    }
-  KEYWORDS:
-    /*tex Multiple |attr| keys possible (before or after dir). */
-    if (scan_keyword("attr")) {
-        scan_register_num();
-        i = cur_val;
-        scan_optional_equals();
-        scan_int();
-        v = cur_val;
-        if (! attr_done) {
-            attr_list = copy_attribute_list(attr_list_cache);
-            attr_done = true;
-        }
-        attr_list = do_set_attribute(attr_list, i, v);
-        goto CONTINUE;
-    }
-    /*tex We only permit one |(b)dir| directive. */
-    if (! dir_done) {
-        if (scan_keyword("bdir")) {
-            scan_int();
-            check_dir_value(cur_val);
-            spec_direction = cur_val;
-            dir_done = true;
-            goto CONTINUE;
-        }
-        if (scan_keyword("dir")) {
-            scan_direction();
-            spec_direction = cur_val;
-            dir_done = true;
-            goto CONTINUE;
-        }
-    }
-    /*tex Only one |to| or |spread| key possible and it comes last. */
-    if (scan_keyword("to")) {
-        spec_code = exactly;
-    } else if (scan_keyword("spread")) {
-        spec_code = additional;
-    } else {
-        spec_code = additional;
-        cur_val = 0;
-        goto FOUND;
-    }
-    scan_normal_dimen();
-    goto FOUND;
-  QUICK:
-    spec_code = additional;
-    cur_val = 0;
-    done = true;
-  FOUND:
-    add_node_attr_ref(attr_list);
-    set_saved_record(0, saved_boxcontext, 0, s);
-    set_saved_record(1, saved_boxspec, spec_code, cur_val);
-    /*tex Adjust |text_dir_ptr| for |scan_spec|. */
-    if (spec_direction != -1) {
-        set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr);
-        text_dir_ptr = new_dir(spec_direction);
-    } else {
-        set_saved_record(2, saved_boxdir, spec_direction, null);
-    }
-    set_saved_record(3, saved_boxattr, 0, attr_list);
-    set_saved_record(4, saved_boxpack, 0, just_pack);
-    save_ptr += 5;
-    new_save_level(c);
-    if (! done) {
-        scan_left_brace();
-    }
-    /*tex No gain in |if (body_direction_par != spec_direction)| etc. */
-    eq_word_define(int_base + body_direction_code, spec_direction);
-    eq_word_define(int_base + par_direction_code, spec_direction);
-    eq_word_define(int_base + text_direction_code, spec_direction);
-}
-
-/*tex
-
-    To figure out the glue setting, |hpack| and |vpack| determine how much
-    stretchability and shrinkability are present, considering all four orders of
-    infinity. The highest order of infinity that has a nonzero coefficient is
-    then used as if no other orders were present.
-
-    For example, suppose that the given list contains six glue nodes with the
-    respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill. Then
-    the total is essentially 2fil; and if a total additional space of 6pt is to
-    be achieved by stretching, the actual amounts of stretch will be 0pt, 0pt,
-    15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be considered. (The
-    `fill' glue is therefore not really stretching infinitely with respect to
-    `fil'; nobody would actually want that to happen.)
-
-    The arrays |total_stretch| and |total_shrink| are used to determine how much
-    glue of each kind is present. A global variable |last_badness| is used to
-    implement \.{\\badness}.
-
-*/
-
-/*tex Glue found by |hpack| or |vpack|. */
-
-scaled total_stretch[5];
-scaled total_shrink[5];
-
-/*tex Badness of the most recently packaged box. */
-
-int last_badness;
-
-/*tex
-
-    If the global variable |adjust_tail| is non-null, the |hpack| routine also
-    removes all occurrences of |ins_node|, |mark_node|, and |adjust_node| items
-    and appends the resulting material onto the list that ends at location
-    |adjust_tail|.
-
-*/
-
-/*tex Tail of adjustment list. */
-
-halfword adjust_tail;
-
-/*tex
-
-    Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to
-    |pre_adjust_tail| instead of |adjust_tail|.
-
-*/
-
-halfword pre_adjust_tail;
-
-halfword last_leftmost_char;
-halfword last_rightmost_char;
-
-/*tex Pointers to the prev and next char of an implicit kern. */
-
-halfword next_char_p;
-halfword prev_char_p;
-
-/*tex This procedure is called repeatedly from inside the line break algorithm. */
-
-void set_prev_char_p(halfword p)
-{
-    prev_char_p = p;
-}
-
-/*tex
-
-    The kern stretch / shrink code was (or had become) rather weird ... the width
-    field is set, and then used in a second calculation, repeatedly, so why is
-    that ... maybe some some weird left-over ... anyway, the values are so small
-    that in practice they are not significant at all when the backend sees them
-    because a few hundred sp positive or negative are just noise there (so
-    adjustlevel 3 has hardly any consequence for the result but is more
-    efficient).
-
-*/
-
-scaled char_stretch(halfword p)
-{
-    internal_font_number f = font(p);
-    int m = font_max_stretch(f);
-    if (m > 0) {
-        int c = character(p);
-        int ef = get_ef_code(f, c);
-        if (ef > 0) {
-            scaled dw = calc_char_width(f, c, m) - char_width(f, c);
-            if (dw > 0) {
-                return round_xn_over_d(dw, ef, 1000);
-            }
-        }
-    }
-    return 0;
-}
-
-scaled char_shrink(halfword p)
-{
-    internal_font_number f = font(p);
-    int m = font_max_shrink(f);
-    if (m > 0) {
-        int c = character(p);
-        int ef = get_ef_code(f, c);
-        if (ef > 0) {
-            scaled dw = char_width(f, c) - calc_char_width(f, c, -m);
-            if (dw > 0) {
-                return round_xn_over_d(dw, ef, 1000);
-            }
-        }
-    }
-    return 0;
-}
-
-scaled kern_stretch(halfword p)
-{
-    int m;
-    scaled d, e, x;
-    scaled w = width(p) ;
-    halfword l;
-    halfword r;
-    if (w == 0)  {
-        /*tex Why bother about zero kerns. */
-        return 0;
-    }
-    l = prev_char_p ;
-    if ((l == null) || (vlink(l) != p)) {
-        /*tex We only care about kerns following a char. */
-        return 0;
-    }
-    r = vlink(p);
-    if (r == null) {
-        /*tex We only care about kerns between a char and something else. */
-    }
-    if (!(is_char_node(l) && is_char_node(r))) {
-        /*tex We want two chars (but but don't care about the fonts). */
-        return 0;
-    }
-    /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */
-    m = (font_max_stretch(font(l)) + font_max_stretch(font(r)))/2;
-    if (m == 0) {
-        /*tex nothing to kern */
-        return 0;
-    }
-    d = round_xn_over_d(w, 1000 + m, 1000);
-    /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */
-    e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ;
-    if (e == 1000) {
-        x = d - w;
-    } else {
-        x = round_xn_over_d(d - w, e, 1000);
-    }
-    return x;
-}
-
-scaled kern_shrink(halfword p)
-{
-    int m;
-    scaled d, e, x;
-    scaled w = width(p) ;
-    halfword l;
-    halfword r;
-    if (w == 0)  {
-        /*tex Why bother about zero kerns. */
-        return 0;
-    }
-    l = prev_char_p ;
-    if ((l == null) || (vlink(l) != p)) {
-        /*tex We only care about kerns following a char. */
-        return 0;
-    }
-    r = vlink(p);
-    if (r == null) {
-        /*tex We only care about kerns between a char and something else. */
-    }
-    if (!(is_char_node(l) && is_char_node(r))) {
-        /*tex We want two chars (but but don't care about the fonts). */
-        return 0;
-    }
-    /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */
-    m = (font_max_shrink(font(l)) + font_max_shrink(font(r)))/2;
-    if (m == 0) {
-        /*tex Nothing to kern. */
-        return 0;
-    }
-    d = round_xn_over_d(w, 1000 - m, 1000);
-    e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ;
-    if (e == 1000) {
-         x = w - d ;
-    } else {
-        x = round_xn_over_d(w - d, e, 1000);
-    }
-    return x;
-}
-
-void do_subst_font(halfword p, int ex_ratio)
-{
-    if (type(p) == disc_node) {
-        halfword r = vlink(pre_break(p));
-        while (r != null) {
-            if (is_char_node(r))
-                do_subst_font(r, ex_ratio);
-            r = vlink(r);
-        }
-        r = vlink(post_break(p));
-        while (r != null) {
-            if (is_char_node(r))
-                do_subst_font(r, ex_ratio);
-            r = vlink(r);
-        }
-        r = vlink(no_break(p));
-        while (r != null) {
-            if (is_char_node(r))
-                do_subst_font(r, ex_ratio);
-            r = vlink(r);
-        }
-        return;
-    }
-    if (! is_char_node(p)) {
-        normal_error("font expansion", "invalid node type");
-        return;
-    } else {
-        internal_font_number f = font(p);
-        int ef = get_ef_code(f, character(p));
-        if (ef == 0)
-            return;
-        if ((font_max_stretch(f) > 0) && (ex_ratio > 0)) {
-            int ex_stretch = ext_xn_over_d(ex_ratio * ef, font_max_stretch(f), 1000000);
-            ex_glyph(p) = fix_expand_value(f, ex_stretch)*1000;
-        } else if ((font_max_shrink(f) > 0) && (ex_ratio < 0)) {
-            int ex_shrink = ext_xn_over_d(ex_ratio * ef, font_max_shrink(f), 1000000);
-            ex_glyph(p) = fix_expand_value(f, ex_shrink)*1000;
-        }
-    }
-}
-
-scaled char_pw(halfword p, int side)
-{
-    internal_font_number f;
-    int c, w;
-    if (side == left_side)
-        last_leftmost_char = null;
-    else
-        last_rightmost_char = null;
-    if (p == null)
-        return 0;
-    if (!is_char_node(p))
-        return 0;
-    f = font(p);
-    if (side == left_side) {
-        c = get_lp_code(f, character(p));
-        last_leftmost_char = p;
-    } else {
-        c = get_rp_code(f, character(p));
-        last_rightmost_char = p;
-    }
-    if (c == 0)
-        return 0;
-    w = quad(f);
-    return round_xn_over_d(w, c, 1000);
-}
-
-halfword new_margin_kern(scaled w, halfword p, int side)
-{
-    halfword k, q;
-    k = new_node(margin_kern_node, side);
-    width(k) = w;
-    if (p == null)
-        normal_error("margin kerning", "invalid pointer to marginal char node");
-    q = new_char(font(p), character(p));
-    margin_char(k) = q;
-    return k;
-}
-
-/*tex
-
-    Here we prepare for |hpack|, which is place where we do font substituting
-    when font expansion is being used.
-
-*/
-
-/*tex The current expansion ratio, needed for recursive call. */
-
-int font_expand_ratio = 0;
-
-int ignore_math_skip(halfword p)
-{
-    if (math_skip_mode == 6) {
-        if (subtype(p) == after) {
-            if (math_skip_boundary(vlink(p))) {
-                return 0;
-            }
-        } else {
-            if (math_skip_boundary(alink(p))) {
-                return 0;
-            }
-        }
-    } else if (math_skip_mode == 7) {
-        if (subtype(p) == after) {
-            if (! math_skip_boundary(vlink(p))) {
-                return 0;
-            }
-        } else {
-            if (! math_skip_boundary(alink(p))) {
-                return 0;
-            }
-        }
-    } else {
-        return 0;
-    }
-    reset_glue_to_zero(p);
-    return 1;
-}
-
-halfword hpack(halfword p, scaled w, int m, int pack_direction)
-{
-    /*tex the box node that will be returned */
-    halfword r;
-    /*tex trails behind |p| */
-    halfword q;
-    /*tex height */
-    scaled h = 0;
-    /*tex depth */
-    scaled d = 0;
-    /*tex natural width */
-    scaled x = 0;
-    scaled_whd whd;
-    /*tex shift amount */
-    scaled s;
-    /*tex order of infinity */
-    int o;
-    /*tex for managing the direction stack */
-    halfword dir_ptr1 = null;
-    /*tex the current direction */
-    int hpack_dir;
-    int disc_level = 0;
-    halfword pack_interrupt[8];
-    scaled font_stretch = 0;
-    scaled font_shrink = 0;
-    int adjust_spacing = adjust_spacing_par;
-    last_badness = 0;
-    /*tex the box node that will be returned */
-    r = new_node(hlist_node, min_quarterword);
-    if (pack_direction == -1) {
-        hpack_dir = text_direction_par;
-    } else {
-        hpack_dir = pack_direction;
-    }
-    box_dir(r) = hpack_dir;
-    /*tex
-
-        A potential optimimization, save a little but neglectable in practice
-        (not so many empty boxes are used):
-
-        \starttyping
-        if (p == null) {
-            width(r) = w;
-            return r;
-        }
-        \stoptyping
-
-    */
-    /*tex push null */
-    push_dir(dir_ptr1,hpack_dir);
-    /*tex hm, adding something to a node address? */
-    q = r + list_offset;
-    vlink(q) = p;
-    if (m == cal_expand_ratio) {
-        /*tex Why not always: */
-        prev_char_p = null;
-    }
-    if (adjust_spacing > 2) {
-        adjust_spacing = 0;
-    }
-    total_stretch[normal] = 0;
-    total_shrink[normal] = 0;
-    total_stretch[sfi] = 0;
-    total_shrink[sfi] = 0;
-    total_stretch[fil] = 0;
-    total_shrink[fil] = 0;
-    total_stretch[fill] = 0;
-    total_shrink[fill] = 0;
-    total_stretch[filll] = 0;
-    total_shrink[filll] = 0;
-  RESWITCH:
-    while ((p != null) || (disc_level > 0)) {
-        if (p == null) {
-            decr(disc_level);
-            p = pack_interrupt[disc_level];
-            goto RESWITCH;
-        }
-        /*tex
-
-            Examine node |p| in the hlist, taking account of its effect on the
-            dimensions of the new box, or moving it to the adjustment list; then
-            advance |p| to the next node.
-
-        */
-        while (is_char_node(p)) {
-            /*tex
-
-                Incorporate character dimensions into the dimensions of the hbox
-                that will contain~it, then move to the next node.
-
-                The following code is part of \TeX's inner loop; i.e., adding
-                another character of text to the user's input will cause each of
-                these instructions to be exercised one more time.
-
-            */
-            if (m >= cal_expand_ratio) {
-                prev_char_p = p;
-                if (m == cal_expand_ratio) {
-                    font_stretch += char_stretch(p);
-                    font_shrink += char_shrink(p);
-                } else if (m == subst_ex_font) {
-                    do_subst_font(p, font_expand_ratio);
-                }
-            }
-            whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
-            x += whd.wd;
-            if (whd.ht > h)
-                h = whd.ht;
-            if (whd.dp > d)
-                d = whd.dp;
-            p = vlink(p);
-        }
-        if (p != null) {
-            switch (type(p)) {
-                case hlist_node:
-                case vlist_node:
-                    /*tex
-
-                        Incorporate box dimensions into the dimensions of the
-                        hbox that will contain~it
-
-                        The code here implicitly uses the fact that running
-                        dimensions are indicated by |null_flag|, which will be
-                        ignored in the calculations because it is a highly
-                        negative number.
-
-                    */
-                    s = shift_amount(p);
-                    whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
-                    x += whd.wd;
-                    if (whd.ht - s > h)
-                        h = whd.ht - s;
-                    if (whd.dp + s > d)
-                        d = whd.dp + s;
-                    break;
-                case rule_node:
-                case unset_node:
-                    x += width(p);
-                    if (height(p) > h)
-                        h = height(p);
-                    if (depth(p) > d)
-                        d = depth(p);
-                    break;
-                case math_node:
-                    /*tex Begin mathskip code. */
-                    if (glue_is_zero(p) || ignore_math_skip(p)) {
-                        x += surround(p);
-                        break;
-                    } else {
-                        /*tex Fall through. */
-                    }
-                    /*tex End mathskip code. */
-                case glue_node:
-                    /*tex Incorporate glue into the horizontal totals. */
-                    x += width(p);
-                    o = stretch_order(p);
-                    total_stretch[o] = total_stretch[o] + stretch(p);
-                    o = shrink_order(p);
-                    total_shrink[o] = total_shrink[o] + shrink(p);
-                    if (subtype(p) >= a_leaders) {
-                        halfword g = leader_ptr(p);
-                        if (height(g) > h)
-                            h = height(g);
-                        if (depth(g) > d)
-                            d = depth(g);
-                    }
-                    break;
-                case kern_node:
-                    x += width(p);
-                    if (subtype(p) == font_kern && adjust_spacing) {
-                        /*tex So only when 1 or 2. */
-                        if (m == cal_expand_ratio) {
-                            font_stretch = font_stretch + kern_stretch(p);
-                            font_shrink = font_shrink + kern_shrink(p);
-                        } else if (m == subst_ex_font) {
-                            /*tex This is the finalizer. */
-                            int k = 0;
-                            if (font_expand_ratio > 0) {
-                                k = kern_stretch(p);
-                            } else if (font_expand_ratio < 0) {
-                                k = kern_shrink(p);
-                            }
-                            ex_kern(p) = k;
-                            x += k;
-                        }
-                    }
-                    break;
-                case disc_node:
-                    if (m == subst_ex_font)
-                        do_subst_font(p, font_expand_ratio);
-                    if ((subtype(p) != select_disc) && (vlink(no_break(p)) != null)) {
-                        pack_interrupt[disc_level] = vlink(p);
-                        incr(disc_level);
-                        p = no_break(p);
-                    }
-                    break;
-                case dir_node:
-                    /*tex Adjust the dir stack for the |hpack| routine. */
-                    if (subtype(p) == normal_dir) {
-                        hpack_dir = dir_dir(p);
-                        push_dir_node(dir_ptr1,p);
-                    } else {
-                        pop_dir_node(dir_ptr1);
-                        if (dir_ptr1 != null)
-                            hpack_dir = dir_dir(dir_ptr1);
-                    }
-                    break;
-                case margin_kern_node:
-                    if (m == cal_expand_ratio) {
-                        int f = font(margin_char(p));
-                        do_subst_font(margin_char(p), 1000);
-                        if (f != font(margin_char(p)))
-                            font_stretch = font_stretch - width(p) - char_pw(margin_char(p), subtype(p));
-                        font(margin_char(p)) = f;
-                        do_subst_font(margin_char(p), -1000);
-                        if (f != font(margin_char(p)))
-                            font_shrink = font_shrink - width(p) - char_pw(margin_char(p), subtype(p));
-                        font(margin_char(p)) = f;
-                    } else if (m == subst_ex_font) {
-                        do_subst_font(margin_char(p), font_expand_ratio);
-                        width(p) = -char_pw(margin_char(p), subtype(p));
-                    }
-                    x += width(p);
-                    break;
-                case ins_node:
-                case mark_node:
-                case adjust_node:
-                    /*tex
-
-                        Transfer node |p| to the adjustment list.\Although node
-                        |q| is not necessarily the immediate predecessor of node
-                        |p|, it always points to some node in the list preceding
-                        |p|. Thus, we can delete nodes by moving |q| when
-                        necessary. The algorithm takes linear time, and the extra
-                        computation does not intrude on the inner loop unless it
-                        is necessary to make a deletion.
-
-                    */
-                    if (adjust_tail != null || pre_adjust_tail != null) {
-                        while (vlink(q) != p)
-                            q = vlink(q);
-                        if (type(p) == adjust_node) {
-                            if (adjust_pre(p) != 0)
-                                update_adjust_list(pre_adjust_tail);
-                            else
-                                update_adjust_list(adjust_tail);
-                            p = vlink(p);
-                            adjust_ptr(vlink(q)) = null;
-                            flush_node(vlink(q));
-                        } else {
-                            vlink(adjust_tail) = p;
-                            adjust_tail = p;
-                            p = vlink(p);
-                        }
-                        vlink(q) = p;
-                        p = q;
-                    }
-                    break;
-                default:
-                    break;
-            }
-            p = vlink(p);
-        }
-    }
-
-    if (adjust_tail != null)
-        vlink(adjust_tail) = null;
-    if (pre_adjust_tail != null)
-        vlink(pre_adjust_tail) = null;
-    height(r) = h;
-    depth(r) = d;
-    /*tex
-
-        Determine the value of |width(r)| and the appropriate glue setting; then
-        |return| or |goto common_ending|.
-
-        When we get to the present part of the program, |x| is the natural width
-        of the box being packaged.
-
-    */
-    if (m == additional)
-        w = x + w;
-    width(r) = w;
-    x = w - x;
-    /*tex Now |x| is the excess to be made up. */
-    if (x == 0) {
-        glue_sign(r) = normal;
-        glue_order(r) = normal;
-        set_glue_ratio_zero(glue_set(r));
-        goto EXIT;
-    } else if (x > 0) {
-        /*tex
-
-            Determine horizontal glue stretch setting, then |return| or
-            |goto common_ending|.
-
-            If |hpack| is called with |m=cal_expand_ratio| we calculate
-            |font_expand_ratio| and return without checking for overfull or
-            underfull box.
-
-        */
-        if (total_stretch[filll] != 0)
-            o = filll;
-        else if (total_stretch[fill] != 0)
-            o = fill;
-        else if (total_stretch[fil] != 0)
-            o = fil;
-        else if (total_stretch[sfi] != 0)
-            o = sfi;
-        else
-            o = normal;
-        if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) {
-            font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0);
-            goto EXIT;
-        }
-        glue_order(r) = (quarterword) o;
-        glue_sign(r) = stretching;
-        if (total_stretch[o] != 0) {
-            glue_set(r) = unfloat((double) x / total_stretch[o]);
-        } else {
-            /*tex There's nothing to stretch. */
-            glue_sign(r) = normal;
-            set_glue_ratio_zero(glue_set(r));
-        }
-        if (o == normal) {
-            if (list_ptr(r) != null) {
-                /*tex
-
-                    Report an underfull hbox and |goto common_ending|, if this
-                    box is sufficiently bad.
-
-                */
-                last_badness = badness(x, total_stretch[normal]);
-                if (last_badness > hbadness_par) {
-                    int callback_id = callback_defined(hpack_quality_callback);
-                    if (callback_id > 0) {
-                        halfword rule = null;
-                        if (last_badness > 100) {
-                            run_callback(callback_id, "SdNdd->N","underfull",last_badness,r,abs(pack_begin_line),line,&rule);
-                        } else {
-                            run_callback(callback_id, "SdNdd->N","loose",last_badness,r,abs(pack_begin_line),line,&rule);
-                        }
-                        if (rule != null) {
-                            while (vlink(q) != null) {
-                                q = vlink(q);
-                            }
-                            couple_nodes(q,rule);
-                        }
-                    } else {
-                        print_ln();
-                        if (last_badness > 100) {
-                            tprint_nl("Underfull \\hbox (badness ");
-                        } else {
-                            tprint_nl("Loose \\hbox (badness ");
-                        }
-                        print_int(last_badness);
-                        goto COMMON_ENDING;
-                    }
-                }
-            }
-        }
-        goto EXIT;
-    } else {
-        /*tex
-
-            Determine horizontal glue shrink setting, then |return| or |goto
-            common_ending|,
-
-        */
-        if (total_shrink[filll] != 0)
-            o = filll;
-        else if (total_shrink[fill] != 0)
-            o = fill;
-        else if (total_shrink[fil] != 0)
-            o = fil;
-        else if (total_shrink[sfi] != 0)
-            o = sfi;
-        else
-            o = normal;
-        if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) {
-            font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0);
-            goto EXIT;
-        }
-        glue_order(r) = (quarterword) o;
-        glue_sign(r) = shrinking;
-        if (total_shrink[o] != 0) {
-            glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]);
-        } else {
-            /*tex There's nothing to shrink. */
-            glue_sign(r) = normal;
-            set_glue_ratio_zero(glue_set(r));
-        }
-        if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
-            int overshoot = -x - total_shrink[normal] ;
-            last_badness = 1000000;
-            /*tex Use the maximum shrinkage */
-            set_glue_ratio_one(glue_set(r));
-            /*tex
-
-                Report an overfull hbox and |goto common_ending|, if this box is
-                sufficiently bad.
-
-            */
-            if ((overshoot > hfuzz_par) || (hbadness_par < 100)) {
-                int callback_id = callback_defined(hpack_quality_callback);
-                halfword rule = null;
-                if (callback_id > 0) {
-                    run_callback(callback_id, "SdNdd->N","overfull",overshoot,r,abs(pack_begin_line),line,&rule);
-                } else if (overfull_rule_par > 0) {
-                    rule = new_rule(normal_rule);
-                    rule_dir(rule) = box_dir(r);
-                    width(rule) = overfull_rule_par;
-                }
-                if (rule != null) {
-                    while (vlink(q) != null) {
-                        q = vlink(q);
-                    }
-                    couple_nodes(q,rule);
-                }
-                if (callback_id == 0) {
-                    print_ln();
-                    tprint_nl("Overfull \\hbox (");
-                    print_scaled(overshoot);
-                    tprint("pt too wide");
-                    goto COMMON_ENDING;
-                }
-            }
-        } else if (o == normal) {
-            if (list_ptr(r) != null) {
-                /*tex
-
-                    Report a tight hbox and |goto common_ending|, if this box is
-                    sufficiently bad.
-
-                */
-                last_badness = badness(-x, total_shrink[normal]);
-                if (last_badness > hbadness_par) {
-                    int callback_id = callback_defined(hpack_quality_callback);
-                    if (callback_id > 0) {
-                        halfword rule = null;
-                        run_callback(callback_id, "SdNdd->N","tight",last_badness,r,abs(pack_begin_line),line,&rule);
-                        if (rule != null) {
-                            while (vlink(q) != null) {
-                                q = vlink(q);
-                            }
-                            couple_nodes(q,rule);
-                        }
-                    } else {
-                        print_ln();
-                        tprint_nl("Tight \\hbox (badness ");
-                        print_int(last_badness);
-                        goto COMMON_ENDING;
-                    }
-                }
-            }
-        }
-        goto EXIT;
-    }
-
-  COMMON_ENDING:
-    /*tex Finish issuing a diagnostic message for an overfull or underfull hbox. */
-    if (output_active) {
-        tprint(") has occurred while \\output is active");
-    } else {
-        if (pack_begin_line != 0) {
-            if (pack_begin_line > 0) {
-                tprint(") in paragraph at lines ");
-            } else {
-                tprint(") in alignment at lines ");
-            }
-            print_int(abs(pack_begin_line));
-            tprint("--");
-        } else {
-            tprint(") detected at line ");
-        }
-        print_int(line);
-    }
-
-    print_ln();
-    font_in_short_display = null_font;
-    short_display(list_ptr(r));
-    print_ln();
-    begin_diagnostic();
-    show_box(r);
-    end_diagnostic(true);
-  EXIT:
-    if ((m == cal_expand_ratio) && (font_expand_ratio != 0)) {
-        font_expand_ratio = fix_int(font_expand_ratio, -1000, 1000);
-        q = list_ptr(r);
-        list_ptr(r) = null;
-        flush_node(r);
-        /*tex This nested call uses the more or less global font_expand_ratio. */
-        r = hpack(q, w, subst_ex_font, hpack_dir);
-    }
-    while (dir_ptr1 != null)
-        pop_dir_node(dir_ptr1);
-    /*tex Here we reset the |font_expand_ratio|. */
-    font_expand_ratio = 0;
-    return r;
-}
-
-halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int pac, int just_pack, halfword attr)
-{
-    halfword q;
-    if (just_pack) {
-        q = vlink(p);
-    } else if (type(p) == temp_node && vlink(p) == null) {
-        q = vlink(p);
-    } else {
-        new_hyphenation(p, qt);
-        /*tex We don't care about the tail in this case. */
-        (void) new_ligkern(p, qt);
-        q = vlink(p);
-        /*tex Maybe here: |alink(p) = null|. */
-        /*tex ignores empty anyway. Maybe also pass tail? */
-        q = lua_hpack_filter(q, w, m, grp, pac, attr);
-    }
-    return hpack(q, w, m, pac);
-}
-
-/*tex
-
-    Here is a function to calculate the natural whd of a (horizontal) node list.
-
-*/
-
-scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, int g_sign, int g_order, int pack_direction)
-{
-    /*tex shift amount */
-    scaled s;
-    /*tex points to a glue specification */
-    halfword g;
-    int hpack_dir;
-    /*tex For recursion */
-    scaled_whd xx;
-    scaled_whd whd, siz = { 0, 0, 0 };
-    scaled gp = 0;
-    scaled gm = 0;
-    if (pack_direction == -1) {
-        hpack_dir = text_direction_par;
-    } else {
-        hpack_dir = pack_direction;
-    }
-    while (p != pp && p != null) {
-        while (is_char_node(p) && p != pp) {
-            whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
-            siz.wd += whd.wd;
-            if (whd.ht > siz.ht)
-                siz.ht = whd.ht;
-            if (whd.dp > siz.dp)
-                siz.dp = whd.dp;
-            p = vlink(p);
-        }
-        if (p != pp && p != null) {
-            switch (type(p)) {
-                case hlist_node:
-                case vlist_node:
-                    s = shift_amount(p);
-                    whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
-                    siz.wd += whd.wd;
-                    if (whd.ht - s > siz.ht)
-                        siz.ht = whd.ht - s;
-                    if (whd.dp + s > siz.dp)
-                        siz.dp = whd.dp + s;
-                    break;
-                case rule_node:
-                case unset_node:
-                    siz.wd += width(p);
-                    if (height(p) > siz.ht)
-                        siz.ht = height(p);
-                    if (depth(p) > siz.dp)
-                        siz.dp = depth(p);
-                    break;
-                case math_node:
-                    /*tex Begin mathskip code. */
-                    if (glue_is_zero(p) || ignore_math_skip(p)) {
-                        siz.wd += surround(p);
-                        break;
-                    } else {
-                        /*tex Fall through. */
-                    }
-                    /*tex End mathskip code. */
-                case glue_node:
-                    siz.wd += width(p);
-                    if (g_sign != normal) {
-                        if (g_sign == stretching) {
-                            if (stretch_order(p) == g_order) {
-                                /*tex
-                                    |siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p)))|
-                                */
-                                gp += stretch(p);
-                            }
-                        } else if (shrink_order(p) == g_order) {
-                            /*tex
-                                |siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));|
-                            */
-                            gm += shrink(p);
-                        }
-                    }
-                    if (subtype(p) >= a_leaders) {
-                        g = leader_ptr(p);
-                        if (height(g) > siz.ht)
-                            siz.ht = height(g);
-                        if (depth(g) > siz.dp)
-                            siz.dp = depth(g);
-                    }
-                    break;
-                case margin_kern_node:
-                    siz.wd += width(p);
-                    break;
-                case kern_node:
-                    siz.wd += width(p) + ex_kern(p);
-                    break;
-                case disc_node:
-                    xx = natural_sizes(no_break(p), null, g_mult, g_sign, g_order, hpack_dir);
-                    siz.wd += xx.wd;
-                    if (xx.ht > siz.ht)
-                        siz.ht = xx.ht;
-                    if (xx.dp > siz.dp)
-                        siz.dp = xx.dp;
-                    break;
-                default:
-                    break;
-            }
-            p = vlink(p);
-        }
-
-    }
-    if (g_sign != normal) {
-        if (g_sign == stretching) {
-            siz.wd += float_round(float_cast(g_mult) * float_cast(gp));
-        } else {
-            siz.wd -= float_round(float_cast(g_mult) * float_cast(gm));
-        }
-    }
-    return siz;
-}
-
-/*tex
-
-    In order to provide a decent indication of where an overfull or underfull box
-    originated, we use a global variable |pack_begin_line| that is set nonzero
-    only when |hpack| is being called by the paragraph builder or the alignment
-    finishing routine.
-
-    The source file line where the current paragraph or alignment began; a
-    negative value denotes alignment:
-
-*/
-
-int pack_begin_line;
-
-/*tex
-
-    The |vpack| subroutine is actually a special case of a slightly more general
-    routine called |vpackage|, which has four parameters. The fourth parameter,
-    which is |max_dimen| in the case of |vpack|, specifies the maximum depth of
-    the page box that is constructed. The depth is first computed by the normal
-    rules; if it exceeds this limit, the reference point is simply moved down
-    until the limiting depth is attained.
-
-*/
-
-halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
-{
-    /*tex the box node that will be returned */
-    halfword r;
-    /*tex width */
-    scaled w = 0;
-    /*tex depth */
-    scaled d = 0;
-    /*tex natural height */
-    scaled x = 0;
-    scaled_whd whd;
-    /*tex shift amount */
-    scaled s;
-    /*tex order of infinity */
-    int o;
-    last_badness = 0;
-    r = new_node(vlist_node, 0);
-    if (pack_direction == -1) {
-        box_dir(r) = body_direction_par;
-    } else {
-        box_dir(r) = pack_direction;
-    }
-    subtype(r) = min_quarterword;
-    shift_amount(r) = 0;
-    list_ptr(r) = p;
-    total_stretch[normal] = 0;
-    total_shrink[normal] = 0;
-    total_stretch[sfi] = 0;
-    total_shrink[sfi] = 0;
-    total_stretch[fil] = 0;
-    total_shrink[fil] = 0;
-    total_stretch[fill] = 0;
-    total_shrink[fill] = 0;
-    total_stretch[filll] = 0;
-    total_shrink[filll] = 0;
-    while (p != null) {
-        /*tex
-
-            Examine node |p| in the vlist, taking account of its effect on the
-            dimensions of the new box; then advance |p| to the next node.
-
-        */
-        if (is_char_node(p)) {
-            confusion("vpack");
-        } else {
-            switch (type(p)) {
-            case hlist_node:
-            case vlist_node:
-                /*tex
-
-                    Incorporate box dimensions into the dimensions of the vbox
-                    that will contain it.
-
-                */
-                s = shift_amount(p);
-                whd = pack_width_height_depth(box_dir(r), box_dir(p), p, false);
-                if (whd.wd + s > w)
-                    w = whd.wd + s;
-                x += d + whd.ht;
-                d = whd.dp;
-                break;
-            case rule_node:
-            case unset_node:
-                x += d + height(p);
-                d = depth(p);
-                if (width(p) > w)
-                    w = width(p);
-                break;
-            case glue_node:
-                /*tex Incorporate glue into the vertical totals. */
-                x += d;
-                d = 0;
-                x += width(p);
-                o = stretch_order(p);
-                total_stretch[o] = total_stretch[o] + stretch(p);
-                o = shrink_order(p);
-                total_shrink[o] = total_shrink[o] + shrink(p);
-                if (subtype(p) >= a_leaders) {
-                    halfword g = leader_ptr(p);
-                    if (width(g) > w)
-                        w = width(g);
-                }
-                break;
-            case kern_node:
-                x += d + width(p);
-                d = 0;
-                break;
-            default:
-                break;
-            }
-        }
-        p = vlink(p);
-    }
-    width(r) = w;
-    if (d > l) {
-        x += d - l;
-        depth(r) = l;
-    } else {
-        depth(r) = d;
-    }
-    /*tex
-
-        Determine the value of |height(r)| and the appropriate glue setting; then
-        |return| or |goto common_ending|.
-
-        When we get to the present part of the program, |x| is the natural height
-        of the box being packaged.
-    */
-    if (m == additional)
-        h = x + h;
-    height(r) = h;
-    x = h - x;
-    /*tex Now |x| is the excess to be made up. */
-    if (x == 0) {
-        glue_sign(r) = normal;
-        glue_order(r) = normal;
-        set_glue_ratio_zero(glue_set(r));
-        return r;
-    } else if (x > 0) {
-        /*tex
-
-            Determine vertical glue stretch setting, then |return| or |goto
-            common_ending|.
-
-        */
-        if (total_stretch[filll] != 0)
-            o = filll;
-        else if (total_stretch[fill] != 0)
-            o = fill;
-        else if (total_stretch[fil] != 0)
-            o = fil;
-        else if (total_stretch[sfi] != 0)
-            o = sfi;
-        else
-            o = normal;
-        glue_order(r) = (quarterword) o;
-        glue_sign(r) = stretching;
-        if (total_stretch[o] != 0) {
-            glue_set(r) = unfloat((double) x / total_stretch[o]);
-        } else {
-            /*tex There's nothing to stretch. */
-            glue_sign(r) = normal;
-            set_glue_ratio_zero(glue_set(r));
-        }
-        if (o == normal) {
-            if (list_ptr(r) != null) {
-                /*tex
-
-                    Report an underfull vbox and |goto common_ending|, if this
-                    box is sufficiently bad.
-
-                */
-                last_badness = badness(x, total_stretch[normal]);
-                if (last_badness > vbadness_par) {
-                    int callback_id = callback_defined(vpack_quality_callback);
-                    if (callback_id > 0) {
-                        if (last_badness > 100) {
-                            run_callback(callback_id, "SdNdd->","underfull",last_badness,r,abs(pack_begin_line),line);
-                        } else {
-                            run_callback(callback_id, "SdNdd->","loose",last_badness,r,abs(pack_begin_line),line);
-                        }
-                        goto EXIT;
-                    } else {
-                        print_ln();
-                        if (last_badness > 100) {
-                            tprint_nl("Underfull \\vbox (badness ");
-                        } else {
-                            tprint_nl("Loose \\vbox (badness ");
-                        }
-                        print_int(last_badness);
-                        goto COMMON_ENDING;
-                    }
-                }
-            }
-        }
-        return r;
-    } else {
-        /*tex
-
-            Determine vertical glue shrink setting, then |return| or |goto
-            common_ending|.
-
-        */
-        if (total_shrink[filll] != 0)
-            o = filll;
-        else if (total_shrink[fill] != 0)
-            o = fill;
-        else if (total_shrink[fil] != 0)
-            o = fil;
-        else if (total_shrink[sfi] != 0)
-            o = sfi;
-        else
-            o = normal;
-        glue_order(r) = (quarterword) o;
-        glue_sign(r) = shrinking;
-        if (total_shrink[o] != 0) {
-            glue_set(r) = unfloat((double) (-x) / total_shrink[o]);
-        } else {
-            /*tex There's nothing to shrink. */
-            glue_sign(r) = normal;
-            set_glue_ratio_zero(glue_set(r));
-        }
-        if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
-            int overshoot = -x - total_shrink[normal];
-            last_badness = 1000000;
-            /*tex Use the maximum shrinkage */
-            set_glue_ratio_one(glue_set(r));
-            /*tex
-
-                Report an overfull vbox and |goto common_ending|, if this box is
-                sufficiently bad.
-
-            */
-            if ((overshoot > vfuzz_par) || (vbadness_par < 100)) {
-                int callback_id = callback_defined(vpack_quality_callback);
-                if (callback_id > 0) {
-                    run_callback(callback_id, "SdNdd->","overfull",overshoot,r,abs(pack_begin_line),line);
-                    goto EXIT;
-                } else {
-                    print_ln();
-                    tprint_nl("Overfull \\vbox (");
-                    print_scaled(-x - total_shrink[normal]);
-                    tprint("pt too high");
-                    goto COMMON_ENDING;
-                }
-            }
-        } else if (o == normal) {
-            if (list_ptr(r) != null) {
-                /*tex
-
-                    Report a tight vbox and |goto common_ending|, if this box is
-                    sufficiently bad.
-
-                */
-                last_badness = badness(-x, total_shrink[normal]);
-                if (last_badness > vbadness_par) {
-                    int callback_id = callback_defined(vpack_quality_callback);
-                    if (callback_id > 0) {
-                        run_callback(callback_id, "SdNdd->","tight",last_badness,r,abs(pack_begin_line),line);
-                        goto EXIT;
-                    } else {
-                        print_ln();
-                        tprint_nl("Tight \\vbox (badness ");
-                        print_int(last_badness);
-                        goto COMMON_ENDING;
-                    }
-                }
-            }
-        }
-        return r;
-    }
-
-  COMMON_ENDING:
-    /*tex Finish issuing a diagnostic message or an overfull or underfull vbox. */
-    if (output_active) {
-        tprint(") has occurred while \\output is active");
-    } else {
-        if (pack_begin_line != 0) {
-            /*tex It's actually negative. */
-            tprint(") in alignment at lines ");
-            print_int(abs(pack_begin_line));
-            tprint("--");
-        } else {
-            tprint(") detected at line ");
-        }
-        print_int(line);
-        print_ln();
-    }
-    begin_diagnostic();
-    show_box(r);
-    end_diagnostic(true);
-  EXIT:
-    return r;
-}
-
-halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, int pack_direction, int just_pack, halfword attr)
-{
-    halfword q = p;
-    if (!just_pack)
-        q = lua_vpack_filter(q, h, m, l, grp, pack_direction, attr);
-    return vpackage(q, h, m, l, pack_direction);
-}
-
-void finish_vcenter(void)
-{
-    halfword p;
-    unsave();
-    save_ptr--;
-    p = vpack(vlink(cur_list.head_field), saved_value(0), saved_level(0), -1);
-    pop_nest();
-    p = math_vcenter_group(p);
-    tail_append(p);
-}
-
-void package(int c)
-{
-    halfword saved0, saved2, saved3, saved4;
-    int grp = cur_group;
-    scaled d = box_max_depth_par;
-    unsave();
-    save_ptr -= 5;
-    saved0 = saved_value(0);
-    saved2 = saved_value(2);
-    saved3 = saved_value(3);
-    saved4 = saved_value(4);
-    if (cur_list.mode_field == -hmode) {
-        cur_box = filtered_hpack(cur_list.head_field, cur_list.tail_field,
-            saved_value(1), saved_level(1), grp, saved_level(2), saved4, saved3);
-        subtype(cur_box) = hbox_list;
-    } else {
-        cur_box = filtered_vpackage(vlink(cur_list.head_field),
-            saved_value(1), saved_level(1), d, grp, saved_level(2), saved4, saved3);
-        if (c == vtop_code) {
-            /*tex
-
-                Read just the height and depth of |cur_box|, for \.{\\vtop}. The
-                height of a `\.{\\vtop}' box is inherited from the first item on
-                its list, if that item is an |hlist_node|, |vlist_node|, or
-                |rule_node|; otherwise the \.{\\vtop} height is zero.
-
-            */
-            scaled h = 0;
-            halfword p = list_ptr(cur_box);
-            if ((p != null) && (type(p) <= rule_node)) {
-                /* hlist, vlist, rule */
-                h = height(p);
-            }
-            depth(cur_box) = depth(cur_box) - h + height(cur_box);
-            height(cur_box) = h;
-        }
-    }
-    if (saved2 != null) {
-        /*tex Adjust back |text_dir_ptr| for |scan_spec| */
-        flush_node_list(text_dir_ptr);
-        text_dir_ptr = saved2;
-
-    }
-    replace_attribute_list(cur_box, saved3);
-    pop_nest();
-    box_end(saved0);
-}
-
-/*tex
-
-    When a box is being appended to the current vertical list, the baselineskip
-    calculation is handled by the |append_to_vlist| routine.
-
-*/
-
-void append_to_vlist(halfword b, int location)
-{
-    /*tex The deficiency of space between baselines: */
-    scaled d;
-    /*tex A new glue node. */
-    halfword p;
-    boolean mirrored = (type(b) == hlist_node) && is_mirrored(box_dir(b)) ;
-    halfword result = null;
-    halfword next_depth = ignore_depth;
-    boolean prev_set = false ;
-    if (lua_appendtovlist_callback(b,location,prev_depth_par,mirrored,&result,&next_depth,&prev_set)) {
-        while (result != null) {
-            couple_nodes(cur_list.tail_field, result);
-            cur_list.tail_field = result;
-            result = vlink(result);
-        }
-        if (prev_set) {
-            prev_depth_par = next_depth;
-        }
-    } else {
-        if (prev_depth_par > ignore_depth) {
-            if (mirrored) {
-                d = width(baseline_skip_par) - prev_depth_par - depth(b);
-            } else {
-                d = width(baseline_skip_par) - prev_depth_par - height(b);
-            }
-            if (d < line_skip_limit_par) {
-                p = new_param_glue(line_skip_code);
-            } else {
-                p = new_skip_param(baseline_skip_code);
-                width(p) = d;
-            }
-            couple_nodes(cur_list.tail_field, p);
-            cur_list.tail_field = p;
-        }
-        couple_nodes(cur_list.tail_field, b);
-        cur_list.tail_field = b;
-        if (mirrored) {
-            prev_depth_par = height(b);
-        } else {
-            prev_depth_par = depth(b);
-        }
-    }
-}
-
-/*tex
-
-    When |saving_vdiscards| is positive then the glue, kern, and penalty nodes
-    removed by the page builder or by \.{\\vsplit} from the top of a vertical
-    list are saved in special lists instead of being discarded.
-
-*/
-
-/*tex last item removed by page builder */
-
-#define tail_page_disc disc_ptr[copy_code]
-
-/*tex first item removed by page builder */
-
-#define page_disc disc_ptr[last_box_code]
-
-/*tex first item removed by \.{\\vsplit} */
-
-#define split_disc disc_ptr[vsplit_code]
-
-/*tex List pointers. */
-
-halfword disc_ptr[(vsplit_code + 1)];
-
-/*tex
-
-    The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation, is
-    considerably simpler than |line_break| because it doesn't have to worry about
-    hyphenation, and because its mission is to discover a single break instead of
-    an optimum sequence of breakpoints. But before we get into the details of
-    |vsplit|, we need to consider a few more basic things.
-
-    A subroutine called |prune_page_top| takes a pointer to a vlist and returns a
-    pointer to a modified vlist in which all glue, kern, and penalty nodes have
-    been deleted before the first box or rule node. However, the first box or
-    rule is actually preceded by a newly created glue node designed so that the
-    topmost baseline will be at distance |split_top_skip| from the top, whenever
-    this is possible without backspacing.
-
-    When the second argument |s| is |false| the deleted nodes are destroyed,
-    otherwise they are collected in a list starting at |split_disc|.
-
-*/
-
-halfword prune_page_top(halfword p, boolean s)
-{
-    halfword q;
-    /*tex Lags one step behind |p|. */
-    halfword prev_p = temp_head;
-    halfword r = null;
-    vlink(temp_head) = p;
-    while (p != null) {
-        switch (type(p)) {
-            case hlist_node:
-            case vlist_node:
-            case rule_node:
-                /*tex Insert glue for |split_top_skip| and set |p:=null|. */
-                q = new_skip_param(split_top_skip_code);
-                vlink(prev_p) = q;
-                vlink(q) = p;
-                if (width(q) > height(p))
-                    width(q) = width(q) - height(p);
-                else
-                    width(q) = 0;
-                p = null;
-                break;
-            case boundary_node:
-            case whatsit_node:
-            case mark_node:
-            case ins_node:
-                prev_p = p;
-                p = vlink(prev_p);
-                break;
-            case glue_node:
-            case kern_node:
-            case penalty_node:
-                q = p;
-                p = vlink(q);
-                vlink(q) = null;
-                vlink(prev_p) = p;
-                if (s) {
-                    if (split_disc == null)
-                        split_disc = q;
-                    else
-                        vlink(r) = q;
-                    r = q;
-                } else {
-                    flush_node_list(q);
-                }
-                break;
-            default:
-                confusion("pruning");
-                break;
-        }
-    }
-    return vlink(temp_head);
-}
-
-/*tex
-
-    The next subroutine finds the best place to break a given vertical list so as
-    to obtain a box of height~|h|, with maximum depth~|d|. A pointer to the
-    beginning of the vertical list is given, and a pointer to the optimum
-    breakpoint is returned. The list is effectively followed by a forced break,
-    i.e., a penalty node with the |eject_penalty|; if the best break occurs at
-    this artificial node, the value |null| is returned.
-
-*/
-
-/*tex The distance from first active node to |cur_p|: */
-
-scaled active_height[10];
-
-/*tex
-
-    An array of six |scaled| distances is used to keep track of the height from
-    the beginning of the list to the current place, just as in |line_break|. In
-    fact, we use one of the same arrays, only changing its name to reflect its
-    new significance.
-
-*/
-
-#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
-#define set_height_zero(A) active_height[A]=0   /* initialize the height to zero */
-
-/*tex
-
-    A global variable |best_height_plus_depth| will be set to the natural size of
-    the box that corresponds to the optimum breakpoint found by |vert_break|.
-    (This value is used by the insertion-splitting algorithm of the page
-    builder.)
-
-*/
-
-/*tex The height of the best box, without stretching or shrinking: */
-
-scaled best_height_plus_depth;
-
-halfword vert_break(halfword p, scaled h, scaled d)
-{
-    /*tex
-        If |p| is a glue node, |type(prev_p)| determines whether |p| is a legal
-        breakpoint, an initial glue node is not a legal breakpoint.
-    */
-    halfword prev_p = p;
-    /*tex penalty value */
-    int pi = 0;
-    /*tex badness at a trial breakpoint */
-    int b;
-    /*tex |type| of the node following a kern */
-    int t;
-    /*tex the smallest badness plus penalties found so far */
-    int least_cost;
-    /*tex the most recent break that leads to |least_cost| */
-    halfword best_place = null;
-    /*tex depth of previous box in the list */
-    scaled prev_dp = 0;
-    least_cost = awful_bad;
-    do_all_six(set_height_zero);
-    while (1) {
-        /*tex
-
-            If node |p| is a legal breakpoint, check if this break is the best
-            known, and |goto done| if |p| is null or if the page-so-far is
-            already too full to accept more stuff.
-
-            A subtle point to be noted here is that the maximum depth~|d| might
-            be negative, so |cur_height| and |prev_dp| might need to be corrected
-            even after a glue or kern node.
-        */
-        if (p == null) {
-            pi = eject_penalty;
-        } else {
-            /*tex
-
-                Use node |p| to update the current height and depth measurements;
-                if this node is not a legal breakpoint, |goto not_found| or
-                |update_heights|, otherwise set |pi| to the associated penalty at
-                the break.
-
-            */
-            switch (type(p)) {
-                case hlist_node:
-                case vlist_node:
-                case rule_node:
-                    cur_height = cur_height + prev_dp + height(p);
-                    prev_dp = depth(p);
-                    goto NOT_FOUND;
-                    break;
-                case boundary_node:
-                case whatsit_node:
-                    goto NOT_FOUND;
-                    break;
-                case glue_node:
-                    if (precedes_break(prev_p))
-                        pi = 0;
-                    else
-                        goto UPDATE_HEIGHTS;
-                    break;
-                case kern_node:
-                    if (vlink(p) == null)
-                        t = penalty_node;
-                    else
-                        t = type(vlink(p));
-                    if (t == glue_node)
-                        pi = 0;
-                    else
-                        goto UPDATE_HEIGHTS;
-                    break;
-                case penalty_node:
-                    pi = penalty(p);
-                    break;
-                case mark_node:
-                case ins_node:
-                    goto NOT_FOUND;
-                    break;
-                default:
-                    confusion("vertbreak");
-                    break;
-            }
-        }
-        /*tex
-
-            Check if node |p| is a new champion breakpoint; then |goto done| if
-            |p| is a forced break or if the page-so-far is already too full.
-
-        */
-        if (pi < inf_penalty) {
-            /*tex Compute the badness, |b|, using |awful_bad| if the box is too full. */
-            if (cur_height < h) {
-                if ((active_height[3] != 0) || (active_height[4] != 0) ||
-                    (active_height[5] != 0) || (active_height[6] != 0))
-                    b = 0;
-                else
-                    b = badness(h - cur_height, active_height[2]);
-            } else if (cur_height - h > active_height[7]) {
-                b = awful_bad;
-            } else {
-                b = badness(cur_height - h, active_height[7]);
-            }
-            if (b < awful_bad) {
-                if (pi <= eject_penalty)
-                    b = pi;
-                else if (b < inf_bad)
-                    b = b + pi;
-                else
-                    b = deplorable;
-            }
-            if (b <= least_cost) {
-                best_place = p;
-                least_cost = b;
-                best_height_plus_depth = cur_height + prev_dp;
-            }
-            if ((b == awful_bad) || (pi <= eject_penalty))
-                goto DONE;
-        }
-        if ((type(p) < glue_node) || (type(p) > kern_node))
-            goto NOT_FOUND;
-      UPDATE_HEIGHTS:
-        /*tex
-
-            Update the current height and depth measurements with respect to a
-            glue or kern node~|p|. Vertical lists that are subject to the
-            |vert_break| procedure should not contain infinite shrinkability,
-            since that would permit any amount of information to ``fit'' on one
-            page.
-
-        */
-        if (type(p) != kern_node) {
-            active_height[2 + stretch_order(p)] += stretch(p);
-            active_height[7] += shrink(p);
-            if ((shrink_order(p) != normal) && (shrink(p) != 0)) {
-                print_err("Infinite glue shrinkage found in box being split");
-                help4(
-                    "The box you are \\vsplitting contains some infinitely",
-                    "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
-                    "Such glue doesn't belong there; but you can safely proceed,",
-                    "since the offensive shrinkability has been made finite."
-                );
-                error();
-                shrink_order(p) = normal;
-            }
-        }
-        cur_height = cur_height + prev_dp + width(p);
-        prev_dp = 0;
-      NOT_FOUND:
-        if (prev_dp > d) {
-            cur_height = cur_height + prev_dp - d;
-            prev_dp = d;
-        }
-        prev_p = p;
-        p = vlink(prev_p);
-    }
-  DONE:
-    return best_place;
-}
-
-/*tex
-
-    Now we are ready to consider |vsplit| itself. Most of its work is
-    accomplished by the two subroutines that we have just considered.
-
-    Given the number of a vlist box |n|, and given a desired page height |h|, the
-    |vsplit| function finds the best initial segment of the vlist and returns a
-    box for a page of height~|h|. The remainder of the vlist, if any, replaces
-    the original box, after removing glue and penalties and adjusting for
-    |split_top_skip|. Mark nodes in the split-off box are used to set the values
-    of |split_first_mark| and |split_bot_mark|; we use the fact that
-    |split_first_mark(x)=null| if and only if |split_bot_mark(x)=null|.
-
-    The original box becomes ``void'' if and only if it has been entirely
-    extracted. The extracted box is ``void'' if and only if the original box was
-    void (or if it was, erroneously, an hlist box).
-
-*/
-
-/*tex Extract a page of height |h| from box |n|: */
-
-halfword vsplit(halfword n, scaled h, int m)
-{
-    /*tex the box to be split */
-    halfword v;
-    /*tex the direction of the box to be split */
-    int vdir;
-    /*tex runs through the vlist */
-    halfword p;
-    /*tex points to where the break occurs */
-    halfword q;
-    /*tex for traversing marks lists */
-    halfword i;
-    v = box(n);
-    vdir = box_dir(v);
-    flush_node_list(split_disc);
-    split_disc = null;
-    for (i = 0; i <= biggest_used_mark; i++) {
-        delete_split_first_mark(i);
-        delete_split_bot_mark(i);
-    }
-    /*tex Dispense with trivial cases of void or bad boxes. */
-    if (v == null) {
-        return null;
-    }
-    if (type(v) != vlist_node) {
-        print_err("\\vsplit needs a \\vbox");
-        help2(
-            "The box you are trying to split is an \\hbox.",
-            "i can't split such a box, so I''ll leave it alone."
-        );
-        error();
-        return null;
-    }
-    q = vert_break(list_ptr(v), h, split_max_depth_par);
-    /*tex
-
-        Look at all the marks in nodes before the break, and set the final link
-        to |null| at the break. It's possible that the box begins with a penalty
-        node that is the ``best'' break, so we must be careful to handle this
-        special case correctly.
-
-    */
-    p = list_ptr(v);
-    if (p == q) {
-        list_ptr(v) = null;
-    } else {
-        while (1) {
-            if (type(p) == mark_node) {
-                if (split_first_mark(mark_class(p)) == null) {
-                    set_split_first_mark(mark_class(p), mark_ptr(p));
-                    set_split_bot_mark(mark_class(p), split_first_mark(mark_class(p)));
-                    set_token_ref_count(split_first_mark(mark_class(p)),
-                        token_ref_count(split_first_mark(mark_class(p))) + 2);
-                } else {
-                    delete_token_ref(split_bot_mark(mark_class(p)));
-                    set_split_bot_mark(mark_class(p), mark_ptr(p));
-                    add_token_ref(split_bot_mark(mark_class(p)));
-                }
-            }
-            if (vlink(p) == q) {
-                vlink(p) = null;
-                break;
-            }
-            p = vlink(p);
-        }
-    }
-    q = prune_page_top(q, saving_vdiscards_par > 0);
-    p = list_ptr(v);
-    list_ptr(v) = null;
-    flush_node(v);
-    if (q == null) {
-        /*tex The |eq_level| of the box stays the same. */
-        box(n) = null;
-    } else {
-        box(n) = filtered_vpackage(q, 0, additional, max_depth_par, split_keep_group, vdir, 0, 0);
-    }
-    if (m == exactly) {
-        return filtered_vpackage(p, h, exactly, split_max_depth_par, split_off_group, vdir, 0, 0);
-    } else {
-        return filtered_vpackage(p, 0, additional, max_depth_par, split_off_group, vdir, 0, 0);
-    }
-}
-
-/*tex
-
-    Now that we can see what eventually happens to boxes, we can consider the
-    first steps in their creation. The |begin_box| routine is called when
-    |box_context| is a context specification, |cur_chr| specifies the type of box
-    desired, and |cur_cmd=make_box|.
-
-*/
-
-void begin_box(int box_context)
-{
-    /*tex run through the current list */
-    halfword q;
-    /*tex 0 or |vmode| or |hmode| */
-    halfword k;
-    /*tex a box number */
-    int n;
-    int spec_direction = -1;
-    int just_pack = 0;
-    int split_mode = exactly ;
-    switch (cur_chr) {
-        case box_code:
-            scan_register_num();
-            cur_box = box(cur_val);
-            /*tex The box becomes void, at the same level. */
-            box(cur_val) = null;
-            break;
-        case copy_code:
-            scan_register_num();
-            cur_box = copy_node_list(box(cur_val));
-            break;
-        case last_box_code:
-            /*tex
-
-                If the current list ends with a box node, delete it from the list
-                and make |cur_box| point to it; otherwise set |cur_box:=null|.
-
-            */
-            cur_box = null;
-            if (abs(cur_list.mode_field) == mmode) {
-                you_cant();
-                help1("Sorry; this \\lastbox will be void.");
-                error();
-            } else if ((cur_list.mode_field == vmode) && (cur_list.head_field == cur_list.tail_field)) {
-                you_cant();
-                help2(
-                    "Sorry...I usually can't take things from the current page.",
-                    "This \\lastbox will therefore be void."
-                );
-                error();
-            } else {
-                if (cur_list.head_field != cur_list.tail_field) {
-                    if ((type(cur_list.tail_field) == hlist_node) || (type(cur_list.tail_field) == vlist_node)) {
-                        /*tex Remove the last box */
-                        q = alink(cur_list.tail_field);
-                        if (q == null || vlink(q) != cur_list.tail_field) {
-                            q = cur_list.head_field;
-                            while (vlink(q) != cur_list.tail_field)
-                                q = vlink(q);
-                        }
-                        uncouple_node(cur_list.tail_field);
-                        cur_box = cur_list.tail_field;
-                        shift_amount(cur_box) = 0;
-                        cur_list.tail_field = q;
-                        vlink(cur_list.tail_field) = null;
-                    }
-                }
-            }
-            break;
-        case vsplit_code:
-            /*tex
-
-                Split off part of a vertical box, make |cur_box| point to it.
-                Here we deal with things like `\.{\\vsplit 13 to 100pt}'.
-
-            */
-            scan_register_num();
-            n = cur_val;
-            if (scan_keyword("upto")) {
-                split_mode = additional ;
-            } else if (!scan_keyword("to")) {
-                print_err("Missing `to' inserted");
-                help2(
-                    "I'm working on `\\vsplit<box number> to <dimen>';",
-                  "will look for the <dimen> next."
-                );
-                error();
-            }
-            scan_normal_dimen();
-            cur_box = vsplit(n, cur_val, split_mode);
-         break;
-        default:
-            /*tex
-
-                Initiate the construction of an hbox or vbox, then |return|. Here
-                is where we enter restricted horizontal mode or internal vertical
-                mode, in order to make a box.
-
-            */
-            switch (cur_chr) {
-                case tpack_code:
-                    cur_chr = vtop_code;
-                    just_pack = 1;
-                    break;
-                case vpack_code:
-                    cur_chr = vtop_code + vmode;
-                    just_pack = 1;
-                    break;
-                case hpack_code:
-                    cur_chr = vtop_code + hmode;
-                    just_pack = 1;
-                    break;
-            }
-            k = cur_chr - vtop_code;
-            set_saved_record(0, saved_boxcontext, 0, box_context);
-            switch (abs(cur_list.mode_field)) {
-                case vmode:
-                    spec_direction = body_direction_par;
-                    break;
-                case hmode:
-                    spec_direction = text_direction_par;
-                    break;
-                case mmode:
-                    spec_direction = math_direction_par;
-                    break;
-            }
-            if (k == hmode) {
-                if ((box_context < box_flag) && (abs(cur_list.mode_field) == vmode))
-                    scan_full_spec(adjusted_hbox_group, spec_direction,just_pack);
-                else
-                    scan_full_spec(hbox_group, spec_direction,just_pack);
-            } else {
-                if (k == vmode) {
-                    scan_full_spec(vbox_group, spec_direction,just_pack);
-                } else {
-                    scan_full_spec(vtop_group, spec_direction,just_pack);
-                    k = vmode;
-                }
-                normal_paragraph();
-            }
-            push_nest();
-            cur_list.mode_field = -k;
-            if (k == vmode) {
-                prev_depth_par = ignore_depth;
-                if (every_vbox_par != null)
-                    begin_token_list(every_vbox_par, every_vbox_text);
-            } else {
-                space_factor_par = 1000;
-                if (every_hbox_par != null)
-                    begin_token_list(every_hbox_par, every_hbox_text);
-            }
-            return;
-            break;
-    }
-    /*tex In simple cases, we use the box immediately. */
-    box_end(box_context);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/postlinebreak.w
@@ -0,0 +1,583 @@
+% postlinebreak.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"
+
+@ So far we have gotten a little way into the |line_break| routine, having
+covered its important |try_break| subroutine. Now let's consider the
+rest of the process.
+
+The main loop of |line_break| traverses the given hlist,
+starting at |vlink(temp_head)|, and calls |try_break| at each legal
+breakpoint. A variable called |auto_breaking| is set to true except
+within math formulas, since glue nodes are not legal breakpoints when
+they appear in formulas.
+
+The current node of interest in the hlist is pointed to by |cur_p|. Another
+variable, |prev_p|, is usually one step behind |cur_p|, but the real
+meaning of |prev_p| is this: If |type(cur_p)=glue_node| then |cur_p| is a legal
+breakpoint if and only if |auto_breaking| is true and |prev_p| does not
+point to a glue node, penalty node, explicit kern node, or math node.
+
+@ The total number of lines that will be set by |post_line_break|
+is |best_line-prev_graf-1|. The last breakpoint is specified by
+|break_node(best_bet)|, and this passive node points to the other breakpoints
+via the |prev_break| links. The finishing-up phase starts by linking the
+relevant passive nodes in forward order, changing |prev_break| to
+|next_break|. (The |next_break| fields actually reside in the same memory
+space as the |prev_break| fields did, but we give them a new name because
+of their new significance.) Then the lines are justified, one by one.
+
+The |post_line_break| must also keep an dir stack, so that it can
+output end direction instructions at the ends of lines
+and begin direction instructions at the beginnings of lines.
+
+@c
+#define next_break prev_break   /*new name for |prev_break| after links are reversed */
+
+/* the ints are actually halfwords */
+void ext_post_line_break(int paragraph_dir,
+                         int right_skip,
+                         int left_skip,
+                         int protrude_chars,
+                         halfword par_shape_ptr,
+                         int adjust_spacing,
+                         halfword inter_line_penalties_ptr,
+                         int inter_line_penalty,
+                         int club_penalty,
+                         halfword club_penalties_ptr,
+                         halfword widow_penalties_ptr,
+                         int widow_penalty,
+                         int broken_penalty,
+                         halfword final_par_glue,
+                         halfword best_bet,
+                         halfword last_special_line,
+                         scaled second_width,
+                         scaled second_indent,
+                         scaled first_width,
+                         scaled first_indent, halfword best_line)
+{
+
+    boolean have_directional = true;
+    halfword q, r;              /* temporary registers for list manipulation */
+    halfword k;
+    scaled w;
+    boolean glue_break;         /* was a break at glue? */
+    boolean disc_break;         /*was the current break at a discretionary node? */
+    boolean post_disc_break;    /*and did it have a nonempty post-break part? */
+    scaled cur_width;           /*width of line number |cur_line| */
+    scaled cur_indent;          /*left margin of line number |cur_line| */
+    int pen;                    /*use when calculating penalties between lines */
+    halfword cur_p;             /* |cur_p|, but localized */
+    halfword cur_line;          /*the current line number being justified */
+
+    dir_ptr = cur_list.dirs_field;
+    /* Reverse the links of the relevant passive nodes, setting |cur_p| to
+       the first breakpoint; */
+    /* The job of reversing links in a list is conveniently regarded as the job
+       of taking items off one stack and putting them on another. In this case we
+       take them off a stack pointed to by |q| and having |prev_break| fields;
+       we put them on a stack pointed to by |cur_p| and having |next_break| fields.
+       Node |r| is the passive node being moved from stack to stack.
+     */
+    q = break_node(best_bet);
+#if 0
+    used_discs = used_disc(best_bet);
+#endif
+    /* |has_direction| */
+    cur_p = null;
+    do {
+        r = q;
+        q = prev_break(q);
+        next_break(r) = cur_p;
+        cur_p = r;
+    } while (q != null);
+    /* |cur_p| is now the first breakpoint; */
+
+    cur_line = cur_list.pg_field + 1;   /* prevgraf+1 */
+
+    do {
+        /* Justify the line ending at breakpoint |cur_p|, and append it to the
+           current vertical list, together with associated penalties and other
+           insertions;    */
+        /* The current line to be justified appears in a horizontal list starting
+           at |vlink(temp_head)| and ending at |cur_break(cur_p)|. If |cur_break(cur_p)| is
+           a glue node, we reset the glue to equal the |right_skip| glue; otherwise
+           we append the |right_skip| glue at the right. If |cur_break(cur_p)| is a
+           discretionary node, we modify the list so that the discretionary break
+           is compulsory, and we set |disc_break| to |true|. We also append
+           the |left_skip| glue at the left of the line, unless it is zero. */
+
+#if 0
+        tprint("BEGIN OF LINE ");
+        print_int(cur_break(cur_p));
+        breadth_max = 100000;
+        depth_threshold = 100000;
+        show_node_list(temp_head);
+#endif
+
+        /* DIR: Insert dir nodes at the beginning of the current line; */
+        for (q = dir_ptr; q != null; q = vlink(q)) {
+            halfword tmp = new_dir(dir_dir(q));
+            halfword nxt = vlink(temp_head);
+            delete_attribute_ref(node_attr(tmp));
+            node_attr(tmp) = node_attr(temp_head);
+            add_node_attr_ref(node_attr(tmp));
+            couple_nodes(temp_head, tmp);
+            try_couple_nodes(tmp, nxt); /* \.{\\break}\.{\\par} */
+        }
+        if (dir_ptr != null) {
+            flush_node_list(dir_ptr);
+            dir_ptr = null;
+        }
+
+        /* Modify the end of the line to reflect the nature of the break and to
+           include \.{\\rightskip}; also set the proper value of |disc_break|; */
+        /* At the end of the following code, |q| will point to the final node on the
+           list about to be justified. In the meanwhile |r| will point to the
+           node we will use to insert end-of-line stuff after. |q==null| means
+           we use the final position of |r| */
+
+        /* begin mathskip code */
+        if (temp_head != null) {
+                q = temp_head;
+                while(q != null) {
+                    if (type(q) == math_node) {
+                        surround(q) = 0 ;
+                        reset_glue_to_zero(q);
+                        break;
+                    } else if ((type(q) == hlist_node) && (subtype(q) == indent_list)) {
+                        /* go on */
+                    } else if (is_char_node(q)) {
+                        break;
+                    } else if (non_discardable(q)) {
+                        break;
+                    } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) {
+                        break;
+                    }
+                    q = vlink(q);
+                }
+            }
+        /* end mathskip code */
+
+        r = cur_break(cur_p);
+        q = null;
+        disc_break = false;
+        post_disc_break = false;
+        glue_break = false;
+
+
+        if (r == null) {
+            for (r = temp_head; vlink(r) != null; r = vlink(r));
+            if (r == final_par_glue) {
+                /* This should almost always be true... */
+                /* TODO assert ? */
+                q = r;
+                /* |q| refers to the last node of the line (and paragraph) */
+                r = alink(r);
+            }
+            /* |r| refers to the node after which the dir nodes should be closed */
+        } else if (type(r) == math_node) {
+            surround(r) = 0;
+            /* begin mathskip code */
+            reset_glue_to_zero(r);
+            /* end mathskip code */
+        } else if (type(r) == glue_node) {
+            copy_glue_values(r,right_skip);
+            subtype(r) = right_skip_code + 1;
+            glue_break = true;
+            /* |q| refers to the last node of the line */
+            q = r;
+            r = alink(r);
+            assert(vlink(r) == q);
+            /* |r| refers to the node after which the dir nodes should be closed */
+        } else if (type(r) == disc_node) {
+            halfword a = alink(r);
+            halfword v = vlink(r);
+            assert(a != null);
+            assert(v != null);
+            switch (subtype(r)) {
+            case select_disc:
+                if (vlink_pre_break(r) != null) {
+                    flush_node_list(vlink_pre_break(r));
+                    vlink_pre_break(r) = null;
+                    tlink_pre_break(r) = null;
+                }
+                if (vlink_no_break(r) != null) {
+                    couple_nodes(a, vlink_no_break(r));
+                    couple_nodes(tlink_no_break(r), r);
+                    vlink_no_break(r) = null;
+                    tlink_no_break(r) = null;
+                }
+
+                assert(type(a) == disc_node && subtype(a) == init_disc);
+                flush_node_list(vlink_no_break(a));
+                vlink_no_break(a) = null;
+                tlink_no_break(a) = null;
+                flush_node_list(vlink_pre_break(a));
+                vlink_pre_break(a) = null;
+                tlink_pre_break(a) = null;
+                flush_node_list(vlink_post_break(a));
+                vlink_post_break(a) = null;
+                tlink_post_break(a) = null;
+
+                break;
+            case init_disc:
+                assert(type(v) == disc_node && subtype(v) == select_disc);
+                subtype(v) = syllable_disc;     /* not special any more */
+                flush_node_list(vlink_no_break(v));
+                vlink_no_break(v) = vlink_post_break(r);
+                tlink_no_break(v) = tlink_post_break(r);
+                vlink_post_break(r) = null;
+                tlink_post_break(r) = null;
+            default:
+                if (vlink_no_break(r) != null) {
+                    flush_node_list(vlink_no_break(r));
+                    vlink_no_break(r) = null;
+                    tlink_no_break(r) = null;
+                }
+                if (vlink_pre_break(r) != null) {
+                    couple_nodes(a, vlink_pre_break(r));
+                    couple_nodes(tlink_pre_break(r), r);
+                    vlink_pre_break(r) = null;
+                    tlink_pre_break(r) = null;
+                }
+            }
+            if (vlink_post_break(r) != null) {
+                couple_nodes(r, vlink_post_break(r));
+                couple_nodes(tlink_post_break(r), v);
+                vlink_post_break(r) = null;
+                tlink_post_break(r) = null;
+                post_disc_break = true;
+            }
+            disc_break = true;
+        } else if (type(r) == kern_node) {
+            width(r) = 0;
+        }
+
+        /* DIR: Adjust the dir stack based on dir nodes in this line; */
+        /* TODO what about the previousparagraph ??? */
+        if (have_directional) {
+            halfword e;
+            halfword p;
+            for (e = vlink(temp_head); e != null && e != cur_break(cur_p); e = vlink(e)) {
+                if (type(e) == dir_node) {
+                    if (dir_dir(e) >= 0) {
+                        dir_ptr = do_push_dir_node(dir_ptr, e);
+                    } else if (dir_ptr != null && dir_dir(dir_ptr) == (dir_dir(e) + dir_swap)) {
+                        dir_ptr = do_pop_dir_node(dir_ptr);
+                    }
+                }
+            }
+            assert(e == cur_break(cur_p));
+
+            /* DIR: Insert dir nodes at the end of the current line; */
+            e = vlink(r);
+            for (p = dir_ptr; p != null; p = vlink(p)) {
+                halfword s = new_dir(dir_dir(p) - dir_swap);
+                delete_attribute_ref(node_attr(s));
+                node_attr(s) = node_attr(r);
+                add_node_attr_ref(node_attr(s));
+                couple_nodes(r, s);
+                try_couple_nodes(s, e);
+                r = s;
+            }
+        }
+        if (passive_right_box(cur_p) != null) {
+            /* TODO: CHECK has |s| below a |alink| ? */
+            halfword s = copy_node_list(passive_right_box(cur_p));
+            halfword e = vlink(r);
+            couple_nodes(r, s);
+            try_couple_nodes(s, e);
+            r = s;
+        }
+        if (q == null) {
+            q = r;
+        }
+        /* Now [q] refers to the last node on the line */
+
+        /* FIXME from this point on we no longer keep alink() valid */
+
+        /* at this point |q| is the rightmost breakpoint; the only exception is
+           the case of a discretionary break with non-empty |pre_break|, then |q|
+           has been changed to the last node of the |pre_break| list */
+        /* If the par ends with a \break command, the last line is utterly empty.
+           That is the case of |q==temp_head| */
+        if (q != temp_head && protrude_chars > 0) {
+            halfword p, ptmp;
+            if (disc_break && (is_char_node(q) || (type(q) != disc_node))) {
+                p = q;          /* |q| has been reset to the last node of |pre_break| */
+                ptmp = p;
+            } else {
+                p = alink(q);   /* get |vlink(p) = q| */
+                assert(vlink(p) == q);
+                ptmp = p;
+            }
+            p = find_protchar_right(vlink(temp_head), p);
+            w = char_pw(p, right_side);
+            if (w != 0) {       /* we have found a marginal kern, append it after |ptmp| */
+                k = new_margin_kern(-w, last_rightmost_char, right_side);
+                delete_attribute_ref(node_attr(k));
+                node_attr(k) = node_attr(p);
+                add_node_attr_ref(node_attr(k));
+                try_couple_nodes(k, vlink(ptmp));
+                couple_nodes(ptmp,k);
+                if (ptmp == q)
+                    q = vlink(q);
+            }
+        }
+        /* if |q| was not a breakpoint at glue and has been reset to |rightskip|
+           then we append |rightskip| after |q| now */
+        if (!glue_break) {
+            /* Put the \.{\\rightskip} glue after node |q|; */
+            halfword r1 = new_glue((right_skip == null ? zero_glue : right_skip));
+            subtype(r1) = right_skip_code+1;
+            try_couple_nodes(r1,vlink(q));
+            delete_attribute_ref(node_attr(r1));
+            node_attr(r1) = node_attr(q);
+            add_node_attr_ref(node_attr(r1));
+            couple_nodes(q,r1);
+            q = r1;
+        }
+
+        /* /Modify the end of the line to reflect the nature of the break and to
+           include \.{\\rightskip}; also set the proper value of |disc_break|; */
+
+        /* Put the \.{\\leftskip} glue at the left and detach this line; */
+        /* The following code begins with |q| at the end of the list to be
+           justified. It ends with |q| at the beginning of that list, and with
+           |vlink(temp_head)| pointing to the remainder of the paragraph, if any. */
+        r = vlink(q);
+        vlink(q) = null;
+
+        q = vlink(temp_head);
+        try_couple_nodes(temp_head, r);
+        if (passive_left_box(cur_p) != null && passive_left_box(cur_p) != 0) {
+            /* omega bits: */
+            halfword s;
+            r = copy_node_list(passive_left_box(cur_p));
+            s = vlink(q);
+            couple_nodes(r,q);
+            q = r;
+            if ((cur_line == cur_list.pg_field + 1) && (s != null)) {
+                if (type(s) == hlist_node) {
+                    if (list_ptr(s) == null) {
+                        q = vlink(q);
+                        try_couple_nodes(r,vlink(s));
+                        try_couple_nodes(s, r);
+                    }
+                }
+            }
+        }
+        /*at this point |q| is the leftmost node; all discardable nodes have been discarded */
+        if (protrude_chars > 0) {
+            halfword p;
+            p = q;
+            p = find_protchar_left(p, false);   /* no more discardables */
+            w = char_pw(p, left_side);
+            if (w != 0) {
+                k = new_margin_kern(-w, last_leftmost_char, left_side);
+                delete_attribute_ref(node_attr(k));
+                node_attr(k) = node_attr(q);
+                add_node_attr_ref(node_attr(k));
+                couple_nodes(k,q);
+                q = k;
+            }
+        }
+        if (! glue_is_zero(left_skip)) {
+            r = new_glue(left_skip);
+            subtype(r) = left_skip_code+1;
+            delete_attribute_ref(node_attr(r));
+            node_attr(r) = node_attr(q);
+            add_node_attr_ref(node_attr(r));
+            couple_nodes(r,q);
+            q = r;
+        }
+        /* /Put the \.{\\leftskip} glue at the left and detach this line; */
+
+        /* Call the packaging subroutine, setting |just_box| to the justified box; */
+        /* Now |q| points to the hlist that represents the current line of the
+           paragraph. We need to compute the appropriate line width, pack the
+           line into a box of this size, and shift the box by the appropriate
+           amount of indentation. */
+        if (cur_line > last_special_line) {
+            cur_width = second_width;
+            cur_indent = second_indent;
+        } else if (par_shape_ptr == null) {
+            cur_width = first_width;
+            cur_indent = first_indent;
+        } else {
+            cur_indent = varmem[(par_shape_ptr + 2 * cur_line)].cint;
+            cur_width = varmem[(par_shape_ptr + 2 * cur_line + 1)].cint;
+        }
+        adjust_tail = adjust_head;
+        pre_adjust_tail = pre_adjust_head;
+        if (adjust_spacing > 0) {
+            just_box = hpack(q, cur_width, cal_expand_ratio, paragraph_dir);
+        } else {
+            just_box = hpack(q, cur_width, exactly, paragraph_dir);
+        }
+        shift_amount(just_box) = cur_indent;
+        subtype(just_box) = line_list;
+        /* /Call the packaging subroutine, setting |just_box| to the justified box; */
+
+        if ((vlink(contrib_head) != null))
+            checked_break_filter(pre_box);
+        if (pre_adjust_head != pre_adjust_tail) {
+            append_list(pre_adjust_head, pre_adjust_tail);
+            checked_break_filter(pre_adjust);
+        }
+        pre_adjust_tail = null;
+        append_to_vlist(just_box,lua_key_index(post_linebreak));
+        checked_break_filter(box);
+        if (adjust_head != adjust_tail) {
+            append_list(adjust_head, adjust_tail);
+            checked_break_filter(adjust);
+        }
+        adjust_tail = null;
+
+        /* /Append the new box to the current vertical list, followed by the list of
+           special nodes taken out of the box by the packager; */
+
+        /* Append a penalty node, if a nonzero penalty is appropriate */
+        /* Penalties between the lines of a paragraph come from club and widow lines,
+           from the |inter_line_penalty| parameter, and from lines that end at
+           discretionary breaks.  Breaking between lines of a two-line paragraph gets
+           both club-line and widow-line penalties. The local variable |pen| will
+           be set to the sum of all relevant penalties for the current line, except
+           that the final line is never penalized. */
+        if (cur_line + 1 != best_line) {
+            q = inter_line_penalties_ptr;
+            if (q != null) {
+                r = cur_line;
+                if (r > penalty(q))
+                    r = penalty(q);
+                pen = penalty(q + r);
+            } else {
+                if (passive_pen_inter(cur_p) != 0) {
+                    pen = passive_pen_inter(cur_p);
+                } else {
+                    pen = inter_line_penalty;
+                }
+            }
+            q = club_penalties_ptr;
+            if (q != null) {
+                r = cur_line - cur_list.pg_field;       /* prevgraf */
+                if (r > penalty(q))
+                    r = penalty(q);
+                pen += penalty(q + r);
+            } else if (cur_line == cur_list.pg_field + 1) {     /* prevgraf */
+                pen += club_penalty;
+            }
+            q = widow_penalties_ptr;
+            if (q != null) {
+                r = best_line - cur_line - 1;
+                if (r > penalty(q))
+                    r = penalty(q);
+                pen += penalty(q + r);
+            } else if (cur_line + 2 == best_line) {
+                pen += widow_penalty;
+            }
+            if (disc_break) {
+                if (passive_pen_broken(cur_p) != 0) {
+                    pen += passive_pen_broken(cur_p);
+                } else {
+                    pen += broken_penalty;
+                }
+            }
+            if (pen != 0) {
+                r = new_penalty(pen,linebreak_penalty);
+                couple_nodes(cur_list.tail_field, r);
+                cur_list.tail_field = r;
+            }
+        }
+        /* /Append a penalty node, if a nonzero penalty is appropriate */
+
+        /* /Justify the line ending at breakpoint |cur_p|, and append it to the
+           current vertical list, together with associated penalties and other
+           insertions;   */
+        incr(cur_line);
+        cur_p = next_break(cur_p);
+        if (cur_p != null && !post_disc_break) {
+            /* Prune unwanted nodes at the beginning of the next line; */
+            /* Glue and penalty and kern and math nodes are deleted at the
+               beginning of a line, except in the anomalous case that the
+               node to be deleted is actually one of the chosen
+               breakpoints. Otherwise the pruning done here is designed to
+               match the lookahead computation in |try_break|, where the
+               |break_width| values are computed for non-discretionary
+               breakpoints. */
+            r = temp_head;
+            /*
+                Normally we have a matching math open and math close node but when we cross a line
+                the open node is removed, including any glue or penalties following it. This is however
+                not that nice for callbacks that rely on symmetry. Of course this only counts for one
+                liners, as we can still have only a begin or end node on a line. The end_of_math lua
+                helper is made robust against this although there you should be aware of the fact that
+                one can end up in the middle of math in callbacks that don't work on whole paragraphs,
+                but at least this branch makes sure that some proper analysis is possible. (todo: check
+                if math glyphs have the subtype marked done).
+
+                Todo: turn math nodes into glues when mathskip otherwise remove them.
+            */
+            while (1) {
+                q = vlink(r);
+                /*
+                if (q == cur_break(cur_p) || is_char_node(q))
+                    break;
+                if (!((type(q) == local_par_node))) {
+                    if (non_discardable(q) || (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern))
+                        break;
+                }
+                */
+                if (type(q) == math_node) {
+                    /* begin mathskip code */
+                    surround(q) = 0 ;
+                    reset_glue_to_zero(q);
+                    /* end mathskip code */
+                }
+                if (q == cur_break(cur_p)) {
+                    break;
+                } else if (is_char_node(q)) {
+                    break;
+                } else if (type(q) == local_par_node) {
+                    /* weird, in the middle somewhere */
+                } else if (non_discardable(q)) {
+                    break;
+                } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) {
+                    break;
+                }
+                r = q;
+            }
+            if (r != temp_head) {
+                vlink(r) = null;
+                flush_node_list(vlink(temp_head));
+                try_couple_nodes(temp_head, q);
+            }
+        }
+    } while (cur_p != null);
+    if ((cur_line != best_line) || (vlink(temp_head) != null))
+        confusion("line breaking");
+    cur_list.pg_field = best_line - 1;  /* prevgraf */
+    cur_list.dirs_field = dir_ptr;      /* |dir_save| */
+    dir_ptr = null;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/postlinebreak.c
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
-
-postlinebreak.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-So far we have gotten a little way into the |line_break| routine, having covered
-its important |try_break| subroutine. Now let's consider the rest of the process.
-
-The main loop of |line_break| traverses the given hlist, starting at
-|vlink(temp_head)|, and calls |try_break| at each legal breakpoint. A variable
-called |auto_breaking| is set to true except within math formulas, since glue
-nodes are not legal breakpoints when they appear in formulas.
-
-The current node of interest in the hlist is pointed to by |cur_p|. Another
-variable, |prev_p|, is usually one step behind |cur_p|, but the real meaning of
-|prev_p| is this: If |type(cur_p)=glue_node| then |cur_p| is a legal breakpoint
-if and only if |auto_breaking| is true and |prev_p| does not point to a glue
-node, penalty node, explicit kern node, or math node.
-
-The total number of lines that will be set by |post_line_break| is
-|best_line-prev_graf-1|. The last breakpoint is specified by
-|break_node(best_bet)|, and this passive node points to the other breakpoints via
-the |prev_break| links. The finishing-up phase starts by linking the relevant
-passive nodes in forward order, changing |prev_break| to |next_break|. (The
-|next_break| fields actually reside in the same memory space as the |prev_break|
-fields did, but we give them a new name because of their new significance.) Then
-the lines are justified, one by one.
-
-The |post_line_break| must also keep an dir stack, so that it can output end
-direction instructions at the ends of lines and begin direction instructions at
-the beginnings of lines.
-
-*/
-
-/*tex The new name for |prev_break| after links are reversed: */
-
-#define next_break prev_break
-
-/*tex The |int|s are actually halfwords. */
-
-void ext_post_line_break(int paragraph_dir,
-                         int right_skip,
-                         int left_skip,
-                         int protrude_chars,
-                         halfword par_shape_ptr,
-                         int adjust_spacing,
-                         halfword inter_line_penalties_ptr,
-                         int inter_line_penalty,
-                         int club_penalty,
-                         halfword club_penalties_ptr,
-                         halfword widow_penalties_ptr,
-                         int widow_penalty,
-                         int broken_penalty,
-                         halfword final_par_glue,
-                         halfword best_bet,
-                         halfword last_special_line,
-                         scaled second_width,
-                         scaled second_indent,
-                         scaled first_width,
-                         scaled first_indent, halfword best_line)
-{
-
-    boolean have_directional = true;
-    /*tex temporary registers for list manipulation */
-    halfword q, r;
-    halfword k;
-    scaled w;
-     /*tex was a break at glue? */
-    boolean glue_break;
-    /*tex was the current break at a discretionary node? */
-    boolean disc_break;
-    /*tex and did it have a nonempty post-break part? */
-    boolean post_disc_break;
-    /*tex width of line number |cur_line| */
-    scaled cur_width;
-    /*tex left margin of line number |cur_line| */
-    scaled cur_indent;
-    /*tex use when calculating penalties between lines */
-    int pen;
-    /*tex |cur_p|, but localized */
-    halfword cur_p;
-    /*tex the current line number being justified */
-    halfword cur_line;
-    /*tex the current direction: */
-    dir_ptr = cur_list.dirs_field;
-    /*tex
-        Reverse the links of the relevant passive nodes, setting |cur_p| to the
-        first breakpoint. The job of reversing links in a list is conveniently
-        regarded as the job of taking items off one stack and putting them on
-        another. In this case we take them off a stack pointed to by |q| and
-        having |prev_break| fields; we put them on a stack pointed to by |cur_p|
-        and having |next_break| fields. Node |r| is the passive node being moved
-        from stack to stack.
-    */
-    q = break_node(best_bet);
-    /*tex |cur_p| will become the first breakpoint; */
-    cur_p = null;
-    do {
-        r = q;
-        q = prev_break(q);
-        next_break(r) = cur_p;
-        cur_p = r;
-    } while (q != null);
-    /*tex prevgraf + 1 */
-    cur_line = cur_list.pg_field + 1;
-    do {
-        /*tex
-            Justify the line ending at breakpoint |cur_p|, and append it to the
-            current vertical list, together with associated penalties and other
-            insertions.
-
-            The current line to be justified appears in a horizontal list
-            starting at |vlink(temp_head)| and ending at |cur_break(cur_p)|. If
-            |cur_break(cur_p)| is a glue node, we reset the glue to equal the
-            |right_skip| glue; otherwise we append the |right_skip| glue at the
-            right. If |cur_break(cur_p)| is a discretionary node, we modify the
-            list so that the discretionary break is compulsory, and we set
-            |disc_break| to |true|. We also append the |left_skip| glue at the
-            left of the line, unless it is zero.
-        */
-        if (dir_ptr != null) {
-            /*tex Insert dir nodes at the beginning of the current line. */
-            for (q = dir_ptr; q != null; q = vlink(q)) {
-                halfword tmp = new_dir(dir_dir(q));
-                halfword nxt = vlink(temp_head);
-                delete_attribute_ref(node_attr(tmp));
-                node_attr(tmp) = node_attr(temp_head);
-                add_node_attr_ref(node_attr(tmp));
-                couple_nodes(temp_head, tmp);
-                /*tex \.{\\break}\.{\\par} */
-                try_couple_nodes(tmp, nxt);
-            }
-            flush_node_list(dir_ptr);
-            dir_ptr = null;
-        }
-        /*tex
-            Modify the end of the line to reflect the nature of the break and to
-            include \.{\\rightskip}; also set the proper value of |disc_break|.
-            At the end of the following code, |q| will point to the final node on
-            the list about to be justified. In the meanwhile |r| will point to
-            the node we will use to insert end-of-line stuff after. |q==null|
-            means we use the final position of |r|.
-        */
-
-        if (temp_head != null) {
-            /*tex begin mathskip code */
-            q = temp_head;
-            while(q != null) {
-                if (type(q) == math_node) {
-                    surround(q) = 0 ;
-                    reset_glue_to_zero(q);
-                    break;
-                } else if ((type(q) == hlist_node) && (subtype(q) == indent_list)) {
-                    /* go on */
-                } else if (is_char_node(q)) {
-                    break;
-                } else if (non_discardable(q)) {
-                    break;
-                } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) {
-                    break;
-                }
-                q = vlink(q);
-            }
-            /*tex end mathskip code */
-        }
-
-        r = cur_break(cur_p);
-        q = null;
-        disc_break = false;
-        post_disc_break = false;
-        glue_break = false;
-
-
-        if (r == null) {
-            for (r = temp_head; vlink(r) != null; r = vlink(r));
-            if (r == final_par_glue) {
-                /*tex This should almost always be true... */
-                q = r;
-                /*tex |q| refers to the last node of the line (and paragraph) */
-                r = alink(r);
-            }
-            /*tex |r| refers to the node after which the dir nodes should be closed */
-        } else if (type(r) == math_node) {
-            surround(r) = 0;
-            /*tex begin mathskip code */
-            reset_glue_to_zero(r);
-            /*tex end mathskip code */
-        } else if (type(r) == glue_node) {
-            copy_glue_values(r,right_skip);
-            subtype(r) = right_skip_code + 1;
-            glue_break = true;
-            /*tex |q| refers to the last node of the line */
-            q = r;
-            r = alink(r);
-            assert(vlink(r) == q);
-            /*tex |r| refers to the node after which the dir nodes should be closed */
-        } else if (type(r) == disc_node) {
-            halfword a = alink(r);
-            halfword v = vlink(r);
-            assert(a != null);
-            assert(v != null);
-            switch (subtype(r)) {
-            case select_disc:
-                if (vlink_pre_break(r) != null) {
-                    flush_node_list(vlink_pre_break(r));
-                    vlink_pre_break(r) = null;
-                    tlink_pre_break(r) = null;
-                }
-                if (vlink_no_break(r) != null) {
-                    couple_nodes(a, vlink_no_break(r));
-                    couple_nodes(tlink_no_break(r), r);
-                    vlink_no_break(r) = null;
-                    tlink_no_break(r) = null;
-                }
-                assert(type(a) == disc_node && subtype(a) == init_disc);
-                flush_node_list(vlink_no_break(a));
-                vlink_no_break(a) = null;
-                tlink_no_break(a) = null;
-                flush_node_list(vlink_pre_break(a));
-                vlink_pre_break(a) = null;
-                tlink_pre_break(a) = null;
-                flush_node_list(vlink_post_break(a));
-                vlink_post_break(a) = null;
-                tlink_post_break(a) = null;
-                break;
-            case init_disc:
-                assert(type(v) == disc_node && subtype(v) == select_disc);
-                subtype(v) = syllable_disc;
-                flush_node_list(vlink_no_break(v));
-                vlink_no_break(v) = vlink_post_break(r);
-                tlink_no_break(v) = tlink_post_break(r);
-                vlink_post_break(r) = null;
-                tlink_post_break(r) = null;
-            default:
-                if (vlink_no_break(r) != null) {
-                    flush_node_list(vlink_no_break(r));
-                    vlink_no_break(r) = null;
-                    tlink_no_break(r) = null;
-                }
-                if (vlink_pre_break(r) != null) {
-                    couple_nodes(a, vlink_pre_break(r));
-                    couple_nodes(tlink_pre_break(r), r);
-                    vlink_pre_break(r) = null;
-                    tlink_pre_break(r) = null;
-                }
-            }
-            if (vlink_post_break(r) != null) {
-                couple_nodes(r, vlink_post_break(r));
-                couple_nodes(tlink_post_break(r), v);
-                vlink_post_break(r) = null;
-                tlink_post_break(r) = null;
-                post_disc_break = true;
-            }
-            disc_break = true;
-        } else if (type(r) == kern_node) {
-            width(r) = 0;
-        }
-        /*tex Adjust the dir stack based on dir nodes in this line. */
-        if (have_directional) {
-            halfword e, p;
-            for (e = vlink(temp_head); e != null && e != cur_break(cur_p); e = vlink(e)) {
-                if (type(e) == dir_node) {
-                    if (subtype(e) == normal_dir) {
-                        dir_ptr = do_push_dir_node(dir_ptr, e);
-                    } else if (dir_ptr != null && dir_dir(dir_ptr) == dir_dir(e)) {
-                        dir_ptr = do_pop_dir_node(dir_ptr);
-                    }
-                }
-            }
-            assert(e == cur_break(cur_p));
-            /*tex Insert dir nodes at the end of the current line. */
-            e = vlink(r);
-            for (p = dir_ptr; p != null; p = vlink(p)) {
-                halfword s = new_dir(dir_dir(p));
-                subtype(s) = cancel_dir;
-                delete_attribute_ref(node_attr(s));
-                node_attr(s) = node_attr(r);
-                add_node_attr_ref(node_attr(s));
-                couple_nodes(r, s);
-                try_couple_nodes(s, e);
-                r = s;
-            }
-        }
-        if (passive_right_box(cur_p) != null) {
-            halfword s = copy_node_list(passive_right_box(cur_p));
-            halfword e = vlink(r);
-            couple_nodes(r, s);
-            try_couple_nodes(s, e);
-            r = s;
-        }
-        if (q == null) {
-            q = r;
-        }
-        /*tex
-            Now [q] refers to the last node on the line and therefore the
-            rightmost breakpoint. The only exception is the case of a
-            discretionary break with non-empty |pre_break|, then |q| has been
-            changed to the last node of the |pre_break| list. If the par ends
-            with a \break command, the last line is utterly empty. That is the
-            case of |q==temp_head|.
-        */
-        if (q != temp_head && protrude_chars > 0) {
-            halfword p, ptmp;
-            if (disc_break && (is_char_node(q) || (type(q) != disc_node))) {
-                /*tex |q| is reset to the last node of |pre_break| */
-                p = q;
-                ptmp = p;
-            } else {
-                /*tex get |vlink(p) = q| */
-                p = alink(q);
-                assert(vlink(p) == q);
-                ptmp = p;
-            }
-            p = find_protchar_right(vlink(temp_head), p);
-            w = char_pw(p, right_side);
-            if (w != 0) {
-                /*tex we have found a marginal kern, append it after |ptmp| */
-                k = new_margin_kern(-w, last_rightmost_char, right_side);
-                delete_attribute_ref(node_attr(k));
-                node_attr(k) = node_attr(p);
-                add_node_attr_ref(node_attr(k));
-                try_couple_nodes(k, vlink(ptmp));
-                couple_nodes(ptmp,k);
-                if (ptmp == q)
-                    q = vlink(q);
-            }
-        }
-        /*tex
-            If |q| was not a breakpoint at glue and has been reset to |rightskip|
-            then we append |rightskip| after |q| now.
-        */
-        if (!glue_break) {
-            /*tex Put the \.{\\rightskip} glue after node |q|. */
-            halfword r1 = new_glue((right_skip == null ? zero_glue : right_skip));
-            subtype(r1) = right_skip_code+1;
-            try_couple_nodes(r1,vlink(q));
-            delete_attribute_ref(node_attr(r1));
-            node_attr(r1) = node_attr(q);
-            add_node_attr_ref(node_attr(r1));
-            couple_nodes(q,r1);
-            q = r1;
-        }
-        /*tex
-            Modify the end of the line to reflect the nature of the break and to
-            include \.{\\rightskip}; also set the proper value of |disc_break|;
-            Also put the \.{\\leftskip} glue at the left and detach this line.
-
-            The following code begins with |q| at the end of the list to be
-            justified. It ends with |q| at the beginning of that list, and with
-            |vlink(temp_head)| pointing to the remainder of the paragraph, if
-            any.
-        */
-        r = vlink(q);
-        vlink(q) = null;
-
-        q = vlink(temp_head);
-        try_couple_nodes(temp_head, r);
-        if (passive_left_box(cur_p) != null && passive_left_box(cur_p) != 0) {
-            halfword s;
-            r = copy_node_list(passive_left_box(cur_p));
-            s = vlink(q);
-            couple_nodes(r,q);
-            q = r;
-            if ((cur_line == cur_list.pg_field + 1) && (s != null)) {
-                if (type(s) == hlist_node) {
-                    if (list_ptr(s) == null) {
-                        q = vlink(q);
-                        try_couple_nodes(r,vlink(s));
-                        try_couple_nodes(s, r);
-                    }
-                }
-            }
-        }
-        /*tex
-            At this point |q| is the leftmost node; all discardable nodes have
-            been discarded
-        */
-        if (protrude_chars > 0) {
-            halfword p;
-            p = q;
-            p = find_protchar_left(p, false);
-            w = char_pw(p, left_side);
-            if (w != 0) {
-                k = new_margin_kern(-w, last_leftmost_char, left_side);
-                delete_attribute_ref(node_attr(k));
-                node_attr(k) = node_attr(q);
-                add_node_attr_ref(node_attr(k));
-                couple_nodes(k,q);
-                q = k;
-            }
-        }
-        if (! glue_is_zero(left_skip)) {
-            r = new_glue(left_skip);
-            subtype(r) = left_skip_code+1;
-            delete_attribute_ref(node_attr(r));
-            node_attr(r) = node_attr(q);
-            add_node_attr_ref(node_attr(r));
-            couple_nodes(r,q);
-            q = r;
-        }
-        /*tex
-            Put the \.{\\leftskip} glue at the left and detach this line. Call
-            the packaging subroutine, setting |just_box| to the justified box.
-            Now |q| points to the hlist that represents the current line of the
-            paragraph. We need to compute the appropriate line width, pack the
-            line into a box of this size, and shift the box by the appropriate
-            amount of indentation.
-        */
-        if (cur_line > last_special_line) {
-            cur_width = second_width;
-            cur_indent = second_indent;
-        } else if (par_shape_ptr == null) {
-            cur_width = first_width;
-            cur_indent = first_indent;
-        } else {
-            cur_indent = varmem[(par_shape_ptr + 2 * cur_line)].cint;
-            cur_width = varmem[(par_shape_ptr + 2 * cur_line + 1)].cint;
-        }
-        adjust_tail = adjust_head;
-        pre_adjust_tail = pre_adjust_head;
-        if (adjust_spacing > 0) {
-            just_box = hpack(q, cur_width, cal_expand_ratio, paragraph_dir);
-        } else {
-            just_box = hpack(q, cur_width, exactly, paragraph_dir);
-        }
-        shift_amount(just_box) = cur_indent;
-        subtype(just_box) = line_list;
-        /*tex
-            Call the packaging subroutine, setting |just_box| to the justified
-            box.
-        */
-        if ((vlink(contrib_head) != null))
-            checked_break_filter(pre_box);
-        if (pre_adjust_head != pre_adjust_tail) {
-            append_list(pre_adjust_head, pre_adjust_tail);
-            checked_break_filter(pre_adjust);
-        }
-        pre_adjust_tail = null;
-        append_to_vlist(just_box,lua_key_index(post_linebreak));
-        checked_break_filter(box);
-        if (adjust_head != adjust_tail) {
-            append_list(adjust_head, adjust_tail);
-            checked_break_filter(adjust);
-        }
-        adjust_tail = null;
-        /*tex
-            Append the new box to the current vertical list, followed by the list
-            of special nodes taken out of the box by the packager. Append a
-            penalty node, if a nonzero penalty is appropriate. Penalties between
-            the lines of a paragraph come from club and widow lines, from the
-            |inter_line_penalty| parameter, and from lines that end at
-            discretionary breaks. Breaking between lines of a two-line paragraph
-            gets both club-line and widow-line penalties. The local variable
-            |pen| will be set to the sum of all relevant penalties for the
-            current line, except that the final line is never penalized.
-        */
-        if (cur_line + 1 != best_line) {
-            q = inter_line_penalties_ptr;
-            if (q != null) {
-                r = cur_line;
-                if (r > penalty(q))
-                    r = penalty(q);
-                pen = penalty(q + r);
-            } else {
-                if (passive_pen_inter(cur_p) != 0) {
-                    pen = passive_pen_inter(cur_p);
-                } else {
-                    pen = inter_line_penalty;
-                }
-            }
-            q = club_penalties_ptr;
-            if (q != null) {
-                /*tex prevgraf */
-                r = cur_line - cur_list.pg_field;
-                if (r > penalty(q))
-                    r = penalty(q);
-                pen += penalty(q + r);
-            } else if (cur_line == cur_list.pg_field + 1) {
-                /*tex prevgraf */
-                pen += club_penalty;
-            }
-            q = widow_penalties_ptr;
-            if (q != null) {
-                r = best_line - cur_line - 1;
-                if (r > penalty(q))
-                    r = penalty(q);
-                pen += penalty(q + r);
-            } else if (cur_line + 2 == best_line) {
-                pen += widow_penalty;
-            }
-            if (disc_break) {
-                if (passive_pen_broken(cur_p) != 0) {
-                    pen += passive_pen_broken(cur_p);
-                } else {
-                    pen += broken_penalty;
-                }
-            }
-            if (pen != 0) {
-                r = new_penalty(pen,linebreak_penalty);
-                couple_nodes(cur_list.tail_field, r);
-                cur_list.tail_field = r;
-            }
-        }
-        /*tex
-            Append a penalty node, if a nonzero penalty is appropriate. Justify
-            the line ending at breakpoint |cur_p|, and append it to the current
-            vertical list, together with associated penalties and other
-            insertions.
-        */
-        incr(cur_line);
-        cur_p = next_break(cur_p);
-        if (cur_p != null && !post_disc_break) {
-            /*tex
-                Prune unwanted nodes at the beginning of the next line. Glue and
-                penalty and kern and math nodes are deleted at the beginning of a
-                line, except in the anomalous case that the node to be deleted is
-                actually one of the chosen breakpoints. Otherwise the pruning
-                done here is designed to match the lookahead computation in
-                |try_break|, where the |break_width| values are computed for
-                non-discretionary breakpoints.
-            */
-            r = temp_head;
-            /*tex
-                Normally we have a matching math open and math close node but
-                when we cross a line the open node is removed, including any glue
-                or penalties following it. This is however not that nice for
-                callbacks that rely on symmetry. Of course this only counts for
-                one liners, as we can still have only a begin or end node on a
-                line. The end_of_math lua helper is made robust against this
-                although there you should be aware of the fact that one can end
-                up in the middle of math in callbacks that don't work on whole
-                paragraphs, but at least this branch makes sure that some proper
-                analysis is possible. (todo: check if math glyphs have the
-                subtype marked done).
-            */
-            while (1) {
-                q = vlink(r);
-                /*
-                if (q == cur_break(cur_p) || is_char_node(q))
-                    break;
-                if (!((type(q) == local_par_node))) {
-                    if (non_discardable(q) || (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern))
-                        break;
-                }
-                */
-                if (type(q) == math_node) {
-                    /*tex begin mathskip code */
-                    surround(q) = 0 ;
-                    reset_glue_to_zero(q);
-                    /*tex end mathskip code */
-                }
-                if (q == cur_break(cur_p)) {
-                    break;
-                } else if (is_char_node(q)) {
-                    break;
-                } else if (type(q) == local_par_node) {
-                    /*tex weird, in the middle somewhere */
-                } else if (non_discardable(q)) {
-                    break;
-                } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) {
-                    break;
-                }
-                r = q;
-            }
-            if (r != temp_head) {
-                vlink(r) = null;
-                flush_node_list(vlink(temp_head));
-                try_couple_nodes(temp_head, q);
-            }
-        }
-    } while (cur_p != null);
-    if ((cur_line != best_line) || (vlink(temp_head) != null))
-        confusion("line breaking");
-    /*tex prevgraf */
-    cur_list.pg_field = best_line - 1;
-    /*tex |dir_save| */
-    cur_list.dirs_field = dir_ptr;
-    dir_ptr = null;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/primitive.c
+++ /dev/null
@@ -1,785 +0,0 @@
-/*
-
-primitive.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-Control sequences are stored and retrieved by means of a fairly standard hash
-table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C in
-{\sl The Art of Computer Programming\/}). Once a control sequence enters the
-table, it is never removed, because there are complicated situations involving
-\.{\\gdef} where the removal of a control sequence at the end of a group would be
-a mistake preventable only by the introduction of a complicated reference-count
-mechanism.
-
-The actual sequence of letters forming a control sequence identifier is stored in
-the |str_pool| array together with all the other strings. An auxiliary array
-|hash| consists of items with two halfword fields per word. The first of these,
-called |next(p)|, points to the next identifier belonging to the same coalesced
-list as the identifier corresponding to~|p|; and the other, called |text(p)|,
-points to the |str_start| entry for |p|'s identifier. If position~|p| of the hash
-table is empty, we have |text(p)=0|; if position |p| is either empty or the end
-of a coalesced hash list, we have |next(p)=0|. An auxiliary pointer variable
-called |hash_used| is maintained in such a way that all locations |p>=hash_used|
-are nonempty. The global variable |cs_count| tells how many multiletter control
-sequences have been defined, if statistics are being kept.
-
-A global boolean variable called |no_new_control_sequence| is set to |true|
-during the time that new hash table entries are forbidden.
-
-*/
-
-/*tex The hash table: */
-
-two_halves *hash;
-
-/*tex Allocation pointer for |hash|: */
-
-halfword hash_used;
-
-/*tex |hash_extra=hash| above |eqtb_size|: */
-
-int hash_extra;
-
-/*tex Maximum of the hash array: */
-
-halfword hash_top;
-
-/*tex Pointer to next high hash location: */
-
-halfword hash_high;
-
-/*tex Are new identifiers legal? */
-
-boolean no_new_control_sequence;
-
-/*tex Total number of known identifiers: */
-
-int cs_count;
-
-/*tex Test if all positions are occupied: */
-
-#define hash_is_full (hash_used==hash_base)
-
-/*tex
-
-    \.{\\primitive} support needs a few extra variables and definitions,
-    like:
-
-*/
-
-#define prim_base 1
-
-/*tex
-
-The arrays |prim| and |prim_eqtb| are used for name -> cmd,chr lookups. The are
-modelled after |hash| and |eqtb|, except that primitives do not have an
-|eq_level|, that field is replaced by |origin|.
-
-*/
-
-/*tex Link for coalesced lists: */
-
-#define prim_next(a) prim[(a)].lhfield
-
-/*tex String number for control sequence name: */
-
-#define prim_text(a) prim[(a)].rh
-
-/*tex Test if all positions are occupied: */
-
-#define prim_is_full (prim_used==prim_base)
-
-#define prim_origin_field(a) (a).hh.b1
-
-#define prim_eq_type_field(a)  (a).hh.b0
-
-#define prim_equiv_field(a) (a).hh.rh
-
-/*tex Level of definition: */
-
-#define prim_origin(a) prim_origin_field(prim_eqtb[(a)])
-
-/*tex Command code for equivalent: */
-
-#define prim_eq_type(a) prim_eq_type_field(prim_eqtb[(a)])
-
-/*tex Equivalent value: */
-
-#define prim_equiv(a) prim_equiv_field(prim_eqtb[(a)])
-
-/*tex Allocation pointer for |prim|: */
-
-static pointer prim_used;
-
-/*tex The primitives table: */
-
-static two_halves prim[(prim_size + 1)];
-
-static memory_word prim_eqtb[(prim_size + 1)];
-
-/*tex
-
-The array |prim_data| works the other way around, it is used for cmd,chr -> name
-lookups.
-
-*/
-
-typedef struct prim_info {
-    /*tex Number of name entries: */
-    halfword subids;
-    /*tex Offset to be used for |chr_code|s: */
-    halfword offset;
-    /*tex Array of names: */
-    str_number *names;
-} prim_info;
-
-static prim_info prim_data[(last_cmd + 1)];
-
-/*tex
-
-Initialize the memory arrays:
-
-*/
-
-void init_primitives(void)
-{
-    int k;
-    memset(prim_data, 0, (sizeof(prim_info) * (last_cmd + 1)));
-    memset(prim, 0, (sizeof(two_halves) * (prim_size + 1)));
-    memset(prim_eqtb, 0, (sizeof(memory_word) * (prim_size + 1)));
-    for (k = 0; k <= prim_size; k++) {
-        prim_eq_type(k) = undefined_cs_cmd;
-    }
-}
-
-/*tex Nothing is used (yet). */
-
-void ini_init_primitives(void)
-{
-    prim_used = prim_size;
-}
-
-
-/*tex
-
-The value of |hash_prime| should be roughly 85\%! of |hash_size|, and it should
-be a prime number. The theory of hashing tells us to expect fewer than two table
-probes, on the average, when the search is successful. [See J.~S. Vitter, {\sl
-Journal of the ACM\/ \bf30} (1983), 231--258.] @^Vitter, Jeffrey Scott@>
-
-*/
-
-static halfword compute_hash(const char *j, unsigned int l, halfword prime_number)
-{
-    int k;
-    halfword h = (unsigned char) *j;
-    for (k = 1; k <= (int)(l - 1); k++) {
-        h = h + h + (unsigned char) *(j + k);
-        while (h >= prime_number) {
-            h = h - prime_number;
-        }
-    }
-    return h;
-}
-
-/*tex
-
-Here is the subroutine that searches the primitive table for an identifier.
-
-*/
-
-pointer prim_lookup(str_number s)
-{
-    /*tex The hash code: */
-    int h;
-    /*tex The index in the |hash| array: */
-    pointer p;
-    unsigned char *j;
-    unsigned l;
-    if (s < STRING_OFFSET) {
-        p = s;
-        if ((p < 0) || (get_prim_eq_type(p) == undefined_cs_cmd)) {
-            p = undefined_primitive;
-        }
-    } else {
-        j = str_string(s);
-        l = (unsigned) str_length(s);
-        h = compute_hash((char *) j, l, prim_prime);
-        /*tex We start searching here; note that |0<=h<hash_prime|. */
-        p = h + prim_base;
-        while (1) {
-            if (prim_text(p) > 0)
-                if (str_length(prim_text(p)) == l)
-                    if (str_eq_str(prim_text(p), s))
-                        goto FOUND;
-            if (prim_next(p) == 0) {
-                if (no_new_control_sequence) {
-                    p = undefined_primitive;
-                } else {
-                    /*tex Insert a new primitive after |p|, then make |p| point to it. */
-                    if (prim_text(p) > 0) {
-                        do {
-                            /*tex Search for an empty location in |prim| */
-                            if (prim_is_full) {
-                                overflow("primitive size", prim_size);
-                            }
-                            decr(prim_used);
-                        } while (prim_text(prim_used) != 0);
-                        prim_next(p) = prim_used;
-                        p = prim_used;
-                    }
-                    prim_text(p) = s;
-                }
-                goto FOUND;
-            }
-            p = prim_next(p);
-        }
-    }
-  FOUND:
-    return p;
-}
-
-/*tex
-
-How to test a csname for primitive-ness?
-
-*/
-
-boolean is_primitive(str_number csname)
-{
-    int n, m;
-    char *ss;
-    m = prim_lookup(csname);
-    ss = makecstring(csname);
-    n = string_lookup(ss, str_length(csname));
-    free(ss);
-    return ((n != undefined_cs_cmd) && (m != undefined_primitive) &&
-        (eq_type(n) == prim_eq_type(m)) && (equiv(n) == prim_equiv(m)));
-}
-
-
-/*tex
-
-A few simple accessors.
-
-*/
-
-quarterword get_prim_eq_type(int p)
-{
-    return prim_eq_type(p);
-}
-
-quarterword get_prim_origin(int p)
-{
-    return prim_origin(p);
-}
-
-halfword get_prim_equiv(int p)
-{
-    return prim_equiv(p);
-}
-
-str_number get_prim_text(int p)
-{
-    return prim_text(p);
-}
-
-
-/*tex
-
-Dumping and undumping.
-
-*/
-
-void dump_primitives(void)
-{
-    int p, q;
-    for (p = 0; p <= prim_size; p++) {
-        dump_hh(prim[p]);
-    }
-    for (p = 0; p <= prim_size; p++) {
-        dump_wd(prim_eqtb[p]);
-    }
-    for (p = 0; p <= last_cmd; p++) {
-        dump_int(prim_data[p].offset);
-        dump_int(prim_data[p].subids);
-        for (q = 0; q < prim_data[p].subids; q++) {
-            dump_int(prim_data[p].names[q]);
-        }
-    }
-}
-
-void undump_primitives(void)
-{
-    int p, q;
-    for (p = 0; p <= prim_size; p++) {
-        undump_hh(prim[p]);
-    }
-    for (p = 0; p <= prim_size; p++) {
-        undump_wd(prim_eqtb[p]);
-    }
-    for (p = 0; p <= last_cmd; p++) {
-        undump_int(prim_data[p].offset);
-        undump_int(prim_data[p].subids);
-        if (prim_data[p].subids > 0) {
-            prim_data[p].names = (str_number *) xmalloc((unsigned) ((unsigned) prim_data[p].subids * sizeof(str_number *)));
-            for (q = 0; q < prim_data[p].subids; q++) {
-                undump_int(prim_data[p].names[q]);
-            }
-        }
-    }
-}
-
-/*tex
-
-We need to put \TeX's ``primitive'' control sequences into the hash table,
-together with their command code (which will be the |eq_type|) and an operand
-(which will be the |equiv|). The |primitive| procedure does this, in a way that
-no \TeX\ user can. The global value |cur_val| contains the new |eqtb| pointer
-after |primitive| has acted.
-
-Because the definitions of the actual user-accessible name of a primitive can be
-postponed until runtime, the function |primitive_def| is needed that does nothing
-except creating the control sequence name.
-
-*/
-
-void primitive_def(const char *s, size_t l, quarterword c, halfword o)
-{
-    int nncs = no_new_control_sequence;
-    no_new_control_sequence = false;
-    /*tex This creates the |text()| string: */
-    cur_val = string_lookup(s, l);
-    no_new_control_sequence = nncs;
-    eq_level(cur_val) = level_one;
-    eq_type(cur_val) = c;
-    equiv(cur_val) = o;
-}
-
-/*tex
-
-The function |store_primitive_name| sets up the bookkeeping for the reverse
-lookup. It is quite paranoid, because it is easy to mess this up accidentally.
-
-The |offset| is needed because sometimes character codes (in |o|) are indices
-into |eqtb| or are offset by a magical value to make sure they do not conflict
-with something else. We don't want the |prim_data[c].names| to have too many
-entries as it will just be wasted room, so |offset| is substracted from |o|
-because creating or accessing the array. The |assert(idx<=0xFFFF)| is not
-strictly needed, but it helps catch errors of this kind.
-
-*/
-
-static void store_primitive_name(str_number s, quarterword c, halfword o, halfword offset)
-{
-    int idx;
-    /*
-    if (prim_data[c].offset != 0 && prim_data[c].offset != offset) {
-        assert(false);
-    }
-    */
-    prim_data[c].offset = offset;
-    idx = ((int) o - offset);
-    /*
-    assert(idx >= 0);
-    assert(idx <= 0xFFFF);
-    */
-    if (prim_data[c].subids < (idx + 1)) {
-        str_number *new = (str_number *) xcalloc((unsigned) (idx + 1), sizeof(str_number *));
-        if (prim_data[c].names != NULL) {
-            /*
-            assert(prim_data[c].subids);
-            */
-            memcpy(new, (prim_data[c].names), (unsigned) (prim_data[c].subids) * sizeof(str_number));
-            free(prim_data[c].names);
-        }
-        prim_data[c].names = new;
-        prim_data[c].subids = idx + 1;
-    }
-    prim_data[c].names[idx] = s;
-}
-
-/*tex
-
-Compared to tex82, |primitive| has two extra parameters. The |off| is an offset
-that will be passed on to |store_primitive_name|, the |cmd_origin| is the bit
-that is used to group primitives by originator.
-
-*/
-
-void primitive(const char *thes, quarterword c, halfword o, halfword off, int cmd_origin)
-{
-    /*tex Needed to fill |prim_eqtb|: */
-    int prim_val;
-    str_number ss;
-    ss = maketexstring(thes);
-    if (cmd_origin == tex_command || cmd_origin == core_command) {
-        primitive_def(thes, strlen(thes), c, o);
-    }
-    prim_val = prim_lookup(ss);
-    prim_origin(prim_val) = (quarterword) cmd_origin;
-    prim_eq_type(prim_val) = c;
-    prim_equiv(prim_val) = o;
-    store_primitive_name(ss, c, o, off);
-}
-
-/*tex
-
-Here is a helper that does the actual hash insertion. This code far from ideal:
-the existance of |hash_extra| changes all the potential (short) coalesced lists
-into a single (long) one. This will create a slowdown.
-
-*/
-
-static halfword insert_id(halfword p, const unsigned char *j, unsigned int l)
-{
-    unsigned saved_cur_length;
-    unsigned saved_cur_string_size;
-    unsigned char *saved_cur_string;
-    const unsigned char *k;
-    if (cs_text(p) > 0) {
-        if (hash_high < hash_extra) {
-            incr(hash_high);
-            /*tex
-                Can't we use |eqtb_top| here (perhaps because that is not
-                finalized yet when called from |primitive|?
-            */
-            cs_next(p) = hash_high + eqtb_size;
-            p = cs_next(p);
-        } else {
-            /*tex
-                Search for an empty location in |hash|.
-            */
-            do {
-                if (hash_is_full)
-                    overflow("hash size", (unsigned) (hash_size + hash_extra));
-                decr(hash_used);
-            } while (cs_text(hash_used) != 0);
-            cs_next(p) = hash_used;
-            p = hash_used;
-        }
-    }
-    saved_cur_length = cur_length;
-    saved_cur_string = cur_string;
-    saved_cur_string_size = cur_string_size;
-    reset_cur_string();
-    for (k = j; k <= j + l - 1; k++) {
-        append_char(*k);
-    }
-    cs_text(p) = make_string();
-    cur_length = saved_cur_length;
-    xfree(cur_string);
-    cur_string = saved_cur_string;
-    cur_string_size = saved_cur_string_size;
-    incr(cs_count);
-    return p;
-}
-
-
-/*tex
-
-Here is the subroutine that searches the hash table for an identifier that
-matches a given string of length |l>1| appearing in |buffer[j.. (j+l-1)]|. If the
-identifier is found, the corresponding hash table address is returned. Otherwise,
-if the global variable |no_new_control_sequence| is |true|, the dummy address
-|undefined_control_sequence| is returned. Otherwise the identifier is inserted
-into the hash table and its location is returned.
-
-*/
-
-pointer id_lookup(int j, int l)
-{
-    /*tex The hash code: */
-    int h;
-    /*tex The index in |hash| array: */
-    pointer p;
-    h = compute_hash((char *) (buffer + j), (unsigned) l, hash_prime);
-    /*tex We start searching here. Note that |0<=h<hash_prime|: */
-    p = h + hash_base;
-    while (1) {
-        if (cs_text(p) > 0)
-            if (str_length(cs_text(p)) == (unsigned) l)
-                if (str_eq_buf(cs_text(p), j))
-                    goto FOUND;
-        if (cs_next(p) == 0) {
-            if (no_new_control_sequence) {
-                p = undefined_control_sequence;
-            } else {
-                p = insert_id(p, (buffer + j), (unsigned) l);
-            }
-            goto FOUND;
-        }
-        p = cs_next(p);
-    }
-  FOUND:
-    return p;
-}
-
-/*tex
-
-Here is a similar subroutine for finding a primitive in the hash.
-This one is based on a C string.
-
-*/
-
-pointer string_lookup(const char *s, size_t l)
-{
-    /*tex The hash code: */
-    int h;
-    /*tex The index in |hash| array: */
-    pointer p;
-    h = compute_hash(s, (unsigned) l, hash_prime);
-    /*tex We start searching here. Note that |0<=h<hash_prime|: */
-    p = h + hash_base;
-    while (1) {
-        if (cs_text(p) > 0)
-            if (str_eq_cstr(cs_text(p), s, l))
-                goto FOUND;
-        if (cs_next(p) == 0) {
-            if (no_new_control_sequence) {
-                p = undefined_control_sequence;
-            } else {
-                p = insert_id(p, (const unsigned char *) s, (unsigned) l);
-            }
-            goto FOUND;
-        }
-        p = cs_next(p);
-    }
-  FOUND:
-    return p;
-}
-
-/*tex
-
-The |print_cmd_chr| routine prints a symbolic interpretation of a command code
-and its modifier. This is used in certain `\.{You can\'t}' error messages, and in
-the implementation of diagnostic routines like \.{\\show}.
-
-The body of |print_cmd_chr| use to be a rather tedious listing of print commands,
-and most of it was essentially an inverse to the |primitive| routine that enters
-a \TeX\ primitive into |eqtb|.
-
-Thanks to |prim_data|, there is no need for all that tediousness. What is left of
-|primt_cnd_chr| are just the exceptions to the general rule that the
-|cmd,chr_code| pair represents in a single primitive command.
-
-*/
-
-#define chr_cmd(A) do { tprint(A); print(chr_code); } while (0)
-
-static void prim_cmd_chr(quarterword cmd, halfword chr_code)
-{
-    int idx = chr_code - prim_data[cmd].offset;
-    if (cmd <= last_cmd &&
-        idx >= 0 && idx < prim_data[cmd].subids &&
-        prim_data[cmd].names != NULL && prim_data[cmd].names[idx] != 0) {
-        tprint_esc("");
-        print(prim_data[cmd].names[idx]);
-    } else {
-        /* \TEX82 didn't print the |cmd,idx| information, but it may be useful. */
-        tprint("[unknown command code! (");
-        print_int(cmd);
-        tprint(", ");
-        print_int(idx);
-        tprint(")]");
-    }
-}
-
-void print_cmd_chr(quarterword cmd, halfword chr_code)
-{
-    int n;
-    switch (cmd) {
-        case left_brace_cmd:
-            chr_cmd("begin-group character ");
-            break;
-        case right_brace_cmd:
-            chr_cmd("end-group character ");
-            break;
-        case math_shift_cmd:
-            chr_cmd("math shift character ");
-            break;
-        case mac_param_cmd:
-            if (chr_code == tab_mark_cmd_code)
-                tprint_esc("alignmark");
-            else
-                chr_cmd("macro parameter character ");
-            break;
-        case sup_mark_cmd:
-            chr_cmd("superscript character ");
-            break;
-        case sub_mark_cmd:
-            chr_cmd("subscript character ");
-            break;
-        case endv_cmd:
-            tprint("end of alignment template");
-            break;
-        case spacer_cmd:
-            chr_cmd("blank space ");
-            break;
-        case letter_cmd:
-            chr_cmd("the letter ");
-            break;
-        case other_char_cmd:
-            chr_cmd("the character ");
-            break;
-        case tab_mark_cmd:
-            if (chr_code == span_code)
-                tprint_esc("span");
-            else if (chr_code == tab_mark_cmd_code)
-                tprint_esc("aligntab");
-            else
-                chr_cmd("alignment tab character ");
-            break;
-        case if_test_cmd:
-            if (chr_code >= unless_code)
-                tprint_esc("unless");
-            prim_cmd_chr(cmd, (chr_code % unless_code));
-            break;
-        case char_given_cmd:
-            tprint_esc("char");
-            print_qhex(chr_code);
-            break;
-        case math_given_cmd:
-            /*tex
-                Okay, it's better for old macro packages that mess with meaning
-                to report a traditional value. A compromise.
-            */
-            tprint_esc("mathchar");
-            show_mathcode_value_old(chr_code);
-            break;
-        case xmath_given_cmd:
-            tprint_esc("Umathchar");
-            show_mathcode_value(mathchar_from_integer(chr_code, umath_mathcode));
-            break;
-        case lua_expandable_call_cmd:
-            tprint("expandable luacall ");
-            print_int(chr_code);
-            break;
-        case lua_local_call_cmd:
-            tprint("local luacall ");
-            print_int(chr_code);
-            break;
-        case lua_call_cmd:
-            tprint("luacall ");
-            print_int(chr_code);
-            break;
-        case set_font_cmd:
-            tprint("select font ");
-            tprint(font_name(chr_code));
-            if (font_size(chr_code) != font_dsize(chr_code)) {
-                tprint(" at ");
-                print_scaled(font_size(chr_code));
-                tprint("pt");
-            }
-            break;
-        case undefined_cs_cmd:
-            tprint("undefined");
-            break;
-        case call_cmd:
-        case long_call_cmd:
-        case outer_call_cmd:
-        case long_outer_call_cmd:
-            n = cmd - call_cmd;
-            if (token_info(token_link(chr_code)) == protected_token)
-                n = n + 4;
-            if (odd(n / 4))
-                tprint_esc("protected");
-            if (odd(n))
-                tprint_esc("long");
-            if (odd(n / 2))
-                tprint_esc("outer");
-            if (n > 0)
-                tprint(" ");
-            tprint("macro");
-            break;
-        case assign_glue_cmd:
-        case assign_mu_glue_cmd:
-            if (chr_code < skip_base) {
-                prim_cmd_chr(cmd, chr_code);
-            } else if (chr_code < mu_skip_base) {
-                tprint_esc("skip");
-                print_int(chr_code - skip_base);
-            } else {
-                tprint_esc("muskip");
-                print_int(chr_code - mu_skip_base);
-            }
-            break;
-        case assign_toks_cmd:
-            if (chr_code >= toks_base) {
-                tprint_esc("toks");
-                print_int(chr_code - toks_base);
-            } else {
-                prim_cmd_chr(cmd, chr_code);
-            }
-            break;
-        case assign_int_cmd:
-            if (chr_code < count_base) {
-                prim_cmd_chr(cmd, chr_code);
-            } else {
-                tprint_esc("count");
-                print_int(chr_code - count_base);
-            }
-            break;
-        case assign_attr_cmd:
-            tprint_esc("attribute");
-            print_int(chr_code - attribute_base);
-            break;
-        case assign_dimen_cmd:
-            if (chr_code < scaled_base) {
-                prim_cmd_chr(cmd, chr_code);
-            } else {
-                tprint_esc("dimen");
-                print_int(chr_code - scaled_base);
-            }
-            break;
-        case normal_cmd:
-            if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) {
-                prim_cmd_chr(cmd, chr_code);
-            } else {
-                tprint("[unknown command! (");
-                print_int(chr_code);
-                tprint(")]");
-            }
-            break;
-        case extension_cmd:
-            if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) {
-                prim_cmd_chr(cmd, chr_code);
-            } else {
-                tprint("[unknown extension! (");
-                print_int(chr_code);
-                tprint(")]");
-
-            }
-            break;
-        case node_cmd:
-            tprint("node ");
-            print_int(chr_code);
-            break;
-        default:
-            /*tex These are most commands, actually. */
-            prim_cmd_chr(cmd, chr_code);
-            break;
-    }
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/primitive.w
@@ -0,0 +1,664 @@
+% primitive.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"
+
+@ Control sequences are stored and retrieved by means of a fairly standard hash
+table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C
+in {\sl The Art of Computer Programming\/}). Once a control sequence enters the
+table, it is never removed, because there are complicated situations
+involving \.{\\gdef} where the removal of a control sequence at the end of
+a group would be a mistake preventable only by the introduction of a
+complicated reference-count mechanism.
+
+The actual sequence of letters forming a control sequence identifier is
+stored in the |str_pool| array together with all the other strings. An
+auxiliary array |hash| consists of items with two halfword fields per
+word. The first of these, called |next(p)|, points to the next identifier
+belonging to the same coalesced list as the identifier corresponding to~|p|;
+and the other, called |text(p)|, points to the |str_start| entry for
+|p|'s identifier. If position~|p| of the hash table is empty, we have
+|text(p)=0|; if position |p| is either empty or the end of a coalesced
+hash list, we have |next(p)=0|. An auxiliary pointer variable called
+|hash_used| is maintained in such a way that all locations |p>=hash_used|
+are nonempty. The global variable |cs_count| tells how many multiletter
+control sequences have been defined, if statistics are being kept.
+
+A global boolean variable called |no_new_control_sequence| is set to
+|true| during the time that new hash table entries are forbidden.
+
+@c
+two_halves *hash;               /* the hash table */
+halfword hash_used;             /* allocation pointer for |hash| */
+int hash_extra;                 /* |hash_extra=hash| above |eqtb_size| */
+halfword hash_top;              /* maximum of the hash array */
+halfword hash_high;             /* pointer to next high hash location */
+boolean no_new_control_sequence;        /* are new identifiers legal? */
+int cs_count;                   /* total number of known identifiers */
+
+#define hash_is_full (hash_used==hash_base)     /* test if all positions are occupied */
+
+@ \.{\\primitive} support needs a few extra variables and definitions
+
+@c
+#define prim_base 1
+
+@ The arrays |prim| and |prim_eqtb| are used for name -> cmd,chr lookups.
+
+ The are  modelled after |hash| and |eqtb|, except that primitives do not
+  have an |eq_level|, that field is replaced by |origin|.
+
+@c
+#define prim_next(a) prim[(a)].lhfield  /* link for coalesced lists */
+#define prim_text(a) prim[(a)].rh       /* string number for control sequence name */
+#define prim_is_full (prim_used==prim_base)     /* test if all positions are occupied */
+
+#define prim_origin_field(a) (a).hh.b1
+#define prim_eq_type_field(a)  (a).hh.b0
+#define prim_equiv_field(a) (a).hh.rh
+#define prim_origin(a) prim_origin_field(prim_eqtb[(a)])        /* level of definition */
+#define prim_eq_type(a) prim_eq_type_field(prim_eqtb[(a)])      /* command code for equivalent */
+#define prim_equiv(a) prim_equiv_field(prim_eqtb[(a)])  /* equivalent value */
+
+static pointer prim_used;       /* allocation pointer for |prim| */
+static two_halves prim[(prim_size + 1)];        /* the primitives table */
+static memory_word prim_eqtb[(prim_size + 1)];
+
+@ The array |prim_data| works the other way around, it is used for
+   cmd,chr -> name lookups.
+
+@c
+typedef struct prim_info {
+    halfword subids;            /* number of name entries */
+    halfword offset;            /* offset to be used for |chr_code|s */
+    str_number *names;          /* array of names */
+} prim_info;
+
+static prim_info prim_data[(last_cmd + 1)];
+
+@ initialize the memory arrays
+@c
+void init_primitives(void)
+{
+    int k;
+    memset(prim_data, 0, (sizeof(prim_info) * (last_cmd + 1)));
+    memset(prim, 0, (sizeof(two_halves) * (prim_size + 1)));
+    memset(prim_eqtb, 0, (sizeof(memory_word) * (prim_size + 1)));
+    for (k = 0; k <= prim_size; k++)
+        prim_eq_type(k) = undefined_cs_cmd;
+}
+
+void ini_init_primitives(void)
+{
+    prim_used = prim_size;      /* nothing is used */
+}
+
+
+@ The value of |hash_prime| should be roughly 85\%! of |hash_size|, and it
+   should be a prime number.  The theory of hashing tells us to expect fewer
+   than two table probes, on the average, when the search is successful.
+   [See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
+   @^Vitter, Jeffrey Scott@>
+
+@c
+static halfword compute_hash(const char *j, unsigned int l,
+                             halfword prime_number)
+{
+    int k;
+    halfword h = (unsigned char) *j;
+    for (k = 1; k <= (int)(l - 1); k++) {
+        h = h + h + (unsigned char) *(j + k);
+        while (h >= prime_number)
+            h = h - prime_number;
+    }
+    return h;
+}
+
+
+@ Here is the subroutine that searches the primitive table for an identifier
+@c
+pointer prim_lookup(str_number s)
+{
+    int h;                      /* hash code */
+    pointer p;                  /* index in |hash| array */
+    unsigned char *j;
+    unsigned l;
+    if (s < STRING_OFFSET) {
+        p = s;
+        if ((p < 0) || (get_prim_eq_type(p) == undefined_cs_cmd)) {
+            p = undefined_primitive;
+        }
+    } else {
+        j = str_string(s);
+        l = (unsigned) str_length(s);
+        h = compute_hash((char *) j, l, prim_prime);
+        p = h + prim_base;      /* we start searching here; note that |0<=h<hash_prime| */
+        while (1) {
+            if (prim_text(p) > 0)
+                if (str_length(prim_text(p)) == l)
+                    if (str_eq_str(prim_text(p), s))
+                        goto FOUND;
+            if (prim_next(p) == 0) {
+                if (no_new_control_sequence) {
+                    p = undefined_primitive;
+                } else {
+                    /* Insert a new primitive after |p|, then make |p| point to it */
+                    if (prim_text(p) > 0) {
+                        do {    /* search for an empty location in |prim| */
+                            if (prim_is_full)
+                                overflow("primitive size", prim_size);
+                            decr(prim_used);
+                        } while (prim_text(prim_used) != 0);
+                        prim_next(p) = prim_used;
+                        p = prim_used;
+                    }
+                    prim_text(p) = s;
+                }
+                goto FOUND;
+            }
+            p = prim_next(p);
+        }
+    }
+  FOUND:
+    return p;
+}
+
+@ how to test a csname for primitive-ness
+@c
+boolean is_primitive(str_number csname)
+{
+    int n, m;
+    char *ss;
+    m = prim_lookup(csname);
+    ss = makecstring(csname);
+    n = string_lookup(ss, str_length(csname));
+    free(ss);
+    return ((n != undefined_cs_cmd) &&
+            (m != undefined_primitive) &&
+            (eq_type(n) == prim_eq_type(m)) && (equiv(n) == prim_equiv(m)));
+}
+
+
+@ a few simple accessors
+@c
+quarterword get_prim_eq_type(int p)
+{
+    return prim_eq_type(p);
+}
+
+quarterword get_prim_origin(int p)
+{
+    return prim_origin(p);
+}
+
+halfword get_prim_equiv(int p)
+{
+    return prim_equiv(p);
+}
+
+str_number get_prim_text(int p)
+{
+    return prim_text(p);
+}
+
+
+@ dumping and undumping
+@c
+void dump_primitives(void)
+{
+    int p, q;
+    for (p = 0; p <= prim_size; p++)
+        dump_hh(prim[p]);
+    for (p = 0; p <= prim_size; p++)
+        dump_wd(prim_eqtb[p]);
+    for (p = 0; p <= last_cmd; p++) {
+        dump_int(prim_data[p].offset);
+        dump_int(prim_data[p].subids);
+        for (q = 0; q < prim_data[p].subids; q++) {
+            dump_int(prim_data[p].names[q]);
+        }
+    }
+}
+
+void undump_primitives(void)
+{
+    int p, q;
+    for (p = 0; p <= prim_size; p++)
+        undump_hh(prim[p]);
+    for (p = 0; p <= prim_size; p++)
+        undump_wd(prim_eqtb[p]);
+
+    for (p = 0; p <= last_cmd; p++) {
+        undump_int(prim_data[p].offset);
+        undump_int(prim_data[p].subids);
+        if (prim_data[p].subids > 0) {
+            prim_data[p].names = (str_number *)
+                xmalloc((unsigned)
+                        ((unsigned) prim_data[p].subids *
+                         sizeof(str_number *)));
+            for (q = 0; q < prim_data[p].subids; q++)
+                undump_int(prim_data[p].names[q]);
+        }
+    }
+}
+
+@   We need to put \TeX's ``primitive'' control sequences into the hash
+   table, together with their command code (which will be the |eq_type|)
+   and an operand (which will be the |equiv|). The |primitive| procedure
+   does this, in a way that no \TeX\ user can. The global value |cur_val|
+   contains the new |eqtb| pointer after |primitive| has acted.
+
+
+@  Because the definitions of the actual user-accessible name of a
+   primitive can be postponed until runtime, the function |primitive_def|
+   is needed that does nothing except creating the control sequence name.
+
+@c
+void primitive_def(const char *s, size_t l, quarterword c, halfword o)
+{
+    int nncs = no_new_control_sequence;
+    no_new_control_sequence = false;
+    cur_val = string_lookup(s, l);      /* this creates the |text()| string */
+    no_new_control_sequence = nncs;
+    eq_level(cur_val) = level_one;
+    eq_type(cur_val) = c;
+    equiv(cur_val) = o;
+}
+
+@ The function |store_primitive_name| sets up the bookkeeping for the
+   reverse lookup. It is quite paranoid, because it is easy to mess this up
+   accidentally.
+
+   The |offset| is needed because sometimes character codes (in |o|)
+   are indices into |eqtb| or are offset by a magical value to make
+   sure they do not conflict with something else. We don't want the
+   |prim_data[c].names| to have too many entries as it will just be
+   wasted room, so |offset| is substracted from |o| because creating
+   or accessing the array. The |assert(idx<=0xFFFF)| is not strictly
+   needed, but it helps catch errors of this kind.
+
+@c
+static void
+store_primitive_name(str_number s, quarterword c, halfword o, halfword offset)
+{
+    int idx;
+    if (prim_data[c].offset != 0 && prim_data[c].offset != offset) {
+        assert(false);
+    }
+    prim_data[c].offset = offset;
+    idx = ((int) o - offset);
+    assert(idx >= 0);
+    assert(idx <= 0xFFFF);
+    if (prim_data[c].subids < (idx + 1)) {
+        str_number *new =
+            (str_number *) xcalloc((unsigned) (idx + 1), sizeof(str_number *));
+        if (prim_data[c].names != NULL) {
+            assert(prim_data[c].subids);
+            memcpy(new, (prim_data[c].names),
+                   (unsigned) (prim_data[c].subids) * sizeof(str_number));
+            free(prim_data[c].names);
+        }
+        prim_data[c].names = new;
+        prim_data[c].subids = idx + 1;
+    }
+    prim_data[c].names[idx] = s;
+}
+
+@ Compared to tex82, |primitive| has two extra parameters. The |off| is an offset
+   that will be passed on to |store_primitive_name|, the |cmd_origin| is the bit
+   that is used to group primitives by originator.
+
+@c
+void
+primitive(const char *thes, quarterword c, halfword o, halfword off,
+          int cmd_origin)
+{
+    int prim_val;               /* needed to fill |prim_eqtb| */
+    str_number ss;
+    assert(o >= off);
+    ss = maketexstring(thes);
+    if (cmd_origin == tex_command || cmd_origin == core_command) {
+        primitive_def(thes, strlen(thes), c, o);
+    }
+    prim_val = prim_lookup(ss);
+    prim_origin(prim_val) = (quarterword) cmd_origin;
+    prim_eq_type(prim_val) = c;
+    prim_equiv(prim_val) = o;
+    store_primitive_name(ss, c, o, off);
+}
+
+
+
+@ Here is a helper that does the actual hash insertion.
+
+@c
+static halfword insert_id(halfword p, const unsigned char *j, unsigned int l)
+{
+    unsigned saved_cur_length;
+    unsigned saved_cur_string_size;
+    unsigned char *saved_cur_string;
+    const unsigned char *k;
+    /* This code far from ideal: the existance of |hash_extra| changes
+       all the potential (short) coalesced lists into a single (long)
+       one. This will create a slowdown. */
+    if (cs_text(p) > 0) {
+        if (hash_high < hash_extra) {
+            incr(hash_high);
+            /* can't use |eqtb_top| here (perhaps because that is not finalized
+               yet when called from |primitive|?) */
+            cs_next(p) = hash_high + eqtb_size;
+            p = cs_next(p);
+        } else {
+            do {
+                if (hash_is_full)
+                    overflow("hash size", (unsigned) (hash_size + hash_extra));
+                decr(hash_used);
+            } while (cs_text(hash_used) != 0);  /* search for an empty location in |hash| */
+            cs_next(p) = hash_used;
+            p = hash_used;
+        }
+    }
+    saved_cur_length = cur_length;
+    saved_cur_string = cur_string;
+    saved_cur_string_size = cur_string_size;
+    reset_cur_string();
+    for (k = j; k <= j + l - 1; k++)
+        append_char(*k);
+    cs_text(p) = make_string();
+    cur_length = saved_cur_length;
+    xfree(cur_string);
+    cur_string = saved_cur_string;
+    cur_string_size = saved_cur_string_size;
+    incr(cs_count);
+    return p;
+}
+
+
+@ Here is the subroutine that searches the hash table for an identifier
+ that matches a given string of length |l>1| appearing in |buffer[j..
+ (j+l-1)]|. If the identifier is found, the corresponding hash table address
+ is returned. Otherwise, if the global variable |no_new_control_sequence|
+ is |true|, the dummy address |undefined_control_sequence| is returned.
+ Otherwise the identifier is inserted into the hash table and its location
+ is returned.
+
+@c
+pointer id_lookup(int j, int l)
+{                               /* search the hash table */
+    int h;                      /* hash code */
+    pointer p;                  /* index in |hash| array */
+
+    h = compute_hash((char *) (buffer + j), (unsigned) l, hash_prime);
+#ifdef VERBOSE
+    {
+        unsigned char *todo = xmalloc(l + 2);
+        strncpy(todo, (buffer + j), l);
+        todo[l] = '\0';
+        todo[l + 1] = '\0';
+        fprintf(stdout, "id_lookup(%s)\n", todo);
+        free(todo);
+    }
+#endif
+    p = h + hash_base;          /* we start searching here; note that |0<=h<hash_prime| */
+    while (1) {
+        if (cs_text(p) > 0)
+            if (str_length(cs_text(p)) == (unsigned) l)
+                if (str_eq_buf(cs_text(p), j))
+                    goto FOUND;
+        if (cs_next(p) == 0) {
+            if (no_new_control_sequence) {
+                p = undefined_control_sequence;
+            } else {
+                p = insert_id(p, (buffer + j), (unsigned) l);
+            }
+            goto FOUND;
+        }
+        p = cs_next(p);
+    }
+  FOUND:
+    return p;
+}
+
+@ Here is a similar subroutine for finding a primitive in the hash.
+This one is based on a C string.
+
+@c
+pointer string_lookup(const char *s, size_t l)
+{                               /* search the hash table */
+    int h;                      /* hash code */
+    pointer p;                  /* index in |hash| array */
+    h = compute_hash(s, (unsigned) l, hash_prime);
+    p = h + hash_base;          /* we start searching here; note that |0<=h<hash_prime| */
+    while (1) {
+        if (cs_text(p) > 0)
+            if (str_eq_cstr(cs_text(p), s, l))
+                goto FOUND;
+        if (cs_next(p) == 0) {
+            if (no_new_control_sequence) {
+                p = undefined_control_sequence;
+            } else {
+                p = insert_id(p, (const unsigned char *) s, (unsigned) l);
+            }
+            goto FOUND;
+        }
+        p = cs_next(p);
+    }
+  FOUND:
+    return p;
+}
+
+@ The |print_cmd_chr| routine prints a symbolic interpretation of a
+   command code and its modifier. This is used in certain `\.{You can\'t}'
+   error messages, and in the implementation of diagnostic routines like
+   \.{\\show}.
+
+   The body of |print_cmd_chr| use to be  a rather tedious listing of print
+   commands, and most of it was essentially an inverse to the |primitive|
+   routine that enters a \TeX\ primitive into |eqtb|.
+
+   Thanks to |prim_data|, there is no need for all that tediousness. What
+   is left of |primt_cnd_chr| are just the exceptions to the general rule
+   that the  |cmd,chr_code| pair represents in a single primitive command.
+
+@c
+#define chr_cmd(A) do { tprint(A); print(chr_code); } while (0)
+
+static void prim_cmd_chr(quarterword cmd, halfword chr_code)
+{
+    int idx = chr_code - prim_data[cmd].offset;
+    if (cmd <= last_cmd &&
+        idx >= 0 && idx < prim_data[cmd].subids &&
+        prim_data[cmd].names != NULL && prim_data[cmd].names[idx] != 0) {
+        tprint_esc("");
+        print(prim_data[cmd].names[idx]);
+    } else {
+        /* TEX82 didn't print the |cmd,idx| information, but it may be useful */
+        tprint("[unknown command code! (");
+        print_int(cmd);
+        tprint(", ");
+        print_int(idx);
+        tprint(")]");
+    }
+}
+
+void print_cmd_chr(quarterword cmd, halfword chr_code)
+{
+    int n;                      /* temp variable */
+    switch (cmd) {
+    case left_brace_cmd:
+        chr_cmd("begin-group character ");
+        break;
+    case right_brace_cmd:
+        chr_cmd("end-group character ");
+        break;
+    case math_shift_cmd:
+        chr_cmd("math shift character ");
+        break;
+    case mac_param_cmd:
+        if (chr_code == tab_mark_cmd_code)
+            tprint_esc("alignmark");
+        else
+            chr_cmd("macro parameter character ");
+        break;
+    case sup_mark_cmd:
+        chr_cmd("superscript character ");
+        break;
+    case sub_mark_cmd:
+        chr_cmd("subscript character ");
+        break;
+    case endv_cmd:
+        tprint("end of alignment template");
+        break;
+    case spacer_cmd:
+        chr_cmd("blank space ");
+        break;
+    case letter_cmd:
+        chr_cmd("the letter ");
+        break;
+    case other_char_cmd:
+        chr_cmd("the character ");
+        break;
+    case tab_mark_cmd:
+        if (chr_code == span_code)
+            tprint_esc("span");
+        else if (chr_code == tab_mark_cmd_code)
+            tprint_esc("aligntab");
+        else
+            chr_cmd("alignment tab character ");
+        break;
+    case if_test_cmd:
+        if (chr_code >= unless_code)
+            tprint_esc("unless");
+        prim_cmd_chr(cmd, (chr_code % unless_code));
+        break;
+    case char_given_cmd:
+        tprint_esc("char");
+        print_hex(chr_code);
+        break;
+    case math_given_cmd:
+        if (math_umathcode_meaning_par == 1) {
+            tprint_esc("Umathchar");
+            show_mathcode_value(mathchar_from_integer(chr_code, tex_mathcode));
+        } else {
+            /* better for old macro packages that mess with meaning */
+            tprint_esc("mathchar");
+            show_mathcode_value_old(chr_code);
+        }
+        break;
+    case xmath_given_cmd:
+        tprint_esc("Umathchar");
+        show_mathcode_value(mathchar_from_integer(chr_code, umath_mathcode));
+        break;
+    case set_font_cmd:
+        tprint("select font ");
+        tprint(font_name(chr_code));
+        if (font_size(chr_code) != font_dsize(chr_code)) {
+            tprint(" at ");
+            print_scaled(font_size(chr_code));
+            tprint("pt");
+        }
+        break;
+    case undefined_cs_cmd:
+        tprint("undefined");
+        break;
+    case call_cmd:
+    case long_call_cmd:
+    case outer_call_cmd:
+    case long_outer_call_cmd:
+        n = cmd - call_cmd;
+        if (token_info(token_link(chr_code)) == protected_token)
+            n = n + 4;
+        if (odd(n / 4))
+            tprint_esc("protected");
+        if (odd(n))
+            tprint_esc("long");
+        if (odd(n / 2))
+            tprint_esc("outer");
+        if (n > 0)
+            tprint(" ");
+        tprint("macro");
+        break;
+    case assign_glue_cmd:
+    case assign_mu_glue_cmd:
+        if (chr_code < skip_base) {
+            prim_cmd_chr(cmd, chr_code);
+        } else if (chr_code < mu_skip_base) {
+            tprint_esc("skip");
+            print_int(chr_code - skip_base);
+        } else {
+            tprint_esc("muskip");
+            print_int(chr_code - mu_skip_base);
+        }
+        break;
+    case assign_toks_cmd:
+        if (chr_code >= toks_base) {
+            tprint_esc("toks");
+            print_int(chr_code - toks_base);
+        } else {
+            prim_cmd_chr(cmd, chr_code);
+        }
+        break;
+    case assign_int_cmd:
+        if (chr_code < count_base) {
+            prim_cmd_chr(cmd, chr_code);
+        } else {
+            tprint_esc("count");
+            print_int(chr_code - count_base);
+        }
+        break;
+    case assign_attr_cmd:
+        tprint_esc("attribute");
+        print_int(chr_code - attribute_base);
+        break;
+    case assign_dimen_cmd:
+        if (chr_code < scaled_base) {
+            prim_cmd_chr(cmd, chr_code);
+        } else {
+            tprint_esc("dimen");
+            print_int(chr_code - scaled_base);
+        }
+        break;
+    case normal_cmd:
+        if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) {
+            prim_cmd_chr(cmd, chr_code);
+        } else {
+            tprint("[unknown command! (");
+            print_int(chr_code);
+            tprint(")]");
+        }
+        break;
+    case extension_cmd:
+        if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) {
+            prim_cmd_chr(cmd, chr_code);
+        } else {
+            tprint("[unknown extension! (");
+            print_int(chr_code);
+            tprint(")]");
+
+        }
+        break;
+    default:
+        /* these are most commands, actually */
+        prim_cmd_chr(cmd, chr_code);
+        break;
+    }
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/printing.w
@@ -0,0 +1,1135 @@
+% printing.w
+%
+% Copyright 2009-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" /* for luatex_banner */
+
+@ @c
+#define wlog(A)  fputc(A,log_file)
+#define wterm(A) fputc(A,term_out)
+
+int new_string_line = 0;
+int escape_controls = 1;
+
+@ Messages that are sent to a user's terminal and to the transcript-log file
+are produced by several `|print|' procedures. These procedures will
+direct their output to a variety of places, based on the setting of
+the global variable |selector|, which has the following possible
+values:
+
+\yskip
+\hang |term_and_log|, the normal setting, prints on the terminal and on the
+  transcript file.
+
+\hang |log_only|, prints only on the transcript file.
+
+\hang |term_only|, prints only on the terminal.
+
+\hang |no_print|, doesn't print at all. This is used only in rare cases
+  before the transcript file is open.
+
+\hang |pseudo|, puts output into a cyclic buffer that is used
+  by the |show_context| routine; when we get to that routine we shall discuss
+  the reasoning behind this curious mode.
+
+\hang |new_string|, appends the output to the current string in the
+  string pool.
+
+\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output.
+
+\yskip
+\noindent The symbolic names `|term_and_log|', etc., have been assigned
+numeric codes that satisfy the convenient relations |no_print+1=term_only|,
+|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.
+
+Three additional global variables, |tally| and |term_offset| and
+|file_offset|, record the number of characters that have been printed
+since they were most recently cleared to zero. We use |tally| to record
+the length of (possibly very long) stretches of printing; |term_offset|
+and |file_offset|, on the other hand, keep track of how many characters
+have appeared so far on the current line that has been output to the
+terminal or to the transcript file, respectively.
+
+@c
+alpha_file log_file;                /* transcript of \TeX\ session */
+int selector = term_only;           /* where to print a message */
+int dig[23];                        /* digits in a number being output */
+int tally = 0;                      /* the number of characters recently printed */
+int term_offset = 0;                /* the number of characters on the current terminal line */
+int file_offset = 0;                /* the number of characters on the current file line */
+packed_ASCII_code trick_buf[(ssup_error_line + 1)]; /* circular buffer for pseudoprinting */
+int trick_count;                    /* threshold for pseudoprinting, explained later */
+int first_count;                    /* another variable for pseudoprinting */
+boolean inhibit_par_tokens = false; /*  for minor adjustments to |show_token_list|  */
+
+@ To end a line of text output, we call |print_ln|
+
+@c
+void print_ln(void)
+{
+    switch (selector) {
+        case no_print:
+            break;
+        case term_only:
+            wterm_cr();
+            term_offset = 0;
+            break;
+        case log_only:
+            wlog_cr();
+            file_offset = 0;
+            break;
+        case term_and_log:
+            wterm_cr();
+            wlog_cr();
+            term_offset = 0;
+            file_offset = 0;
+            break;
+        case pseudo:
+            break;
+        case new_string:
+            if (new_string_line > 0)
+                print_char(new_string_line);
+            break;
+        default:
+            fprintf(write_file[selector], "\n");
+            break;
+    }
+    /* |tally| is not affected */
+}
+
+@ The |print_char| procedure sends one byte to the desired destination.
+All printing comes through |print_ln| or |print_char|, except for the
+case of |tprint| (see below).
+
+The checking of the line length is an inheritance from previosu engines
+and we might drop it after release 1.0. We're not too picky about the exact
+match of that length because we have utf output so length is then a bit
+fuzzy anyway.
+
+@c
+#define needs_escaping(A) \
+    ((! escape_controls) || (A>=0x20) || (A==0x0A) || (A==0x0D) || (A==0x09))
+
+#define escaped_char(A) \
+    A+64
+
+#define wterm_char(A) \
+    if (needs_escaping(A)) { \
+        wterm(A); \
+    } else { \
+        if (term_offset+2>=max_print_line) { \
+            wterm_cr(); \
+            term_offset=0; \
+        } \
+        wterm('^'); \
+        wterm('^'); \
+        wterm(escaped_char(A)); \
+        term_offset += 2; \
+    }
+
+/*
+
+#define needs_wrapping(A,B) \
+  (((A>=0xF0)&&(B+4>=max_print_line)) || \
+   ((A>=0xE0)&&(B+3>=max_print_line)) || \
+   ((A>=0xC0)&&(B+2>=max_print_line)))
+
+we have mostly ascii in logs, so ...
+
+*/
+
+#define needs_wrapping(A,B) \
+  (   (A>=0xC0) && \
+    (((A>=0xF0) && (B+4>=max_print_line)) || \
+     ((A>=0xE0) && (B+3>=max_print_line)) || \
+     (             (B+2>=max_print_line))) \
+  )
+
+#define fix_term_offset(A) \
+    if (needs_wrapping(A,term_offset)){ \
+        wterm_cr(); \
+        term_offset=0; \
+    }
+
+#define fix_log_offset(A) \
+    if (needs_wrapping(A,file_offset)){ \
+        wlog_cr(); \
+        file_offset=0; \
+    }
+
+void print_char(int s)
+{
+    if (s < 0 || s > 255) {
+        formatted_warning("print","weird character %i",s);
+        return;
+    }
+    if (s == new_line_char_par) {
+        if (selector < pseudo) {
+            print_ln();
+            return;
+        }
+    }
+    switch (selector) {
+        case no_print:
+            break;
+        case term_only:
+            fix_term_offset(s);
+            wterm_char(s);
+            incr(term_offset);
+            if (term_offset == max_print_line) {
+                wterm_cr();
+                term_offset = 0;
+            }
+            break;
+        case log_only:
+            fix_log_offset(s);
+            wlog(s);
+            incr(file_offset);
+            if (file_offset == max_print_line) {
+                wlog_cr();
+                file_offset = 0;
+            }
+            break;
+        case term_and_log:
+            fix_term_offset(s);
+            fix_log_offset(s);
+            wterm_char(s);
+            wlog(s);
+            incr(term_offset);
+            incr(file_offset);
+            if (term_offset == max_print_line) {
+                wterm_cr();
+                term_offset = 0;
+            }
+            if (file_offset == max_print_line) {
+                wlog_cr();
+                file_offset = 0;
+            }
+            break;
+        case pseudo:
+            if (tally < trick_count)
+                trick_buf[tally % error_line] = (packed_ASCII_code) s;
+            break;
+        case new_string:
+            append_char(s);
+            break;
+        default:
+            fprintf(write_file[selector], "%c", s);
+    }
+    incr(tally);
+}
+
+@ An entire string is output by calling |print|. Note that if we are outputting
+the single standard ASCII character \.c, we could call |print("c")|, since
+|"c"=99| is the number of a single-character string, as explained above. But
+|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char|
+routine when it knows that this is safe. (The present implementation
+assumes that it is always safe to print a visible ASCII character.)
+
+@^system dependencies@>
+
+The first 256 entries above the 17th unicode plane are used for a
+special trick: when \TeX\ has to print items in that range, it will
+instead print the character that results from substracting 0x110000
+from that value. This allows byte-oriented output to things like
+\.{\\specials} and \.{\\pdfextension literals}. Todo: Perhaps it would be useful
+to do the same substraction while typesetting.
+
+@c
+void print(int s)
+{
+    if (s >= str_ptr) {
+        normal_warning("print","bad string pointer");
+        return;
+    } else if (s < STRING_OFFSET) {
+        if (s < 0) {
+            normal_warning("print","bad string offset");
+        } else {
+            /* TH not sure about this, disabled for now! */
+            if ((false) && (selector > pseudo)) {
+                /* internal strings are not expanded */
+                print_char(s);
+                return;
+            }
+            if (s == new_line_char_par) {
+                if (selector < pseudo) {
+                    print_ln();
+                    return;
+                }
+            }
+            if (s <= 0x7F) {
+                print_char(s);
+            } else if (s <= 0x7FF) {
+                print_char(0xC0 + (s / 0x40));
+                print_char(0x80 + (s % 0x40));
+            } else if (s <= 0xFFFF) {
+                print_char(0xE0 + (s / 0x1000));
+                print_char(0x80 + ((s % 0x1000) / 0x40));
+                print_char(0x80 + ((s % 0x1000) % 0x40));
+            } else if (s >= 0x110000) {
+                int c = s - 0x110000;
+                if (c >= 256) {
+                    formatted_warning("print", "bad raw byte to print (c=%d), skipped",c);
+                } else {
+                    print_char(c);
+                }
+            } else {
+                print_char(0xF0 + (s / 0x40000));
+                print_char(0x80 + ((s % 0x40000) / 0x1000));
+                print_char(0x80 + (((s % 0x40000) % 0x1000) / 0x40));
+                print_char(0x80 + (((s % 0x40000) % 0x1000) % 0x40));
+            }
+        }
+        return;
+    }
+    if (selector == new_string) {
+        append_string(str_string(s), (unsigned) str_length(s));
+        return;
+    }
+    lprint(&str_lstring(s));
+}
+
+void lprint (lstring *ss) {
+    unsigned char *j, *l;       /* current character code position */
+    j = ss->s;
+    l = j + ss->l;
+    while (j < l) {
+        /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80  */
+        /* I don't bother checking the last two bytes explicitly */
+        if ((j < l - 4) && (*j == 0xF4) && (*(j + 1) == 0x90)) {
+            int c = (*(j + 2) - 128) * 64 + (*(j + 3) - 128);
+            assert(c >= 0 && c < 256);
+            print_char(c);
+            j = j + 4;
+        } else {
+            print_char(*j);
+            incr(j);
+        }
+    }
+}
+
+@ The procedure |print_nl| is like |print|, but it makes sure that the
+string appears at the beginning of a new line.
+
+@c
+void print_nlp(void)
+{                               /* move to beginning of a line */
+    if (new_string_line > 0) {
+        print_char(new_string_line);
+    } else if (((term_offset > 0) && (odd(selector))) ||
+               ((file_offset > 0) && (selector >= log_only))) {
+        print_ln();
+    }
+}
+
+void print_nl(str_number s)
+{                               /* prints string |s| at beginning of line */
+    print_nlp();
+    print(s);
+}
+
+@ |char *| versions of the same procedures. |tprint| is
+different because it uses buffering, which works well because
+most of the output actually comes through |tprint|.
+
+@c
+#define t_flush_buffer(target,offset) \
+    buffer[i++] = '\n'; \
+    buffer[i++] = '\0';\
+    fputs(buffer, target); \
+    i = 0; \
+    buffer[0] = '\0'; \
+    offset=0;
+
+void tprint(const char *sss)
+{
+    char *buffer = NULL;
+    int i = 0; /* buffer index */
+    int newlinechar = new_line_char_par;
+    int dolog = 0;
+    int doterm = 0;
+    switch (selector) {
+        case no_print:
+            return;
+            break;
+        case term_only:
+            doterm = 1;
+            break;
+        case log_only:
+            dolog = 1;
+            break;
+        case term_and_log:
+            dolog = 1;
+            doterm = 1;
+            break;
+        case pseudo:
+            while (*sss) {
+               if (tally < trick_count) {
+                   trick_buf[tally % error_line] = (packed_ASCII_code) *sss++;
+               tally++;
+               } else {
+                   return;
+               }
+            }
+            return;
+            break;
+        case new_string:
+            append_string((const unsigned char *)sss, (unsigned) strlen(sss));
+            return;
+            break;
+        default:
+            {
+                char *newstr = xstrdup(sss);
+                char *s;
+                for (s=newstr;*s;s++) {
+                    if (*s == newlinechar) {
+                        *s = '\n';
+                    }
+                }
+                fputs(newstr, write_file[selector]);
+                free(newstr);
+                return;
+            }
+            break;
+    }
+    /* what is left is the 3 term/log settings */
+    if (dolog || doterm) {
+        buffer = xmalloc(strlen(sss)*3);
+        if (dolog) {
+            const unsigned char *ss = (const unsigned char *) sss;
+            while (*ss) {
+                int s = *ss++;
+                if (needs_wrapping(s,file_offset) || s == newlinechar) {
+                    t_flush_buffer(log_file,file_offset);
+                }
+                if (s != newlinechar) {
+                    buffer[i++] = s;
+                    if (file_offset++ == max_print_line) {
+                        t_flush_buffer(log_file,file_offset);
+                    }
+                }
+            }
+            if (*buffer) {
+                buffer[i++] = '\0';
+                fputs(buffer, log_file);
+                buffer[0] = '\0';
+            }
+            i = 0;
+        }
+        if (doterm) {
+            const unsigned char *ss = (const unsigned char *) sss;
+            while (*ss) {
+                int s = *ss++;
+                if (needs_wrapping(s,term_offset) || s == newlinechar) {
+                    t_flush_buffer(term_out,term_offset);
+                }
+                if (s != newlinechar) {
+                    if (needs_escaping(s)) {
+                        buffer[i++] = s;
+                    } else {
+                        buffer[i++] = '^';
+                        buffer[i++] = '^';
+                        buffer[i++] = escaped_char(s);
+                        term_offset += 2;
+                    }
+                    if (++term_offset == max_print_line) {
+                        t_flush_buffer(term_out,term_offset);
+                    }
+                }
+            }
+            if (*buffer) {
+                buffer[i++] = '\0';
+                fputs(buffer, term_out);
+            }
+        }
+        free(buffer);
+    }
+}
+
+void tprint_nl(const char *s)
+{
+    print_nlp();
+    tprint(s);
+}
+
+@ Here is the very first thing that \TeX\ prints: a headline that identifies
+the version number and format package. The |term_offset| variable is temporarily
+incorrect, but the discrepancy is not serious since we assume that the banner
+and format identifier together will occupy at most |max_print_line|
+character positions.
+
+@c
+void print_banner(const char *v)
+{
+    int callback_id = callback_defined(start_run_callback);
+    if (callback_id == 0) {
+        fprintf(term_out, "This is " MyName ", Version %s%s ", v, WEB2CVERSION);
+        if (format_ident > 0)
+            print(format_ident);
+        print_ln();
+        if (show_luahashchars){
+            wterm(' ');
+            fprintf(term_out,"Number of bits used by the hash function (" my_name "): %d",LUAI_HASHLIMIT);
+        print_ln();
+        }
+        if (shellenabledp) {
+            wterm(' ');
+            if (restrictedshell)
+                fprintf(term_out, "restricted ");
+            fprintf(term_out, "system commands enabled.\n");
+        }
+    } else if (callback_id > 0) {
+        run_callback(callback_id, "->");
+    }
+}
+
+@ @c
+void log_banner(const char *v)
+{
+    const char *months[] = { "   ",
+        "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+        "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
+    };
+    unsigned month = (unsigned) month_par;
+    if (month > 12)
+        month = 0;
+    fprintf(log_file, "This is " MyName ", Version %s%s ", v, WEB2CVERSION);
+    print(format_ident);
+    print_char(' ');
+    print_char(' ');
+    print_int(day_par);
+    print_char(' ');
+    fprintf(log_file, "%s", months[month]);
+    print_char(' ');
+    print_int(year_par);
+    print_char(' ');
+    print_two(time_par / 60);
+    print_char(':');
+    print_two(time_par % 60);
+    if (shellenabledp) {
+        wlog_cr();
+        wlog(' ');
+        if (restrictedshell)
+            fprintf(log_file, "restricted ");
+        fprintf(log_file, "system commands enabled.");
+    }
+    if (filelineerrorstylep) {
+        wlog_cr();
+        fprintf(log_file, " file:line:error style messages enabled.");
+    }
+}
+
+@ @c
+void print_version_banner(void)
+{
+    fprintf(term_out, "%s", luatex_banner);
+}
+
+@ The procedure |print_esc| prints a string that is preceded by
+the user's escape character (which is usually a backslash).
+
+@c
+void print_esc(str_number s)
+{
+    int c = escape_char_par; /* Set variable |c| to the current escape character */
+    if (c >= 0 && c < STRING_OFFSET)
+        print(c);
+    print(s);
+}
+
+@ This prints escape character, then |s|.
+
+@c
+void tprint_esc(const char *s)
+{
+    int c = escape_char_par; /* Set variable |c| to the current escape character */
+    if (c >= 0 && c < STRING_OFFSET)
+        print(c);
+    tprint(s);
+}
+
+@ An array of digits in the range |0..15| is printed by |print_the_digs|.
+
+@c
+void print_the_digs(eight_bits k)
+{
+    /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
+    while (k-- > 0) {
+        if (dig[k] < 10)
+            print_char('0' + dig[k]);
+        else
+            print_char('A' - 10 + dig[k]);
+    }
+}
+
+@ The following procedure, which prints out the decimal representation of a
+given integer |n|, has been written carefully so that it works properly
+if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
+to negative arguments, since such operations are not implemented consistently
+by all PASCAL compilers.
+
+@c
+void print_int(longinteger n)
+{
+    int k = 0;     /* index to current digit; we assume that $|n|<10^{23}$ */
+    longinteger m; /* used to negate |n| in possibly dangerous cases */
+    if (n < 0) {
+        print_char('-');
+        if (n > -100000000) {
+            n = -n;
+        } else {
+            m = -1 - n;
+            n = m / 10;
+            m = (m % 10) + 1;
+            k = 1;
+            if (m < 10)
+                dig[0] = (int) m;
+            else {
+                dig[0] = 0;
+                incr(n);
+            }
+        }
+    }
+    do {
+        dig[k] = (int) (n % 10);
+        n = n / 10;
+        incr(k);
+    } while (n != 0);
+    print_the_digs((eight_bits) k);
+}
+
+
+@ Here is a trivial procedure to print two digits; it is usually called with
+a parameter in the range |0<=n<=99|.
+
+@c
+void print_two(int n)
+{
+    n = abs(n) % 100;
+    print_char('0' + (n / 10));
+    print_char('0' + (n % 10));
+}
+
+@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
+
+@c
+void print_hex(int n)
+{
+    int k = 0 ; /* index to current digit; we assume that $0\L n<16^{22}$ */
+    print_char('"');
+    do {
+        dig[k] = n % 16;
+        n = n / 16;
+        incr(k);
+    } while (n != 0);
+    print_the_digs((eight_bits) k);
+}
+
+@ Roman numerals are produced by the |print_roman_int| routine.  Readers
+who like puzzles might enjoy trying to figure out how this tricky code
+works; therefore no explanation will be given. Notice that 1990 yields
+\.{mcmxc}, not \.{mxm}.
+
+@c
+void print_roman_int(int n)
+{
+    char *j, *k; /* mysterious indices */
+    int u, v;    /* mysterious numbers */
+    char mystery[] = "m2d5c2l5x2v5i";
+    j = (char *) mystery;
+    v = 1000;
+    while (1) {
+        while (n >= v) {
+            print_char(*j);
+            n = n - v;
+        }
+        if (n <= 0) {
+            /* nonpositive input produces no output */
+            return;
+        }
+        k = j + 2;
+        u = v / (*(k - 1) - '0');
+        if (*(k - 1) == '2') {
+            k = k + 2;
+            u = u / (*(k - 1) - '0');
+        }
+        if (n + u >= v) {
+            print_char(*k);
+            n = n + u;
+        } else {
+            j = j + 2;
+            v = v / (*(j - 1) - '0');
+        }
+    }
+}
+
+@ The |print| subroutine will not print a string that is still being
+created. The following procedure will.
+
+@c
+void print_current_string(void)
+{
+    unsigned j = 0; /* points to current character code */
+    while (j < cur_length)
+        print_char(cur_string[j++]);
+}
+
+@ The procedure |print_cs| prints the name of a control sequence, given
+a pointer to its address in |eqtb|. A space is printed after the name
+unless it is a single nonletter or an active character. This procedure
+might be invoked with invalid data, so it is ``extra robust.'' The
+individual characters must be printed one at a time using |print|, since
+they may be unprintable.
+
+@c
+void print_cs(int p)
+{
+    str_number t = cs_text(p);
+    if (p < hash_base) {
+        /* nullcs */
+        if (p == null_cs) {
+            tprint_esc("csname");
+            tprint_esc("endcsname");
+            print_char(' ');
+        } else {
+            tprint_esc("IMPOSSIBLE.");
+        }
+    } else if ((p >= undefined_control_sequence) &&
+               ((p <= eqtb_size) || p > eqtb_size + hash_extra)) {
+        tprint_esc("IMPOSSIBLE.");
+    } else if (t >= str_ptr) {
+        tprint_esc("NONEXISTENT.");
+    } else {
+        if (is_active_cs(t)) {
+            print(active_cs_value(t));
+        } else {
+            print_esc(t);
+            if (single_letter(t)) {
+                if (get_cat_code(cat_code_table_par, pool_to_unichar(str_string(t))) == letter_cmd)
+                    print_char(' ');
+            } else {
+                print_char(' ');
+            }
+        }
+    }
+}
+
+@ Here is a similar procedure; it avoids the error checks, and it never
+prints a space after the control sequence.
+
+@c
+void sprint_cs(pointer p)
+{
+    str_number t;
+    if (p == null_cs) {
+        tprint_esc("csname");
+        tprint_esc("endcsname");
+    } else {
+        t = cs_text(p);
+        if (is_active_cs(t))
+            print(active_cs_value(t));
+        else
+            print_esc(t);
+    }
+}
+
+void sprint_cs_name(pointer p)
+{
+    str_number t;
+    if (p != null_cs) {
+        t = cs_text(p);
+        if (is_active_cs(t))
+            print(active_cs_value(t));
+        else
+            print(t);
+    }
+}
+
+@ This procedure is never called when |interaction<scroll_mode|.
+
+@c
+void prompt_input(const char *s)
+{
+    wake_up_terminal();
+    tprint(s);
+    term_input();
+}
+
+@ Then there is a subroutine that prints glue stretch and shrink, possibly
+followed by the name of finite units:
+
+@c
+void print_glue(scaled d, int order, const char *s)
+{
+    print_scaled(d);
+    if ((order < normal) || (order > filll)) {
+        tprint("foul");
+    } else if (order > normal) {
+        tprint("fi");
+        while (order > sfi) {
+            print_char('l');
+            decr(order);
+        }
+    } else if (s != NULL) {
+        tprint(s);
+    }
+}
+
+@ The next subroutine prints a whole glue specification
+
+@c
+void print_spec(int p, const char *s)
+{
+    if (p < 0) {
+        print_char('*');
+    } else {
+        print_scaled(width(p));
+        if (s != NULL)
+            tprint(s);
+        if (stretch(p) != 0) {
+            tprint(" plus ");
+            print_glue(stretch(p), stretch_order(p), s);
+        }
+        if (shrink(p) != 0) {
+            tprint(" minus ");
+            print_glue(shrink(p), shrink_order(p), s);
+        }
+    }
+}
+
+@ We can reinforce our knowledge of the data structures just introduced
+by considering two procedures that display a list in symbolic form.
+The first of these, called |short_display|, is used in ``overfull box''
+messages to give the top-level description of a list. The other one,
+called |show_node_list|, prints a detailed description of exactly what
+is in the data structure.
+
+The philosophy of |short_display| is to ignore the fine points about exactly
+what is inside boxes, except that ligatures and discretionary breaks are
+expanded. As a result, |short_display| is a recursive procedure, but the
+recursion is never more than one level deep.
+@^recursion@>
+
+A global variable |font_in_short_display| keeps track of the font code that
+is assumed to be present when |short_display| begins; deviations from this
+font will be printed.
+
+@c
+int font_in_short_display; /* an internal font number */
+
+@ Boxes, rules, inserts, whatsits, marks, and things in general that are
+sort of ``complicated'' are indicated only by printing `\.{[]}'.
+
+@c
+
+/*
+So, 0, 1 as well as any large value will behave the same as before. The reason
+for this extension is that a \name not always makes sense.
+
+0   \foo xyz
+1   \foo (bar)
+2   <bar> xyz
+3   <bar @@ ..> xyz
+4   <id>
+5   <id: bar>
+6   <id: bar @@ ..> xyz
+
+*/
+
+void print_font_identifier(internal_font_number f)
+{
+    str_number fonttext;
+    fonttext = font_id_text(f);
+    if (tracing_fonts_par >= 2 && tracing_fonts_par <= 6) {
+        /* < > is less likely to clash with text parenthesis */
+        tprint("<");
+        if (tracing_fonts_par >= 2 && tracing_fonts_par <= 3) {
+            print_font_name(f);
+            if (tracing_fonts_par >= 3 || font_size(f) != font_dsize(f)) {
+                tprint(" @@ ");
+                print_scaled(font_size(f));
+                tprint("pt");
+            }
+        } else if (tracing_fonts_par >= 4 && tracing_fonts_par <= 6) {
+            print_int(f);
+            if (tracing_fonts_par >= 5) {
+                tprint(": ");
+                print_font_name(f);
+                if (tracing_fonts_par >= 6 || font_size(f) != font_dsize(f)) {
+                    tprint(" @@ ");
+                    print_scaled(font_size(f));
+                    tprint("pt");
+                }
+            }
+        }
+        print_char('>');
+    } else {
+        /* old method, inherited from pdftex  */
+        if (fonttext > 0) {
+            print_esc(fonttext);
+        } else {
+            tprint_esc("FONT");
+            print_int(f);
+        }
+        if (tracing_fonts_par > 0) {
+            tprint(" (");
+            print_font_name(f);
+            if (font_size(f) != font_dsize(f)) {
+                tprint("@@");
+                print_scaled(font_size(f));
+                tprint("pt");
+            }
+            print_char(')');
+        }
+    }
+}
+
+@ This prints highlights of list |p|.
+
+@c
+void short_display(int p)
+{
+    while (p != null) {
+        if (is_char_node(p)) {
+            if (lig_ptr(p) != null) {
+                short_display(lig_ptr(p));
+            } else {
+                if (font(p) != font_in_short_display) {
+                    if (!is_valid_font(font(p)))
+                        print_char('*');
+                    else
+                        print_font_identifier(font(p));
+                    print_char(' ');
+                    font_in_short_display = font(p);
+                }
+                print(character(p));
+            }
+        } else {
+            /* Print a short indication of the contents of node |p| */
+            print_short_node_contents(p);
+        }
+        p = vlink(p);
+    }
+}
+
+@ The |show_node_list| routine requires some auxiliary subroutines: one to
+print a font-and-character combination, one to print a token list without
+its reference count, and one to print a rule dimension.
+
+@ This prints |char_node| data.
+
+@c
+void print_font_and_char(int p)
+{
+    if (!is_valid_font(font(p)))
+        print_char('*');
+    else
+        print_font_identifier(font(p));
+    print_char(' ');
+    print(character(p));
+}
+
+@ This prints token list data in braces
+
+@c
+void print_mark(int p)
+{
+    print_char('{');
+    if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end))
+        tprint_esc("CLOBBERED.");
+    else
+        show_token_list(token_link(p), null, max_print_line - 10);
+    print_char('}');
+}
+
+@ This prints dimensions of a rule node.
+
+@c
+void print_rule_dimen(scaled d)
+{
+    if (is_running(d))
+        print_char('*');
+    else
+        print_scaled(d);
+}
+
+@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive,
+@^recursion@>
+up to a given maximum number of levels.  The history of nesting is indicated
+by the current string, which will be printed at the beginning of each line;
+the length of this string, namely |cur_length|, is the depth of nesting.
+
+A global variable called |depth_threshold| is used to record the maximum
+depth of nesting for which |show_node_list| will show information.  If we
+have |depth_threshold=0|, for example, only the top level information will
+be given and no sublists will be traversed. Another global variable, called
+|breadth_max|, tells the maximum number of items to show at each level;
+|breadth_max| had better be positive, or you won't see anything.
+
+@c
+int depth_threshold; /* maximum nesting depth in box displays */
+int breadth_max;     /* maximum number of items shown at the same list level */
+
+@ The recursive machinery is started by calling |show_box|. Assign the values
+|depth_threshold:=show_box_depth| and |breadth_max:=show_box_breadth|
+
+@c
+void show_box(halfword p)
+{
+    depth_threshold = show_box_depth_par;
+    breadth_max = show_box_breadth_par;
+    if (breadth_max <= 0)
+        breadth_max = 5;
+    /* the show starts at |p| */
+    show_node_list(p);
+    print_ln();
+}
+
+@ Helper for debugging purposes. It prints highlights of list |p|
+
+@c
+void short_display_n(int p, int m)
+{
+    int i = 0;
+    font_in_short_display = null_font;
+    if (p == null)
+        return;
+    while (p != null) {
+        if (is_char_node(p)) {
+            if (p <= max_halfword) {
+                if (font(p) != font_in_short_display) {
+                    if (!is_valid_font(font(p)))
+                        print_char('*');
+                    else
+                        print_font_identifier(font(p));
+                    print_char(' ');
+                    font_in_short_display = font(p);
+                }
+                print(character(p));
+            }
+        } else {
+            if ( (type(p) == glue_node) ||
+                 (type(p) == disc_node) ||
+                 (type(p) == penalty_node) ||
+                ((type(p) == kern_node) && (subtype(p) == explicit_kern ||
+                                            subtype(p) == italic_kern   ))) {
+                incr(i);
+            }
+            if (i >= m)
+                return;
+            if (type(p) == disc_node) {
+                print_char('|');
+                short_display(vlink(pre_break(p)));
+                print_char('|');
+                short_display(vlink(post_break(p)));
+                print_char('|');
+            } else {
+                /* Print a short indication of the contents of node |p| */
+                print_short_node_contents(p);
+            }
+        }
+        p = vlink(p);
+        if (p == null)
+            return;
+    }
+    update_terminal();
+}
+
+@ When debugging a macro package, it can be useful to see the exact
+control sequence names in the format file.  For example, if ten new
+csnames appear, it's nice to know what they are, to help pinpoint where
+they came from.  (This isn't a truly ``basic'' printing procedure, but
+that's a convenient module in which to put it.)
+
+@c
+void print_csnames(int hstart, int hfinish)
+{
+    int h;
+    unsigned char *c, *l;
+    fprintf(stderr, "fmtdebug:csnames from %d to %d:", (int) hstart, (int) hfinish);
+    for (h = hstart; h <= hfinish; h++) {
+        if (cs_text(h) > 0) {
+            /* we have anything at this position */
+            c = str_string(cs_text(h));
+            l = c + str_length(cs_text(h));
+            while (c < l) {
+                /* print the characters */
+                fputc(*c++, stderr);
+            }
+            fprintf(stderr, "|");
+        }
+    }
+}
+
+@ A helper for printing file:line:error style messages.  Look for a
+filename in |full_source_filename_stack|, and if we fail to find
+one fall back on the non-file:line:error style.
+
+@c
+void print_file_line(void)
+{
+    int level = in_open;
+    while ((level > 0) && (full_source_filename_stack[level] == 0))
+        decr(level);
+    if (level == 0) {
+        tprint_nl("! ");
+    } else {
+        tprint_nl("");
+        tprint(full_source_filename_stack[level]);
+        print_char(':');
+        if (level == in_open)
+            print_int(line);
+        else
+            print_int(line_stack[level + 1]);
+        tprint(": ");
+    }
+}
+
+@ \TeX\ is occasionally supposed to print diagnostic information that
+goes only into the transcript file, unless |tracing_online| is positive.
+Here are two routines that adjust the destination of print commands:
+
+@c
+void begin_diagnostic(void)
+{
+    global_old_setting = selector;
+    if ((tracing_online_par <= 0) && (selector == term_and_log)) {
+        decr(selector);
+        if (history == spotless)
+            history = warning_issued;
+    }
+}
+
+@ Restore proper conditions after tracing.
+
+@c
+void end_diagnostic(boolean blank_line)
+{
+    tprint_nl("");
+    if (blank_line)
+        print_ln();
+    selector = global_old_setting;
+}
+
+@ Of course we had better declare another global variable, if the previous
+routines are going to work.
+
+@c
+int global_old_setting;
--- texlive-bin.orig/texk/web2c/luatexdir/tex/printing.c
+++ /dev/null
@@ -1,1281 +0,0 @@
-/*
-
-printing.w
-
-Copyright 2009-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-#define wlog(A)  fputc(A,log_file)
-#define wterm(A) fputc(A,term_out)
-
-int new_string_line = 0;
-int escape_controls = 1;
-
-/*tex
-
-Messages that are sent to a user's terminal and to the transcript-log file are
-produced by several `|print|' procedures. These procedures will direct their
-output to a variety of places, based on the setting of the global variable
-|selector|, which has the following possible values:
-
-\startitemize
-
-\startitem
-    |term_and_log|, the normal setting, prints on the terminal and on the
-    transcript file.
-\stopitem
-
-\startitem
-    |log_only|, prints only on the transcript file.
-\stopitem
-
-\startitem
-    |term_only|, prints only on the terminal.
-\stopitem
-
-\startitem
-    |no_print|, doesn't print at all. This is used only in rare cases before the
-    transcript file is open.
-\stopitem
-
-\startitem
-    |pseudo|, puts output into a cyclic buffer that is used by the |show_context|
-    routine; when we get to that routine we shall discuss the reasoning behind
-    this curious mode.
-\stopitem
-
-\startitem
-    |new_string|, appends the output to the current string in the string pool.
-\stopitem
-
-\startitem
-    0 to 15, prints on one of the sixteen files for \.{\\write} output.
-\stopitem
-
-\stopitemize
-
-The symbolic names `|term_and_log|', etc., have been assigned numeric codes that
-satisfy the convenient relations |no_print+1=term_only|, |no_print+2=log_only|,
-|term_only+2=log_only+1=term_and_log|.
-
-Three additional global variables, |tally| and |term_offset| and |file_offset|,
-record the number of characters that have been printed since they were most
-recently cleared to zero. We use |tally| to record the length of (possibly very
-long) stretches of printing; |term_offset| and |file_offset|, on the other hand,
-keep track of how many characters have appeared so far on the current line that
-has been output to the terminal or to the transcript file, respectively.
-
-*/
-
-/*tex transcript of \TeX\ session */
-
-alpha_file log_file;
-
-/*tex where to print a message */
-
-int selector = term_only;
-
-/*tex digits in a number being output */
-
-int dig[23];
-
-/*tex the number of characters recently printed */
-
-int tally = 0;
-
-/*tex the number of characters on the current terminal line */
-
-int term_offset = 0;
-
-/*tex the number of characters on the current file line */
-
-int file_offset = 0;
-
-/*tex circular buffer for pseudoprinting */
-
-packed_ASCII_code trick_buf[(ssup_error_line + 1)];
-
-/*tex threshold for pseudoprinting, explained later */
-
-int trick_count;
-
-/*tex another variable for pseudoprinting */
-
-int first_count;
-
-/*tex for minor adjustments to |show_token_list|  */
-
-boolean inhibit_par_tokens = false;
-
-/*tex
-
-To end a line of text output, we call |print_ln|
-
-*/
-
-void print_ln(void)
-{
-    switch (selector) {
-        case no_print:
-            break;
-        case term_only:
-            wterm_cr();
-            term_offset = 0;
-            break;
-        case log_only:
-            wlog_cr();
-            file_offset = 0;
-            break;
-        case term_and_log:
-            wterm_cr();
-            wlog_cr();
-            term_offset = 0;
-            file_offset = 0;
-            break;
-        case pseudo:
-            break;
-        case new_string:
-            if (new_string_line > 0)
-                print_char(new_string_line);
-            break;
-        default:
-            fprintf(write_file[selector], "\n");
-            break;
-    }
-    /*tex |tally| is not affected */
-}
-
-/*tex
-
-The |print_char| procedure sends one byte to the desired destination. All
-printing comes through |print_ln| or |print_char|, except for the case of
-|tprint| (see below).
-
-The checking of the line length is an inheritance from previosu engines and we
-might drop it after release 1.0. We're not too picky about the exact match of
-that length because we have utf output so length is then a bit fuzzy anyway.
-
-*/
-
-#define needs_escaping(A) \
-    ((! escape_controls) || (A>=0x20) || (A==0x0A) || (A==0x0D) || (A==0x09))
-
-#define escaped_char(A) \
-    A+64
-
-#define wterm_char(A) \
-    if (needs_escaping(A)) { \
-        wterm(A); \
-    } else { \
-        if (term_offset+2>=max_print_line) { \
-            wterm_cr(); \
-            term_offset=0; \
-        } \
-        wterm('^'); \
-        wterm('^'); \
-        wterm(escaped_char(A)); \
-        term_offset += 2; \
-    }
-
-#define needs_wrapping(A,B) \
-  (   (A>=0xC0) && \
-    (((A>=0xF0) && (B+4>=max_print_line)) || \
-     ((A>=0xE0) && (B+3>=max_print_line)) || \
-     (             (B+2>=max_print_line))) \
-  )
-
-#define fix_term_offset(A) \
-    if (needs_wrapping(A,term_offset)){ \
-        wterm_cr(); \
-        term_offset=0; \
-    }
-
-#define fix_log_offset(A) \
-    if (needs_wrapping(A,file_offset)){ \
-        wlog_cr(); \
-        file_offset=0; \
-    }
-
-void print_char(int s)
-{
-    if (s < 0 || s > 255) {
-        formatted_warning("print","weird character %i",s);
-        return;
-    }
-    if (s == new_line_char_par) {
-        if (selector < pseudo) {
-            print_ln();
-            return;
-        }
-    }
-    switch (selector) {
-        case no_print:
-            break;
-        case term_only:
-            fix_term_offset(s);
-            wterm_char(s);
-            incr(term_offset);
-            if (term_offset == max_print_line) {
-                wterm_cr();
-                term_offset = 0;
-            }
-            break;
-        case log_only:
-            fix_log_offset(s);
-            wlog(s);
-            incr(file_offset);
-            if (file_offset == max_print_line) {
-                wlog_cr();
-                file_offset = 0;
-            }
-            break;
-        case term_and_log:
-            fix_term_offset(s);
-            fix_log_offset(s);
-            wterm_char(s);
-            wlog(s);
-            incr(term_offset);
-            incr(file_offset);
-            if (term_offset == max_print_line) {
-                wterm_cr();
-                term_offset = 0;
-            }
-            if (file_offset == max_print_line) {
-                wlog_cr();
-                file_offset = 0;
-            }
-            break;
-        case pseudo:
-            if (tally < trick_count)
-                trick_buf[tally % error_line] = (packed_ASCII_code) s;
-            break;
-        case new_string:
-            append_char(s);
-            break;
-        default:
-            fprintf(write_file[selector], "%c", s);
-    }
-    incr(tally);
-}
-
-/*tex
-
-An entire string is output by calling |print|. Note that if we are outputting the
-single standard ASCII character \.c, we could call |print("c")|, since |"c"=99|
-is the number of a single-character string, as explained above. But
-|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char| routine
-when it knows that this is safe. (The present implementation assumes that it is
-always safe to print a visible ASCII character.)
-
-The first 256 entries above the 17th unicode plane are used for a special trick:
-when \TeX\ has to print items in that range, it will instead print the character
-that results from substracting 0x110000 from that value. This allows
-byte-oriented output to things like \.{\\specials} and \.{\\pdfextension
-literals}. Todo: Perhaps it would be useful to do the same substraction while
-typesetting.
-
-
-*/
-
-void print(int s)
-{
-    if (s >= str_ptr) {
-        normal_warning("print","bad string pointer");
-        return;
-    } else if (s < STRING_OFFSET) {
-        if (s < 0) {
-            normal_warning("print","bad string offset");
-        } else {
-            /*tex We're not sure about this so it's disabled for now! */
-            if ((false) && (selector > pseudo)) {
-                /*tex internal strings are not expanded */
-                print_char(s);
-                return;
-            }
-            if (s == new_line_char_par) {
-                if (selector < pseudo) {
-                    print_ln();
-                    return;
-                }
-            }
-            if (s <= 0x7F) {
-                print_char(s);
-            } else if (s <= 0x7FF) {
-                print_char(0xC0 + (s / 0x40));
-                print_char(0x80 + (s % 0x40));
-            } else if (s <= 0xFFFF) {
-                print_char(0xE0 + (s / 0x1000));
-                print_char(0x80 + ((s % 0x1000) / 0x40));
-                print_char(0x80 + ((s % 0x1000) % 0x40));
-            } else if (s >= 0x110000) {
-                int c = s - 0x110000;
-                if (c >= 256) {
-                    formatted_warning("print", "bad raw byte to print (c=%d), skipped",c);
-                } else {
-                    print_char(c);
-                }
-            } else {
-                print_char(0xF0 + (s / 0x40000));
-                print_char(0x80 + ((s % 0x40000) / 0x1000));
-                print_char(0x80 + (((s % 0x40000) % 0x1000) / 0x40));
-                print_char(0x80 + (((s % 0x40000) % 0x1000) % 0x40));
-            }
-        }
-        return;
-    }
-    if (selector == new_string) {
-        append_string(str_string(s), (unsigned) str_length(s));
-        return;
-    }
-    lprint(&str_lstring(s));
-}
-
-void lprint (lstring *ss) {
-    /*tex current character code position */
-    unsigned char *j, *l;
-    j = ss->s;
-    l = j + ss->l;
-    while (j < l) {
-        /*tex I don't bother checking the last two bytes explicitly */
-        /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80 */
-        if ((j < l - 4) && (*j == 0xF4) && (*(j + 1) == 0x90)) {
-            int c = (*(j + 2) - 128) * 64 + (*(j + 3) - 128);
-            assert(c >= 0 && c < 256);
-            print_char(c);
-            j = j + 4;
-        } else {
-            print_char(*j);
-            incr(j);
-        }
-    }
-}
-
-/*tex
-
-The procedure |print_nl| is like |print|, but it makes sure that the string
-appears at the beginning of a new line.
-
-*/
-
-/*tex Moev to the beginning of the next line. */
-
-void print_nlp(void)
-{
-    if (new_string_line > 0) {
-        print_char(new_string_line);
-    } else if (((term_offset > 0) && (odd(selector))) ||
-               ((file_offset > 0) && (selector >= log_only))) {
-        print_ln();
-    }
-}
-
-/*tex Prints string |s| at beginning of the next line. */
-
-void print_nl(str_number s)
-{
-    print_nlp();
-    print(s);
-}
-
-/*tex
-
-|char *| versions of the same procedures. |tprint| is different because it uses
-buffering, which works well because most of the output actually comes through
-|tprint|.
-
-*/
-
-#define t_flush_buffer(target,offset) \
-    buffer[i++] = '\n'; \
-    buffer[i++] = '\0';\
-    fputs(buffer, target); \
-    i = 0; \
-    buffer[0] = '\0'; \
-    offset=0;
-
-void tprint(const char *sss)
-{
-    char *buffer = NULL;
-    int i = 0;
-    int newlinechar = new_line_char_par;
-    int dolog = 0;
-    int doterm = 0;
-    switch (selector) {
-        case no_print:
-            return;
-            break;
-        case term_only:
-            doterm = 1;
-            break;
-        case log_only:
-            dolog = 1;
-            break;
-        case term_and_log:
-            dolog = 1;
-            doterm = 1;
-            break;
-        case pseudo:
-            while (*sss) {
-               if (tally < trick_count) {
-                   trick_buf[tally % error_line] = (packed_ASCII_code) *sss++;
-               tally++;
-               } else {
-                   return;
-               }
-            }
-            return;
-            break;
-        case new_string:
-            append_string((const unsigned char *)sss, (unsigned) strlen(sss));
-            return;
-            break;
-        default:
-            {
-                char *newstr = xstrdup(sss);
-                char *s;
-                for (s=newstr;*s;s++) {
-                    if (*s == newlinechar) {
-                        *s = '\n';
-                    }
-                }
-                fputs(newstr, write_file[selector]);
-                free(newstr);
-                return;
-            }
-            break;
-    }
-    /*tex What is left is the 3 term/log settings. */
-    if (dolog || doterm) {
-        buffer = xmalloc(strlen(sss)*3);
-        if (dolog) {
-            const unsigned char *ss = (const unsigned char *) sss;
-            while (*ss) {
-                int s = *ss++;
-                if (needs_wrapping(s,file_offset) || s == newlinechar) {
-                    t_flush_buffer(log_file,file_offset);
-                }
-                if (s != newlinechar) {
-                    buffer[i++] = s;
-                    if (file_offset++ == max_print_line) {
-                        t_flush_buffer(log_file,file_offset);
-                    }
-                }
-            }
-            if (*buffer) {
-                buffer[i++] = '\0';
-                fputs(buffer, log_file);
-                buffer[0] = '\0';
-            }
-            i = 0;
-        }
-        if (doterm) {
-            const unsigned char *ss = (const unsigned char *) sss;
-            while (*ss) {
-                int s = *ss++;
-                if (needs_wrapping(s,term_offset) || s == newlinechar) {
-                    t_flush_buffer(term_out,term_offset);
-                }
-                if (s != newlinechar) {
-                    if (needs_escaping(s)) {
-                        buffer[i++] = s;
-                    } else {
-                        buffer[i++] = '^';
-                        buffer[i++] = '^';
-                        buffer[i++] = escaped_char(s);
-                        term_offset += 2;
-                    }
-                    if (++term_offset == max_print_line) {
-                        t_flush_buffer(term_out,term_offset);
-                    }
-                }
-            }
-            if (*buffer) {
-                buffer[i++] = '\0';
-                fputs(buffer, term_out);
-            }
-        }
-        free(buffer);
-    }
-}
-
-void tprint_nl(const char *s)
-{
-    print_nlp();
-    tprint(s);
-}
-
-/*tex
-
-Here is the very first thing that \TeX\ prints: a headline that identifies the
-version number and format package. The |term_offset| variable is temporarily
-incorrect, but the discrepancy is not serious since we assume that the banner and
-format identifier together will occupy at most |max_print_line| character
-positions.
-
-*/
-
-void print_banner(const char *v)
-{
-    int callback_id = callback_defined(start_run_callback);
-    if (callback_id == 0) {
-        fprintf(term_out, "This is " MyName ", Version %s%s ", v, WEB2CVERSION);
-        if (format_ident > 0)
-            print(format_ident);
-        print_ln();
-        if (show_luahashchars) {
-            wterm(' ');
-            fprintf(term_out,"Number of bits used by the hash function (" my_name "): %d",LUAI_HASHLIMIT);
-            print_ln();
-        }
-        if (shellenabledp) {
-            wterm(' ');
-            if (restrictedshell) {
-                fprintf(term_out, "restricted ");
-            }
-            fprintf(term_out, "system commands enabled.\n");
-        }
-    } else if (callback_id > 0) {
-        run_callback(callback_id, "->");
-    }
-}
-
-void log_banner(const char *v)
-{
-    const char *months[] = { "   ",
-        "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
-        "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
-    };
-    unsigned month = (unsigned) month_par;
-    if (month > 12)
-        month = 0;
-    fprintf(log_file, "This is " MyName ", Version %s%s ", v, WEB2CVERSION);
-    print(format_ident);
-    print_char(' ');
-    print_char(' ');
-    print_int(day_par);
-    print_char(' ');
-    fprintf(log_file, "%s", months[month]);
-    print_char(' ');
-    print_int(year_par);
-    print_char(' ');
-    print_two(time_par / 60);
-    print_char(':');
-    print_two(time_par % 60);
-    if (shellenabledp) {
-        wlog_cr();
-        wlog(' ');
-        if (restrictedshell)
-            fprintf(log_file, "restricted ");
-        fprintf(log_file, "system commands enabled.");
-    }
-    if (filelineerrorstylep) {
-        wlog_cr();
-        fprintf(log_file, " file:line:error style messages enabled.");
-    }
-}
-
-void print_version_banner(void)
-{
-    fprintf(term_out, "%s", luatex_banner);
-}
-
-/*tex
-
-The procedure |print_esc| prints a string that is preceded by the user's escape
-character (which is usually a backslash).
-
-*/
-
-void print_esc(str_number s)
-{
-    /*tex Set variable |c| to the current escape character: */
-    int c = escape_char_par;
-    if (c >= 0 && c < 0x110000)
-        print(c);
-    print(s);
-}
-
-/*tex
-
-This prints escape character, then |s|.
-
-*/
-
-void tprint_esc(const char *s)
-{
-    /*tex Set variable |c| to the current escape character: */
-    int c = escape_char_par;
-    if (c >= 0 && c < 0x110000)
-        print(c);
-    tprint(s);
-}
-
-/*tex
-
-An array of digits in the range |0..15| is printed by |print_the_digs|.
-
-*/
-
-void print_the_digs(eight_bits k)
-{
-    /*tex prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
-    while (k-- > 0) {
-        if (dig[k] < 10)
-            print_char('0' + dig[k]);
-        else
-            print_char('A' - 10 + dig[k]);
-    }
-}
-
-/*tex
-
-The following procedure, which prints out the decimal representation of a given
-integer |n|, has been written carefully so that it works properly if |n=0| or if
-|(-n)| would cause overflow. It does not apply |mod| or |div| to negative
-arguments, since such operations are not implemented consistently by all PASCAL
-compilers.
-
-*/
-
-void print_int(longinteger n)
-{
-    /*tex index to current digit; we assume that $|n|<10^{23}$ */
-    int k = 0;
-    /*tex used to negate |n| in possibly dangerous cases */
-    longinteger m;
-    if (n < 0) {
-        print_char('-');
-        if (n > -100000000) {
-            n = -n;
-        } else {
-            m = -1 - n;
-            n = m / 10;
-            m = (m % 10) + 1;
-            k = 1;
-            if (m < 10)
-                dig[0] = (int) m;
-            else {
-                dig[0] = 0;
-                incr(n);
-            }
-        }
-    }
-    do {
-        dig[k] = (int) (n % 10);
-        n = n / 10;
-        incr(k);
-    } while (n != 0);
-    print_the_digs((eight_bits) k);
-}
-
-/*tex
-
-Here is a trivial procedure to print two digits; it is usually called with a
-parameter in the range |0<=n<=99|.
-
-*/
-
-void print_two(int n)
-{
-    n = abs(n) % 100;
-    print_char('0' + (n / 10));
-    print_char('0' + (n % 10));
-}
-
-/*tex
-
-Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
-
-*/
-
-void print_qhex(int n)
-{
-    /*tex index to current digit; we assume that $0\L n<16^{22}$ */
-    int k = 0 ;
-    print_char('"');
-    do {
-        dig[k] = n % 16;
-        n = n / 16;
-        incr(k);
-    } while (n != 0);
-    print_the_digs((eight_bits) k);
-}
-
-/*tex
-
-Roman numerals are produced by the |print_roman_int| routine. Readers who like
-puzzles might enjoy trying to figure out how this tricky code works; therefore no
-explanation will be given. Notice that 1990 yields \.{mcmxc}, not \.{mxm}.
-
-*/
-
-void print_roman_int(int n)
-{
-    char *j, *k;
-    int u, v;
-    char mystery[] = "m2d5c2l5x2v5i";
-    j = (char *) mystery;
-    v = 1000;
-    while (1) {
-        while (n >= v) {
-            print_char(*j);
-            n = n - v;
-        }
-        if (n <= 0) {
-            /*tex nonpositive input produces no output */
-            return;
-        }
-        k = j + 2;
-        u = v / (*(k - 1) - '0');
-        if (*(k - 1) == '2') {
-            k = k + 2;
-            u = u / (*(k - 1) - '0');
-        }
-        if (n + u >= v) {
-            print_char(*k);
-            n = n + u;
-        } else {
-            j = j + 2;
-            v = v / (*(j - 1) - '0');
-        }
-    }
-}
-
-/*tex
-
-The |print| subroutine will not print a string that is still being created. The
-following procedure will.
-
-*/
-
-void print_current_string(void)
-{
-    /*tex points to current character code */
-    unsigned j = 0;
-    while (j < cur_length)
-        print_char(cur_string[j++]);
-}
-
-/*tex
-
-The procedure |print_cs| prints the name of a control sequence, given a pointer
-to its address in |eqtb|. A space is printed after the name unless it is a single
-nonletter or an active character. This procedure might be invoked with invalid
-data, so it is ``extra robust.'' The individual characters must be printed one at
-a time using |print|, since they may be unprintable.
-
-*/
-
-void print_cs(int p)
-{
-    str_number t = cs_text(p);
-    if (p < hash_base) {
-        /* nullcs */
-        if (p == null_cs) {
-            tprint_esc("csname");
-            tprint_esc("endcsname");
-            print_char(' ');
-        } else {
-            tprint_esc("IMPOSSIBLE.");
-        }
-    } else if ((p >= undefined_control_sequence) &&
-               ((p <= eqtb_size) || p > eqtb_size + hash_extra)) {
-        tprint_esc("IMPOSSIBLE.");
-    } else if (t >= str_ptr) {
-        tprint_esc("NONEXISTENT.");
-    } else {
-        if (is_active_cs(t)) {
-            print(active_cs_value(t));
-        } else {
-            print_esc(t);
-            if (single_letter(t)) {
-                if (get_cat_code(cat_code_table_par, pool_to_unichar(str_string(t))) == letter_cmd)
-                    print_char(' ');
-            } else {
-                print_char(' ');
-            }
-        }
-    }
-}
-
-/*tex
-
-Here is a similar procedure; it avoids the error checks, and it never prints a
-space after the control sequence.
-
-*/
-void sprint_cs(pointer p)
-{
-    str_number t;
-    if (p == null_cs) {
-        tprint_esc("csname");
-        tprint_esc("endcsname");
-    } else {
-        t = cs_text(p);
-        if (is_active_cs(t))
-            print(active_cs_value(t));
-        else
-            print_esc(t);
-    }
-}
-
-void sprint_cs_name(pointer p)
-{
-    str_number t;
-    if (p != null_cs) {
-        t = cs_text(p);
-        if (is_active_cs(t))
-            print(active_cs_value(t));
-        else
-            print(t);
-    }
-}
-
-/*tex
-
-This procedure is never called when |interaction<scroll_mode|.
-
-*/
-
-void prompt_input(const char *s)
-{
-    wake_up_terminal();
-    tprint(s);
-    term_input();
-}
-
-/*tex
-
-Then there is a subroutine that prints glue stretch and shrink, possibly followed
-by the name of finite units:
-
-*/
-
-void print_glue(scaled d, int order, const char *s)
-{
-    print_scaled(d);
-    if ((order < normal) || (order > filll)) {
-        tprint("foul");
-    } else if (order > normal) {
-        tprint("fi");
-        while (order > sfi) {
-            print_char('l');
-            decr(order);
-        }
-    } else if (s != NULL) {
-        tprint(s);
-    }
-}
-
-/*tex
-
-The next subroutine prints a whole glue specification
-
-*/
-
-void print_spec(int p, const char *s)
-{
-    if (p < 0) {
-        print_char('*');
-    } else {
-        print_scaled(width(p));
-        if (s != NULL)
-            tprint(s);
-        if (stretch(p) != 0) {
-            tprint(" plus ");
-            print_glue(stretch(p), stretch_order(p), s);
-        }
-        if (shrink(p) != 0) {
-            tprint(" minus ");
-            print_glue(shrink(p), shrink_order(p), s);
-        }
-    }
-}
-
-/*tex
-
-We can reinforce our knowledge of the data structures just introduced by
-considering two procedures that display a list in symbolic form. The first of
-these, called |short_display|, is used in ``overfull box'' messages to give the
-top-level description of a list. The other one, called |show_node_list|, prints a
-detailed description of exactly what is in the data structure.
-
-The philosophy of |short_display| is to ignore the fine points about exactly what
-is inside boxes, except that ligatures and discretionary breaks are expanded. As
-a result, |short_display| is a recursive procedure, but the recursion is never
-more than one level deep. @^recursion@>
-
-A global variable |font_in_short_display| keeps track of the font code that is
-assumed to be present when |short_display| begins; deviations from this font will
-be printed.
-
-*/
-
-/*tex An internal font number: */
-
-int font_in_short_display;
-
-/*tex
-
-Boxes, rules, inserts, whatsits, marks, and things in general that are sort of
-``complicated'' are indicated only by printing `\.{[]}'.
-
-We print a bit more than original \TEX. A value of 0 or 1 or any large value will
-behave the same as before. The reason for this extension is that a |name| not
-always makes sense.
-
-\starttyping
-0   \foo xyz
-1   \foo (bar)
-2   <bar> xyz
-3   <bar @ ..> xyz
-4   <id>
-5   <id: bar>
-6   <id: bar @ ..> xyz
-\stoptyping
-
-*/
-
-void print_font_identifier(internal_font_number f)
-{
-    str_number fonttext;
-    fonttext = font_id_text(f);
-    if (tracing_fonts_par >= 2 && tracing_fonts_par <= 6) {
-        /*tex < > is less likely to clash with text parenthesis */
-        tprint("<");
-        if (tracing_fonts_par >= 2 && tracing_fonts_par <= 3) {
-            print_font_name(f);
-            if (tracing_fonts_par >= 3 || font_size(f) != font_dsize(f)) {
-                tprint(" @ ");
-                print_scaled(font_size(f));
-                tprint("pt");
-            }
-        } else if (tracing_fonts_par >= 4 && tracing_fonts_par <= 6) {
-            print_int(f);
-            if (tracing_fonts_par >= 5) {
-                tprint(": ");
-                print_font_name(f);
-                if (tracing_fonts_par >= 6 || font_size(f) != font_dsize(f)) {
-                    tprint(" @ ");
-                    print_scaled(font_size(f));
-                    tprint("pt");
-                }
-            }
-        }
-        print_char('>');
-    } else {
-        /*tex old method, inherited from pdftex  */
-        if (fonttext > 0) {
-            print_esc(fonttext);
-        } else {
-            tprint_esc("FONT");
-            print_int(f);
-        }
-        if (tracing_fonts_par > 0) {
-            tprint(" (");
-            print_font_name(f);
-            if (font_size(f) != font_dsize(f)) {
-                tprint("@");
-                print_scaled(font_size(f));
-                tprint("pt");
-            }
-            print_char(')');
-        }
-    }
-}
-
-/*tex
-
-This prints highlights of list |p|.
-
-*/
-
-void short_display(int p)
-{
-    while (p != null) {
-        if (is_char_node(p)) {
-            if (lig_ptr(p) != null) {
-                short_display(lig_ptr(p));
-            } else {
-                if (font(p) != font_in_short_display) {
-                    if (!is_valid_font(font(p)))
-                        print_char('*');
-                    else
-                        print_font_identifier(font(p));
-                    print_char(' ');
-                    font_in_short_display = font(p);
-                }
-                print(character(p));
-            }
-        } else {
-            /*tex Print a short indication of the contents of node |p| */
-            print_short_node_contents(p);
-        }
-        p = vlink(p);
-    }
-}
-
-/*tex
-
-The |show_node_list| routine requires some auxiliary subroutines: one to print a
-font-and-character combination, one to print a token list without its reference
-count, and one to print a rule dimension.
-
-*/
-
-/*tex
-
-This prints |char_node| data.
-
-*/
-
-void print_font_and_char(int p)
-{
-    if (!is_valid_font(font(p)))
-        print_char('*');
-    else
-        print_font_identifier(font(p));
-    print_char(' ');
-    print(character(p));
-}
-
-/*tex
-
-This prints token list data in braces
-
-*/
-
-void print_mark(int p)
-{
-    print_char('{');
-    if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end))
-        tprint_esc("CLOBBERED.");
-    else
-        show_token_list(token_link(p), null, max_print_line - 10);
-    print_char('}');
-}
-
-/*tex
-
-This prints dimensions of a rule node.
-
-*/
-
-void print_rule_dimen(scaled d)
-{
-    if (is_running(d))
-        print_char('*');
-    else
-        print_scaled(d);
-}
-
-/*tex
-
-Since boxes can be inside of boxes, |show_node_list| is inherently recursive,
-@^recursion@> up to a given maximum number of levels. The history of nesting is
-indicated by the current string, which will be printed at the beginning of each
-line; the length of this string, namely |cur_length|, is the depth of nesting.
-
-A global variable called |depth_threshold| is used to record the maximum depth of
-nesting for which |show_node_list| will show information. If we have
-|depth_threshold=0|, for example, only the top level information will be given
-and no sublists will be traversed. Another global variable, called |breadth_max|,
-tells the maximum number of items to show at each level; |breadth_max| had better
-be positive, or you won't see anything.
-
-*/
-
-/*tex The maximum nesting depth in box displays: */
-
-int depth_threshold;
-
-/*tex The maximum number of items shown at the same list level: */
-
-int breadth_max;
-
-/*tex
-
-The recursive machinery is started by calling |show_box|. Assign the values
-|depth_threshold:=show_box_depth| and |breadth_max:=show_box_breadth|
-
-*/
-
-void show_box(halfword p)
-{
-    depth_threshold = show_box_depth_par;
-    breadth_max = show_box_breadth_par;
-    if (breadth_max <= 0)
-        breadth_max = 5;
-    /*tex the show starts at |p| */
-    show_node_list(p);
-    print_ln();
-}
-
-/*tex
-
-Helper for debugging purposes. It prints highlights of list |p|
-
-*/
-
-void short_display_n(int p, int m)
-{
-    int i = 0;
-    font_in_short_display = null_font;
-    if (p == null)
-        return;
-    while (p != null) {
-        if (is_char_node(p)) {
-            if (p <= max_halfword) {
-                if (font(p) != font_in_short_display) {
-                    if (!is_valid_font(font(p)))
-                        print_char('*');
-                    else
-                        print_font_identifier(font(p));
-                    print_char(' ');
-                    font_in_short_display = font(p);
-                }
-                print(character(p));
-            }
-        } else {
-            if ( (type(p) == glue_node) ||
-                 (type(p) == disc_node) ||
-                 (type(p) == penalty_node) ||
-                ((type(p) == kern_node) && (subtype(p) == explicit_kern ||
-                                            subtype(p) == italic_kern   ))) {
-                incr(i);
-            }
-            if (i >= m)
-                return;
-            if (type(p) == disc_node) {
-                print_char('|');
-                short_display(vlink(pre_break(p)));
-                print_char('|');
-                short_display(vlink(post_break(p)));
-                print_char('|');
-            } else {
-                /*tex Print a short indication of the contents of node |p| */
-                print_short_node_contents(p);
-            }
-        }
-        p = vlink(p);
-        if (p == null)
-            return;
-    }
-    update_terminal();
-}
-
-/*tex
-
-When debugging a macro package, it can be useful to see the exact control
-sequence names in the format file. For example, if ten new csnames appear, it's
-nice to know what they are, to help pinpoint where they came from. (This isn't a
-truly ``basic'' printing procedure, but that's a convenient module in which to
-put it.)
-
-*/
-
-void print_csnames(int hstart, int hfinish)
-{
-    int h;
-    unsigned char *c, *l;
-    fprintf(stderr, "fmtdebug:csnames from %d to %d:", (int) hstart, (int) hfinish);
-    for (h = hstart; h <= hfinish; h++) {
-        if (cs_text(h) > 0) {
-            /*tex We have anything at this position. */
-            c = str_string(cs_text(h));
-            l = c + str_length(cs_text(h));
-            while (c < l) {
-                /*tex Print the characters. */
-                fputc(*c++, stderr);
-            }
-            fprintf(stderr, "|");
-        }
-    }
-}
-
-/*tex
-
-A helper for printing file:line:error style messages. Look for a filename in
-|full_source_filename_stack|, and if we fail to find one fall back on the
-non-file:line:error style.
-
-*/
-
-void print_file_line(void)
-{
-    int level = in_open;
-    while ((level > 0) && (full_source_filename_stack[level] == 0))
-        decr(level);
-    if (level == 0) {
-        tprint_nl("! ");
-    } else {
-        tprint_nl("");
-        tprint(full_source_filename_stack[level]);
-        print_char(':');
-        if (level == in_open)
-            print_int(line);
-        else
-            print_int(line_stack[level + 1]);
-        tprint(": ");
-    }
-}
-
-/*tex
-
-\TeX\ is occasionally supposed to print diagnostic information that goes only
-into the transcript file, unless |tracing_online| is positive. Here are two
-routines that adjust the destination of print commands:
-
-*/
-
-void begin_diagnostic(void)
-{
-    global_old_setting = selector;
-    if ((tracing_online_par <= 0) && (selector == term_and_log)) {
-        decr(selector);
-        if (history == spotless)
-            history = warning_issued;
-    }
-}
-
-/*tex
-
-Restore proper conditions after tracing.
-
-*/
-
-void end_diagnostic(boolean blank_line)
-{
-    tprint_nl("");
-    if (blank_line)
-        print_ln();
-    selector = global_old_setting;
-}
-
-/*tex
-
-Of course we had better declare another global variable, if the previous routines
-are going to work.
-
-*/
-
-int global_old_setting;
--- texlive-bin.orig/texk/web2c/luatexdir/tex/scanning.c
+++ /dev/null
@@ -1,2743 +0,0 @@
-/*
-
-Copyright 2009-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-static void scan_expr(void);
-
-/*tex
-
-    Let's turn now to some procedures that \TeX\ calls upon frequently to digest
-    certain kinds of patterns in the input. Most of these are quite simple; some
-    are quite elaborate. Almost all of the routines call |get_x_token|, which can
-    cause them to be invoked recursively.
-
-    The |scan_left_brace| routine is called when a left brace is supposed to be
-    the next non-blank token. (The term ``left brace'' means, more precisely, a
-    character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to appear
-    before the |left_brace|.
-
-*/
-
-/* This reads a mandatory |left_brace|: */
-
-void scan_left_brace(void)
-{
-    /*tex 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) {
-        print_err("Missing { inserted");
-        help4(
-            "A left brace was mandatory here, so I've put one in.",
-            "You might want to delete and/or insert some corrections",
-            "so that I will find a matching right brace soon.",
-            "If you're confused by all this, try typing `I}' now."
-        );
-        back_error();
-        cur_tok = left_brace_token + '{';
-        cur_cmd = left_brace_cmd;
-        cur_chr = '{';
-        incr(align_state);
-    }
-}
-
-/*tex
-
-    The |scan_optional_equals| routine looks for an optional `\.=' sign preceded
-    by optional spaces; `\.{\\relax}' is not ignored here.
-
-*/
-
-void scan_optional_equals(void)
-{
-    /*tex Get the next non-blank non-call token */
-    do {
-        get_x_token();
-    } while (cur_cmd == spacer_cmd);
-    if (cur_tok != other_token + '=')
-        back_input();
-}
-
-/*tex
-
-    Here is a procedure that sounds an alarm when mu and non-mu units are being
-    switched.
-
-*/
-
-static void mu_error(void)
-{
-    print_err("Incompatible glue units");
-    help1("I'm going to assume that 1mu=1pt when they're mixed.");
-    error();
-}
-
-/*tex
-
-    The next routine `|scan_something_internal|' is used to fetch internal
-    numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}'
-    when expanding constructions like `\.{\\the\\toks0}' and
-    `\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int|
-    procedure, which calls |scan_something_internal|; on the other hand,
-    |scan_something_internal| also calls |scan_int|, for constructions like
-    `\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we have to
-    declare |scan_int| as a |forward| procedure. A few other procedures are also
-    declared at this point.
-
-    \TeX\ doesn't know exactly what to expect when |scan_something_internal|
-    begins. For example, an integer or dimension or glue value could occur
-    immediately after `\.{\\hskip}'; and one can even say \.{\\the} with respect
-    to token lists in constructions like `\.{\\xdef\\o\{\\the\\output\}}'. On the
-    other hand, only integers are allowed after a construction like
-    `\.{\\count}'. To handle the various possibilities, |scan_something_internal|
-    has a |level| parameter, which tells the ``highest'' kind of quantity that
-    |scan_something_internal| is allowed to produce. Eight levels are
-    distinguished, namely |int_val|, |attr_val|, |dimen_val|, |glue_val|,
-    |mu_val|, |dir_val|, |ident_val|, and |tok_val|.
-
-    The output of |scan_something_internal| (and of the other routines
-    |scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global
-    variable |cur_val|, and its level is put into |cur_val_level|. The highest
-    values of |cur_val_level| are special: |mu_val| is used only when |cur_val|
-    points to something in a ``muskip'' register, or to one of the three
-    parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip}; |ident_val|
-    is used only when |cur_val| points to a font identifier; |tok_val| is used
-    only when |cur_val| points to |null| or to the reference count of a token
-    list. The last two cases are allowed only when |scan_something_internal| is
-    called with |level=tok_val|.
-
-    If the output is glue, |cur_val| will point to a glue specification, and the
-    reference count of that glue will have been updated to reflect this
-    reference; if the output is a nonempty token list, |cur_val| will point to
-    its reference count, but in this case the count will not have been updated.
-    Otherwise |cur_val| will contain the integer or scaled value in question.
-
-*/
-
-/*tex The value returned by numeric scanners: */
-
-int cur_val;
-
-/*tex Delcodes are sometimes 51 digits: */
-
-int cur_val1;
-
-/*tex The level of this value: */
-
-int cur_val_level;
-
-#define scanned_result(A,B) do { \
-    cur_val=A; \
-    cur_val_level=B; \
-} while (0)
-
-/*tex
-
-    When a |glue_val| changes to a |dimen_val|, we use the width component of the
-    glue; there is no need to decrease the reference count, since it has not yet
-    been increased. When a |dimen_val| changes to an |int_val|, we use scaled
-    points so that the value doesn't actually change. And when a |mu_val| changes
-    to a |glue_val|, the value doesn't change either.
-
-*/
-
-static void downgrade_cur_val(boolean delete_glue)
-{
-    if (cur_val_level == glue_val_level) {
-        halfword m = cur_val;
-        cur_val = width(m);
-        if (delete_glue)
-            flush_node(m);
-    } else if (cur_val_level == mu_val_level) {
-        mu_error();
-    }
-    decr(cur_val_level);
-}
-
-void negate_cur_val(boolean modify_glue)
-{
-    if (cur_val_level >= glue_val_level) {
-        if (modify_glue) {
-            /*tex We modify in-place. */
-        } else {
-            cur_val = new_spec(cur_val);
-        }
-        negate(width(cur_val));
-        negate(stretch(cur_val));
-        negate(shrink(cur_val));
-    } else {
-        negate(cur_val);
-    }
-}
-
-/*tex
-
-    Some of the internal items can be fetched both routines, and these have been
-    split off into the next routine, that returns true if the command code was
-    understood.
-
-*/
-
-static boolean short_scan_something_internal(int cmd, int chr, int level, boolean negative)
-{
-    /*tex |chr_code| part of the operand token */
-    halfword m;
-    /*tex general purpose index */
-    halfword q;
-    /*tex index into |nest| */
-    int p;
-    int save_cur_chr;
-    boolean succeeded = true;
-    m = chr;
-    switch (cmd) {
-        case assign_toks_cmd:
-            scanned_result(equiv(m), tok_val_level);
-            break;
-        case assign_int_cmd:
-            scanned_result(eqtb[m].cint, int_val_level);
-            break;
-        case assign_attr_cmd:
-            scanned_result(eqtb[m].cint, int_val_level);
-            break;
-        case assign_dimen_cmd:
-            scanned_result(eqtb[m].cint, dimen_val_level);
-            break;
-        case assign_glue_cmd:
-            scanned_result(equiv(m), glue_val_level);
-            break;
-        case assign_mu_glue_cmd:
-            scanned_result(equiv(m), mu_val_level);
-            break;
-        case assign_direction_cmd:
-            if (m == (int_base + line_direction_code)) {
-                m = int_base + text_direction_code;
-            }
-            scanned_result(eqtb[m].cint, int_val_level);
-            break;
-        case assign_dir_cmd:
-            if (m == (int_base + line_direction_code)) {
-                m = int_base + text_direction_code;
-            }
-            scanned_result(eqtb[m].cint, dir_val_level);
-            break;
-        case math_style_cmd:
-            scanned_result(m, int_val_level);
-            break;
-        case set_aux_cmd:
-            /*tex Fetch the |space_factor| or the |prev_depth|. */
-            if (abs(cur_list.mode_field) != m) {
-                print_err("Improper ");
-                print_cmd_chr(set_aux_cmd, m);
-                help4(
-                    "You can refer to \\spacefactor only in horizontal mode;",
-                    "you can refer to \\prevdepth only in vertical mode; and",
-                    "neither of these is meaningful inside \\write. So",
-                    "I'm forgetting what you said and using zero instead."
-                );
-                error();
-                if (level != tok_val_level)
-                    scanned_result(0, dimen_val_level);
-                else
-                    scanned_result(0, int_val_level);
-            } else if (m == vmode) {
-                scanned_result(prev_depth_par, dimen_val_level);
-            } else {
-                scanned_result(space_factor_par, int_val_level);
-            }
-            break;
-        case set_prev_graf_cmd:
-            /*tex Fetch the |prev_graf| */
-            if (cur_list.mode_field == 0) {
-                /*tex |prev_graf=0| within \.{\\write} */
-                scanned_result(0, int_val_level);
-            } else {
-                p = nest_ptr;
-                while (abs(nest[p].mode_field) != vmode)
-                    decr(p);
-                scanned_result(nest[p].pg_field, int_val_level);
-            }
-            break;
-        case set_page_int_cmd:
-            /*tex Fetch the |dead_cycles| or the |insert_penalties| */
-            if (m == 0)
-                cur_val = dead_cycles;
-            else if (m == 2)
-                cur_val = interaction;
-            else
-                cur_val = insert_penalties;
-            cur_val_level = int_val_level;
-            break;
-        case set_page_dimen_cmd:
-            /*tex Fetch something on the |page_so_far|. */
-            if ((page_contents == empty) && (!output_active)) {
-                if (m == 0)
-                    cur_val = max_dimen;
-                else
-                    cur_val = 0;
-            } else {
-                cur_val = page_so_far[m];
-            }
-            cur_val_level = dimen_val_level;
-            break;
-        case set_tex_shape_cmd:
-            /*tex Fetch the |par_shape| size. */
-            if (par_shape_par_ptr == null)
-                cur_val = 0;
-            else
-                cur_val = vinfo(par_shape_par_ptr + 1);
-            cur_val_level = int_val_level;
-            break;
-        case set_etex_shape_cmd:
-            /*tex Fetch a penalties array element. */
-            scan_int();
-            if ((equiv(m) == null) || (cur_val < 0)) {
-                cur_val = 0;
-            } else {
-                if (cur_val > penalty(equiv(m)))
-                    cur_val = penalty(equiv(m));
-                cur_val = penalty(equiv(m) + cur_val);
-            }
-            cur_val_level = int_val_level;
-            break;
-        case char_given_cmd:
-        case math_given_cmd:
-        case xmath_given_cmd:
-            scanned_result(cur_chr, int_val_level);
-            break;
-        case last_item_cmd:
-            /*tex
-
-                Because the items in this case directly refer to |cur_chr|, it
-                needs to be saved and restored.
-
-            */
-            save_cur_chr = cur_chr;
-            cur_chr = chr;
-            /*tex
-
-                Fetch an item in the current node, if appropriate. Here is where
-                \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\ } are implemented.
-                The reference count for \.{\\lastskip} will be updated later.
-
-                We also handle \.{\\inputlineno} and \.{\\badness} here, because
-                they are legal in similar contexts.
-
-            */
-            if (m >= input_line_no_code) {
-                if (m >= eTeX_glue) {
-                    /*tex Process an expression and |return|. */
-                    if (m < eTeX_mu) {
-                        if (m == mu_to_glue_code) {
-                            scan_mu_glue();
-                        };
-                        cur_val_level = glue_val_level;
-                    } else if (m < eTeX_expr) {
-                        if (m == glue_to_mu_code) {
-                            scan_normal_glue();
-                        }
-                        cur_val_level = mu_val_level;
-                    } else {
-                        cur_val_level = m - eTeX_expr + int_val_level;
-                        scan_expr();
-                    }
-                    /*tex
-
-                        This code for reducing |cur_val_level| and\slash or
-                        negating the result is similar to the one for all the
-                        other cases of |scan_something_internal|; we free a
-                        glue_spec when needed.
-
-                    */
-                    while (cur_val_level > level) {
-                        downgrade_cur_val(true);
-                    }
-                    if (negative) {
-                        /*tex
-
-                            We get a new glue spec node with negated values and
-                            the old intermediate is deleted.
-
-                        */
-                        negate_cur_val(true);
-                    }
-                    return succeeded;
-                } else if (m >= eTeX_dim) {
-                    switch (m) {
-                        case font_char_wd_code:
-                        case font_char_ht_code:
-                        case font_char_dp_code:
-                        case font_char_ic_code:
-                            scan_font_ident();
-                            q = cur_val;
-                            scan_char_num();
-                            if (char_exists(q, cur_val)) {
-                                switch (m) {
-                                case font_char_wd_code:
-                                    cur_val = char_width(q, cur_val);
-                                    break;
-                                case font_char_ht_code:
-                                    cur_val = char_height(q, cur_val);
-                                    break;
-                                case font_char_dp_code:
-                                    cur_val = char_depth(q, cur_val);
-                                    break;
-                                case font_char_ic_code:
-                                    cur_val = char_italic(q, cur_val);
-                                    break;
-				}
-                            } else {
-                                cur_val = 0;
-                            }
-                            break;
-                        case par_shape_length_code:
-                        case par_shape_indent_code:
-                        case par_shape_dimen_code:
-                            q = cur_chr - par_shape_length_code;
-                            scan_int();
-                            if ((par_shape_par_ptr == null) || (cur_val <= 0)) {
-                                cur_val = 0;
-                            } else {
-                                if (q == 2) {
-                                    q = cur_val % 2;
-                                    cur_val = (cur_val + q) / 2;
-                                }
-                                if (cur_val > vinfo(par_shape_par_ptr + 1))
-                                    cur_val = vinfo(par_shape_par_ptr + 1);
-                                cur_val =
-                                    varmem[par_shape_par_ptr + 2 * cur_val - q + 1].cint;
-                            }
-                            cur_val_level = dimen_val_level;
-                            break;
-                        case glue_stretch_code:
-                        case glue_shrink_code:
-                            scan_normal_glue();
-                            q = cur_val;
-                            if (m == glue_stretch_code)
-                                cur_val = stretch(q);
-                            else
-                                cur_val = shrink(q);
-                            flush_node(q);
-                            break;
-                    }
-                    cur_val_level = dimen_val_level;
-                } else {
-                    switch (m) {
-                        case input_line_no_code:
-                            cur_val = line;
-                            break;
-                        case badness_code:
-                            cur_val = last_badness;
-                            break;
-                        case luatex_version_code:
-                            cur_val = get_luatexversion();
-                            break;
-                        case last_saved_box_resource_index_code:
-                            cur_val = last_saved_box_index;
-                            break;
-                        case last_saved_image_resource_index_code:
-                            cur_val = last_saved_image_index;
-                            break;
-                        case last_saved_image_resource_pages_code:
-                            cur_val = last_saved_image_pages;
-                            break;
-                        case last_x_pos_code:
-                            cur_val = last_position.h;
-                            break;
-                        case last_y_pos_code:
-                            cur_val = last_position.v;
-                            break;
-                        case random_seed_code:
-                            cur_val = random_seed;
-                            break;
-                        case eTeX_version_code:
-                            cur_val = eTeX_version;
-                            break;
-                        case eTeX_minor_version_code:
-                            cur_val = eTeX_minor_version;
-                            break;
-                        case current_group_level_code:
-                            cur_val = cur_level - level_one;
-                            break;
-                        case current_group_type_code:
-                            cur_val = cur_group;
-                            break;
-                        case current_if_level_code:
-                            q = cond_ptr;
-                            cur_val = 0;
-                            while (q != null) {
-                                incr(cur_val);
-                                q = vlink(q);
-                            }
-                            break;
-                        case current_if_type_code:
-                            if (cond_ptr == null)
-                                cur_val = 0;
-                            else if (cur_if < unless_code)
-                                cur_val = cur_if + 1;
-                            else
-                                cur_val = -(cur_if - unless_code + 1);
-                            break;
-                        case current_if_branch_code:
-                            if ((if_limit == or_code) || (if_limit == else_code))
-                                cur_val = 1;
-                            else if (if_limit == fi_code)
-                                cur_val = -1;
-                            else
-                                cur_val = 0;
-                            break;
-                        case glue_stretch_order_code:
-                        case glue_shrink_order_code:
-                            scan_normal_glue();
-                            q = cur_val;
-                            if (m == glue_stretch_order_code)
-                                cur_val = stretch_order(q);
-                            else
-                                cur_val = shrink_order(q);
-                            flush_node(q);
-                            break;
-                    }
-                    cur_val_level = int_val_level;
-                }
-            } else {
-                if (cur_chr == glue_val_level)
-                    cur_val = zero_glue;
-                else
-                    cur_val = 0;
-                if (cur_chr == last_node_type_code) {
-                    cur_val_level = int_val_level;
-                    if ((cur_list.tail_field == cur_list.head_field)
-                        || (cur_list.mode_field == 0))
-                        cur_val = -1;
-                } else {
-                    /*tex assumes identical values */
-                    cur_val_level = cur_chr;
-                }
-                if ((cur_list.tail_field != contrib_head) &&
-                    !is_char_node(cur_list.tail_field) &&
-                    (cur_list.mode_field != 0)) {
-                    switch (cur_chr) {
-                        case lastpenalty_code:
-                            if (type(cur_list.tail_field) == penalty_node)
-                                cur_val = penalty(cur_list.tail_field);
-                            break;
-                        case lastkern_code:
-                            if (type(cur_list.tail_field) == kern_node)
-                                cur_val = width(cur_list.tail_field);
-                            break;
-                        case lastskip_code:
-                            if (type(cur_list.tail_field) == glue_node)
-                                cur_val = cur_list.tail_field;
-                            if (subtype(cur_list.tail_field) == mu_glue)
-                                cur_val_level = mu_val_level;
-                            break;
-                        case last_node_type_code:
-                            cur_val = visible_last_node_type(cur_list.tail_field);
-                            break;
-                    }
-                } else if ((cur_list.mode_field == vmode) && (cur_list.tail_field == cur_list.head_field)) {
-                    switch (cur_chr) {
-                        case lastpenalty_code:
-                            cur_val = last_penalty;
-                            break;
-                        case lastkern_code:
-                            cur_val = last_kern;
-                            break;
-                        case lastskip_code:
-                            if (last_glue != max_halfword)
-                                cur_val = last_glue;
-                            break;
-                        case last_node_type_code:
-                            cur_val = last_node_type;
-                            break;
-                    }
-                }
-            }
-            cur_chr = save_cur_chr;
-            break;
-        default:
-            succeeded = false;
-    }
-    if (succeeded) {
-        while (cur_val_level > level) {
-            /*tex Convert |cur_val| to a lower level. */
-            downgrade_cur_val(false);
-        }
-        /*tex
-
-            Fix the reference count, if any, and negate |cur_val| if |negative|.
-            If |cur_val| points to a glue specification at this point, the
-            reference count for the glue does not yet include the reference by
-            |cur_val|. If |negative| is |true|, |cur_val_level| is known to be
-            |<=mu_val|.
-
-         */
-        if (negative) {
-            /*tex We create a new (negated) glue spec and keep the old one. */
-            negate_cur_val(false);
-        } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) {
-			cur_val = new_spec(cur_val);
-        }
-    }
-    return succeeded;
-}
-
-/*tex
-
-    First, here is a short routine that is called from lua code. All the real
-    work is delegated to |short_scan_something_internal| that is shared between
-    this routine and |scan_something_internal|.
-
-*/
-
-void scan_something_simple(halfword cmd, halfword subitem)
-{
-    /*tex Negative is never true. */
-    if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) {
-        /*tex Complain that |texlib| can not do this; give zero result. */
-        print_err("You can't use `");
-        print_cmd_chr((quarterword) cmd, subitem);
-        tprint("' as tex library index");
-        help1("I'm forgetting what you said and using zero instead.");
-        error();
-        scanned_result(0, int_val_level);
-    }
-}
-
-/*tex
-
-    OK, we're ready for |scan_something_internal| itself. A second parameter,
-    |negative|, is set |true| if the value that is found should be negated. It is
-    assumed that |cur_cmd| and |cur_chr| represent the first token of the
-    internal quantity to be scanned; an error will be signalled if
-    |cur_cmd<min_internal| or |cur_cmd>max_internal|.
-
-*/
-
-/*tex Fetch an internal parameter: */
-
-void scan_something_internal(int level, boolean negative)
-{
-    /*tex |chr_code| part of the operand token */
-    halfword m;
-    /*tex accumulators */
-    int n, k;
-  RESTART:
-    m = cur_chr;
-    if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) {
-        switch (cur_cmd) {
-            case def_char_code_cmd:
-                /*tex Fetch a character code from some table */
-                scan_char_num();
-                if (m == math_code_base) {
-                    cur_val1 = get_math_code_num(cur_val);
-                    scanned_result(cur_val1, int_val_level);
-                } else if (m == lc_code_base) {
-                    cur_val1 = get_lc_code(cur_val);
-                    scanned_result(cur_val1, int_val_level);
-                } else if (m == uc_code_base) {
-                    cur_val1 = get_uc_code(cur_val);
-                    scanned_result(cur_val1, int_val_level);
-                } else if (m == sf_code_base) {
-                    cur_val1 = get_sf_code(cur_val);
-                    scanned_result(cur_val1, int_val_level);
-                } else if (m == cat_code_base) {
-                    cur_val1 = get_cat_code(cat_code_table_par, cur_val);
-                    scanned_result(cur_val1, int_val_level);
-                } else {
-                    confusion("def_char");
-                }
-                break;
-            case def_del_code_cmd:
-            case extdef_del_code_cmd:
-                /*tex Fetch a character code from some table. */
-                scan_char_num();
-                cur_val1 = get_del_code_num(cur_val);
-                scanned_result(cur_val1, int_val_level);
-                break;
-            case extdef_math_code_cmd:
-                /*tex Fetch an extended math code table value. */
-                scan_char_num();
-                cur_val1 = get_math_code_num(cur_val);
-                scanned_result(cur_val1, int_val_level);
-                break;
-            case toks_register_cmd:
-            case set_font_cmd:
-            case def_font_cmd:
-            case letterspace_font_cmd:
-            case copy_font_cmd:
-                /*tex Fetch a token list or font identifier, provided that |level=tok_val|. */
-                if (level != tok_val_level) {
-                    print_err("Missing number, treated as zero");
-                    help3(
-                        "A number should have been here; I inserted `0'.",
-                        "(If you can't figure out why I needed to see a number,",
-                        "look up `weird error' in the index to The TeXbook.)"
-                    );
-                    back_error();
-                    scanned_result(0, dimen_val_level);
-                } else if (cur_cmd == toks_register_cmd) {
-                    scan_register_num();
-                    m = toks_base + cur_val;
-                    scanned_result(equiv(m), tok_val_level);
-                } else {
-                    back_input();
-                    scan_font_ident();
-                    scanned_result(font_id_base + cur_val, ident_val_level);
-                }
-                break;
-            case set_font_id_cmd:
-                scan_int();
-                scanned_result(font_id_base + cur_val, ident_val_level);
-                break;
-            case def_family_cmd:
-                /*tex Fetch a math font identifier. */
-                scan_char_num();
-                cur_val1 = fam_fnt(cur_val, m);
-                scanned_result(font_id_base + cur_val1, ident_val_level);
-                break;
-            case set_math_param_cmd:
-                /*tex Fetch a math parameter. */
-                cur_val1 = 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_val = display_style;
-                    back_error();
-                } else {
-                    cur_val = cur_chr;
-                }
-                if (cur_val1 < math_param_first_mu_glue) {
-                    if (cur_val1 == math_param_radical_degree_raise) {
-                        cur_val1 = get_math_param(cur_val1, cur_chr);
-                        scanned_result(cur_val1, int_val_level);
-                    } else {
-                        cur_val1 = get_math_param(cur_val1, cur_chr);
-                        scanned_result(cur_val1, dimen_val_level);
-                    }
-                } else {
-                    cur_val1 = get_math_param(cur_val1, cur_chr);
-                    if (cur_val1 == thin_mu_skip_code)
-                        cur_val1 = thin_mu_skip_par;
-                    else if (cur_val1 == med_mu_skip_code)
-                        cur_val1 = med_mu_skip_par;
-                    else if (cur_val1 == thick_mu_skip_code)
-                        cur_val1 = thick_mu_skip_par;
-                    scanned_result(cur_val1, mu_val_level);
-                }
-                break;
-            case assign_box_dir_cmd:
-                scan_register_num();
-                m = cur_val;
-                if (box(m) != null)
-                    cur_val = box_dir(box(m));
-                else
-                    cur_val = 0;
-                cur_val_level = dir_val_level;
-                break;
-            case set_box_dimen_cmd:
-                /*tex Fetch a box dimension. */
-                scan_register_num();
-                if (box(cur_val) == null)
-                    cur_val = 0;
-                else
-                    cur_val = varmem[box(cur_val) + m].cint;
-                cur_val_level = dimen_val_level;
-                break;
-            case assign_font_dimen_cmd:
-                /*tex Fetch a font dimension. */
-                get_font_dimen();
-                break;
-            case assign_font_int_cmd:
-                /*tex Fetch a font integer. */
-                scan_font_ident();
-                if (m == 0) {
-                    scanned_result(hyphen_char(cur_val), int_val_level);
-                } else if (m == 1) {
-                    scanned_result(skew_char(cur_val), int_val_level);
-                } else if (m == no_lig_code) {
-                    scanned_result(test_no_ligatures(cur_val), int_val_level);
-                } else {
-                    n = cur_val;
-                    scan_char_num();
-                    k = cur_val;
-                    switch (m) {
-                        case lp_code_base:
-                            scanned_result(get_lp_code(n, k), int_val_level);
-                            break;
-                        case rp_code_base:
-                            scanned_result(get_rp_code(n, k), int_val_level);
-                            break;
-                        case ef_code_base:
-                            scanned_result(get_ef_code(n, k), int_val_level);
-                            break;
-                        case tag_code:
-                            scanned_result(get_tag_code(n, k), int_val_level);
-                            break;
-                    }
-                }
-                break;
-            case register_cmd:
-                /*tex Fetch a register */
-                scan_register_num();
-                switch (m) {
-                    case int_val_level:
-                        cur_val = count(cur_val);
-                        break;
-                    case attr_val_level:
-                        cur_val = attribute(cur_val);
-                        break;
-                    case dimen_val_level:
-                        cur_val = dimen(cur_val);
-                        break;
-                    case glue_val_level:
-                        cur_val = skip(cur_val);
-                        break;
-                    case mu_val_level:
-                        cur_val = mu_skip(cur_val);
-                        break;
-                }
-                cur_val_level = m;
-                break;
-            case ignore_spaces_cmd:
-                /*tex Trap unexpandable primitives. */
-                if (cur_chr == 1) {
-                    /*tex
-
-                        Reset |cur_tok| for unexpandable primitives, goto
-                        restart. This block deals with unexpandable
-                        \.{\\primitive} appearing at a spot where an integer or
-                        an internal values should have been found. It fetches the
-                        next token then resets |cur_cmd|, |cur_cs|, and
-                        |cur_tok|, based on the primitive value of that token. No
-                        expansion takes place, because the next token may be all
-                        sorts of things. This could trigger further expansion
-                        creating new errors.
-
-                    */
-                    get_token();
-                    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 = token_val(cur_cmd, cur_chr);
-                    } else {
-                        cur_cmd = relax_cmd;
-                        cur_chr = 0;
-                        cur_tok = cs_token_flag + frozen_relax;
-                        cur_cs = frozen_relax;
-                    }
-                    goto RESTART;
-                }
-                break;
-            case hyph_data_cmd:
-                switch (cur_chr) {
-                    case 0:
-                    case 1:
-                        goto DEFAULT;
-                        break;
-                    case 2:
-                        cur_val = get_pre_hyphen_char(language_par);
-                        cur_val_level = int_val_level;
-                        break;
-                    case 3:
-                        cur_val = get_post_hyphen_char(language_par);
-                        cur_val_level = int_val_level;
-                        break;
-                    case 4:
-                        cur_val = get_pre_exhyphen_char(language_par);
-                        cur_val_level = int_val_level;
-                        break;
-                    case 5:
-                        cur_val = get_post_exhyphen_char(language_par);
-                        cur_val_level = int_val_level;
-                        break;
-                    case 6:
-                        cur_val = get_hyphenation_min(language_par);
-                        cur_val_level = int_val_level;
-                        break;
-                    case 7:
-                        scan_int();
-                        cur_val = get_hj_code(language_par,cur_val);
-                        cur_val_level = int_val_level;
-                        break;
-                }
-                break;
-            default:
-                DEFAULT:
-                /*tex Complain that \.{\\the} can not do this; give zero result. */
-                print_err("You can't use `");
-                print_cmd_chr((quarterword) cur_cmd, cur_chr);
-                tprint("' after \\the");
-                help1("I'm forgetting what you said and using zero instead.");
-                error();
-                if (level != tok_val_level)
-                    scanned_result(0, dimen_val_level);
-                else
-                    scanned_result(0, int_val_level);
-                break;
-        }
-        while (cur_val_level > level) {
-            /*tex Convert |cur_val| to a lower level. */
-            downgrade_cur_val(false);
-        }
-        /*tex
-
-            Fix the reference count, if any, and negate |cur_val| if |negative|.
-            If |cur_val| points to a glue specification at this point, the
-            reference count for the glue does not yet include the reference by
-            |cur_val|. If |negative| is |true|, |cur_val_level| is known to be
-            |<=mu_val|.
-
-         */
-        if (negative) {
-            /*tex We create a new (negated) glue spec and keep the old one. */
-            negate_cur_val(false);
-        } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) {
-			cur_val = new_spec(cur_val);
-        }
-    }
-}
-
-/*tex
-
-    It is nice to have routines that say what they do, so the original
-    |scan_eight_bit_int| is superceded by |scan_register_num| and
-    |scan_mark_num|. It may become split up even further in the future.
-
-    Many of the |restricted classes| routines are the essentially the same except
-    for the upper limit and the error message, so it makes sense to combine these
-    all into one function.
-
-*/
-
-void scan_limited_int(int max, const char *name)
-{
-    char hlp[80];
-    scan_int();
-    if ((cur_val < 0) || (cur_val > max)) {
-        if (name == NULL) {
-            snprintf(hlp, 80, "Since I expected to read a number between 0 and %d,", max);
-            print_err("Bad number");
-        } else {
-            char msg[80];
-            snprintf(hlp, 80, "A %s must be between 0 and %d.", name, max);
-            snprintf(msg, 80, "Bad %s", name);
-            print_err(msg);
-        }
-        help2(hlp, "I changed this one to zero.");
-        int_error(cur_val);
-        cur_val = 0;
-    }
-}
-
-void scan_fifteen_bit_int(void)
-{
-    scan_real_fifteen_bit_int();
-    cur_val = ((cur_val / 0x1000) * 0x1000000) + (((cur_val % 0x1000) / 0x100) * 0x10000) + (cur_val % 0x100);
-}
-
-void scan_fifty_one_bit_int(void)
-{
-    int iiii;
-    scan_int();
-    if ((cur_val < 0) || (cur_val > 0777777777)) {
-        print_err("Bad delimiter code");
-        help2(
-            "A numeric delimiter (first part) must be between 0 and 2^{27}-1.",
-            "I changed this one to zero."
-        );
-        int_error(cur_val);
-        cur_val = 0;
-    }
-    iiii = cur_val;
-    scan_int();
-    if ((cur_val < 0) || (cur_val > 0xFFFFFF)) {
-        print_err("Bad delimiter code");
-        help2(
-            "A numeric delimiter (second part) must be between 0 and 2^{24}-1.",
-            "I changed this one to zero."
-        );
-        int_error(cur_val);
-        cur_val = 0;
-    }
-    cur_val1 = cur_val;
-    cur_val = iiii;
-}
-
-/*tex
-
-    An integer number can be preceded by any number of spaces and `\.+' or `\.-'
-    signs. Then comes either a decimal constant (i.e., radix 10), an octal
-    constant (i.e., radix 8, preceded by~'), a hexadecimal constant (radix 16,
-    preceded by~"), an alphabetic constant (preceded by~`), or an internal
-    variable. After scanning is complete, |cur_val| will contain the answer,
-    which must be at most $2^{31}-1=2147483647$ in absolute value. The value of
-    |radix| is set to 10, 8, or 16 in the cases of decimal, octal, or hexadecimal
-    constants, otherwise |radix| is set to zero. An optional space follows a
-    constant.
-
-*/
-
-/*tex |scan_int| sets this to 8, 10, 16, or zero */
-
-int radix;
-
-/*tex
-
-    The |scan_int| routine is used also to scan the integer part of a fraction;
-    for example, the `\.3' in `\.{3.14159}' will be found by |scan_int|. The
-    |scan_dimen| routine assumes that |cur_tok=point_token| after the integer
-    part of such a fraction has been scanned by |scan_int|, and that the decimal
-    point has been backed up to be scanned again.
-
-*/
-
-/*tex Sets |cur_val| to an integer: */
-
-void scan_int(void)
-{
-    /*tex should the answer be negated? */
-    boolean negative;
-    /*tex |$2^{31}$ / radix|, the threshold of danger */
-    int m;
-    /*tex the digit just scanned */
-    int d;
-    /*tex have no digits appeared? */
-    boolean vacuous;
-    /*tex has an error message been issued? */
-    boolean OK_so_far;
-    radix = 0;
-    OK_so_far = true;
-    /*tex Get the next non-blank non-sign token; set |negative| appropriately. */
-    negative = false;
-    do {
-        /*tex Get the next non-blank non-call token. */
-        do {
-            get_x_token();
-        } while (cur_cmd == spacer_cmd);
-        if (cur_tok == other_token + '-') {
-            negative = !negative;
-            cur_tok = other_token + '+';
-        }
-    } while (cur_tok == other_token + '+');
-
-  RESTART:
-    if (cur_tok == alpha_token) {
-        /*tex
-
-            Scan an alphabetic character code into |cur_val|. A space is ignored
-            after an alphabetic character constant, so that such constants behave
-            like numeric ones.
-
-        */
-        /*tex Suppress macro expansion: */
-        get_token();
-        if (cur_tok < cs_token_flag) {
-            cur_val = cur_chr;
-            if (cur_cmd <= right_brace_cmd) {
-                if (cur_cmd == right_brace_cmd)
-                    incr(align_state);
-                else
-                    decr(align_state);
-            }
-        } else {
-            /*tex The value of a csname in this context is its name. */
-            str_number txt = cs_text(cur_tok - cs_token_flag);
-            if (is_active_cs(txt))
-                cur_val = active_cs_value(txt);
-            else if (single_letter(txt))
-                cur_val = pool_to_unichar(str_string(txt));
-            else
-                cur_val = (biggest_char + 1);
-        }
-        if (cur_val > biggest_char) {
-            print_err("Improper alphabetic constant");
-            help2(
-                "A one-character control sequence belongs after a ` mark.",
-                "So I'm essentially inserting \\0 here."
-            );
-            cur_val = '0';
-            back_error();
-        } else {
-            /*tex Scan an optional space. */
-            get_x_token();
-            if (cur_cmd != spacer_cmd)
-                back_input();
-        }
-
-    } else if (cur_tok == cs_token_flag + frozen_primitive) {
-        /*tex
-
-            Reset |cur_tok| for unexpandable primitives, goto restart This block
-            deals with unexpandable \.{\\primitive} appearing at a spot where an
-            integer or an internal values should have been found. It fetches the
-            next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on
-            the primitive value of that token. No expansion takes place, because
-            the next token may be all sorts of things. This could trigger further
-            expansion creating new errors.
-
-        */
-        get_token();
-        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 = token_val(cur_cmd, cur_chr);
-        } else {
-            cur_cmd = relax_cmd;
-            cur_chr = 0;
-            cur_tok = cs_token_flag + frozen_relax;
-            cur_cs = frozen_relax;
-        }
-        goto RESTART;
-    } else if (cur_cmd == math_style_cmd) {
-        cur_val = cur_chr;
-    } else if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
-        scan_something_internal(int_val_level, false);
-    } else {
-        /*tex Scan a numeric constant. */
-        radix = 10;
-        m = 214748364;
-        if (cur_tok == octal_token) {
-            radix = 8;
-            m = 02000000000;
-            get_x_token();
-        } else if (cur_tok == hex_token) {
-            radix = 16;
-            m = 01000000000;
-            get_x_token();
-        }
-        vacuous = true;
-        cur_val = 0;
-        /*tex Accumulate the constant until |cur_tok| is not a suitable digit. */
-        while (1) {
-            if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token) && (cur_tok <= nine_token)) {
-                d = cur_tok - zero_token;
-            } else if (radix == 16) {
-                if ((cur_tok <= A_token + 5) && (cur_tok >= A_token)) {
-                    d = cur_tok - A_token + 10;
-                } else if ((cur_tok <= other_A_token + 5) && (cur_tok >= other_A_token)) {
-                    d = cur_tok - other_A_token + 10;
-                } else {
-                    break;
-                }
-            } else {
-                break;
-            }
-            vacuous = false;
-            if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) {
-                if (OK_so_far) {
-                    print_err("Number too big");
-                    help2(
-                        "I can only go up to 2147483647='17777777777=\"7FFFFFFF,",
-                        "so I'm using that number instead of yours."
-                    );
-                    error();
-                    cur_val = infinity;
-                    OK_so_far = false;
-                }
-            } else {
-                cur_val = cur_val * radix + d;
-            }
-            get_x_token();
-        }
-        if (vacuous) {
-            /*tex Express astonishment that no number was here */
-            print_err("Missing number, treated as zero");
-            help3(
-                "A number should have been here; I inserted `0'.",
-                "(If you can't figure out why I needed to see a number,",
-                "look up `weird error' in the index to The TeXbook.)"
-            );
-            back_error();
-        } else if (cur_cmd != spacer_cmd) {
-            back_input();
-        }
-    }
-    if (negative)
-        negate(cur_val);
-}
-
-/*tex
-
-    The following code is executed when |scan_something_internal| was called
-    asking for |mu_val|, when we really wanted a ``mudimen'' instead of
-    ``muglue.''
-
-*/
-
-static void coerce_glue(void)
-{
-    int v;
-    if (cur_val_level >= glue_val_level) {
-        v = width(cur_val);
-        flush_node(cur_val);
-        cur_val = v;
-    }
-}
-
-/*tex
-
-    The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to a
-    |scaled| value, i.e., an integral number of sp. One of its main tasks is
-    therefore to interpret the abbreviations for various kinds of units and to
-    convert measurements to scaled points.
-
-    There are three parameters: |mu| is |true| if the finite units must be
-    `\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed; |inf| is
-    |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}' are
-    permitted; and |shortcut| is |true| if |cur_val| already contains an integer
-    and only the units need to be considered.
-
-    The order of infinity that was found in the case of infinite glue is returned
-    in the global variable |cur_order|.
-
-*/
-
-/*tex The order of infinity found by |scan_dimen|: */
-
-int cur_order;
-
-/*tex
-
-    Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen| may
-    begin with |scan_int|. This explains why it is convenient to use |scan_int|
-    also for the integer part of a decimal fraction.
-
-    Several branches of |scan_dimen| work with |cur_val| as an integer and with
-    an auxiliary fraction |f|, so that the actual quantity of interest is
-    $|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked''
-    representation is put into the single word |cur_val|, which suddenly switches
-    significance from |integer| to |scaled|.
-
-    The necessary conversion factors can all be specified exactly as fractions
-    whose numerator and denominator add to 32768 or less. According to the
-    definitions here, $\rm2660\,dd\approx1000.33297\,mm$; this agrees well with
-    the value $\rm1000.333\,mm$ cited by Bosshard \^{Bosshard, Hans Rudolf} in
-    {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). The Didot
-    point has been newly standardized in 1978; it's now exactly $\rm
-    1\,nd=0.375\,mm$. Conversion uses the equation
-    $0.375=21681/20320/72.27\cdot25.4$. The new Cicero follows the new Didot
-    point; $\rm 1\,nc=12\,nd$. These would lead to the ratios $21681/20320$ and
-    $65043/5080$, respectively. The closest approximations supported by the
-    algorithm would be $11183/10481$ and $1370/107$. In order to maintain the
-    relation $\rm 1\,nc=12\,nd$, we pick the ratio $685/642$ for $\rm nd$,
-    however.
-
-*/
-
-static void scan_dimen_mu_error(void) {
-    print_err("Illegal unit of measure (mu inserted)");
-    help4(
-        "The unit of measurement in math glue must be mu.",
-        "To recover gracefully from this error, it's best to",
-        "delete the erroneous units; e.g., type `2' to delete",
-        "two letters. (See Chapter 27 of The TeXbook.)"
-    );
-    error();
-}
-
-static void scan_dimen_unknown_unit_error(void) {
-    print_err("Illegal unit of measure (pt inserted)");
-    help6(
-        "Dimensions can be in units of em, ex, in, pt, pc,",
-        "cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!",
-        "I'll assume that you meant to say pt, for printer's points.",
-        "To recover gracefully from this error, it's best to",
-        "delete the erroneous units; e.g., type `2' to delete",
-        "two letters. (See Chapter 27 of The TeXbook.)"
-    );
-    error();
-}
-
-static void scan_dimen_out_of_range_error(void) {
-    print_err("Dimension too large");
-    help2(
-        "I can't work with sizes bigger than about 19 feet.",
-        "Continue and I'll use the largest value I can."
-    );
-    error();
-}
-
-#define set_conversion(A,B) do { num=(A); denom=(B); } while(0)
-
-/*tex
-
-    This function sets |cur_val| to a dimension. It could be optimized a bit more
-    (but not now, something for luatex > 1).
-
-*/
-
-void scan_dimen(boolean mu, boolean inf, boolean shortcut)
-{
-    /*tex should the answer be negated? */
-    boolean negative = false;
-    boolean is_true  = false;
-    /*tex numerator of a fraction whose denominator is $2^{16}$ */
-    int f = 0;
-    /*tex conversion ratio for the scanned units */
-    int num = 0;
-    int denom = 0;
-    /*tex top of decimal digit stack */
-    halfword q;
-    /*tex an internal dimension */
-    scaled v;
-    /*tex temporary storage of |cur_val| */
-    int save_cur_val;
-    arith_error = false;
-    cur_order = normal;
-    if (!shortcut) {
-        /*tex Get the next non-blank non-sign. */
-        do {
-            /*tex Get the next non-blank non-call token. */
-            do {
-                get_x_token();
-            } while (cur_cmd == spacer_cmd);
-            if (cur_tok == minus_token) {
-                negative = !negative;
-                cur_tok = plus_token;
-            }
-        } while (cur_tok == plus_token);
-        if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
-            /*tex Fetch an internal dimension and |goto attach_sign|, or fetch an internal integer. */
-            if (mu) {
-                scan_something_internal(mu_val_level, false);
-                coerce_glue();
-                if (cur_val_level == mu_val_level) {
-                    goto ATTACH_SIGN;
-                } else if (cur_val_level != int_val_level) {
-                    mu_error();
-                }
-            } else {
-                scan_something_internal(dimen_val_level, false);
-                if (cur_val_level == dimen_val_level) {
-                    goto ATTACH_SIGN;
-                }
-            }
-        } else {
-            back_input();
-            if (cur_tok == continental_point_token) {
-                cur_tok = point_token;
-            }
-            if (cur_tok != point_token) {
-                scan_int();
-            } else {
-                radix = 10;
-                cur_val = 0;
-            }
-            if (cur_tok == continental_point_token) {
-                cur_tok = point_token;
-            }
-            if ((radix == 10) && (cur_tok == point_token)) {
-                /*tex
-
-                    Scan decimal fraction. When the following code is executed,
-                    we have |cur_tok=point_token|, but this token has been backed
-                    up using |back_input|; we must first discard it. It turns out
-                    that a decimal point all by itself is equivalent to
-                    `\.{0.0}'. Let's hope people don't use that fact.
-
-                */
-                int k = 0;
-                halfword p = null;
-                int kk;
-                /*tex The |point_token| is being re-scanned. */
-                get_token();
-                while (1) {
-                    get_x_token();
-                    if ((cur_tok > nine_token) || (cur_tok < zero_token))
-                        break;
-                    if (k < 17) {
-                        /*tex Digits for |k>=17| cannot affect the result. */
-                        q = get_avail();
-                        set_token_link(q, p);
-                        set_token_info(q, cur_tok - zero_token);
-                        p = q;
-                        incr(k);
-                    }
-                }
-                for (kk = k; kk >= 1; kk--) {
-                    dig[kk - 1] = token_info(p);
-                    q = p;
-                    p = token_link(p);
-                    free_avail(q);
-                }
-                f = round_decimals(k);
-                if (cur_cmd != spacer_cmd) {
-                    back_input();
-                }
-            }
-        }
-    }
-    if (cur_val < 0) {
-        /*tex In this case |f=0|. */
-        negative = !negative;
-        negate(cur_val);
-    }
-    /*tex
-
-        Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there
-        are |x| sp per unit; |goto attach_sign| if the units are internal. Now
-        comes the harder part: At this point in the program, |cur_val| is a
-        nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1;
-        we want to multiply the sum of these two quantities by the appropriate
-        factor, based on the specified units, in order to produce a |scaled|
-        result, and we want to do the calculation with fixed point arithmetic
-        that does not overflow.
-
-    */
-    if (inf) {
-         if (scan_keyword("fi")) {
-             cur_order = sfi;
-             if (scan_keyword("l")) {
-                cur_order = fil;
-                if (scan_keyword("l")) {
-                    cur_order = fill;
-                    if (scan_keyword("l")) {
-                        cur_order = filll;
-                    }
-                }
-            }
-            goto ATTACH_FRACTION;
-         }
-    }
-    /*tex
-
-        Scan for (u)units that are internal dimensions; |goto attach_sign| with
-        |cur_val| set if found.
-
-    */
-    save_cur_val = cur_val;
-    /*tex
-
-        Get the next non-blank non-call; a pitty if just backed up the input.
-
-    */
-    do {
-        get_x_token();
-    } while (cur_cmd == spacer_cmd);
-
-    if ((cur_cmd < min_internal_cmd) || (cur_cmd > max_internal_cmd)) {
-        back_input();
-    } else {
-        /*tex |math_given_cmd|, |xmath_given_cmd| and |last_item_cmd} */
-        if (mu) {
-            scan_something_internal(mu_val_level, false);
-            coerce_glue();
-            if (cur_val_level != mu_val_level) {
-                mu_error();
-            }
-        } else {
-            scan_something_internal(dimen_val_level, false);
-        }
-        v = cur_val;
-        goto FOUND;
-    }
-    /*tex Bah |true| forces to split the unit scanner. */
-    if (mu) {
-        /*tex Scan for (m)\.{mu} units and |goto attach_fraction|. */
-        if (! scan_keyword("mu")) {
-            scan_dimen_mu_error();
-        }
-        goto ATTACH_FRACTION;
-    } else if (scan_keyword("em")) {
-        v = quad(get_cur_font());
-    } else if (scan_keyword("ex")) {
-        v = x_height(get_cur_font());
-    } else if (scan_keyword("px")) {
-        v = px_dimen_par;
-    } else {
-        goto PICKUP_UNIT;
-    }
-    /*tex Scan an optional space (after em, ex or px) */
-    get_x_token();
-    if (cur_cmd != spacer_cmd) {
-        back_input();
-    }
-  FOUND:
-    cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000));
-    goto ATTACH_SIGN;
-    /*tex
-
-        Scan for (a)all other units and adjust |cur_val| and |f| accordingly;
-        |goto done| in the case of scaled points.
-
-    */
-  PICKUP_UNIT:
-    if (scan_keyword("pt")) {
-        /*tex The easy case: */
-        goto SCALE_VALUE;
-    } else if (scan_keyword("mm")) {
-        set_conversion(7227, 2540);
-        goto SCALE_VALUE;
-    } else if (scan_keyword("cm")) {
-        set_conversion(7227, 254);
-        goto SCALE_VALUE;
-    } else if (scan_keyword("sp")) {
-        goto DONE;
-    } else if (scan_keyword("bp")) {
-        set_conversion(7227, 7200);
-        goto SCALE_VALUE;
-    } else if (scan_keyword("in")) {
-        set_conversion(7227, 100);
-        goto SCALE_VALUE;
-    } else if (scan_keyword("dd")) {
-        set_conversion(1238, 1157);
-        goto SCALE_VALUE;
-    } else if (scan_keyword("cc")) {
-        set_conversion(14856, 1157);
-        goto SCALE_VALUE;
-    } else if (scan_keyword("pc")) {
-        set_conversion(12, 1);
-        goto SCALE_VALUE;
-    } else if (scan_keyword("nd")) {
-        set_conversion(685, 642);
-        goto SCALE_VALUE;
-    } else if (scan_keyword("nc")) {
-        set_conversion(1370, 107);
-        goto SCALE_VALUE;
-    } else if (!is_true && scan_keyword("true")) {
-        is_true = true;
-        goto PICKUP_UNIT;
-    }
-    /*tex Complain about unknown unit and |goto done2|. */
-    scan_dimen_unknown_unit_error();
-    goto BAD_NEWS;
-  SCALE_VALUE:
-    /*tex Adjust |f| for the magnification ratio. */
-    if (is_true) {
-        /*tex Maybe at some point we will drop mag completely, even in \DVI\ mode. */
-        if (output_mode_used <= OMODE_DVI) {
-            prepare_mag();
-            if (mag_par != 1000) {
-                cur_val = xn_over_d(cur_val, 1000, mag_par);
-                f = (1000 * f + 0200000 * tex_remainder) / mag_par;
-                cur_val = cur_val + (f / 0200000);
-                f = f % 0200000;
-            }
-        } else {
-            /*tex in \PDF\ mode we're always |true|. */
-            one_true_inch = one_inch;
-        }
-    }
-    if (num) {
-        cur_val = xn_over_d(cur_val, num, denom);
-        f = (num * f + 0200000 * tex_remainder) / denom;
-        cur_val = cur_val + (f / 0200000);
-        f = f % 0200000;
-    }
-  BAD_NEWS:
-  ATTACH_FRACTION:
-    if (cur_val >= 040000) {
-        arith_error = true;
-    } else {
-        cur_val = cur_val * unity + f;
-    }
-  DONE:
-    /*tex Scan an optional space; this happens too often. */
-    get_x_token();
-    if (cur_cmd != spacer_cmd) {
-        back_input();
-    }
-  ATTACH_SIGN:
-    if (arith_error || (abs(cur_val) >= 010000000000)) {
-        /*tex Report that this dimension is out of range. */
-        scan_dimen_out_of_range_error();
-        cur_val = max_dimen;
-        arith_error = false;
-    }
-    if (negative) {
-        negate(cur_val);
-    }
-}
-
-/*tex
-
-    The final member of \TeX's value-scanning trio is |scan_glue|, which makes
-    |cur_val| point to a glue specification. The reference count of that glue
-    spec will take account of the fact that |cur_val| is pointing to~it.
-
-    The |level| parameter should be either |glue_val| or |mu_val|.
-
-    Since |scan_dimen| was so much more complex than |scan_int|, we might expect
-    |scan_glue| to be even worse. But fortunately, it is very simple, since most
-    of the work has already been done.
-
-*/
-
-void scan_glue(int level)
-{
-    /*tex should the answer be negated? */
-    boolean negative = false;
-    /*tex new glue specification */
-    halfword q = null;
-    /*tex does |level=mu_val|? */
-    boolean mu = (level == mu_val_level);
-    /*tex Get the next non-blank non-sign. */
-    do {
-        /*tex Get the next non-blank non-call token. */
-        do {
-            get_x_token();
-        } while (cur_cmd == spacer_cmd);
-        if (cur_tok == minus_token) {
-            negative = !negative;
-            cur_tok = plus_token;
-        }
-    } while (cur_tok == plus_token);
-    if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
-        scan_something_internal(level, negative);
-        if (cur_val_level >= glue_val_level) {
-            if (cur_val_level != level)
-                mu_error();
-            return;
-        }
-        if (cur_val_level == int_val_level)
-            scan_dimen(mu, false, true);
-        else if (level == mu_val_level)
-            mu_error();
-    } else {
-        back_input();
-        scan_dimen(mu, false, false);
-        if (negative)
-            negate(cur_val);
-    }
-    /*tex
-
-        Create a new glue specification whose width is |cur_val|; scan for its
-        stretch and shrink components.
-
-    */
-    q = new_spec(zero_glue);
-    width(q) = cur_val;
-    if (scan_keyword("plus")) {
-        scan_dimen(mu, true, false);
-        stretch(q) = cur_val;
-        stretch_order(q) = (quarterword) cur_order;
-    }
-    if (scan_keyword("minus")) {
-        scan_dimen(mu, true, false);
-        shrink(q) = cur_val;
-        shrink_order(q) = (quarterword) cur_order;
-    }
-    cur_val = q;
-}
-
-/*tex
-
-    This procedure is supposed to scan something like `\.{\\skip\\count12}',
-    i.e., whatever can follow `\.{\\the}', and it constructs a token list
-    containing something like `\.{-3.0pt minus 0.5fill}'.
-
-*/
-
-halfword the_toks(void)
-{
-    /*tex holds |selector| setting */
-    int old_setting;
-    /*tex used for copying a token list */
-    halfword p, q, r;
-    /*tex value of |cur_chr| */
-    int c;
-    str_number s;
-    halfword retval;
-    /*tex Handle \.{\\unexpanded} or \.{\\detokenize} and |return|. */
-    if (odd(cur_chr)) {
-        c = cur_chr;
-        scan_general_text();
-        if (c == 1) {
-            return cur_val;
-        } else {
-            old_setting = selector;
-            selector = new_string;
-            p = get_avail();
-            set_token_link(p, token_link(temp_token_head));
-            token_show(p);
-            flush_list(p);
-            selector = old_setting;
-            s = make_string();
-            retval = str_toks(str_lstring(s));
-            flush_str(s);
-            return retval;
-        }
-    }
-    get_x_token();
-    scan_something_internal(tok_val_level, false);
-    if (cur_val_level >= ident_val_level) {
-        /*tex Copy the token list */
-        p = temp_token_head;
-        set_token_link(p, null);
-        if (cur_val_level == ident_val_level) {
-            store_new_token(cs_token_flag + cur_val);
-        } else if (cur_val != null) {
-            /*tex Do not copy the reference count! */
-            r = token_link(cur_val);
-            while (r != null) {
-                fast_store_new_token(token_info(r));
-                r = token_link(r);
-            }
-        }
-        return p;
-    } else {
-        old_setting = selector;
-        selector = new_string;
-        switch (cur_val_level) {
-            case int_val_level:
-                print_int(cur_val);
-                break;
-            case attr_val_level:
-                print_int(cur_val);
-                break;
-            case dir_val_level:
-                print_dir_par(cur_val);
-                break;
-            case dimen_val_level:
-                print_scaled(cur_val);
-                tprint("pt");
-                break;
-            case glue_val_level:
-                print_spec(cur_val, "pt");
-                flush_node(cur_val);
-                break;
-            case mu_val_level:
-                print_spec(cur_val, "mu");
-                flush_node(cur_val);
-                break;
-        }
-        selector = old_setting;
-        s = make_string();
-        retval = str_toks(str_lstring(s));
-        flush_str(s);
-        return retval;
-    }
-}
-
-str_number the_scanned_result(void)
-{
-    /*tex holds |selector| setting */
-    int old_setting;
-    /*tex return value */
-    str_number r;
-    old_setting = selector;
-    selector = new_string;
-    if (cur_val_level >= ident_val_level) {
-        if (cur_val != null) {
-            show_token_list(token_link(cur_val), null, -1);
-            r = make_string();
-        } else {
-            r = get_nullstr();
-        }
-    } else {
-        switch (cur_val_level) {
-            case int_val_level:
-                print_int(cur_val);
-                break;
-            case attr_val_level:
-                print_int(cur_val);
-                break;
-            case dir_val_level:
-                print_dir_par(cur_val);
-                break;
-            case dimen_val_level:
-                print_scaled(cur_val);
-                tprint("pt");
-                break;
-            case glue_val_level:
-                print_spec(cur_val, "pt");
-                flush_node(cur_val);
-                break;
-            case mu_val_level:
-                print_spec(cur_val, "mu");
-                flush_node(cur_val);
-                break;
-        }
-        r = make_string();
-    }
-    selector = old_setting;
-    return r;
-}
-
-/*tex
-
-    The following routine is used to implement `\.{\\fontdimen} |n| |f|'. The
-    boolean parameter |writing| is set |true| if the calling program intends to
-    change the parameter value.
-
-*/
-
-static void font_param_error(int f)
-{
-    print_err("Font ");
-    print_esc(font_id_text(f));
-    tprint(" has only ");
-    print_int(font_params(f));
-    tprint(" fontdimen parameters");
-    help2(
-        "To increase the number of font parameters, you must",
-        "use \\fontdimen immediately after the \\font is loaded."
-    );
-    error();
-}
-
-void set_font_dimen(void)
-{
-    internal_font_number f;
-    int n;
-    scan_int();
-    n = cur_val;
-    scan_font_ident();
-    f = cur_val;
-    if (n <= 0) {
-        font_param_error(f);
-    } else if (n > font_params(f)) {
-        if (font_used(f)) {
-            font_param_error(f);
-        } else {
-            /*tex Increase the number of parameters in the font. */
-            do {
-                set_font_param(f, (font_params(f) + 1), 0);
-            } while (n != font_params(f));
-        }
-    }
-    scan_optional_equals();
-    scan_normal_dimen();
-    set_font_param(f, n, cur_val);
-}
-
-void get_font_dimen(void)
-{
-    internal_font_number f;
-    /*tex The parameter number: */
-    int n;
-    scan_int();
-    n = cur_val;
-    scan_font_ident();
-    f = cur_val;
-    /*tex Initialize return value: */
-    cur_val = 0;
-    if (n <= 0) {
-        font_param_error(f);
-        goto EXIT;
-    } else if (n > font_params(f)) {
-        if (font_used(f)) {
-            font_param_error(f);
-            goto EXIT;
-        } else {
-            /*tex Increase the number of parameters in the font. */
-            do {
-                set_font_param(f, (font_params(f) + 1), 0);
-            } while (n != font_params(f));
-        }
-    }
-    cur_val = font_param(f, n);
-  EXIT:
-    scanned_result(cur_val, dimen_val_level);
-}
-
-/*tex
-
-    Here's a similar procedure that returns a pointer to a rule node. This
-    routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule};
-    therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store
-    the default rule dimensions in the node, then to override them if
-    `\.{height}' or `\.{width}' or `\.{depth}' specifications are found (in any
-    order).
-
-*/
-
-halfword scan_rule_spec(void)
-{
-    /*tex |width|, |depth|, and |height| all equal |null_flag| now */
-    halfword q;
-    if (cur_cmd == no_vrule_cmd) {
-        q = new_rule(empty_rule);
-        cur_cmd = vrule_cmd;
-    } else if (cur_cmd == no_hrule_cmd) {
-        q = new_rule(empty_rule);
-        cur_cmd = hrule_cmd;
-    } else {
-        q = new_rule(normal_rule);
-    }
-    if (cur_cmd == vrule_cmd) {
-        width(q) = default_rule;
-        rule_dir(q) = body_direction_par;
-    } else {
-        height(q) = default_rule;
-        depth(q) = 0;
-        rule_dir(q) = text_direction_par;
-    }
-  RESWITCH:
-    if (scan_keyword("width")) {
-        scan_normal_dimen();
-        width(q) = cur_val;
-        goto RESWITCH;
-    }
-    if (scan_keyword("height")) {
-        scan_normal_dimen();
-        height(q) = cur_val;
-        goto RESWITCH;
-    }
-    if (scan_keyword("depth")) {
-        scan_normal_dimen();
-        depth(q) = cur_val;
-        goto RESWITCH;
-    }
-    return q;
-}
-
-/*tex Declare procedures that scan font-related stuff. */
-
-void scan_font_ident(void)
-{
-    internal_font_number f;
-    halfword m;
-    /*tex Get the next non-blank non-call. */
-    do {
-        get_x_token();
-    } while (cur_cmd == spacer_cmd);
-    if ((cur_cmd == def_font_cmd) || (cur_cmd == letterspace_font_cmd) || (cur_cmd == copy_font_cmd)) {
-        f = get_cur_font();
-    } else if (cur_cmd == set_font_cmd) {
-        f = cur_chr;
-        set_font_touched(f, 1);
-    } else if (cur_cmd == def_family_cmd) {
-        m = cur_chr;
-        scan_math_family_int();
-        f = fam_fnt(cur_val, m);
-        set_font_touched(f, 1);
-    } else {
-        print_err("Missing font identifier");
-        help2(
-            "I was looking for a control sequence whose",
-            "current meaning has been defined by \\font."
-        );
-        back_error();
-        f = null_font;
-    }
-    cur_val = f;
-}
-
-/*tex
-
-    The |scan_general_text| procedure is much like |scan_toks(false,false)|, but
-    will be invoked via |expand|, i.e., recursively.
-
-    The token list (balanced text) created by |scan_general_text| begins at
-    |link(temp_token_head)| and ends at |cur_val|. (If |cur_val=temp_token_head|,
-    the list is empty.)
-
-*/
-
-void scan_general_text(void)
-{
-    /*tex Here we save |scanner_status|: */
-    int s;
-    /*tex Here we save |warning_index|: */
-    halfword w;
-    /*tex Here we save |def_ref|: */
-    halfword d;
-    /*tex The tail of the token list being built: */
-    halfword p;
-    /*tex The new node being added to the token list via |store_new_token|: */
-    halfword q;
-    /*tex The number of unmatched left braces: */
-    halfword unbalance;
-    s = scanner_status;
-    w = warning_index;
-    d = def_ref;
-    scanner_status = absorbing;
-    warning_index = cur_cs;
-    p = get_avail();
-    def_ref = p;
-    set_token_ref_count(def_ref, 0);
-    p = def_ref;
-    /*tex Remove the compulsory left brace. */
-    scan_left_brace();
-    unbalance = 1;
-    while (1) {
-        get_token();
-        if (cur_tok < right_brace_limit) {
-            if (cur_cmd < right_brace_cmd) {
-                incr(unbalance);
-            } else {
-                decr(unbalance);
-                if (unbalance == 0)
-                    break;
-            }
-        }
-        store_new_token(cur_tok);
-    }
-    q = token_link(def_ref);
-    /*tex Discard reference count. */
-    free_avail(def_ref);
-    if (q == null)
-        cur_val = temp_token_head;
-    else
-        cur_val = p;
-    set_token_link(temp_token_head, q);
-    scanner_status = s;
-    warning_index = w;
-    def_ref = d;
-}
-
-/*tex
-
-    The |get_x_or_protected| procedure is like |get_x_token| except that
-    protected macros are not expanded. It sets |cur_cmd|, |cur_chr|,
-    |cur_tok|,and expands non-protected macros.
-
-*/
-
-void get_x_or_protected(void)
-{
-
-    while (1) {
-        get_token();
-        if (cur_cmd <= max_command_cmd)
-            return;
-        if ((cur_cmd >= call_cmd) && (cur_cmd < end_template_cmd)) {
-            if (token_info(token_link(cur_chr)) == protected_token)
-                return;
-        }
-        expand();
-    }
-}
-
-/*tex
-
-    |scan_toks|. This function returns a pointer to the tail of a new token list,
-    and it also makes |def_ref| point to the reference count at the head of that
-    list.
-
-    There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| is
-    true, the goal is to create the token list for a macro definition; otherwise
-    the goal is to create the token list for some other \TeX\ primitive:
-    \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase}, \.{\\uppercase},
-    \.{\\message}, \.{\\errmessage}, \.{\\write}, or \.{\\special}. In the latter
-    cases a left brace must be scanned next; this left brace will not be part of
-    the token list, nor will the matching right brace that comes at the end. If
-    |xpand| is false, the token list will simply be copied from the input using
-    |get_token|. Otherwise all expandable tokens will be expanded until
-    unexpandable tokens are left, except that the results of expanding
-    `\.{\\the}' are not expanded further. If both |macro_def| and |xpand| are
-    true, the expansion applies only to the macro body (i.e., to the material
-    following the first |left_brace| character).
-
-    The value of |cur_cs| when |scan_toks| begins should be the |eqtb| address of
-    the control sequence to display in ``runaway'' error messages.
-
-*/
-
-halfword scan_toks(boolean macro_def, boolean xpand)
-{
-    /*tex token representing the highest parameter number */
-    halfword t;
-    /*tex saved token */
-    halfword s;
-    /*tex tail of the token list being built */
-    halfword p;
-    /*tex new node being added to the token list via |store_new_token| */
-    halfword q;
-    /*tex number of unmatched left braces */
-    halfword unbalance;
-    /*tex possible `\.{\#\{}' token */
-    halfword hash_brace;
-    if (macro_def)
-        scanner_status = defining;
-    else
-        scanner_status = absorbing;
-    warning_index = cur_cs;
-    p = get_avail();
-    def_ref = p;
-    set_token_ref_count(def_ref, 0);
-    p = def_ref;
-    hash_brace = 0;
-    t = zero_token;
-    if (macro_def) {
-        /*tex Scan and build the parameter part of the macro definition. */
-        while (1) {
-            /*tex Set |cur_cmd|, |cur_chr|, |cur_tok|: */
-            get_token();
-            if (cur_tok < right_brace_limit)
-                break;
-            if (cur_cmd == mac_param_cmd) {
-                /*tex
-
-                        If the next character is a parameter number, make
-                        |cur_tok| a |match| token; but if it is a left brace,
-                        store `|left_brace|, |end_match|', set |hash_brace|, and
-                        |goto done|.
-
-                */
-                s = match_token + cur_chr;
-                get_token();
-                if (cur_cmd == left_brace_cmd) {
-                    hash_brace = cur_tok;
-                    store_new_token(cur_tok);
-                    store_new_token(end_match_token);
-                    goto DONE;
-                }
-                if (t == nine_token) {
-                    print_err("You already have nine parameters");
-                    help1("I'm going to ignore the # sign you just used.");
-                    error();
-                } else {
-                    incr(t);
-                    if (cur_tok != t) {
-                        print_err("Parameters must be numbered consecutively");
-                        help2(
-                            "I've inserted the digit you should have used after the #.",
-                            "Type `1' to delete what you did use."
-                        );
-                        back_error();
-                    }
-                    cur_tok = s;
-                }
-            }
-            store_new_token(cur_tok);
-        }
-        store_new_token(end_match_token);
-        if (cur_cmd == right_brace_cmd) {
-            /*tex Express shock at the missing left brace; |goto found|. */
-            print_err("Missing { inserted");
-            incr(align_state);
-            help2(
-                "Where was the left brace? You said something like `\\def\\a}',",
-                "which I'm going to interpret as `\\def\\a{}'."
-            );
-            error();
-            goto FOUND;
-        }
-
-    } else {
-        /*tex Remove the compulsory left brace. */
-        scan_left_brace();
-    }
-  DONE:
-    /*tex Scan and build the body of the token list; |goto found| when finished. */
-    unbalance = 1;
-    while (1) {
-        if (xpand) {
-            /*tex
-
-                Expand the next part of the input. Here we insert an entire token
-                list created by |the_toks| without expanding it further.
-
-            */
-            while (1) {
-                get_next();
-                if (cur_cmd >= call_cmd) {
-                    if (token_info(token_link(cur_chr)) == protected_token) {
-                        cur_cmd = relax_cmd;
-                        cur_chr = no_expand_flag;
-                    }
-                }
-                if (cur_cmd <= max_command_cmd)
-                    break;
-                if (cur_cmd != the_cmd) {
-                    expand();
-                } else {
-                    q = the_toks();
-                    if (token_link(temp_token_head) != null) {
-                        set_token_link(p, token_link(temp_token_head));
-                        p = q;
-                    }
-                }
-            }
-            x_token();
-        } else {
-            get_token();
-        }
-        if (cur_tok < right_brace_limit) {
-            if (cur_cmd < right_brace_cmd) {
-                incr(unbalance);
-            } else {
-                decr(unbalance);
-                if (unbalance == 0)
-                    goto FOUND;
-            }
-        } else if (cur_cmd == mac_param_cmd) {
-            if (macro_def) {
-                /*tex Look for parameter number or \.{\#\#}. */
-                s = cur_tok;
-                if (xpand)
-                    get_x_token();
-                else
-                    get_token();
-                if (cur_cmd != mac_param_cmd) {
-                    if ((cur_tok <= zero_token) || (cur_tok > t)) {
-                        print_err("Illegal parameter number in definition of ");
-                        sprint_cs(warning_index);
-                        help3(
-                            "You meant to type ## instead of #, right?",
-                            "Or maybe a } was forgotten somewhere earlier, and things",
-                            "are all screwed up? I'm going to assume that you meant ##."
-                        );
-                        back_error();
-                        cur_tok = s;
-                    } else {
-                        cur_tok = out_param_token - '0' + cur_chr;
-                    }
-                }
-            }
-        }
-        store_new_token(cur_tok);
-    }
-  FOUND:
-    scanner_status = normal;
-    if (hash_brace != 0)
-        store_new_token(hash_brace);
-    return p;
-}
-
-/*tex
-
-    Here we declare two trivial procedures in order to avoid mutually recursive
-    procedures with parameters.
-
-*/
-
-void scan_normal_glue(void)
-{
-    scan_glue(glue_val_level);
-}
-
-void scan_mu_glue(void)
-{
-    scan_glue(mu_val_level);
-}
-
-/*tex
-
-    The |scan_expr| procedure scans and evaluates an expression. Evaluating an
-    expression is a recursive process: When the left parenthesis of a
-    subexpression is scanned we descend to the next level of recursion; the
-    previous level is resumed with the matching right parenthesis.
-
-*/
-
-typedef enum {
-    /*tex \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */
-    expr_none  = 0,
-    /*tex \.( $\langle\it expr\rangle$ \.+ seen */
-    expr_add   = 1,
-    /*tex \.( $\langle\it expr\rangle$ \.- seen */
-    expr_sub   = 2,
-    /*tex $\langle\it term\rangle$ \.* seen */
-    expr_mult  = 3,
-    /*tex $\langle\it term\rangle$ \./ seen */
-    expr_div   = 4,
-    /*tex $\langle\it term\rangle$ \.* $\langle\it factor\rangle$ \./ seen */
-    expr_scale = 5,
-} expression_states;
-
-/*tex
-
-    We want to make sure that each term and (intermediate) result is in the
-    proper range. Integer values must not exceed |infinity| ($2^{31}-1$) in
-    absolute value, dimensions must not exceed |max_dimen| ($2^{30}-1$). We avoid
-    the absolute value of an integer, because this might fail for the value
-    $-2^{31}$ using 32-bit arithmetic.
-
-*/
-
-/* Clear a number or dimension and set |arith_error|: */
-
-#define num_error(A) do { \
-    arith_error=true; \
-    A=0; \
-} while (0)
-
-/*tex Clear a glue spec and set |arith_error|: */
-
-#define glue_error(A) do { \
-    arith_error=true; \
-    reset_glue_to_zero(A); \
-} while (0)
-
-#define normalize_glue(A) do { \
-    if (stretch(A)==0) stretch_order(A)=normal; \
-    if (shrink(A)==0) shrink_order(A)=normal; \
-} while (0)
-
-/*tex
-
-    Parenthesized subexpressions can be inside expressions, and this nesting has
-    a stack. Seven local variables represent the top of the expression stack: |p|
-    points to pushed-down entries, if any; |l| specifies the type of expression
-    currently beeing evaluated; |e| is the expression so far and |r| is the state
-    of its evaluation; |t| is the term so far and |s| is the state of its
-    evaluation; finally |n| is the numerator for a combined multiplication and
-    division, if any.
-
-*/
-
-#define expr_add_sub(A,B,C) add_or_sub((A),(B),(C),(r==expr_sub))
-#define expr_a(A,B) expr_add_sub((A),(B),max_dimen)
-
-/*tex
-
-    The function |add_or_sub(x,y,max_answer,negative)| computes the sum (for
-    |negative=false|) or difference (for |negative=true|) of |x| and |y|,
-    provided the absolute value of the result does not exceed |max_answer|.
-
-*/
-
-inline static int add_or_sub(int x, int y, int max_answer, boolean negative)
-{
-    int a;
-    if (negative)
-        negate(y);
-    if (x >= 0) {
-        if (y <= max_answer - x)
-            a = x + y;
-        else
-            num_error(a);
-    } else if (y >= -max_answer - x) {
-        a = x + y;
-    } else {
-        num_error(a);
-    }
-    return a;
-}
-
-#define expr_m(A) A = nx_plus_y((A),f,0)
-#define expr_d(A) A=quotient((A),f)
-
-/*tex
-
-    The function |quotient(n,d)| computes the rounded quotient $q=\lfloor
-    n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive.
-
-*/
-
-inline static int quotient(int n, int d)
-{
-    /*tex Should the answer be negated? */
-    boolean negative;
-    /*tex The answer: */
-    int a;
-    if (d == 0) {
-        num_error(a);
-    } else {
-        if (d > 0) {
-            negative = false;
-        } else {
-            negate(d);
-            negative = true;
-        }
-        if (n < 0) {
-            negate(n);
-            negative = !negative;
-        }
-        a = n / d;
-        n = n - a * d;
-        /*tex Avoid certain compiler optimizations! */
-        d = n - d;
-        if (d + n >= 0)
-            incr(a);
-        if (negative)
-            negate(a);
-    }
-    return a;
-}
-
-#define expr_s(A) A=fract((A),n,f,max_dimen)
-
-/*tex
-
-    Finally, the function |fract(x,n,d,max_answer)| computes the integer
-    $q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive and
-    the result does not exceed |max_answer|. We can't use floating point
-    arithmetic since the routine must produce identical results in all cases; and
-    it would be too dangerous to multiply by~|n| and then divide by~|d|, in
-    separate operations, since overflow might well occur. Hence this subroutine
-    simulates double precision arithmetic, somewhat analogous to Metafont's
-    |make_fraction| and |take_fraction| routines.
-
-*/
-
-int fract(int x, int n, int d, int max_answer)
-{
-    /*tex should the answer be negated? */
-    boolean negative;
-    /*tex the answer */
-    int a;
-    /*tex a proper fraction */
-    int f;
-    /*tex smallest integer such that |2*h>=d| */
-    int h;
-    /*tex intermediate remainder */
-    int r;
-    /*tex temp variable */
-    int t;
-    if (d == 0)
-        goto TOO_BIG;
-    a = 0;
-    if (d > 0) {
-        negative = false;
-    } else {
-        negate(d);
-        negative = true;
-    }
-    if (x < 0) {
-        negate(x);
-        negative = !negative;
-    } else if (x == 0) {
-        goto DONE;
-    }
-    if (n < 0) {
-        negate(n);
-        negative = !negative;
-    }
-    t = n / d;
-    if (t > max_answer / x)
-        goto TOO_BIG;
-    a = t * x;
-    n = n - t * d;
-    if (n == 0)
-        goto FOUND;
-    t = x / d;
-    if (t > (max_answer - a) / n)
-        goto TOO_BIG;
-    a = a + t * n;
-    x = x - t * d;
-    if (x == 0)
-        goto FOUND;
-    if (x < n) {
-        t = x;
-        x = n;
-        n = t;
-    }
-    /*tex
-
-        Now |0<n<=x<d| and we compute $f=\lfloor xn/d+{1\over2}\rfloor$. The loop
-        here preserves the following invariant relations between |f|, |x|, |n|,
-        and~|r|: (i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor
-        x_0n_0/d+{1\over2}\rfloor$; (ii)~|-d<=r<0<n<=x<d|, where $x_0$, $n_0$ are
-        the original values of~$x$ and $n$.
-
-        Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|,
-        because the latter could overflow.
-
-    */
-    f = 0;
-    r = (d / 2) - d;
-    h = -r;
-    while (1) {
-        if (odd(n)) {
-            r = r + x;
-            if (r >= 0) {
-                r = r - d;
-                incr(f);
-            }
-        }
-        n = n / 2;
-        if (n == 0)
-            break;
-        if (x < h) {
-            x = x + x;
-        } else {
-            t = x - d;
-            x = t + x;
-            f = f + n;
-            if (x < n) {
-                if (x == 0)
-                    break;
-                t = x;
-                x = n;
-                n = t;
-            }
-        }
-    }
-    if (f > (max_answer - a))
-        goto TOO_BIG;
-    a = a + f;
-  FOUND:
-    if (negative)
-        negate(a);
-    goto DONE;
-  TOO_BIG:
-    num_error(a);
-  DONE:
-    return a;
-}
-
-/*tex Scans and evaluates an expression: */
-
-static void scan_expr(void)
-{
-    /*tex saved values of |arith_error| */
-    boolean a, b;
-    /*tex type of expression */
-    int l;
-    /*tex state of expression so far */
-    int r;
-    /*tex state of term so far */
-    int s;
-    /*tex next operation or type of next factor */
-    int o;
-    /*tex expression so far */
-    int e;
-    /*tex term so far */
-    int t;
-    /*tex current factor */
-    int f;
-    /*tex numerator of combined multiplication and division */
-    int n;
-    /*tex top of expression stack */
-    halfword p;
-    /*tex for stack manipulations */
-    halfword q;
-    l = cur_val_level;
-    a = arith_error;
-    b = false;
-    p = null;
-    /*tex Scan and evaluate an expression |e| of type |l|. */
-  RESTART:
-    r = expr_none;
-    e = 0;
-    s = expr_none;
-    t = 0;
-    n = 0;
-  CONTINUE:
-    if (s == expr_none)
-        o = l;
-    else
-        o = int_val_level;
-    /*tex
-
-        Scan a factor |f| of type |o| or start a subexpression. Get the next
-        non-blank non-call token.
-
-    */
-    do {
-        get_x_token();
-    } while (cur_cmd == spacer_cmd);
-
-    if (cur_tok == other_token + '(') {
-        /*tex Push the expression stack and |goto restart|. */
-        q = new_node(expr_node, 0);
-        vlink(q) = p;
-        expr_type(q) = (quarterword) l;
-        expr_state(q) = (quarterword) (4 * s + r);
-        expr_e_field(q) = e;
-        expr_t_field(q) = t;
-        expr_n_field(q) = n;
-        p = q;
-        l = o;
-        goto RESTART;
-    }
-    back_input();
-    if ((o == int_val_level) || (o == attr_val_level))
-        scan_int();
-    else if (o == dimen_val_level)
-        scan_normal_dimen();
-    else if (o == glue_val_level)
-        scan_normal_glue();
-    else
-        scan_mu_glue();
-    f = cur_val;
-
-  FOUND:
-    /*tex
-
-        Scan the next operator and set |o| and Get the next non-blank non-call
-        token.
-
-    */
-    do {
-        get_x_token();
-    } while (cur_cmd == spacer_cmd);
-
-    if (cur_tok == other_token + '+') {
-        o = expr_add;
-    } else if (cur_tok == other_token + '-') {
-        o = expr_sub;
-    } else if (cur_tok == other_token + '*') {
-        o = expr_mult;
-    } else if (cur_tok == other_token + '/') {
-        o = expr_div;
-    } else {
-        o = expr_none;
-        if (p == null) {
-            if (cur_cmd != relax_cmd)
-                back_input();
-        } else if (cur_tok != other_token + ')') {
-            print_err("Missing ) inserted for expression");
-            help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't.");
-            back_error();
-        }
-    }
-    arith_error = b;
-    /*tex Make sure that |f| is in the proper range. */
-    if (((l == int_val_level) || (l == attr_val_level)) || (s > expr_sub)) {
-        if ((f > infinity) || (f < -infinity))
-            num_error(f);
-    } else if (l == dimen_val_level) {
-        if (abs(f) > max_dimen)
-            num_error(f);
-    } else {
-        if ((abs(width(f)) > max_dimen) || (abs(stretch(f)) > max_dimen) || (abs(shrink(f)) > max_dimen))
-            glue_error(f);
-    }
-    /*tex Cases for evaluation of the current term. */
-    switch (s) {
-        case expr_none:
-            /*tex
-
-                Applying the factor |f| to the partial term |t| (with the
-                operator |s|) is delayed until the next operator |o| has been
-                scanned. Here we handle the first factor of a partial term. A
-                glue spec has to be copied unless the next operator is a right
-                parenthesis; this allows us later on to simply modify the glue
-                components.
-
-            */
-            t = f;
-            if ((l >= glue_val_level) && (o != expr_none)) {
-                /*tex Do we really need to copy here? */
-                normalize_glue(t);
-            } else {
-                t = f;
-            }
-            break;
-        case expr_mult:
-            /*tex
-
-                If a multiplication is followed by a division, the two operations
-                are combined into a `scaling' operation. Otherwise the term |t|
-                is multiplied by the factor |f|.
-
-            */
-            if (o == expr_div) {
-                n = f;
-                o = expr_scale;
-            } else if ((l == int_val_level) || (l == attr_val_level)) {
-                t = mult_integers(t, f);
-            } else if (l == dimen_val_level) {
-                expr_m(t);
-            } else {
-                expr_m(width(t));
-                expr_m(stretch(t));
-                expr_m(shrink(t));
-            }
-            break;
-        case expr_div:
-            /*tex Here we divide the term |t| by the factor |f|. */
-            if (l < glue_val_level) {
-                expr_d(t);
-            } else {
-                expr_d(width(t));
-                expr_d(stretch(t));
-                expr_d(shrink(t));
-            }
-            break;
-        case expr_scale:
-            /*tex Here the term |t| is multiplied by the quotient $n/f$. */
-            if ((l == int_val_level) || (l == attr_val_level)) {
-                t = fract(t, n, f, infinity);
-            } else if (l == dimen_val_level) {
-                expr_s(t);
-            } else {
-                expr_s(width(t));
-                expr_s(stretch(t));
-                expr_s(shrink(t));
-            }
-            break;
-    }
-    if (o > expr_sub) {
-        s = o;
-    } else {
-        /*tex
-
-            Evaluate the current expression. When a term |t| has been completed
-            it is copied to, added to, or subtracted from the expression |e|.
-
-        */
-        s = expr_none;
-        if (r == expr_none) {
-            e = t;
-        } else if ((l == int_val_level) || (l == attr_val_level)) {
-            e = expr_add_sub(e, t, infinity);
-        } else if (l == dimen_val_level) {
-            e = expr_a(e, t);
-        } else {
-            /*tex
-
-                Compute the sum or difference of two glue specs. We know that
-                |stretch_order(e)>normal| implies |stretch(e)<>0| and
-                |shrink_order(e)>normal| implies |shrink(e)<>0|.
-
-            */
-            width(e) = expr_a(width(e), width(t));
-            if (stretch_order(e) == stretch_order(t)) {
-                stretch(e) = expr_a(stretch(e), stretch(t));
-            } else if ((stretch_order(e) < stretch_order(t)) && (stretch(t) != 0)) {
-                stretch(e) = stretch(t);
-                stretch_order(e) = stretch_order(t);
-            }
-            if (shrink_order(e) == shrink_order(t)) {
-                shrink(e) = expr_a(shrink(e), shrink(t));
-            } else if ((shrink_order(e) < shrink_order(t)) && (shrink(t) != 0)) {
-                shrink(e) = shrink(t);
-                shrink_order(e) = shrink_order(t);
-            }
-            flush_node(t);
-            normalize_glue(e);
-        }
-        r = o;
-    }
-    b = arith_error;
-    if (o != expr_none)
-        goto CONTINUE;
-    if (p != null) {
-        /*tex Pop the expression stack and |goto found|. */
-        f = e;
-        q = p;
-        e = expr_e_field(q);
-        t = expr_t_field(q);
-        n = expr_n_field(q);
-        s = expr_state(q) / 4;
-        r = expr_state(q) % 4;
-        l = expr_type(q);
-        p = vlink(q);
-        flush_node(q);
-        goto FOUND;
-    }
-
-    if (b) {
-        print_err("Arithmetic overflow");
-        help2(
-            "I can't evaluate this expression,",
-            "since the result is out of range."
-        );
-        error();
-        if (l >= glue_val_level) {
-            reset_glue_to_zero(e);
-        } else {
-            e = 0;
-        }
-    }
-    arith_error = a;
-    cur_val = e;
-    cur_val_level = l;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/scanning.w
@@ -0,0 +1,2621 @@
+% scanning.w
+%
+% Copyright 2009-2012 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
+static void scan_expr(void);
+
+@ Let's turn now to some procedures that \TeX\ calls upon frequently to digest
+certain kinds of patterns in the input. Most of these are quite simple;
+some are quite elaborate. Almost all of the routines call |get_x_token|,
+which can cause them to be invoked recursively.
+
+The |scan_left_brace| routine is called when a left brace is supposed to be
+the next non-blank token. (The term ``left brace'' means, more precisely,
+a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to
+appear before the |left_brace|.
+
+@c
+void scan_left_brace(void)
+{                               /* reads a mandatory |left_brace| */
+    /* 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) {
+        print_err("Missing { inserted");
+        help4("A left brace was mandatory here, so I've put one in.",
+              "You might want to delete and/or insert some corrections",
+              "so that I will find a matching right brace soon.",
+              "If you're confused by all this, try typing `I}' now.");
+        back_error();
+        cur_tok = left_brace_token + '{';
+        cur_cmd = left_brace_cmd;
+        cur_chr = '{';
+        incr(align_state);
+    }
+}
+
+@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded
+by optional spaces; `\.{\\relax}' is not ignored here.
+
+@c
+void scan_optional_equals(void)
+{
+    /* Get the next non-blank non-call token */
+    do {
+        get_x_token();
+    } while (cur_cmd == spacer_cmd);
+    if (cur_tok != other_token + '=')
+        back_input();
+}
+
+@ Here is a procedure that sounds an alarm when mu and non-mu units
+are being switched.
+
+@c
+static void mu_error(void)
+{
+    print_err("Incompatible glue units");
+    help1("I'm going to assume that 1mu=1pt when they're mixed.");
+    error();
+}
+
+@ The next routine `|scan_something_internal|' is used to fetch internal
+numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}'
+when expanding constructions like `\.{\\the\\toks0}' and
+`\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int|
+procedure, which calls |scan_something_internal|; on the other hand,
+|scan_something_internal| also calls |scan_int|, for constructions like
+`\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we
+have to declare |scan_int| as a |forward| procedure. A few other
+procedures are also declared at this point.
+
+\TeX\ doesn't know exactly what to expect when
+|scan_something_internal| begins.  For example, an integer or
+dimension or glue value could occur immediately after `\.{\\hskip}';
+and one can even say \.{\\the} with respect to token lists in
+constructions like `\.{\\xdef\\o\{\\the\\output\}}'.  On the other
+hand, only integers are allowed after a construction like
+`\.{\\count}'. To handle the various possibilities,
+|scan_something_internal| has a |level| parameter, which tells the
+``highest'' kind of quantity that |scan_something_internal| is allowed
+to produce. Eight levels are distinguished, namely |int_val|,
+|attr_val|, |dimen_val|, |glue_val|, |mu_val|, |dir_val|, |ident_val|,
+and |tok_val|.
+
+The output of |scan_something_internal| (and of the other routines
+|scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global
+variable |cur_val|, and its level is put into |cur_val_level|. The highest
+values of |cur_val_level| are special: |mu_val| is used only when
+|cur_val| points to something in a ``muskip'' register, or to one of the
+three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip};
+|ident_val| is used only when |cur_val| points to a font identifier;
+|tok_val| is used only when |cur_val| points to |null| or to the reference
+count of a token list. The last two cases are allowed only when
+|scan_something_internal| is called with |level=tok_val|.
+
+If the output is glue, |cur_val| will point to a glue specification, and
+the reference count of that glue will have been updated to reflect this
+reference; if the output is a nonempty token list, |cur_val| will point to
+its reference count, but in this case the count will not have been updated.
+Otherwise |cur_val| will contain the integer or scaled value in question.
+
+@c
+int cur_val;                    /* value returned by numeric scanners */
+int cur_val1;                   /* delcodes are sometimes 51 digits */
+int cur_val_level;              /* the ``level'' of this value */
+
+#define scanned_result(A,B) do { \
+    cur_val=A; \
+    cur_val_level=B; \
+} while (0)
+
+@ When a |glue_val| changes to a |dimen_val|, we use the width component
+of the glue; there is no need to decrease the reference count, since it
+has not yet been increased.  When a |dimen_val| changes to an |int_val|,
+we use scaled points so that the value doesn't actually change. And when a
+|mu_val| changes to a |glue_val|, the value doesn't change either.
+
+@c
+static void downgrade_cur_val(boolean delete_glue)
+{
+    if (cur_val_level == glue_val_level) {
+        halfword m = cur_val;
+        cur_val = width(m);
+        if (delete_glue)
+            flush_node(m);
+    } else if (cur_val_level == mu_val_level) {
+        mu_error();
+    }
+    decr(cur_val_level);
+}
+
+/*
+void negate_cur_val(boolean delete_glue)
+{
+    if (cur_val_level >= glue_val_level) {
+        halfword m = cur_val;
+        cur_val = new_spec(m);
+        if (delete_glue)
+            flush_node(m);
+        negate(width(cur_val));
+        negate(stretch(cur_val));
+        negate(shrink(cur_val));
+    } else {
+        negate(cur_val);
+    }
+}
+*/
+
+void negate_cur_val(boolean modify_glue)
+{
+    if (cur_val_level >= glue_val_level) {
+        if (modify_glue) {
+            /* we modify in-place */
+        } else {
+            cur_val = new_spec(cur_val);
+        }
+        negate(width(cur_val));
+        negate(stretch(cur_val));
+        negate(shrink(cur_val));
+    } else {
+        negate(cur_val);
+    }
+}
+
+@ Some of the internal items can be fetched both routines,
+and these have been split off into the next routine, that
+returns true if the command code was understood
+
+@c
+static boolean short_scan_something_internal(int cmd, int chr, int level,
+                                             boolean negative)
+{
+    halfword m;                 /* |chr_code| part of the operand token */
+    halfword q;                 /* general purpose index */
+    int p;                      /* index into |nest| */
+    int save_cur_chr;
+    boolean succeeded = true;
+    m = chr;
+    switch (cmd) {
+    case assign_toks_cmd:
+        scanned_result(equiv(m), tok_val_level);
+        break;
+    case assign_int_cmd:
+        scanned_result(eqtb[m].cint, int_val_level);
+        break;
+    case assign_attr_cmd:
+        scanned_result(eqtb[m].cint, int_val_level);
+        break;
+    case assign_dir_cmd:
+        if (m == (int_base + line_direction_code)) {
+            m = int_base + text_direction_code;
+        }
+        scanned_result(eqtb[m].cint, dir_val_level);
+        break;
+    case assign_dimen_cmd:
+        scanned_result(eqtb[m].cint, dimen_val_level);
+        break;
+    case assign_glue_cmd:
+        scanned_result(equiv(m), glue_val_level);
+        break;
+    case assign_mu_glue_cmd:
+        scanned_result(equiv(m), mu_val_level);
+        break;
+    case math_style_cmd:
+        scanned_result(m, int_val_level);
+        break;
+    case set_aux_cmd:
+        /* Fetch the |space_factor| or the |prev_depth| */
+        if (abs(cur_list.mode_field) != m) {
+            print_err("Improper ");
+            print_cmd_chr(set_aux_cmd, m);
+            help4("You can refer to \\spacefactor only in horizontal mode;",
+                  "you can refer to \\prevdepth only in vertical mode; and",
+                  "neither of these is meaningful inside \\write. So",
+                  "I'm forgetting what you said and using zero instead.");
+            error();
+            if (level != tok_val_level)
+                scanned_result(0, dimen_val_level);
+            else
+                scanned_result(0, int_val_level);
+        } else if (m == vmode) {
+            scanned_result(prev_depth_par, dimen_val_level);
+        } else {
+            scanned_result(space_factor_par, int_val_level);
+        }
+        break;
+    case set_prev_graf_cmd:
+        /* Fetch the |prev_graf| */
+        if (cur_list.mode_field == 0) {
+            scanned_result(0, int_val_level);   /* |prev_graf=0| within \.{\\write} */
+        } else {
+            p = nest_ptr;
+            while (abs(nest[p].mode_field) != vmode)
+                decr(p);
+            scanned_result(nest[p].pg_field, int_val_level);
+        }
+        break;
+    case set_page_int_cmd:
+        /* Fetch the |dead_cycles| or the |insert_penalties| */
+        if (m == 0)
+            cur_val = dead_cycles;
+        else if (m == 2)
+            cur_val = interaction;      /* interactionmode */
+        else
+            cur_val = insert_penalties;
+        cur_val_level = int_val_level;
+        break;
+    case set_page_dimen_cmd:
+        /* Fetch something on the |page_so_far| */
+        if ((page_contents == empty) && (!output_active)) {
+            if (m == 0)
+                cur_val = max_dimen;
+            else
+                cur_val = 0;
+        } else {
+            cur_val = page_so_far[m];
+        }
+        cur_val_level = dimen_val_level;
+        break;
+    case set_tex_shape_cmd:
+        /* Fetch the |par_shape| size */
+        if (par_shape_par_ptr == null)
+            cur_val = 0;
+        else
+            cur_val = vinfo(par_shape_par_ptr + 1);
+        cur_val_level = int_val_level;
+        break;
+    case set_etex_shape_cmd:
+        /* Fetch a penalties array element */
+        scan_int();
+        if ((equiv(m) == null) || (cur_val < 0)) {
+            cur_val = 0;
+        } else {
+            if (cur_val > penalty(equiv(m)))
+                cur_val = penalty(equiv(m));
+            cur_val = penalty(equiv(m) + cur_val);
+        }
+        cur_val_level = int_val_level;
+        break;
+    case char_given_cmd:
+    case math_given_cmd:
+    case xmath_given_cmd:
+        scanned_result(cur_chr, int_val_level);
+        break;
+    case last_item_cmd:
+        /* Because the items in this case directly refer to |cur_chr|,
+           it needs to be saved and restored */
+        save_cur_chr = cur_chr;
+        cur_chr = chr;
+        /* Fetch an item in the current node, if appropriate */
+        /* Here is where \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\   } are
+           implemented. The reference count for \.{\\lastskip} will be updated later.
+
+           We also handle \.{\\inputlineno} and \.{\\badness} here, because they are
+           legal in similar contexts. */
+
+        if (m >= input_line_no_code) {
+            if (m >= eTeX_glue) {
+                /* Process an expression and |return| */
+                if (m < eTeX_mu) {
+                    switch (m) {
+                    case mu_to_glue_code:
+                        scan_mu_glue();
+                        break;
+                    };          /* there are no other cases */
+                    cur_val_level = glue_val_level;
+                } else if (m < eTeX_expr) {
+                    switch (m) {
+                    case glue_to_mu_code:
+                        scan_normal_glue();
+                        break;
+                    }           /* there are no other cases */
+                    cur_val_level = mu_val_level;
+                } else {
+                    cur_val_level = m - eTeX_expr + int_val_level;
+                    scan_expr();
+                }
+                /* This code for reducing |cur_val_level| and\slash or negating the
+                   result is similar to the one for all the other cases of
+                   |scan_something_internal|; we free a glue_spec when needed.
+                 */
+                while (cur_val_level > level) {
+                    downgrade_cur_val(true);
+                }
+                if (negative) {
+                    /*
+                        we get a new glue spec node with negated values and the old
+                        intermediate is deleted
+                    */
+                    negate_cur_val(true);
+                }
+                return succeeded;
+
+            } else if (m >= eTeX_dim) {
+                switch (m) {
+                case font_char_wd_code:
+                case font_char_ht_code:
+                case font_char_dp_code:
+                case font_char_ic_code:
+                    scan_font_ident();
+                    q = cur_val;
+                    scan_char_num();
+                    if (char_exists(q, cur_val)) {
+                        switch (m) {
+                        case font_char_wd_code:
+                            cur_val = char_width(q, cur_val);
+                            break;
+                        case font_char_ht_code:
+                            cur_val = char_height(q, cur_val);
+                            break;
+                        case font_char_dp_code:
+                            cur_val = char_depth(q, cur_val);
+                            break;
+                        case font_char_ic_code:
+                            cur_val = char_italic(q, cur_val);
+                            break;
+                        }       /* there are no other cases */
+                    } else {
+                        cur_val = 0;
+                    }
+                    break;
+                case par_shape_length_code:
+                case par_shape_indent_code:
+                case par_shape_dimen_code:
+                    q = cur_chr - par_shape_length_code;
+                    scan_int();
+                    if ((par_shape_par_ptr == null) || (cur_val <= 0)) {
+                        cur_val = 0;
+                    } else {
+                        if (q == 2) {
+                            q = cur_val % 2;
+                            cur_val = (cur_val + q) / 2;
+                        }
+                        if (cur_val > vinfo(par_shape_par_ptr + 1))
+                            cur_val = vinfo(par_shape_par_ptr + 1);
+                        cur_val =
+                            varmem[par_shape_par_ptr + 2 * cur_val - q + 1].cint;
+                    }
+                    cur_val_level = dimen_val_level;
+                    break;
+                case glue_stretch_code:
+                case glue_shrink_code:
+                    scan_normal_glue();
+                    q = cur_val;
+                    if (m == glue_stretch_code)
+                        cur_val = stretch(q);
+                    else
+                        cur_val = shrink(q);
+                    flush_node(q);
+                    break;
+                }               /* there are no other cases */
+                cur_val_level = dimen_val_level;
+            } else {
+                switch (m) {
+                case input_line_no_code:
+                    cur_val = line;
+                    break;
+                case badness_code:
+                    cur_val = last_badness;
+                    break;
+                case luatex_version_code:
+                    cur_val = get_luatexversion();
+                    break;
+                case last_saved_box_resource_index_code:
+                    cur_val = last_saved_box_index;
+                    break;
+                case last_saved_image_resource_index_code:
+                    cur_val = last_saved_image_index;
+                    break;
+                case last_saved_image_resource_pages_code:
+                    cur_val = last_saved_image_pages;
+                    break;
+                case last_x_pos_code:
+                    cur_val = last_position.h;
+                    break;
+                case last_y_pos_code:
+                    cur_val = last_position.v;
+                    break;
+                case random_seed_code:
+                    cur_val = random_seed;
+                    break;
+                case eTeX_version_code:
+                    cur_val = eTeX_version;
+                    break;
+                case eTeX_minor_version_code:
+                    cur_val = eTeX_minor_version;
+                    break;
+                case current_group_level_code:
+                    cur_val = cur_level - level_one;
+                    break;
+                case current_group_type_code:
+                    cur_val = cur_group;
+                    break;
+                case current_if_level_code:
+                    q = cond_ptr;
+                    cur_val = 0;
+                    while (q != null) {
+                        incr(cur_val);
+                        q = vlink(q);
+                    }
+                    break;
+                case current_if_type_code:
+                    if (cond_ptr == null)
+                        cur_val = 0;
+                    else if (cur_if < unless_code)
+                        cur_val = cur_if + 1;
+                    else
+                        cur_val = -(cur_if - unless_code + 1);
+                    break;
+                case current_if_branch_code:
+                    if ((if_limit == or_code) || (if_limit == else_code))
+                        cur_val = 1;
+                    else if (if_limit == fi_code)
+                        cur_val = -1;
+                    else
+                        cur_val = 0;
+                    break;
+                case glue_stretch_order_code:
+                case glue_shrink_order_code:
+                    scan_normal_glue();
+                    q = cur_val;
+                    if (m == glue_stretch_order_code)
+                        cur_val = stretch_order(q);
+                    else
+                        cur_val = shrink_order(q);
+                    flush_node(q);
+                    break;
+                }               /* there are no other cases */
+                cur_val_level = int_val_level;
+            }
+        } else {
+            if (cur_chr == glue_val_level)
+                cur_val = zero_glue; /* a pointer */
+            else
+                cur_val = 0;
+            if (cur_chr == last_node_type_code) {
+                cur_val_level = int_val_level;
+                if ((cur_list.tail_field == cur_list.head_field)
+                    || (cur_list.mode_field == 0))
+                    cur_val = -1;
+            } else {
+                cur_val_level = cur_chr;        /* assumes identical values */
+            }
+            if ((cur_list.tail_field != contrib_head) &&
+                !is_char_node(cur_list.tail_field) &&
+                (cur_list.mode_field != 0)) {
+                switch (cur_chr) {
+                case lastpenalty_code:
+                    if (type(cur_list.tail_field) == penalty_node)
+                        cur_val = penalty(cur_list.tail_field);
+                    break;
+                case lastkern_code:
+                    if (type(cur_list.tail_field) == kern_node)
+                        cur_val = width(cur_list.tail_field);
+                    break;
+                case lastskip_code:
+                    if (type(cur_list.tail_field) == glue_node)
+                     // cur_val = new_spec(cur_list.tail_field);
+                        cur_val = cur_list.tail_field;
+                    if (subtype(cur_list.tail_field) == mu_glue)
+                        cur_val_level = mu_val_level;
+                    break;
+                case last_node_type_code:
+                    cur_val = visible_last_node_type(cur_list.tail_field);
+                    break;
+                }               /* there are no other cases */
+            } else if ((cur_list.mode_field == vmode)
+                       && (cur_list.tail_field == cur_list.head_field)) {
+                switch (cur_chr) {
+                case lastpenalty_code:
+                    cur_val = last_penalty;
+                    break;
+                case lastkern_code:
+                    cur_val = last_kern;
+                    break;
+                case lastskip_code:
+                    if (last_glue != max_halfword)
+                        cur_val = last_glue;
+                    break;
+                case last_node_type_code:
+                    cur_val = last_node_type;
+                    break;
+                }               /* there are no other cases */
+            }
+        }
+        cur_chr = save_cur_chr;
+        break;
+    default:
+        succeeded = false;
+    }
+    if (succeeded) {
+        while (cur_val_level > level) {
+            /* Convert |cur_val| to a lower level */
+            downgrade_cur_val(false);
+        }
+        /* Fix the reference count, if any, and negate |cur_val| if |negative| */
+        /* If |cur_val| points to a glue specification at this point, the reference
+           count for the glue does not yet include the reference by |cur_val|.
+           If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|.
+         */
+        if (negative) {
+            /* we create a new (negated) glue spec and keep the old one */
+            negate_cur_val(false);
+        } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) {
+			cur_val = new_spec(cur_val);
+        }
+    }
+    return succeeded;
+}
+
+@ First, here is a short routine that is called from lua code. All
+the  real work is delegated to |short_scan_something_internal| that
+is shared between this routine and |scan_something_internal|.
+
+@c
+void scan_something_simple(halfword cmd, halfword subitem)
+{
+    /* negative is never true */
+    if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) {
+        /* Complain that |texlib| can not do this; give zero result */
+        print_err("You can't use `");
+        print_cmd_chr((quarterword) cmd, subitem);
+        tprint("' as tex library index");
+        help1("I'm forgetting what you said and using zero instead.");
+        error();
+        scanned_result(0, int_val_level);
+    }
+}
+
+@ OK, we're ready for |scan_something_internal| itself. A second parameter,
+|negative|, is set |true| if the value that is found should be negated.
+It is assumed that |cur_cmd| and |cur_chr| represent the first token of
+the internal quantity to be scanned; an error will be signalled if
+|cur_cmd<min_internal| or |cur_cmd>max_internal|.
+
+@c
+void scan_something_internal(int level, boolean negative)
+{
+    /* fetch an internal parameter */
+    halfword m;                 /* |chr_code| part of the operand token */
+    int n, k;                   /* accumulators */
+  RESTART:
+    m = cur_chr;
+    if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) {
+        switch (cur_cmd) {
+        case def_char_code_cmd:
+            /* Fetch a character code from some table */
+            scan_char_num();
+            if (m == math_code_base) {
+                cur_val1 = get_math_code_num(cur_val);
+                scanned_result(cur_val1, int_val_level);
+            } else if (m == lc_code_base) {
+                cur_val1 = get_lc_code(cur_val);
+                scanned_result(cur_val1, int_val_level);
+            } else if (m == uc_code_base) {
+                cur_val1 = get_uc_code(cur_val);
+                scanned_result(cur_val1, int_val_level);
+            } else if (m == sf_code_base) {
+                cur_val1 = get_sf_code(cur_val);
+                scanned_result(cur_val1, int_val_level);
+            } else if (m == cat_code_base) {
+                cur_val1 = get_cat_code(cat_code_table_par, cur_val);
+                scanned_result(cur_val1, int_val_level);
+            } else {
+                confusion("def_char");
+            }
+            break;
+        case def_del_code_cmd:
+        case extdef_del_code_cmd: /* bonus */
+            /* Fetch a character code from some table */
+            scan_char_num();
+            cur_val1 = get_del_code_num(cur_val);
+            scanned_result(cur_val1, int_val_level);
+            break;
+        case extdef_math_code_cmd:
+            /* Fetch an extended math code table value */
+            scan_char_num();
+            cur_val1 = get_math_code_num(cur_val);
+            scanned_result(cur_val1, int_val_level);
+            break;
+        case toks_register_cmd:
+        case set_font_cmd:
+        case def_font_cmd:
+        case letterspace_font_cmd:
+        case copy_font_cmd:
+            /* Fetch a token list or font identifier, provided that |level=tok_val| */
+            if (level != tok_val_level) {
+                print_err("Missing number, treated as zero");
+                help3("A number should have been here; I inserted `0'.",
+                      "(If you can't figure out why I needed to see a number,",
+                      "look up `weird error' in the index to The TeXbook.)");
+                back_error();
+                scanned_result(0, dimen_val_level);
+            } else if (cur_cmd == toks_register_cmd) {
+                scan_register_num();
+                m = toks_base + cur_val;
+                scanned_result(equiv(m), tok_val_level);
+            } else {
+                back_input();
+                scan_font_ident();
+                scanned_result(font_id_base + cur_val, ident_val_level);
+            }
+            break;
+        case set_font_id_cmd:
+            scan_int();
+            scanned_result(font_id_base + cur_val, ident_val_level);
+            break;
+        case def_family_cmd:
+            /* Fetch a math font identifier */
+            scan_char_num();
+            cur_val1 = fam_fnt(cur_val, m);
+            scanned_result(font_id_base + cur_val1, ident_val_level);
+            break;
+        case set_math_param_cmd:
+            /* Fetch a math param */
+            cur_val1 = 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_val = display_style;
+                back_error();
+            } else {
+                cur_val = cur_chr;
+            }
+            if (cur_val1 < math_param_first_mu_glue) {
+                if (cur_val1 == math_param_radical_degree_raise) {
+                    cur_val1 = get_math_param(cur_val1, cur_chr);
+                    scanned_result(cur_val1, int_val_level);
+                } else {
+                    cur_val1 = get_math_param(cur_val1, cur_chr);
+                    scanned_result(cur_val1, dimen_val_level);
+                }
+            } else {
+                cur_val1 = get_math_param(cur_val1, cur_chr);
+                if (cur_val1 == thin_mu_skip_code)
+                    cur_val1 = thin_mu_skip_par;
+                else if (cur_val1 == med_mu_skip_code)
+                    cur_val1 = med_mu_skip_par;
+                else if (cur_val1 == thick_mu_skip_code)
+                    cur_val1 = thick_mu_skip_par;
+                scanned_result(cur_val1, mu_val_level);
+            }
+            break;
+        case assign_box_dir_cmd:
+            scan_register_num();
+            m = cur_val;
+            if (box(m) != null)
+                cur_val = box_dir(box(m));
+            else
+                cur_val = 0;
+            cur_val_level = dir_val_level;
+            break;
+        case set_box_dimen_cmd:
+            /* Fetch a box dimension */
+            scan_register_num();
+            if (box(cur_val) == null)
+                cur_val = 0;
+            else
+                cur_val = varmem[box(cur_val) + m].cint;
+            cur_val_level = dimen_val_level;
+            break;
+        case assign_font_dimen_cmd:
+            /* Fetch a font dimension */
+            get_font_dimen();
+            break;
+        case assign_font_int_cmd:
+            /* Fetch a font integer */
+            scan_font_ident();
+            if (m == 0) {
+                scanned_result(hyphen_char(cur_val), int_val_level);
+            } else if (m == 1) {
+                scanned_result(skew_char(cur_val), int_val_level);
+            } else if (m == no_lig_code) {
+                scanned_result(test_no_ligatures(cur_val), int_val_level);
+            } else {
+                n = cur_val;
+                scan_char_num();
+                k = cur_val;
+                switch (m) {
+                case lp_code_base:
+                    scanned_result(get_lp_code(n, k), int_val_level);
+                    break;
+                case rp_code_base:
+                    scanned_result(get_rp_code(n, k), int_val_level);
+                    break;
+                case ef_code_base:
+                    scanned_result(get_ef_code(n, k), int_val_level);
+                    break;
+                case tag_code:
+                    scanned_result(get_tag_code(n, k), int_val_level);
+                    break;
+                }
+            }
+            break;
+        case register_cmd:
+            /* Fetch a register */
+            scan_register_num();
+            switch (m) {
+            case int_val_level:
+                cur_val = count(cur_val);
+                break;
+            case attr_val_level:
+                cur_val = attribute(cur_val);
+                break;
+            case dimen_val_level:
+                cur_val = dimen(cur_val);
+                break;
+            case glue_val_level:
+                cur_val = skip(cur_val);
+                break;
+            case mu_val_level:
+                cur_val = mu_skip(cur_val);
+                break;
+            }                   /* there are no other cases */
+            cur_val_level = m;
+            break;
+        case ignore_spaces_cmd:        /* trap unexpandable primitives */
+            if (cur_chr == 1) {
+                /* Reset |cur_tok| for unexpandable primitives, goto restart */
+                /* This block deals with unexpandable \.{\\primitive} appearing at a spot where
+                   an integer or an internal values should have been found. It fetches the
+                   next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on the
+                   primitive value of that token. No expansion takes place, because the
+                   next token may be all sorts of things. This could trigger further
+                   expansion creating new errors.
+                 */
+                get_token();
+                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 = token_val(cur_cmd, cur_chr);
+                } else {
+                    cur_cmd = relax_cmd;
+                    cur_chr = 0;
+                    cur_tok = cs_token_flag + frozen_relax;
+                    cur_cs = frozen_relax;
+                }
+                goto RESTART;
+            }
+            break;
+        case hyph_data_cmd:
+            switch (cur_chr) {
+                case 0:
+                case 1:
+                    goto DEFAULT;
+                    break;
+                case 2:
+                    cur_val = get_pre_hyphen_char(language_par);
+                    cur_val_level = int_val_level;
+                    break;
+                case 3:
+                    cur_val = get_post_hyphen_char(language_par);
+                    cur_val_level = int_val_level;
+                    break;
+                case 4:
+                    cur_val = get_pre_exhyphen_char(language_par);
+                    cur_val_level = int_val_level;
+                    break;
+                case 5:
+                    cur_val = get_post_exhyphen_char(language_par);
+                    cur_val_level = int_val_level;
+                    break;
+                case 6:
+                    cur_val = get_hyphenation_min(language_par);
+                    cur_val_level = int_val_level;
+                    break;
+                case 7:
+                    scan_int();
+                    cur_val = get_hj_code(language_par,cur_val);
+                    cur_val_level = int_val_level;
+                    break;
+            }
+            break;
+        default:
+            DEFAULT:
+            /* Complain that \.{\\the} can not do this; give zero result */
+            print_err("You can't use `");
+            print_cmd_chr((quarterword) cur_cmd, cur_chr);
+            tprint("' after \\the");
+            help1("I'm forgetting what you said and using zero instead.");
+            error();
+            if (level != tok_val_level)
+                scanned_result(0, dimen_val_level);
+            else
+                scanned_result(0, int_val_level);
+            break;
+        }
+        while (cur_val_level > level) {
+            /* Convert |cur_val| to a lower level */
+            downgrade_cur_val(false);
+        }
+        /* Fix the reference count, if any, and negate |cur_val| if |negative| */
+        /* If |cur_val| points to a glue specification at this point, the reference
+           count for the glue does not yet include the reference by |cur_val|.
+           If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|.
+         */
+        if (negative) {
+            /* we create a new (negated) glue spec and keep the old one */
+            negate_cur_val(false);
+        } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) {
+			cur_val = new_spec(cur_val);
+        }
+    }
+}
+
+@ It is nice to have routines that say what they do, so the original
+|scan_eight_bit_int| is superceded by |scan_register_num| and
+|scan_mark_num|. It may become split up even further in the future.
+
+Many of the |restricted classes| routines are the essentially
+the same except for the upper limit and the error message, so it makes
+sense to combine these all into one function.
+
+@c
+void scan_limited_int(int max, const char *name)
+{
+    char hlp[80];
+    scan_int();
+    if ((cur_val < 0) || (cur_val > max)) {
+        if (name == NULL) {
+            snprintf(hlp, 80,
+                     "Since I expected to read a number between 0 and %d,",
+                     max);
+            print_err("Bad number");
+        } else {
+            char msg[80];
+            snprintf(hlp, 80, "A %s must be between 0 and %d.", name, max);
+            snprintf(msg, 80, "Bad %s", name);
+            print_err(msg);
+        }
+        help2(hlp, "I changed this one to zero.");
+        int_error(cur_val);
+        cur_val = 0;
+    }
+}
+
+@ @c
+void scan_fifteen_bit_int(void)
+{
+    scan_real_fifteen_bit_int();
+    cur_val = ((cur_val / 0x1000) * 0x1000000) +
+        (((cur_val % 0x1000) / 0x100) * 0x10000) + (cur_val % 0x100);
+}
+
+@ @c
+void scan_fifty_one_bit_int(void)
+{
+    int iiii;
+    scan_int();
+    if ((cur_val < 0) || (cur_val > 0777777777)) {
+        print_err("Bad delimiter code");
+        help2
+            ("A numeric delimiter (first part) must be between 0 and 2^{27}-1.",
+             "I changed this one to zero.");
+        int_error(cur_val);
+        cur_val = 0;
+    }
+    iiii = cur_val;
+    scan_int();
+    if ((cur_val < 0) || (cur_val > 0xFFFFFF)) {
+        print_err("Bad delimiter code");
+        help2
+            ("A numeric delimiter (second part) must be between 0 and 2^{24}-1.",
+             "I changed this one to zero.");
+        int_error(cur_val);
+        cur_val = 0;
+    }
+    cur_val1 = cur_val;
+    cur_val = iiii;
+}
+
+@ An integer number can be preceded by any number of spaces and `\.+' or
+`\.-' signs. Then comes either a decimal constant (i.e., radix 10), an
+octal constant (i.e., radix 8, preceded by~'), a hexadecimal constant
+(radix 16, preceded by~"), an alphabetic constant (preceded by~`), or
+an internal variable. After scanning is complete,
+|cur_val| will contain the answer, which must be at most
+$2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to
+10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants,
+otherwise |radix| is set to zero. An optional space follows a constant.
+
+@c
+int radix;                      /* |scan_int| sets this to 8, 10, 16, or zero */
+
+@ The |scan_int| routine is used also to scan the integer part of a
+  fraction; for example, the `\.3' in `\.{3.14159}' will be found by
+  |scan_int|. The |scan_dimen| routine assumes that |cur_tok=point_token|
+  after the integer part of such a fraction has been scanned by |scan_int|,
+  and that the decimal point has been backed up to be scanned again.
+
+@c
+void scan_int(void)
+{                               /* sets |cur_val| to an integer */
+    boolean negative;           /* should the answer be negated? */
+    int m;                      /* |$2^{31}$ / radix|, the threshold of danger */
+    int d;                      /* the digit just scanned */
+    boolean vacuous;            /* have no digits appeared? */
+    boolean OK_so_far;          /* has an error message been issued? */
+    radix = 0;
+    OK_so_far = true;
+    /* Get the next non-blank non-sign token; set |negative| appropriately */
+    negative = false;
+    do {
+        /* Get the next non-blank non-call token */
+        do {
+            get_x_token();
+        } while (cur_cmd == spacer_cmd);
+        if (cur_tok == other_token + '-') {
+            negative = !negative;
+            cur_tok = other_token + '+';
+        }
+    } while (cur_tok == other_token + '+');
+
+  RESTART:
+    if (cur_tok == alpha_token) {
+        /* Scan an alphabetic character code into |cur_val| */
+        /* A space is ignored after an alphabetic character constant, so that
+           such constants behave like numeric ones. */
+        get_token();            /* suppress macro expansion */
+        if (cur_tok < cs_token_flag) {
+            cur_val = cur_chr;
+            if (cur_cmd <= right_brace_cmd) {
+                if (cur_cmd == right_brace_cmd)
+                    incr(align_state);
+                else
+                    decr(align_state);
+            }
+        } else {                /* the value of a csname in this context is its name */
+            str_number txt = cs_text(cur_tok - cs_token_flag);
+            if (is_active_cs(txt))
+                cur_val = active_cs_value(txt);
+            else if (single_letter(txt))
+                cur_val = pool_to_unichar(str_string(txt));
+            else
+                cur_val = (biggest_char + 1);
+        }
+        if (cur_val > biggest_char) {
+            print_err("Improper alphabetic constant");
+            help2("A one-character control sequence belongs after a ` mark.",
+                  "So I'm essentially inserting \\0 here.");
+            cur_val = '0';
+            back_error();
+        } else {
+            /* Scan an optional space */
+            get_x_token();
+            if (cur_cmd != spacer_cmd)
+                back_input();
+        }
+
+    } else if (cur_tok == cs_token_flag + frozen_primitive) {
+        /* Reset |cur_tok| for unexpandable primitives, goto restart */
+        /* This block deals with unexpandable \.{\\primitive} appearing at a spot where
+           an integer or an internal values should have been found. It fetches the
+           next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on the
+           primitive value of that token. No expansion takes place, because the
+           next token may be all sorts of things. This could trigger further
+           expansion creating new errors.
+         */
+        get_token();
+        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 = token_val(cur_cmd, cur_chr);
+        } else {
+            cur_cmd = relax_cmd;
+            cur_chr = 0;
+            cur_tok = cs_token_flag + frozen_relax;
+            cur_cs = frozen_relax;
+        }
+        goto RESTART;
+    } else if (cur_cmd == math_style_cmd) {
+        cur_val = cur_chr;
+    } else if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
+        scan_something_internal(int_val_level, false);
+    } else {
+        /* Scan a numeric constant */
+        radix = 10;
+        m = 214748364;
+        if (cur_tok == octal_token) {
+            radix = 8;
+            m = 02000000000;
+            get_x_token();
+        } else if (cur_tok == hex_token) {
+            radix = 16;
+            m = 01000000000;
+            get_x_token();
+        }
+        vacuous = true;
+        cur_val = 0;
+        /* Accumulate the constant until |cur_tok| is not a suitable digit */
+        while (1) {
+            if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token)
+                && (cur_tok <= zero_token + 9)) {
+                d = cur_tok - zero_token;
+            } else if (radix == 16) {
+                if ((cur_tok <= A_token + 5) && (cur_tok >= A_token)) {
+                    d = cur_tok - A_token + 10;
+                } else if ((cur_tok <= other_A_token + 5)
+                           && (cur_tok >= other_A_token)) {
+                    d = cur_tok - other_A_token + 10;
+                } else {
+                    break;
+                }
+            } else {
+                break;
+            }
+            vacuous = false;
+            if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) {
+                if (OK_so_far) {
+                    print_err("Number too big");
+                    help2
+                        ("I can only go up to 2147483647='17777777777=\"7FFFFFFF,",
+                         "so I'm using that number instead of yours.");
+                    error();
+                    cur_val = infinity;
+                    OK_so_far = false;
+                }
+            } else {
+                cur_val = cur_val * radix + d;
+            }
+            get_x_token();
+        }
+        if (vacuous) {
+            /* Express astonishment that no number was here */
+            print_err("Missing number, treated as zero");
+            help3("A number should have been here; I inserted `0'.",
+                  "(If you can't figure out why I needed to see a number,",
+                  "look up `weird error' in the index to The TeXbook.)");
+            back_error();
+        } else if (cur_cmd != spacer_cmd) {
+            back_input();
+        }
+    }
+    if (negative)
+        negate(cur_val);
+}
+
+@ The following code is executed when |scan_something_internal| was
+called asking for |mu_val|, when we really wanted a ``mudimen'' instead
+of ``muglue.''
+
+@c
+static void coerce_glue(void)
+{
+    int v;
+    if (cur_val_level >= glue_val_level) {
+        v = width(cur_val);
+        flush_node(cur_val);
+        cur_val = v;
+    }
+}
+
+@ The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to
+a |scaled| value, i.e., an integral number of sp. One of its main tasks
+is therefore to interpret the abbreviations for various kinds of units and
+to convert measurements to scaled points.
+
+There are three parameters: |mu| is |true| if the finite units must be
+`\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed;
+|inf| is |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}'
+are permitted; and |shortcut| is |true| if |cur_val| already contains
+an integer and only the units need to be considered.
+
+The order of infinity that was found in the case of infinite glue is returned
+in the global variable |cur_order|.
+
+@c
+int cur_order;                  /* order of infinity found by |scan_dimen| */
+
+
+@ Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen|
+may begin with |scan_int|. This explains why it is convenient to use
+|scan_int| also for the integer part of a decimal fraction.
+
+Several branches of |scan_dimen| work with |cur_val| as an integer and
+with an auxiliary fraction |f|, so that the actual quantity of interest is
+$|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked''
+representation is put into the single word |cur_val|, which suddenly
+switches significance from |integer| to |scaled|.
+
+@
+The necessary conversion factors can all be specified exactly as
+fractions whose numerator and denominator add to 32768 or less.
+According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$;
+this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard
+\^{Bosshard, Hans Rudolf}
+in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980).
+The Didot point has been newly standardized in 1978;
+it's now exactly $\rm 1\,nd=0.375\,mm$.
+Conversion uses the equation $0.375=21681/20320/72.27\cdot25.4$.
+The new Cicero follows the new Didot point; $\rm 1\,nc=12\,nd$.
+These would lead to the ratios $21681/20320$ and $65043/5080$,
+respectively.
+The closest approximations supported by the algorithm would be
+$11183/10481$ and $1370/107$.  In order to maintain the
+relation $\rm 1\,nc=12\,nd$, we pick the ratio $685/642$ for
+$\rm nd$, however.
+
+@c
+
+static void scan_dimen_mu_error(void) {
+    print_err("Illegal unit of measure (mu inserted)");
+    help4("The unit of measurement in math glue must be mu.",
+          "To recover gracefully from this error, it's best to",
+          "delete the erroneous units; e.g., type `2' to delete",
+          "two letters. (See Chapter 27 of The TeXbook.)");
+    error();
+}
+
+static void scan_dimen_unknown_unit_error(void) {
+    print_err("Illegal unit of measure (pt inserted)");
+    help6("Dimensions can be in units of em, ex, in, pt, pc,",
+          "cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!",
+          "I'll assume that you meant to say pt, for printer's points.",
+          "To recover gracefully from this error, it's best to",
+          "delete the erroneous units; e.g., type `2' to delete",
+          "two letters. (See Chapter 27 of The TeXbook.)");
+    error();
+}
+
+static void scan_dimen_out_of_range_error(void) {
+    print_err("Dimension too large");
+    help2("I can't work with sizes bigger than about 19 feet.",
+          "Continue and I'll use the largest value I can.");
+    error();
+}
+
+#define set_conversion(A,B) do { num=(A); denom=(B); } while(0)
+
+/*
+    This function sets |cur_val| to a dimension. It could be optimized a bit
+    more (but not now, something for luatex > 1).
+*/
+
+void scan_dimen(boolean mu, boolean inf, boolean shortcut)
+{
+    boolean negative = false; /* should the answer be negated? */
+    boolean is_true  = false;
+    int f = 0;                /* numerator of a fraction whose denominator is $2^{16}$ */
+    int num = 0;              /* conversion ratio for the scanned units */
+    int denom = 0;
+    halfword q;               /* top of decimal digit stack */
+    scaled v;                 /* an internal dimension */
+    int save_cur_val;         /* temporary storage of |cur_val| */
+    arith_error = false;
+    cur_order = normal;
+    if (!shortcut) {
+        /* Get the next non-blank non-sign... */
+        do {
+            /* Get the next non-blank non-call token */
+            do {
+                get_x_token();
+            } while (cur_cmd == spacer_cmd);
+            if (cur_tok == other_token + '-') {
+                negative = !negative;
+                cur_tok = other_token + '+';
+            }
+        } while (cur_tok == other_token + '+');
+        if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
+            /* Fetch an internal dimension and |goto attach_sign|, or fetch an internal integer */
+            if (mu) {
+                scan_something_internal(mu_val_level, false);
+                coerce_glue();
+                if (cur_val_level == mu_val_level) {
+                    goto ATTACH_SIGN;
+                } else if (cur_val_level != int_val_level) {
+                    mu_error();
+                }
+            } else {
+                scan_something_internal(dimen_val_level, false);
+                if (cur_val_level == dimen_val_level) {
+                    goto ATTACH_SIGN;
+                }
+            }
+        } else {
+            back_input();
+            if (cur_tok == continental_point_token) {
+                cur_tok = point_token;
+            }
+            if (cur_tok != point_token) {
+                scan_int();
+            } else {
+                radix = 10;
+                cur_val = 0;
+            }
+            if (cur_tok == continental_point_token) {
+                cur_tok = point_token;
+            }
+            if ((radix == 10) && (cur_tok == point_token)) {
+                /*
+                    Scan decimal fraction. When the following code is executed, we have
+                    |cur_tok=point_token|, but this token has been backed up using |back_input|;
+                    we must first discard it. It turns out that a decimal point all by itself
+                    is equivalent to `\.{0.0}'. Let's hope people don't use that fact.
+                */
+                int k = 0;
+                halfword p = null;
+                int kk;
+                get_token(); /* |point_token| is being re-scanned */
+                while (1) {
+                    get_x_token();
+                    if ((cur_tok > zero_token + 9) || (cur_tok < zero_token))
+                        break;
+                    if (k < 17) {
+                        /* digits for |k>=17| cannot affect the result */
+                        q = get_avail();
+                        set_token_link(q, p);
+                        set_token_info(q, cur_tok - zero_token);
+                        p = q;
+                        incr(k);
+                    }
+                }
+                for (kk = k; kk >= 1; kk--) {
+                    dig[kk - 1] = token_info(p);
+                    q = p;
+                    p = token_link(p);
+                    free_avail(q);
+                }
+                f = round_decimals(k);
+                if (cur_cmd != spacer_cmd) {
+                    back_input();
+                }
+            }
+        }
+    }
+    if (cur_val < 0) {
+        /* in this case |f=0| */
+        negative = !negative;
+        negate(cur_val);
+    }
+    /*
+        Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there
+        are |x| sp per unit; |goto attach_sign| if the units are internal. Now comes
+        the harder part: At this point in the program, |cur_val| is a nonnegative
+        integer and $f/2^{16}$ is a nonnegative fraction less than 1; we want to
+        multiply the sum of these two quantities by the appropriate factor, based
+        on the specified units, in order to produce a |scaled| result, and we want
+        to do the calculation with fixed point arithmetic that does not overflow.
+    */
+    if (inf) {
+        /*
+            Scan for (f)\.{fil} units; |goto attach_fraction| if found. In traditional
+            \TeX, a specification like `\.{filllll}' or `\.{fill L L L}' will lead to
+            two error messages (one for each additional keyword \.{"l"}). Not so for
+            \LuaTeX, it just parses the construct in reverse.
+
+             if (scan_keyword("filll")) {
+                 cur_order = filll;
+                 goto ATTACH_FRACTION;
+             } else if (scan_keyword("fill")) {
+                 cur_order = fill;
+                 goto ATTACH_FRACTION;
+             } else if (scan_keyword("fil")) {
+                 cur_order = fil;
+                 goto ATTACH_FRACTION;
+             } else if (scan_keyword("fi")) {
+                 cur_order = sfi;
+                 goto ATTACH_FRACTION;
+             }
+
+            But ... it failed in alignments so now we do this. And, as we support an extra
+            l we don't issue an error message (we didn't do that anyway).
+         */
+         if (scan_keyword("fi")) {
+             cur_order = sfi;
+             if (scan_keyword("l")) {
+                cur_order = fil;
+                if (scan_keyword("l")) {
+                    cur_order = fill;
+                    if (scan_keyword("l")) {
+                        cur_order = filll;
+                    }
+                }
+            }
+            goto ATTACH_FRACTION;
+         }
+    }
+    /*
+        Scan for (u)units that are internal dimensions; |goto attach_sign| with
+        |cur_val| set if found
+    */
+    save_cur_val = cur_val;
+    /* Get the next non-blank non-call... a pitty if just backed up the input */
+    do {
+        get_x_token();
+    } while (cur_cmd == spacer_cmd);
+
+    if ((cur_cmd < min_internal_cmd) || (cur_cmd > max_internal_cmd)) {
+        back_input();
+    } else {
+        /* math_given_cmd xmath_given_cmd last_item_cmd */
+        if (mu) {
+            scan_something_internal(mu_val_level, false);
+            coerce_glue();
+            if (cur_val_level != mu_val_level) {
+                mu_error();
+            }
+        } else {
+            scan_something_internal(dimen_val_level, false);
+        }
+        v = cur_val;
+        goto FOUND;
+    }
+    /* bah ... true forces to split the unit scanner */
+    if (mu) {
+        /* Scan for (m)\.{mu} units and |goto attach_fraction| */
+        if (! scan_keyword("mu")) {
+            scan_dimen_mu_error();
+        }
+        goto ATTACH_FRACTION;
+    } else if (scan_keyword("em")) {
+        v = quad(get_cur_font());
+    } else if (scan_keyword("ex")) {
+        v = x_height(get_cur_font());
+    } else if (scan_keyword("px")) {
+        v = px_dimen_par;
+    } else {
+        goto PICKUP_UNIT;
+    }
+    /* Scan an optional space (after em, ex or px) */
+    get_x_token();
+    if (cur_cmd != spacer_cmd) {
+        back_input();
+    }
+  FOUND:
+    cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000));
+    goto ATTACH_SIGN;
+    /*
+        Scan for (a)all other units and adjust |cur_val| and |f| accordingly;
+        |goto done| in the case of scaled points
+    */
+  PICKUP_UNIT:
+    if (scan_keyword("pt")) {
+        goto SCALE_VALUE;   /* the easy case */
+    } else if (scan_keyword("mm")) {
+        set_conversion(7227, 2540);
+        goto SCALE_VALUE;
+    } else if (scan_keyword("cm")) {
+        set_conversion(7227, 254);
+        goto SCALE_VALUE;
+    } else if (scan_keyword("sp")) {
+        goto DONE;
+    } else if (scan_keyword("bp")) {
+        set_conversion(7227, 7200);
+        goto SCALE_VALUE;
+    } else if (scan_keyword("in")) {
+        set_conversion(7227, 100);
+        goto SCALE_VALUE;
+    } else if (scan_keyword("dd")) {
+        set_conversion(1238, 1157);
+        goto SCALE_VALUE;
+    } else if (scan_keyword("cc")) {
+        set_conversion(14856, 1157);
+        goto SCALE_VALUE;
+    } else if (scan_keyword("pc")) {
+        set_conversion(12, 1);
+        goto SCALE_VALUE;
+    } else if (scan_keyword("nd")) {
+        set_conversion(685, 642);
+        goto SCALE_VALUE;
+    } else if (scan_keyword("nc")) {
+        set_conversion(1370, 107);
+        goto SCALE_VALUE;
+    } else if (!is_true && scan_keyword("true")) {
+        is_true = true;
+        goto PICKUP_UNIT;
+    }
+    /* Complain about unknown unit and |goto done2| */
+    scan_dimen_unknown_unit_error();
+    goto BAD_NEWS;
+  SCALE_VALUE:
+    /* Adjust (f) for the magnification ratio */
+    if (is_true) {
+        /* maybe at some point we will drop mag completely, even in dvi mode */
+        if (output_mode_used <= OMODE_DVI) {
+            prepare_mag();
+            if (mag_par != 1000) {
+                cur_val = xn_over_d(cur_val, 1000, mag_par);
+                f = (1000 * f + 0200000 * tex_remainder) / mag_par;
+                cur_val = cur_val + (f / 0200000);
+                f = f % 0200000;
+            }
+        } else {
+            /* in pdf mode we're always true */
+            one_true_inch = one_inch; /* saveguard */
+        }
+    }
+    /* */
+    if (num) {
+        cur_val = xn_over_d(cur_val, num, denom);
+        f = (num * f + 0200000 * tex_remainder) / denom;
+        cur_val = cur_val + (f / 0200000);
+        f = f % 0200000;
+    }
+  BAD_NEWS:
+  ATTACH_FRACTION:
+    if (cur_val >= 040000) {
+        arith_error = true;
+    } else {
+        cur_val = cur_val * unity + f;
+    }
+  DONE:
+    /* Scan an optional space */ /* happens too often */
+    get_x_token();
+    if (cur_cmd != spacer_cmd) {
+        back_input();
+    }
+  ATTACH_SIGN:
+    if (arith_error || (abs(cur_val) >= 010000000000)) {
+        /* Report that this dimension is out of range */
+        scan_dimen_out_of_range_error();
+        cur_val = max_dimen;
+        arith_error = false;
+    }
+    if (negative) {
+        negate(cur_val);
+    }
+}
+
+@ The final member of \TeX's value-scanning trio is |scan_glue|, which
+makes |cur_val| point to a glue specification. The reference count of that
+glue spec will take account of the fact that |cur_val| is pointing to~it.
+
+The |level| parameter should be either |glue_val| or |mu_val|.
+
+Since |scan_dimen| was so much more complex than |scan_int|, we might expect
+|scan_glue| to be even worse. But fortunately, it is very simple, since
+most of the work has already been done.
+
+@c
+void scan_glue(int level)
+{
+    boolean negative = false;             /* should the answer be negated? */
+    halfword q = null;                    /* new glue specification */
+    boolean mu = (level == mu_val_level); /* does |level=mu_val|? */
+    /* Get the next non-blank non-sign ... */
+    do {
+        /* Get the next non-blank non-call token */
+        do {
+            get_x_token();
+        } while (cur_cmd == spacer_cmd);
+        if (cur_tok == other_token + '-') {
+            negative = !negative;
+            cur_tok = other_token + '+';
+        }
+    } while (cur_tok == other_token + '+');
+    if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
+        scan_something_internal(level, negative);
+        if (cur_val_level >= glue_val_level) {
+            if (cur_val_level != level)
+                mu_error();
+            return;
+        }
+        if (cur_val_level == int_val_level)
+            scan_dimen(mu, false, true);
+        else if (level == mu_val_level)
+            mu_error();
+    } else {
+        back_input();
+        scan_dimen(mu, false, false);
+        if (negative)
+            negate(cur_val);
+    }
+    /*
+        Create a new glue specification whose width is |cur_val|; scan for its
+        stretch and shrink components.
+    */
+    q = new_spec(zero_glue);
+    width(q) = cur_val;
+    if (scan_keyword("plus")) {
+        scan_dimen(mu, true, false);
+        stretch(q) = cur_val;
+        stretch_order(q) = (quarterword) cur_order;
+    }
+    if (scan_keyword("minus")) {
+        scan_dimen(mu, true, false);
+        shrink(q) = cur_val;
+        shrink_order(q) = (quarterword) cur_order;
+    }
+    cur_val = q;
+}
+
+@ This is an omega routine
+@c
+void scan_scaled(void)
+{                               /* sets |cur_val| to a scaled value */
+    boolean negative;           /* should the answer be negated? */
+    int f;                      /* numerator of a fraction whose denominator is $2^{16}$ */
+    int k, kk;                  /* number of digits in a decimal fraction */
+    halfword p, q;              /* top of decimal digit stack */
+    f = 0;
+    arith_error = false;
+    negative = false;
+    /* Get the next non-blank non-sign... */
+    do {
+        /* Get the next non-blank non-call token */
+        do {
+            get_x_token();
+        } while (cur_cmd == spacer_cmd);
+        if (cur_tok == other_token + '-') {
+            negative = !negative;
+            cur_tok = other_token + '+';
+        }
+    } while (cur_tok == other_token + '+');
+
+    back_input();
+    if (cur_tok == continental_point_token)
+        cur_tok = point_token;
+    if (cur_tok != point_token) {
+        scan_int();
+    } else {
+        radix = 10;
+        cur_val = 0;
+    }
+    if (cur_tok == continental_point_token)
+        cur_tok = point_token;
+    if ((radix == 10) && (cur_tok == point_token)) {
+        /* Scan decimal fraction */
+        /* TODO: merge this with the same block in |scan_dimen| */
+        /* When the following code is executed, we have |cur_tok=point_token|, but this
+           token has been backed up using |back_input|; we must first discard it.
+
+           It turns out that a decimal point all by itself is equivalent to `\.{0.0}'.
+           Let's hope people don't use that fact. */
+
+        k = 0;
+        p = null;
+        get_token();            /* |point_token| is being re-scanned */
+        while (1) {
+            get_x_token();
+            if ((cur_tok > zero_token + 9) || (cur_tok < zero_token))
+                break;
+            if (k < 17) {       /* digits for |k>=17| cannot affect the result */
+                q = get_avail();
+                set_token_link(q, p);
+                set_token_info(q, cur_tok - zero_token);
+                p = q;
+                incr(k);
+            }
+        }
+        for (kk = k; kk >= 1; kk--) {
+            dig[kk - 1] = token_info(p);
+            q = p;
+            p = token_link(p);
+            free_avail(q);
+        }
+        f = round_decimals(k);
+        if (cur_cmd != spacer_cmd)
+            back_input();
+
+    }
+    if (cur_val < 0) {          /* in this case |f=0| */
+        negative = !negative;
+        negate(cur_val);
+    }
+    if (cur_val > 040000)
+        arith_error = true;
+    else
+        cur_val = cur_val * unity + f;
+    if (arith_error || (abs(cur_val) >= 010000000000)) {
+        print_err("Stack number too large");
+        error();
+    }
+    if (negative)
+        negate(cur_val);
+}
+
+@ This procedure is supposed to scan something like `\.{\\skip\\count12}',
+i.e., whatever can follow `\.{\\the}', and it constructs a token list
+containing something like `\.{-3.0pt minus 0.5fill}'.
+
+@c
+halfword the_toks(void)
+{
+    int old_setting;            /* holds |selector| setting */
+    halfword p, q, r;           /* used for copying a token list */
+    int c;                      /* value of |cur_chr| */
+    str_number s;
+    halfword retval;
+    /* Handle \.{\\unexpanded} or \.{\\detokenize} and |return| */
+    if (odd(cur_chr)) {
+        c = cur_chr;
+        scan_general_text();
+        if (c == 1) {
+            return cur_val;
+        } else {
+            old_setting = selector;
+            selector = new_string;
+            p = get_avail();
+            set_token_link(p, token_link(temp_token_head));
+            token_show(p);
+            flush_list(p);
+            selector = old_setting;
+            s = make_string();
+            retval = str_toks(str_lstring(s));
+            flush_str(s);
+            return retval;
+        }
+    }
+    get_x_token();
+    scan_something_internal(tok_val_level, false);
+    if (cur_val_level >= ident_val_level) {
+        /* Copy the token list */
+        p = temp_token_head;
+        set_token_link(p, null);
+        if (cur_val_level == ident_val_level) {
+            store_new_token(cs_token_flag + cur_val);
+        } else if (cur_val != null) {
+            r = token_link(cur_val);    /* do not copy the reference count */
+            while (r != null) {
+                fast_store_new_token(token_info(r));
+                r = token_link(r);
+            }
+        }
+        return p;
+    } else {
+        old_setting = selector;
+        selector = new_string;
+        switch (cur_val_level) {
+        case int_val_level:
+            print_int(cur_val);
+            break;
+        case attr_val_level:
+            print_int(cur_val);
+            break;
+        case dir_val_level:
+            print_dir(cur_val);
+            break;
+        case dimen_val_level:
+            print_scaled(cur_val);
+            tprint("pt");
+            break;
+        case glue_val_level:
+            print_spec(cur_val, "pt");
+            flush_node(cur_val);
+            break;
+        case mu_val_level:
+            print_spec(cur_val, "mu");
+            flush_node(cur_val);
+            break;
+        }                       /* there are no other cases */
+        selector = old_setting;
+        s = make_string();
+        retval = str_toks(str_lstring(s));
+        flush_str(s);
+        return retval;
+    }
+}
+
+@ @c
+str_number the_scanned_result(void)
+{
+    int old_setting;            /* holds |selector| setting */
+    str_number r;               /* return value * */
+    old_setting = selector;
+    selector = new_string;
+    if (cur_val_level >= ident_val_level) {
+        if (cur_val != null) {
+            show_token_list(token_link(cur_val), null, -1);
+            r = make_string();
+        } else {
+            r = get_nullstr();
+        }
+    } else {
+        switch (cur_val_level) {
+        case int_val_level:
+            print_int(cur_val);
+            break;
+        case attr_val_level:
+            print_int(cur_val);
+            break;
+        case dir_val_level:
+            print_dir(cur_val);
+            break;
+        case dimen_val_level:
+            print_scaled(cur_val);
+            tprint("pt");
+            break;
+        case glue_val_level:
+            print_spec(cur_val, "pt");
+            flush_node(cur_val);
+            break;
+        case mu_val_level:
+            print_spec(cur_val, "mu");
+            flush_node(cur_val);
+            break;
+        }                       /* there are no other cases */
+        r = make_string();
+    }
+    selector = old_setting;
+    return r;
+}
+
+@ The following routine is used to implement `\.{\\fontdimen} |n| |f|'.
+The boolean parameter |writing| is set |true| if the calling program
+intends to change the parameter value.
+
+@c
+static void font_param_error(int f)
+{
+    print_err("Font ");
+    print_esc(font_id_text(f));
+    tprint(" has only ");
+    print_int(font_params(f));
+    tprint(" fontdimen parameters");
+    help2("To increase the number of font parameters, you must",
+          "use \\fontdimen immediately after the \\font is loaded.");
+    error();
+}
+
+void set_font_dimen(void)
+{
+    internal_font_number f;
+    int n;                      /* the parameter number */
+    scan_int();
+    n = cur_val;
+    scan_font_ident();
+    f = cur_val;
+    if (n <= 0) {
+        font_param_error(f);
+    } else {
+        if (n > font_params(f)) {
+            if (font_used(f)) {
+                font_param_error(f);
+            } else {
+                /* Increase the number of parameters in the font */
+                do {
+                    set_font_param(f, (font_params(f) + 1), 0);
+                } while (n != font_params(f));
+            }
+        }
+    }
+    scan_optional_equals();
+    scan_normal_dimen();
+    set_font_param(f, n, cur_val);
+}
+
+void get_font_dimen(void)
+{
+    internal_font_number f;
+    int n;                      /* the parameter number */
+    scan_int();
+    n = cur_val;
+    scan_font_ident();
+    f = cur_val;
+    cur_val = 0;                /* initialize return value */
+    if (n <= 0) {
+        font_param_error(f);
+        goto EXIT;
+    } else {
+        if (n > font_params(f)) {
+            if (font_used(f)) {
+                font_param_error(f);
+                goto EXIT;
+            } else {
+                /* Increase the number of parameters in the font */
+                do {
+                    set_font_param(f, (font_params(f) + 1), 0);
+                } while (n != font_params(f));
+
+            }
+        }
+    }
+    cur_val = font_param(f, n);
+  EXIT:
+    scanned_result(cur_val, dimen_val_level);
+}
+
+@ Here's a similar procedure that returns a pointer to a rule node. This
+routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule};
+therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store
+the default rule dimensions in the node, then to override them if
+`\.{height}' or `\.{width}' or `\.{depth}' specifications are
+found (in any order).
+
+@c
+halfword scan_rule_spec(void)
+{
+    /* |width|, |depth|, and |height| all equal |null_flag| now */
+    halfword q;
+    if (cur_cmd == no_vrule_cmd) {
+        q = new_rule(empty_rule);
+        cur_cmd = vrule_cmd;
+    } else if (cur_cmd == no_hrule_cmd) {
+        q = new_rule(empty_rule);
+        cur_cmd = hrule_cmd;
+    } else {
+        q = new_rule(normal_rule);
+    }
+    if (cur_cmd == vrule_cmd) {
+        width(q) = default_rule;
+        rule_dir(q) = body_direction_par;
+    } else {
+        height(q) = default_rule;
+        depth(q) = 0;
+        rule_dir(q) = text_direction_par;
+    }
+  RESWITCH:
+    if (scan_keyword("width")) {
+        scan_normal_dimen();
+        width(q) = cur_val;
+        goto RESWITCH;
+    }
+    if (scan_keyword("height")) {
+        scan_normal_dimen();
+        height(q) = cur_val;
+        goto RESWITCH;
+    }
+    if (scan_keyword("depth")) {
+        scan_normal_dimen();
+        depth(q) = cur_val;
+        goto RESWITCH;
+    }
+    return q;
+}
+
+
+@ Declare procedures that scan font-related stuff
+
+@c
+void scan_font_ident(void)
+{
+    internal_font_number f;
+    halfword m;
+    /* Get the next non-blank non-call... */
+    do {
+        get_x_token();
+    } while (cur_cmd == spacer_cmd);
+
+    if ((cur_cmd == def_font_cmd) || (cur_cmd == letterspace_font_cmd) || (cur_cmd == copy_font_cmd)) {
+        f = get_cur_font();
+    } else if (cur_cmd == set_font_cmd) {
+        f = cur_chr;
+        set_font_touched(f, 1);
+    } else if (cur_cmd == def_family_cmd) {
+        m = cur_chr;
+        scan_math_family_int();
+        f = fam_fnt(cur_val, m);
+        set_font_touched(f, 1);
+    } else {
+        print_err("Missing font identifier");
+        help2("I was looking for a control sequence whose",
+              "current meaning has been defined by \\font.");
+        back_error();
+        f = null_font;
+    }
+    cur_val = f;
+}
+
+@ The |scan_general_text| procedure is much like |scan_toks(false,false)|,
+but will be invoked via |expand|, i.e., recursively.
+
+The token list (balanced text) created by |scan_general_text| begins
+at |link(temp_token_head)| and ends at |cur_val|.  (If |cur_val=temp_token_head|,
+the list is empty.)
+
+@c
+void scan_general_text(void)
+{
+    int s;                      /* to save |scanner_status| */
+    halfword w;                 /* to save |warning_index| */
+    halfword d;                 /* to save |def_ref| */
+    halfword p;                 /* tail of the token list being built */
+    halfword q;                 /* new node being added to the token list via |store_new_token| */
+    halfword unbalance;         /* number of unmatched left braces */
+    s = scanner_status;
+    w = warning_index;
+    d = def_ref;
+    scanner_status = absorbing;
+    warning_index = cur_cs;
+    p = get_avail();
+    def_ref = p;
+    set_token_ref_count(def_ref, 0);
+    p = def_ref;
+    scan_left_brace();          /* remove the compulsory left brace */
+    unbalance = 1;
+    while (1) {
+        get_token();
+        if (cur_tok < right_brace_limit) {
+            if (cur_cmd < right_brace_cmd) {
+                incr(unbalance);
+            } else {
+                decr(unbalance);
+                if (unbalance == 0)
+                    break;
+            }
+        }
+        store_new_token(cur_tok);
+    }
+    q = token_link(def_ref);
+    free_avail(def_ref);        /* discard reference count */
+    if (q == null)
+        cur_val = temp_token_head;
+    else
+        cur_val = p;
+    set_token_link(temp_token_head, q);
+    scanner_status = s;
+    warning_index = w;
+    def_ref = d;
+}
+
+@ The |get_x_or_protected| procedure is like |get_x_token| except that
+protected macros are not expanded.
+
+@c
+void get_x_or_protected(void)
+{                               /* sets |cur_cmd|, |cur_chr|, |cur_tok|,
+                                   and expands non-protected macros */
+    while (1) {
+        get_token();
+        if (cur_cmd <= max_command_cmd)
+            return;
+        if ((cur_cmd >= call_cmd) && (cur_cmd < end_template_cmd)) {
+            if (token_info(token_link(cur_chr)) == protected_token)
+                return;
+        }
+        expand();
+    }
+}
+
+@ |scan_toks|. This function returns a pointer to the tail of a new token
+list, and it also makes |def_ref| point to the reference count at the
+head of that list.
+
+There are two boolean parameters, |macro_def| and |xpand|. If |macro_def|
+is true, the goal is to create the token list for a macro definition;
+otherwise the goal is to create the token list for some other \TeX\
+primitive: \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase},
+\.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or
+\.{\\special}. In the latter cases a left brace must be scanned next; this
+left brace will not be part of the token list, nor will the matching right
+brace that comes at the end. If |xpand| is false, the token list will
+simply be copied from the input using |get_token|. Otherwise all expandable
+tokens will be expanded until unexpandable tokens are left, except that
+the results of expanding `\.{\\the}' are not expanded further.
+If both |macro_def| and |xpand| are true, the expansion applies
+only to the macro body (i.e., to the material following the first
+|left_brace| character).
+
+The value of |cur_cs| when |scan_toks| begins should be the |eqtb|
+address of the control sequence to display in ``runaway'' error
+messages.
+
+@c
+halfword scan_toks(boolean macro_def, boolean xpand)
+{
+    halfword t;                 /* token representing the highest parameter number */
+    halfword s;                 /* saved token */
+    halfword p;                 /* tail of the token list being built */
+    halfword q;                 /* new node being added to the token list via |store_new_token| */
+    halfword unbalance;         /* number of unmatched left braces */
+    halfword hash_brace;        /* possible `\.{\#\{}' token */
+    if (macro_def)
+        scanner_status = defining;
+    else
+        scanner_status = absorbing;
+    warning_index = cur_cs;
+    p = get_avail();
+    def_ref = p;
+    set_token_ref_count(def_ref, 0);
+    p = def_ref;
+    hash_brace = 0;
+    t = zero_token;
+    if (macro_def) {
+        /* Scan and build the parameter part of the macro definition */
+        while (1) {
+            get_token();        /* set |cur_cmd|, |cur_chr|, |cur_tok| */
+            if (cur_tok < right_brace_limit)
+                break;
+            if (cur_cmd == mac_param_cmd) {
+                /* If the next character is a parameter number, make |cur_tok|
+                   a |match| token; but if it is a left brace, store
+                   `|left_brace|, |end_match|', set |hash_brace|, and |goto done|;
+                 */
+                s = match_token + cur_chr;
+                get_token();
+                if (cur_cmd == left_brace_cmd) {
+                    hash_brace = cur_tok;
+                    store_new_token(cur_tok);
+                    store_new_token(end_match_token);
+                    goto DONE;
+                }
+                if (t == zero_token + 9) {
+                    print_err("You already have nine parameters");
+                    help1("I'm going to ignore the # sign you just used.");
+                    error();
+                } else {
+                    incr(t);
+                    if (cur_tok != t) {
+                        print_err("Parameters must be numbered consecutively");
+                        help2
+                            ("I've inserted the digit you should have used after the #.",
+                             "Type `1' to delete what you did use.");
+                        back_error();
+                    }
+                    cur_tok = s;
+                }
+            }
+            store_new_token(cur_tok);
+        }
+        store_new_token(end_match_token);
+        if (cur_cmd == right_brace_cmd) {
+            /* Express shock at the missing left brace; |goto found| */
+            print_err("Missing { inserted");
+            incr(align_state);
+            help2
+                ("Where was the left brace? You said something like `\\def\\a}',",
+                 "which I'm going to interpret as `\\def\\a{}'.");
+            error();
+            goto FOUND;
+        }
+
+    } else {
+        scan_left_brace();      /* remove the compulsory left brace */
+    }
+  DONE:
+    /* Scan and build the body of the token list; |goto found| when finished */
+    unbalance = 1;
+    while (1) {
+        if (xpand) {
+            /* Expand the next part of the input */
+            /* Here we insert an entire token list created by |the_toks| without
+               expanding it further. */
+            while (1) {
+                get_next();
+                if (cur_cmd >= call_cmd) {
+                    if (token_info(token_link(cur_chr)) == protected_token) {
+                        cur_cmd = relax_cmd;
+                        cur_chr = no_expand_flag;
+                    }
+                }
+                if (cur_cmd <= max_command_cmd)
+                    break;
+                if (cur_cmd != the_cmd) {
+                    expand();
+                } else {
+                    q = the_toks();
+                    if (token_link(temp_token_head) != null) {
+                        set_token_link(p, token_link(temp_token_head));
+                        p = q;
+                    }
+                }
+            }
+            x_token();
+
+        } else {
+            get_token();
+        }
+        if (cur_tok < right_brace_limit) {
+            if (cur_cmd < right_brace_cmd) {
+                incr(unbalance);
+            } else {
+                decr(unbalance);
+                if (unbalance == 0)
+                    goto FOUND;
+            }
+        } else if (cur_cmd == mac_param_cmd) {
+            if (macro_def) {
+                /* Look for parameter number or \.{\#\#} */
+                s = cur_tok;
+                if (xpand)
+                    get_x_token();
+                else
+                    get_token();
+                if (cur_cmd != mac_param_cmd) {
+                    if ((cur_tok <= zero_token) || (cur_tok > t)) {
+                        print_err("Illegal parameter number in definition of ");
+                        sprint_cs(warning_index);
+                        help3("You meant to type ## instead of #, right?",
+                              "Or maybe a } was forgotten somewhere earlier, and things",
+                              "are all screwed up? I'm going to assume that you meant ##.");
+                        back_error();
+                        cur_tok = s;
+                    } else {
+                        cur_tok = out_param_token - '0' + cur_chr;
+                    }
+                }
+            }
+        }
+        store_new_token(cur_tok);
+    }
+  FOUND:
+    scanner_status = normal;
+    if (hash_brace != 0)
+        store_new_token(hash_brace);
+    return p;
+}
+
+@ Here we declare two trivial procedures in order to avoid mutually
+recursive procedures with parameters.
+
+@c
+void scan_normal_glue(void)
+{
+    scan_glue(glue_val_level);
+}
+
+void scan_mu_glue(void)
+{
+    scan_glue(mu_val_level);
+}
+
+@ The |scan_expr| procedure scans and evaluates an expression.
+
+@ Evaluating an expression is a recursive process:  When the left
+parenthesis of a subexpression is scanned we descend to the next level
+of recursion; the previous level is resumed with the matching right
+parenthesis.
+
+@c
+typedef enum {
+    expr_none  = 0, /* \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */
+    expr_add   = 1, /* \.( $\langle\it expr\rangle$ \.+ seen */
+    expr_sub   = 2, /* \.( $\langle\it expr\rangle$ \.- seen */
+    expr_mult  = 3, /* $\langle\it term\rangle$ \.* seen */
+    expr_div   = 4, /* $\langle\it term\rangle$ \./ seen */
+    expr_scale = 5, /* $\langle\it term\rangle$ \.*  $\langle\it factor\rangle$ \./ seen */
+} expression_states;
+
+@  We want to make sure that each term and (intermediate) result is in
+  the proper range.  Integer values must not exceed |infinity|
+  ($2^{31}-1$) in absolute value, dimensions must not exceed |max_dimen|
+  ($2^{30}-1$).  We avoid the absolute value of an integer, because this
+  might fail for the value $-2^{31}$ using 32-bit arithmetic.
+
+@   clear a number or dimension and set |arith_error|
+
+@c
+#define num_error(A) do { \
+    arith_error=true; \
+    A=0; \
+} while (0)
+
+@   clear a glue spec and set |arith_error|
+
+@c
+#define glue_error(A) do { \
+    arith_error=true; \
+    reset_glue_to_zero(A); \
+} while (0)
+
+#define normalize_glue(A) do { \
+    if (stretch(A)==0) stretch_order(A)=normal; \
+    if (shrink(A)==0) shrink_order(A)=normal; \
+} while (0)
+
+@ Parenthesized subexpressions can be inside expressions, and this
+nesting has a stack.  Seven local variables represent the top of the
+expression stack:  |p| points to pushed-down entries, if any; |l|
+specifies the type of expression currently beeing evaluated; |e| is the
+expression so far and |r| is the state of its evaluation; |t| is the
+term so far and |s| is the state of its evaluation; finally |n| is the
+numerator for a combined multiplication and division, if any.
+
+@c
+#define expr_add_sub(A,B,C) add_or_sub((A),(B),(C),(r==expr_sub))
+#define expr_a(A,B) expr_add_sub((A),(B),max_dimen)
+
+@
+  The function |add_or_sub(x,y,max_answer,negative)| computes the sum
+  (for |negative=false|) or difference (for |negative=true|) of |x| and
+  |y|, provided the absolute value of the result does not exceed
+  |max_answer|.
+
+@c
+inline static int add_or_sub(int x, int y, int max_answer, boolean negative)
+{
+    int a;                      /* the answer */
+    if (negative)
+        negate(y);
+    if (x >= 0) {
+        if (y <= max_answer - x)
+            a = x + y;
+        else
+            num_error(a);
+    } else if (y >= -max_answer - x) {
+        a = x + y;
+    } else {
+        num_error(a);
+    }
+    return a;
+}
+
+#define expr_m(A) A = nx_plus_y((A),f,0)
+#define expr_d(A) A=quotient((A),f)
+
+@ The function |quotient(n,d)| computes the rounded quotient
+$q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive.
+
+@c
+inline static int quotient(int n, int d)
+{
+    boolean negative;           /* should the answer be negated? */
+    int a;                      /* the answer */
+    if (d == 0) {
+        num_error(a);
+    } else {
+        if (d > 0) {
+            negative = false;
+        } else {
+            negate(d);
+            negative = true;
+        }
+        if (n < 0) {
+            negate(n);
+            negative = !negative;
+        }
+        a = n / d;
+        n = n - a * d;
+        d = n - d;              /* avoid certain compiler optimizations! */
+        if (d + n >= 0)
+            incr(a);
+        if (negative)
+            negate(a);
+    }
+    return a;
+}
+
+#define expr_s(A) A=fract((A),n,f,max_dimen)
+
+@ Finally, the function |fract(x,n,d,max_answer)| computes the integer
+$q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive
+and the result does not exceed |max_answer|.  We can't use floating
+point arithmetic since the routine must produce identical results in all
+cases; and it would be too dangerous to multiply by~|n| and then divide
+by~|d|, in separate operations, since overflow might well occur.  Hence
+this subroutine simulates double precision arithmetic, somewhat
+analogous to Metafont's |make_fraction| and |take_fraction| routines.
+
+@c
+int fract(int x, int n, int d, int max_answer)
+{
+    boolean negative;           /* should the answer be negated? */
+    int a;                      /* the answer */
+    int f;                      /* a proper fraction */
+    int h;                      /* smallest integer such that |2*h>=d| */
+    int r;                      /* intermediate remainder */
+    int t;                      /* temp variable */
+    if (d == 0)
+        goto TOO_BIG;
+    a = 0;
+    if (d > 0) {
+        negative = false;
+    } else {
+        negate(d);
+        negative = true;
+    }
+    if (x < 0) {
+        negate(x);
+        negative = !negative;
+    } else if (x == 0) {
+        goto DONE;
+    }
+    if (n < 0) {
+        negate(n);
+        negative = !negative;
+    }
+    t = n / d;
+    if (t > max_answer / x)
+        goto TOO_BIG;
+    a = t * x;
+    n = n - t * d;
+    if (n == 0)
+        goto FOUND;
+    t = x / d;
+    if (t > (max_answer - a) / n)
+        goto TOO_BIG;
+    a = a + t * n;
+    x = x - t * d;
+    if (x == 0)
+        goto FOUND;
+    if (x < n) {
+        t = x;
+        x = n;
+        n = t;
+    }
+    /* now |0<n<=x<d| */
+    /* Compute $f=\lfloor xn/d+{1\over2}\rfloor$; */
+    /* The loop here preserves the following invariant relations
+       between |f|, |x|, |n|, and~|r|:
+       (i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor x_0n_0/d+{1\over2}\rfloor$;
+       (ii)~|-d<=r<0<n<=x<d|, where $x_0$, $n_0$ are the original values of~$x$
+       and $n$. */
+    /* Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|,
+       because the latter could overflow. */
+    f = 0;
+    r = (d / 2) - d;
+    h = -r;
+    while (1) {
+        if (odd(n)) {
+            r = r + x;
+            if (r >= 0) {
+                r = r - d;
+                incr(f);
+            }
+        }
+        n = n / 2;
+        if (n == 0)
+            break;
+        if (x < h) {
+            x = x + x;
+        } else {
+            t = x - d;
+            x = t + x;
+            f = f + n;
+            if (x < n) {
+                if (x == 0)
+                    break;
+                t = x;
+                x = n;
+                n = t;
+            }
+        }
+    }
+
+    if (f > (max_answer - a))
+        goto TOO_BIG;
+    a = a + f;
+  FOUND:
+    if (negative)
+        negate(a);
+    goto DONE;
+  TOO_BIG:
+    num_error(a);
+  DONE:
+    return a;
+}
+
+@ @c
+static void scan_expr(void)
+{                               /* scans and evaluates an expression */
+    boolean a, b;               /* saved values of |arith_error| */
+    int l;                      /* type of expression */
+    int r;                      /* state of expression so far */
+    int s;                      /* state of term so far */
+    int o;                      /* next operation or type of next factor */
+    int e;                      /* expression so far */
+    int t;                      /* term so far */
+    int f;                      /* current factor */
+    int n;                      /* numerator of combined multiplication and division */
+    halfword p;                 /* top of expression stack */
+    halfword q;                 /* for stack manipulations */
+    l = cur_val_level;
+    a = arith_error;
+    b = false;
+    p = null;
+    /* Scan and evaluate an expression |e| of type |l| */
+  RESTART:
+    r = expr_none;
+    e = 0;
+    s = expr_none;
+    t = 0;
+    n = 0;
+  CONTINUE:
+    if (s == expr_none)
+        o = l;
+    else
+        o = int_val_level;
+    /* Scan a factor |f| of type |o| or start a subexpression */
+    /* Get the next non-blank non-call token */
+    do {
+        get_x_token();
+    } while (cur_cmd == spacer_cmd);
+
+    if (cur_tok == other_token + '(') {
+        /* Push the expression stack and |goto restart| */
+        q = new_node(expr_node, 0);
+        vlink(q) = p;
+        expr_type(q) = (quarterword) l;
+        expr_state(q) = (quarterword) (4 * s + r);
+        expr_e_field(q) = e;
+        expr_t_field(q) = t;
+        expr_n_field(q) = n;
+        p = q;
+        l = o;
+        goto RESTART;
+    }
+    back_input();
+    if ((o == int_val_level) || (o == attr_val_level))
+        scan_int();
+    else if (o == dimen_val_level)
+        scan_normal_dimen();
+    else if (o == glue_val_level)
+        scan_normal_glue();
+    else
+        scan_mu_glue();
+    f = cur_val;
+
+  FOUND:
+    /* Scan the next operator and set |o| */
+    /* Get the next non-blank non-call token */
+    do {
+        get_x_token();
+    } while (cur_cmd == spacer_cmd);
+
+    if (cur_tok == other_token + '+') {
+        o = expr_add;
+    } else if (cur_tok == other_token + '-') {
+        o = expr_sub;
+    } else if (cur_tok == other_token + '*') {
+        o = expr_mult;
+    } else if (cur_tok == other_token + '/') {
+        o = expr_div;
+    } else {
+        o = expr_none;
+        if (p == null) {
+            if (cur_cmd != relax_cmd)
+                back_input();
+        } else if (cur_tok != other_token + ')') {
+            print_err("Missing ) inserted for expression");
+            help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't.");
+            back_error();
+        }
+    }
+
+    arith_error = b;
+    /* Make sure that |f| is in the proper range */
+    if (((l == int_val_level) || (l == attr_val_level)) || (s > expr_sub)) {
+        if ((f > infinity) || (f < -infinity))
+            num_error(f);
+    } else if (l == dimen_val_level) {
+        if (abs(f) > max_dimen)
+            num_error(f);
+    } else {
+        if ((abs(width(f)) > max_dimen) || (abs(stretch(f)) > max_dimen) || (abs(shrink(f)) > max_dimen))
+            glue_error(f);
+    }
+
+    switch (s) {
+        /* Cases for evaluation of the current term */
+    case expr_none:
+        /*
+           Applying the factor |f| to the partial term |t| (with the operator
+           |s|) is delayed until the next operator |o| has been scanned.  Here we
+           handle the first factor of a partial term.  A glue spec has to be copied
+           unless the next operator is a right parenthesis; this allows us later on
+           to simply modify the glue components.
+         */
+        t = f;
+        if ((l >= glue_val_level) && (o != expr_none)) {
+	        /* do we really need to copy here ? */
+//            t = new_spec(f);
+//            flush_node(f);
+            normalize_glue(t);
+        } else {
+            t = f;
+        }
+        break;
+    case expr_mult:
+        /* If a multiplication is followed by a division, the two operations are
+           combined into a `scaling' operation.  Otherwise the term |t| is
+           multiplied by the factor |f|. */
+        if (o == expr_div) {
+            n = f;
+            o = expr_scale;
+        } else if ((l == int_val_level) || (l == attr_val_level)) {
+            t = mult_integers(t, f);
+        } else if (l == dimen_val_level) {
+            expr_m(t);
+        } else {
+            expr_m(width(t));
+            expr_m(stretch(t));
+            expr_m(shrink(t));
+        }
+        break;
+    case expr_div:
+        /* Here we divide the term |t| by the factor |f| */
+        if (l < glue_val_level) {
+            expr_d(t);
+        } else {
+            expr_d(width(t));
+            expr_d(stretch(t));
+            expr_d(shrink(t));
+        }
+        break;
+    case expr_scale:
+        /* Here the term |t| is multiplied by the quotient $n/f$. */
+        if ((l == int_val_level) || (l == attr_val_level)) {
+            t = fract(t, n, f, infinity);
+        } else if (l == dimen_val_level) {
+            expr_s(t);
+        } else {
+            expr_s(width(t));
+            expr_s(stretch(t));
+            expr_s(shrink(t));
+        }
+        break;
+    }                           /* there are no other cases */
+    if (o > expr_sub) {
+        s = o;
+    } else {
+        /* Evaluate the current expression */
+        /* When a term |t| has been completed it is copied to, added to, or
+           subtracted from the expression |e|. */
+        s = expr_none;
+        if (r == expr_none) {
+            e = t;
+        } else if ((l == int_val_level) || (l == attr_val_level)) {
+            e = expr_add_sub(e, t, infinity);
+        } else if (l == dimen_val_level) {
+            e = expr_a(e, t);
+        } else {
+            /* Compute the sum or difference of two glue specs */
+            /* We know that |stretch_order(e)>normal| implies |stretch(e)<>0| and
+               |shrink_order(e)>normal| implies |shrink(e)<>0|. */
+            width(e) = expr_a(width(e), width(t));
+            if (stretch_order(e) == stretch_order(t)) {
+                stretch(e) = expr_a(stretch(e), stretch(t));
+            } else if ((stretch_order(e) < stretch_order(t)) && (stretch(t) != 0)) {
+                stretch(e) = stretch(t);
+                stretch_order(e) = stretch_order(t);
+            }
+            if (shrink_order(e) == shrink_order(t)) {
+                shrink(e) = expr_a(shrink(e), shrink(t));
+            } else if ((shrink_order(e) < shrink_order(t)) && (shrink(t) != 0)) {
+                shrink(e) = shrink(t);
+                shrink_order(e) = shrink_order(t);
+            }
+            flush_node(t);
+            normalize_glue(e);
+        }
+        r = o;
+    }
+    b = arith_error;
+    if (o != expr_none)
+        goto CONTINUE;
+    if (p != null) {
+        /* Pop the expression stack and |goto found| */
+        f = e;
+        q = p;
+        e = expr_e_field(q);
+        t = expr_t_field(q);
+        n = expr_n_field(q);
+        s = expr_state(q) / 4;
+        r = expr_state(q) % 4;
+        l = expr_type(q);
+        p = vlink(q);
+        flush_node(q);
+        goto FOUND;
+    }
+
+    if (b) {
+        print_err("Arithmetic overflow");
+        help2("I can't evaluate this expression,",
+              "since the result is out of range.");
+        error();
+        if (l >= glue_val_level) {
+            reset_glue_to_zero(e);
+        } else {
+            e = 0;
+        }
+    }
+    arith_error = a;
+    cur_val = e;
+    cur_val_level = l;
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/stringpool.w
@@ -0,0 +1,353 @@
+% stringpool.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"
+
+@ Control sequence names and diagnostic messages are variable-length strings
+of eight-bit characters. Since PASCAL did not have a well-developed string
+mechanism, \TeX\ did all of its string processing by homegrown methods.
+
+Elaborate facilities for dynamic strings are not needed, so all of the
+necessary operations can be handled with a simple data structure.
+The array |str_pool| contains all of the (eight-bit) bytes off all
+of the strings, and the array |str_start| contains indices of the starting
+points of each string. Strings are referred to by integer numbers, so that
+string number |s| comprises the characters |str_pool[j]| for
+|str_start_macro(s)<=j<str_start_macro(s+1)|. Additional integer variables
+|pool_ptr| and |str_ptr| indicate the number of entries used so far
+in |str_pool| and |str_start|, respectively; locations
+|str_pool[pool_ptr]| and |str_start_macro(str_ptr)| are
+ready for the next string to be allocated.
+
+String numbers 0 to |biggest_char| are reserved for strings that correspond to 
+single UNICODE characters. This is in accordance with the conventions of \.{WEB}
+which converts single-character strings into the ASCII code number of the
+single character involved.
+
+@c
+lstring *string_pool;           /* the array of strings */
+lstring *_string_pool;          /* this variable lives |STRING_OFFSET| below |string_pool| 
+                                   (handy for debugging: 
+                                   |_string_pool[str_ptr] == str_string(str_ptr)| */
+
+str_number str_ptr = (STRING_OFFSET + 1);       /* number of the current string being created */
+str_number init_str_ptr;        /* the starting value of |str_ptr| */
+
+unsigned char *cur_string;      /*  current string buffer */
+unsigned cur_length;            /* current index in that buffer */
+unsigned cur_string_size;       /*  malloced size of |cur_string| */
+unsigned pool_size;             /* occupied byte count */
+
+
+@ Once a sequence of characters has been appended to |cur_string|, it
+officially becomes a string when the function |make_string| is called.
+This function returns the identification number of the new string as its
+value.
+
+@c
+void reset_cur_string(void)
+{
+    cur_length = 0;
+    cur_string_size = 255;
+    cur_string = (unsigned char *) xmalloc(256);
+    memset(cur_string, 0, 256);
+}
+
+@  current string enters the pool 
+@c
+str_number make_string(void)
+{
+    if (str_ptr == (max_strings + STRING_OFFSET))
+        overflow("number of strings",
+                 (unsigned) (max_strings - init_str_ptr + STRING_OFFSET));
+    str_room(1);
+    cur_string[cur_length] = '\0';      /* now |lstring.s| is always a valid C string */
+    str_string(str_ptr) = (unsigned char *) cur_string;
+    str_length(str_ptr) = cur_length;
+    pool_size += cur_length;
+    reset_cur_string();
+#if 0
+    printf("Made a string: %s (s=%d)\n", (char *)str_string(str_ptr), (int)str_ptr);
+#endif
+    str_ptr++;
+    return (str_ptr - 1);
+}
+
+@ @c
+int pool_to_unichar(unsigned char *t)
+{
+    return (int) str2uni(t);
+}
+
+
+
+@ The following subroutine compares string |s| with another string of the
+same length that appears in |buffer| starting at position |k|;
+the result is |true| if and only if the strings are equal.
+Empirical tests indicate that |str_eq_buf| is used in such a way that
+it tends to return |true| about 80 percent of the time.
+
+@c
+boolean str_eq_buf(str_number s, int k)
+{                               /* test equality of strings */
+    int a;                      /* a unicode character */
+    if (s < STRING_OFFSET) {
+        a = buffer_to_unichar(k);
+        if (a != s)
+            return false;
+    } else {
+        unsigned char *j = str_string(s);
+        unsigned char *l = j + str_length(s);
+        while (j < l) {
+            if (*j++ != buffer[k++])
+                return false;
+        }
+    }
+    return true;
+}
+
+
+@ Here is a similar routine, but it compares two strings in the string pool,
+and it does not assume that they have the same length.
+
+@c
+boolean str_eq_str(str_number s, str_number t)
+{                               /* test equality of strings */
+    int a = 0;                  /* a utf char */
+    unsigned char *j, *k, *l;   /* running indices */
+    if (s < STRING_OFFSET) {
+        if (t >= STRING_OFFSET) {
+            k = str_string(t);
+            if (s <= 0x7F && (str_length(t) == 1) && *k == s)
+                return true;
+            a = pool_to_unichar(k);
+            if (a != s)
+                return false;
+        } else {
+            if (t != s)
+                return false;
+        }
+    } else if (t < STRING_OFFSET) {
+        j = str_string(s);
+        if (t <= 0x7F && (str_length(s) == 1) && *j == t)
+            return true;
+        a = pool_to_unichar(j);
+        if (a != t)
+            return false;
+    } else {
+        if (str_length(s) != str_length(t))
+            return false;
+        k = str_string(t);
+        j = str_string(s);
+        l = j + str_length(s);
+        while (j < l) {
+            if (*j++ != *k++)
+                return false;
+        }
+    }
+    return true;
+}
+
+@ string compare 
+@c
+boolean str_eq_cstr(str_number r, const char *s, size_t l)
+{
+    if (l != (size_t) str_length(r))
+        return false;
+    return (strncmp((const char *) (str_string(r)), s, l) == 0);
+}
+
+
+@ The initial values of |str_pool|, |str_start|, |pool_ptr|,
+and |str_ptr| are computed by the \.{INITEX} program, based in part
+on the information that \.{WEB} has output while processing \TeX.
+
+The first |string_offset| strings are single-characters strings matching
+Unicode. There is no point in generating all of these. But |str_ptr| has
+initialized properly, otherwise |print_char| cannot see the difference
+between characters and strings.
+
+
+@ initializes the string pool, but returns |false| if something goes wrong 
+@c
+boolean get_strings_started(void)
+{
+    reset_cur_string();
+    return true;
+}
+
+@ The string recycling routines.
+   \TeX{} uses 2 upto 4 {\it new\/} strings when scanning a filename in an
+   \.{\\input}, \.{\\openin}, or \.{\\openout} operation.  These strings are
+   normally lost because the reference to them are not saved after finishing
+   the operation.  |search_string| searches through the string pool for the
+   given string and returns either 0 or the found string number.
+
+@c
+str_number search_string(str_number search)
+{
+    str_number s;               /* running index */
+    size_t len;                 /* length of searched string */
+    len = str_length(search);
+    if (len == 0) {
+        return get_nullstr();
+    } else {
+        s = search - 1;         /* start search with newest string below |s|; |search>1|! */
+        while (s >= STRING_OFFSET) {
+            /* first |string_offset| strings depend on implementation!! */
+            if (str_length(s) == len)
+                if (str_eq_str(s, search))
+                    return s;
+            s--;
+        }
+    }
+    return 0;
+}
+
+@ @c
+str_number maketexstring(const char *s)
+{
+    if (s == NULL || *s == 0)
+        return get_nullstr();
+    return maketexlstring(s, strlen(s));
+}
+
+@ @c
+str_number maketexlstring(const char *s, size_t l)
+{
+    if (s == NULL || l == 0)
+        return get_nullstr();
+    str_string(str_ptr) = xmalloc((unsigned) (l + 1));
+    memcpy(str_string(str_ptr), s, (l + 1));
+    str_length(str_ptr) = (unsigned) l;
+    str_ptr++;
+    return (str_ptr - 1);
+}
+
+@ append a C string to a TeX string
+@c
+void append_string(const unsigned char *s, unsigned l)
+{
+    if (s == NULL || *s == 0)
+        return;
+    l = (unsigned) strlen((const char *) s);
+    str_room(l);
+    memcpy(cur_string + cur_length, s, l);
+    cur_length += l;
+    return;
+}
+
+@ @c
+char *makecstring(int s)
+{
+    size_t l;
+    return makeclstring(s, &l);
+}
+
+@ @c
+char *makeclstring(int s, size_t * len)
+{
+    if (s < STRING_OFFSET) {
+        *len = (size_t) utf8_size(s);
+        return (char *) uni2str((unsigned) s);
+    } else {
+        unsigned l = (unsigned) str_length(s);
+        char *cstrbuf = xmalloc(l + 1);
+        memcpy(cstrbuf, str_string(s), l);
+        cstrbuf[l] = '\0';
+        *len = (size_t) l;
+        return cstrbuf;
+    }
+}
+
+@ @c
+int dump_string_pool(void)
+{
+    int j;
+    int l;
+    int k = str_ptr;
+    dump_int(k - STRING_OFFSET);
+    for (j = STRING_OFFSET + 1; j < k; j++) {
+        l = (int) str_length(j);
+        if (str_string(j) == NULL)
+            l = -1;
+        dump_int(l);
+        if (l > 0)
+            dump_things(*str_string(j), str_length(j));
+    }
+    return (k - STRING_OFFSET);
+}
+
+@ @c
+int undump_string_pool(void)
+{
+    int j;
+    int x;
+    undump_int(str_ptr);
+    if (max_strings < str_ptr + strings_free)
+        max_strings = str_ptr + strings_free;
+    str_ptr += STRING_OFFSET;
+    if (ini_version)
+        libcfree(string_pool);
+    init_string_pool_array((unsigned) max_strings);
+    for (j = STRING_OFFSET + 1; j < str_ptr; j++) {
+        undump_int(x);
+        if (x >= 0) {
+            str_length(j) = (unsigned) x;
+            pool_size += (unsigned) x;
+            str_string(j) = xmallocarray(unsigned char, (unsigned) (x + 1));
+            undump_things(*str_string(j), (unsigned) x);
+            *(str_string(j) + str_length(j)) = '\0';
+        } else {
+            str_length(j) = 0;
+        }
+    }
+    init_str_ptr = str_ptr;
+    return str_ptr;
+}
+
+@ @c
+void init_string_pool_array(unsigned s)
+{
+    string_pool = xmallocarray(lstring, s);
+    _string_pool = string_pool - STRING_OFFSET;
+    memset(string_pool, 0, s * sizeof(lstring));
+    /* seed the null string */
+    string_pool[0].s = xmalloc(1);
+    string_pool[0].s[0] = '\0';
+}
+
+@ To destroy an already made string, we say |flush_str|. 
+@c
+void flush_str(str_number s)
+{
+#if 0	
+    printf("Flushing a string: %s (s=%d,str_ptr=%d)\n", (char *)str_string(s), (int)s, (int)str_ptr); 
+#endif
+    if (s > STRING_OFFSET) {    /* don't ever delete the null string */
+        pool_size -= (unsigned) str_length(s);
+        str_length(s) = 0;
+        xfree(str_string(s));
+    }
+    while (str_string((str_ptr - 1)) == NULL)
+        str_ptr--;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/stringpool.c
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
-
-stringpool.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-Control sequence names and diagnostic messages are variable-length strings of
-eight-bit characters. Since PASCAL did not have a well-developed string
-mechanism, \TeX\ did all of its string processing by homegrown methods.
-
-Elaborate facilities for dynamic strings are not needed, so all of the necessary
-operations can be handled with a simple data structure. The array |str_pool|
-contains all of the (eight-bit) bytes off all of the strings, and the array
-|str_start| contains indices of the starting points of each string. Strings are
-referred to by integer numbers, so that string number |s| comprises the
-characters |str_pool[j]| for |str_start_macro(s)<=j<str_start_macro(s+1)|.
-Additional integer variables |pool_ptr| and |str_ptr| indicate the number of
-entries used so far in |str_pool| and |str_start|, respectively; locations
-|str_pool[pool_ptr]| and |str_start_macro(str_ptr)| are ready for the next string
-to be allocated.
-
-String numbers 0 to |biggest_char| are reserved for strings that correspond to
-single UNICODE characters. This is in accordance with the conventions of \.{WEB}
-which converts single-character strings into the ASCII code number of the single
-character involved.
-
-*/
-
-/*tex The array of strings: */
-
-lstring *string_pool;
-
-/*tex
-    This variable lives |STRING_OFFSET| below |string_pool| (handy for debugging:
-    |_string_pool[str_ptr] == str_string(str_ptr)|:
-*/
-
-lstring *_string_pool;
-
-/*tex The number of the current string being created: */
-
-str_number str_ptr = (STRING_OFFSET + 1);
-
-/*tex The starting value of |str_ptr|: */
-
-str_number init_str_ptr;
-
-/*tex
-    The current string buffer, the current index in that buffer, the malloced
-    size of |cur_string| and the occupied byte count:
-*/
-
-unsigned char *cur_string;
-unsigned cur_length;
-unsigned cur_string_size;
-unsigned pool_size;
-
-/*tex
-
-Once a sequence of characters has been appended to |cur_string|, it officially
-becomes a string when the function |make_string| is called. This function returns
-the identification number of the new string as its value.
-
-*/
-
-void reset_cur_string(void)
-{
-    cur_length = 0;
-    cur_string_size = 255;
-    cur_string = (unsigned char *) xmalloc(256);
-    memset(cur_string, 0, 256);
-}
-
-str_number make_string(void)
-{
-    if (str_ptr == (max_strings + STRING_OFFSET)) {
-        overflow(
-            "number of strings",
-             (unsigned) (max_strings - init_str_ptr + STRING_OFFSET)
-        );
-    }
-    str_room(1);
-    cur_string[cur_length] = '\0';      /* now |lstring.s| is always a valid C string */
-    str_string(str_ptr) = (unsigned char *) cur_string;
-    str_length(str_ptr) = cur_length;
-    pool_size += cur_length;
-    reset_cur_string();
-    str_ptr++;
-    return (str_ptr - 1);
-}
-
-int pool_to_unichar(unsigned char *t)
-{
-    return (int) str2uni(t);
-}
-
-/*tex
-
-The following subroutine compares string |s| with another string of the same
-length that appears in |buffer| starting at position |k|; the result is |true| if
-and only if the strings are equal. Empirical tests indicate that |str_eq_buf| is
-used in such a way that it tends to return |true| about 80 percent of the time.
-
-*/
-
-boolean str_eq_buf(str_number s, int k)
-{
-    int a;
-    if (s < STRING_OFFSET) {
-        a = buffer_to_unichar(k);
-        if (a != s)
-            return false;
-    } else {
-        unsigned char *j = str_string(s);
-        unsigned char *l = j + str_length(s);
-        while (j < l) {
-            if (*j++ != buffer[k++])
-                return false;
-        }
-    }
-    return true;
-}
-
-/*tex
-
-Here is a similar routine, but it compares two strings in the string pool, and it
-does not assume that they have the same length.
-
-*/
-
-boolean str_eq_str(str_number s, str_number t)
-{
-    int a = 0;
-    unsigned char *j, *k, *l;
-    if (s < STRING_OFFSET) {
-        if (t >= STRING_OFFSET) {
-            k = str_string(t);
-            if (s <= 0x7F && (str_length(t) == 1) && *k == s)
-                return true;
-            a = pool_to_unichar(k);
-            if (a != s)
-                return false;
-        } else {
-            if (t != s)
-                return false;
-        }
-    } else if (t < STRING_OFFSET) {
-        j = str_string(s);
-        if (t <= 0x7F && (str_length(s) == 1) && *j == t)
-            return true;
-        a = pool_to_unichar(j);
-        if (a != t)
-            return false;
-    } else {
-        if (str_length(s) != str_length(t))
-            return false;
-        k = str_string(t);
-        j = str_string(s);
-        l = j + str_length(s);
-        while (j < l) {
-            if (*j++ != *k++)
-                return false;
-        }
-    }
-    return true;
-}
-
-/*tex A string compare helper: */
-
-boolean str_eq_cstr(str_number r, const char *s, size_t l)
-{
-    if (l != (size_t) str_length(r))
-        return false;
-    return (strncmp((const char *) (str_string(r)), s, l) == 0);
-}
-
-
-/*tex
-
-The initial values of |str_pool|, |str_start|, |pool_ptr|, and |str_ptr| are
-computed by the \.{INITEX} program, based in part on the information that \.{WEB}
-has output while processing \TeX.
-
-The first |string_offset| strings are single-characters strings matching Unicode.
-There is no point in generating all of these. But |str_ptr| has initialized
-properly, otherwise |print_char| cannot see the difference between characters and
-strings.
-
-*/
-
-boolean get_strings_started(void)
-{
-    reset_cur_string();
-    return true;
-}
-
-/*tex
-
-The string recycling routines. \TeX{} uses 2 upto 4 {\it new\/} strings when
-scanning a filename in an \.{\\input}, \.{\\openin}, or \.{\\openout} operation.
-These strings are normally lost because the reference to them are not saved after
-finishing the operation. |search_string| searches through the string pool for the
-given string and returns either 0 or the found string number.
-
-*/
-
-str_number search_string(str_number search)
-{
-    str_number s;
-    size_t len;
-    len = str_length(search);
-    if (len == 0) {
-        return get_nullstr();
-    } else {
-        /*tex We start the search with newest string below |s|; |search>1|! */
-        s = search - 1;
-        while (s >= STRING_OFFSET) {
-            /* The first |string_offset| of strings depend on the implementation! */
-            if (str_length(s) == len)
-                if (str_eq_str(s, search))
-                    return s;
-            s--;
-        }
-    }
-    return 0;
-}
-
-str_number maketexstring(const char *s)
-{
-    if (s == NULL || *s == 0)
-        return get_nullstr();
-    return maketexlstring(s, strlen(s));
-}
-
-str_number maketexlstring(const char *s, size_t l)
-{
-    if (s == NULL || l == 0)
-        return get_nullstr();
-    str_string(str_ptr) = xmalloc((unsigned) (l + 1));
-    memcpy(str_string(str_ptr), s, (l + 1));
-    str_length(str_ptr) = (unsigned) l;
-    str_ptr++;
-    return (str_ptr - 1);
-}
-
-/*tex
-
-    This appends a C string to a \TEX\ string:
-
-*/
-
-void append_string(const unsigned char *s, unsigned l)
-{
-    if (s == NULL || *s == 0)
-        return;
-    l = (unsigned) strlen((const char *) s);
-    str_room(l);
-    memcpy(cur_string + cur_length, s, l);
-    cur_length += l;
-    return;
-}
-
-char *makecstring(int s)
-{
-    size_t l;
-    return makeclstring(s, &l);
-}
-
-char *makeclstring(int s, size_t * len)
-{
-    if (s < STRING_OFFSET) {
-        *len = (size_t) utf8_size(s);
-        return (char *) uni2str((unsigned) s);
-    } else {
-        unsigned l = (unsigned) str_length(s);
-        char *cstrbuf = xmalloc(l + 1);
-        memcpy(cstrbuf, str_string(s), l);
-        cstrbuf[l] = '\0';
-        *len = (size_t) l;
-        return cstrbuf;
-    }
-}
-
-int dump_string_pool(void)
-{
-    int j;
-    int l;
-    int k = str_ptr;
-    dump_int(k - STRING_OFFSET);
-    for (j = STRING_OFFSET + 1; j < k; j++) {
-        l = (int) str_length(j);
-        if (str_string(j) == NULL)
-            l = -1;
-        dump_int(l);
-        if (l > 0)
-            dump_things(*str_string(j), str_length(j));
-    }
-    return (k - STRING_OFFSET);
-}
-
-int undump_string_pool(void)
-{
-    int j;
-    int x;
-    undump_int(str_ptr);
-    if (max_strings < str_ptr + strings_free)
-        max_strings = str_ptr + strings_free;
-    str_ptr += STRING_OFFSET;
-    if (ini_version)
-        libcfree(string_pool);
-    init_string_pool_array((unsigned) max_strings);
-    for (j = STRING_OFFSET + 1; j < str_ptr; j++) {
-        undump_int(x);
-        if (x >= 0) {
-            str_length(j) = (unsigned) x;
-            pool_size += (unsigned) x;
-            str_string(j) = xmallocarray(unsigned char, (unsigned) (x + 1));
-            undump_things(*str_string(j), (unsigned) x);
-            *(str_string(j) + str_length(j)) = '\0';
-        } else {
-            str_length(j) = 0;
-        }
-    }
-    init_str_ptr = str_ptr;
-    return str_ptr;
-}
-
-void init_string_pool_array(unsigned s)
-{
-    string_pool = xmallocarray(lstring, s);
-    _string_pool = string_pool - STRING_OFFSET;
-    memset(string_pool, 0, s * sizeof(lstring));
-    /* seed the null string */
-    string_pool[0].s = xmalloc(1);
-    string_pool[0].s[0] = '\0';
-}
-
-/*tex
-
-    To destroy an already made string, we say |flush_str|.
-
-*/
-
-void flush_str(str_number s)
-{
-    if (s > STRING_OFFSET) {
-        /*tex Don't ever delete the null string! */
-        pool_size -= (unsigned) str_length(s);
-        str_length(s) = 0;
-        xfree(str_string(s));
-    }
-    while (str_string((str_ptr - 1)) == NULL)
-        str_ptr--;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/texdeffont.w
@@ -0,0 +1,188 @@
+% texdeffont.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"
+
+@ When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number
+to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the
+|eqtb| location of a ``frozen'' control sequence that will always select
+the font.
+
+@c
+int font_bytes;
+
+void set_cur_font(internal_font_number f)
+{
+    int a = 0;                  /* never global */
+    define(cur_font_loc, data_cmd, f);
+}
+
+@ @c
+static char *scaled_to_string(scaled s)
+{                               /* prints scaled real, rounded to five digits */
+    static char result[16];
+    int n, k;
+    scaled delta;               /* amount of allowable inaccuracy */
+    k = 0;
+    if (s < 0) {
+        result[k++] = '-';
+        s = -s;                 /* print the sign, if negative */
+    }
+    {
+        int l = 0;
+        char digs[8] = { 0 };
+        n = s / unity;
+        /* process the integer part */
+        do {
+            digs[l++] = (char) (n % 10);
+            n = n / 10;;
+        } while (n > 0);
+        while (l > 0) {
+            result[k++] = (char) (digs[--l] + '0');
+        }
+    }
+    result[k++] = '.';
+    s = 10 * (s % unity) + 5;
+    delta = 10;
+    do {
+        if (delta > unity)
+            s = s + 0100000 - 050000;   /* round the last digit */
+        result[k++] = (char) ('0' + (s / unity));
+        s = 10 * (s % unity);
+        delta = delta * 10;
+    } while (s > delta);
+
+    result[k] = 0;
+    return (char *) result;
+}
+
+@ @c
+void tex_def_font(small_number a)
+{
+    pointer u;                  /* user's font identifier */
+    internal_font_number f;     /* runs through existing fonts */
+    str_number t;               /* name for the frozen font identifier */
+    int old_setting;            /* holds |selector| setting */
+    scaled s = -1000;           /* stated ``at'' size, or negative of scaled magnification */
+    int natural_dir = -1;       /* the natural direction of the font */
+    char *fn;
+    if (job_name == 0)
+        open_log_file();        /* avoid confusing \.{texput} with the font name */
+    get_r_token();
+    u = cur_cs;
+    if (u >= null_cs)
+        t = cs_text(u);
+    else
+        t = maketexstring("FONT");
+    if (a >= 4) {
+        geq_define(u, set_font_cmd, null_font);
+    } else {
+        eq_define(u, set_font_cmd, null_font);
+    }
+    scan_optional_equals();
+    /* Get the next non-blank non-call token; */
+    do {
+        get_x_token();
+    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+    if (cur_cmd != left_brace_cmd) {
+        back_input();
+        scan_file_name();
+        if (cur_area != get_nullstr() || cur_ext != get_nullstr()) {
+            /* Have to do some rescue-ing here, fonts only have a name,
+               no area nor extension */
+            old_setting = selector;
+            selector = new_string;
+            if (cur_area != get_nullstr()) {
+                print(cur_area);
+            }
+            if (cur_name != get_nullstr()) {
+                print(cur_name);
+            }
+            if (cur_ext != get_nullstr()) {
+                print(cur_ext);
+            }
+            selector = old_setting;
+            cur_name = make_string();
+            cur_ext = get_nullstr();
+            cur_area = get_nullstr();
+        }
+    } else {
+        back_input();
+        (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)|; *//* what did that do ? */
+        cur_name = make_string();
+        cur_ext = get_nullstr();
+        cur_area = get_nullstr();
+    }
+    /* Scan the font size specification; */
+    name_in_progress = true;    /* this keeps |cur_name| from being changed */
+    if (scan_keyword("at")) {
+        /* Put the positive `at' size into |s| */
+        scan_normal_dimen();
+        s = cur_val;
+        if ((s <= 0) || (s >= 01000000000)) {
+            char err[256];
+            const char *errhelp[] =
+                { "I can only handle fonts at positive sizes that are",
+                "less than 2048pt, so I've changed what you said to 10pt.",
+                NULL
+            };
+            snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt",
+                     scaled_to_string(s));
+            tex_error(err, errhelp);
+            s = 10 * unity;
+        }
+    } else if (scan_keyword("scaled")) {
+        scan_int();
+        s = -cur_val;
+        if ((cur_val <= 0) || (cur_val > 32768)) {
+            char err[256];
+            const char *errhelp[] =
+                { "The magnification ratio must be between 1 and 32768.",
+                NULL
+            };
+            snprintf(err, 255,
+                     "Illegal magnification has been changed to 1000 (%d)",
+                     (int) cur_val);
+            tex_error(err, errhelp);
+            s = -1000;
+        }
+    }
+    if (scan_keyword("naturaldir")) {
+        scan_direction();
+        natural_dir = cur_val;
+    }
+    name_in_progress = false;
+    fn = makecstring(cur_name);
+    f = read_font_info(u, fn, s, natural_dir);
+    xfree(fn);
+    equiv(u) = f;
+
+    eqtb[font_id_base + f] = eqtb[u];
+    cs_text(font_id_base + f) = t;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/texdeffont.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
-
-texdeffont.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number to the
-user's font~\.{\\f}. Adding this number to |font_id_base| gives the |eqtb|
-location of a ``frozen'' control sequence that will always select the font.
-
-The variable |a| in the following code indicates the global nature of the value
-to be set. It's used in the |define| macro. Here we're never global.
-
-There's not much scanner code here because the other scanners are defined where
-they make most sense.
-
-*/
-
-int font_bytes;
-
-void set_cur_font(internal_font_number f)
-{
-    int a = 0;
-    define(cur_font_loc, data_cmd, f);
-}
-
-/*tex
-
-    This prints a scaled real, rounded to five digits.
-
-*/
-
-static char *scaled_to_string(scaled s)
-{
-    static char result[16];
-    int n, k;
-    /*tex The amount of allowable inaccuracy: */
-    scaled delta;
-    k = 0;
-    if (s < 0) {
-        /*tex Only print the sign, if negative */
-        result[k++] = '-';
-        s = -s;
-    }
-    {
-        int l = 0;
-        char digs[8] = { 0 };
-        n = s / unity;
-        /*tex Process the integer part: */
-        do {
-            digs[l++] = (char) (n % 10);
-            n = n / 10;;
-        } while (n > 0);
-        while (l > 0) {
-            result[k++] = (char) (digs[--l] + '0');
-        }
-    }
-    result[k++] = '.';
-    s = 10 * (s % unity) + 5;
-    delta = 10;
-    do {
-        if (delta > unity) {
-            /*tex Round the last digit: */
-            s = s + 0100000 - 050000;
-        }
-        result[k++] = (char) ('0' + (s / unity));
-        s = 10 * (s % unity);
-        delta = delta * 10;
-    } while (s > delta);
-    result[k] = 0;
-    return (char *) result;
-}
-
-void tex_def_font(small_number a)
-{
-    /*tex The user's font identifier. */
-    pointer u;
-    /*tex This runs through existing fonts. */
-    internal_font_number f;
-    /*tex The name for the frozen font identifier. */
-    str_number t;
-    /*tex Thos holds the |selector| setting. */
-    int old_setting;
-    /*tex Stated `at' size, or negative of scaled magnification. */
-    scaled s = -1000;
-    /*tex The natural direction of the font. */
-    int natural_dir = -1;
-    char *fn;
-    if (job_name == 0) {
-        /*tex Avoid confusing \.{texput} with the font name. */
-        open_log_file();
-    }
-    get_r_token();
-    u = cur_cs;
-    if (u >= null_cs)
-        t = cs_text(u);
-    else
-        t = maketexstring("FONT");
-    if (a >= 4) {
-        geq_define(u, set_font_cmd, null_font);
-    } else {
-        eq_define(u, set_font_cmd, null_font);
-    }
-    scan_optional_equals();
-    /*tex Get the next non-blank non-call token. */
-    do {
-        get_x_token();
-    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
-
-    if (cur_cmd != left_brace_cmd) {
-        back_input();
-        scan_file_name();
-        if (cur_area != get_nullstr() || cur_ext != get_nullstr()) {
-            /*tex
-                Have to do some rescue-ing here, fonts only have a name, no area
-                nor extension.
-            */
-            old_setting = selector;
-            selector = new_string;
-            if (cur_area != get_nullstr()) {
-                print(cur_area);
-            }
-            if (cur_name != get_nullstr()) {
-                print(cur_name);
-            }
-            if (cur_ext != get_nullstr()) {
-                print(cur_ext);
-            }
-            selector = old_setting;
-            cur_name = make_string();
-            cur_ext = get_nullstr();
-            cur_area = get_nullstr();
-        }
-    } else {
-        back_input();
-        (void) scan_toks(false, true);
-        old_setting = selector;
-        selector = new_string;
-        token_show(def_ref);
-        selector = old_setting;
-        flush_list(def_ref);
-        cur_name = make_string();
-        cur_ext = get_nullstr();
-        cur_area = get_nullstr();
-    }
-    /*tex
-        Scan the font size specification. The next variable keeps |cur_name| from
-        being changed
-    */
-    name_in_progress = true;
-    if (scan_keyword("at")) {
-        /*tex Put the positive `at' size into |s|. */
-        scan_normal_dimen();
-        s = cur_val;
-        if ((s <= 0) || (s >= 01000000000)) {
-            char err[256];
-            const char *errhelp[] = {
-                "I can only handle fonts at positive sizes that are",
-                "less than 2048pt, so I've changed what you said to 10pt.",
-                NULL
-            };
-            snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt", scaled_to_string(s));
-            tex_error(err, errhelp);
-            s = 10 * unity;
-        }
-    } else if (scan_keyword("scaled")) {
-        scan_int();
-        s = -cur_val;
-        if ((cur_val <= 0) || (cur_val > 32768)) {
-            char err[256];
-            const char *errhelp[] = {
-                "The magnification ratio must be between 1 and 32768.",
-                NULL
-            };
-            snprintf(err, 255, "Illegal magnification has been changed to 1000 (%d)", (int) cur_val);
-            tex_error(err, errhelp);
-            s = -1000;
-        }
-    }
-    /*tex
-        There is no real reason to support this obsolete key as there are no useful
-        fonts out there so let's get rid of this overhead. This also means that
-        |natural_dir| can go away.
-    */
-    /*
-    if (scan_keyword("naturaldir")) {
-        scan_direction();
-        natural_dir = cur_val;
-    }
-    */
-    name_in_progress = false;
-    fn = makecstring(cur_name);
-    f = read_font_info(u, fn, s, natural_dir);
-    xfree(fn);
-    equiv(u) = f;
-    eqtb[font_id_base + f] = eqtb[u];
-    cs_text(font_id_base + f) = t;
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/texfileio.c
+++ /dev/null
@@ -1,1509 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#include <string.h>
-#include <kpathsea/absolute.h>
-
-/*tex
-
-The bane of portability is the fact that different operating systems treat input
-and output quite differently, perhaps because computer scientists have not given
-sufficient attention to this problem. People have felt somehow that input and
-output are not part of ``real'' programming. Well, it is true that some kinds of
-programming are more fun than others. With existing input/output conventions
-being so diverse and so messy, the only sources of joy in such parts of the code
-are the rare occasions when one can find a way to make the program a little less
-bad than it might have been. We have two choices, either to attack I/O now and
-get it over with, or to postpone I/O until near the end. Neither prospect is very
-attractive, so let's get it over with.
-
-The basic operations we need to do are (1)~inputting and outputting of text, to
-or from a file or the user's terminal; (2)~inputting and outputting of eight-bit
-bytes, to or from a file; (3)~instructing the operating system to initiate
-(``open'') or to terminate (``close'') input or output from a specified file;
-(4)~testing whether the end of an input file has been reached.
-
-\TeX\ needs to deal with two kinds of files. We shall use the term |alpha_file|
-for a file that contains textual data, and the term |byte_file| for a file that
-contains eight-bit binary information. These two types turn out to be the same on
-many computers, but sometimes there is a significant distinction, so we shall be
-careful to distinguish between them. Standard protocols for transferring such
-files from computer to computer, via high-speed networks, are now becoming
-available to more and more communities of users.
-
-The program actually makes use also of a third kind of file, called a
-|word_file|, when dumping and reloading base information for its own
-initialization. We shall define a word file later; but it will be possible for us
-to specify simple operations on word files before they are defined.
-
-We finally did away with |nameoffile| and |namelength|, but the variables have to
-be kept otherwise there will be link errors from |openclose.c| in the web2c
-library
-
-*/
-
-char *nameoffile;
-int namelength;
-
-/*tex
-
-    When input files are opened via a callback, they will also be read using
-    callbacks. for that purpose, the |open_read_file_callback| returns an integer
-    to uniquely identify a callback table. This id replaces the file point |f| in
-    this case, because the input does not have to be a file in the traditional
-    sense.
-
-    Signalling this fact is achieved by having two arrays of integers.
-
-*/
-
-int *input_file_callback_id;
-int read_file_callback_id[17];
-
-/*tex
-
-    Here we handle |-output-directory|. We assume that it is OK to look here
-    first. Possibly it would be better to replace lookups in "." with lookups in
-    the |output_directory| followed by "." but to do this requires much more
-    invasive surgery in libkpathsea.
-
-*/
-
-static char *find_in_output_directory(const char *s)
-{
-    if (output_directory && !kpse_absolute_p(s, false)) {
-        FILE *f_ptr;
-        char *ftemp = concat3(output_directory, DIR_SEP_STRING, s);
-        /*tex This code is used for input files only. */
-        f_ptr = fopen(ftemp, "rb");
-        if (f_ptr) {
-            fclose(f_ptr);
-            return ftemp;
-        } else {
-            free(ftemp);
-
-        }
-    }
-    return NULL;
-}
-
-/*tex
-
-    Find an \.{\\input} or \.{\\read} file. |n| differentiates between those
-    case.
-
-*/
-
-int kpse_available(const char *m) {
-    if (!kpse_init) {
-        fprintf(stdout,"missing kpse replacement callback '%s', quitting\n",m);
-        exit(1);
-    }
-    return 1 ;
-}
-
-char *luatex_find_read_file(const char *s, int n, int callback_index)
-{
-    char *ftemp = NULL;
-    int callback_id = callback_defined(callback_index);
-    if (callback_id > 0) {
-        (void) run_callback(callback_id, "dS->R", n, s, &ftemp);
-    } else if (kpse_available("find_read_file")) {
-        /*tex Use kpathsea here. */
-        ftemp = find_in_output_directory(s);
-        if (!ftemp)
-            ftemp = kpse_find_file(s, kpse_tex_format, 1);
-    }
-    if (ftemp) {
-        if (fullnameoffile)
-            free(fullnameoffile);
-        fullnameoffile = xstrdup(ftemp);
-    }
-    return ftemp;
-}
-
-/*tex Find other files types. */
-
-char *luatex_find_file(const char *s, int callback_index)
-{
-    char *ftemp = NULL;
-    int callback_id = callback_defined(callback_index);
-    if (callback_id > 0) {
-        (void) run_callback(callback_id, "S->R", s, &ftemp);
-    } else if (kpse_available("find_read_file")) {
-        /*tex Use kpathsea here. */
-        switch (callback_index) {
-            case find_enc_file_callback:
-                ftemp = kpse_find_file(s, kpse_enc_format, 0);
-                break;
-            case find_map_file_callback:
-                ftemp = kpse_find_file(s, kpse_fontmap_format, 0);
-                break;
-            case find_type1_file_callback:
-                ftemp = kpse_find_file(s, kpse_type1_format, 0);
-                break;
-            case find_truetype_file_callback:
-                ftemp = kpse_find_file(s, kpse_truetype_format, 0);
-                break;
-            case find_opentype_file_callback:
-                ftemp = kpse_find_file(s, kpse_opentype_format, 0);
-                if (ftemp == NULL)
-                    ftemp = kpse_find_file(s, kpse_truetype_format, 0);
-                break;
-            case find_data_file_callback:
-                ftemp = find_in_output_directory(s);
-                if (!ftemp)
-                    ftemp = kpse_find_file(s, kpse_tex_format, 1);
-                break;
-            case find_font_file_callback:
-                ftemp = kpse_find_file(s, kpse_ofm_format, 1);
-                if (ftemp == NULL)
-                    ftemp = kpse_find_file(s, kpse_tfm_format, 1);
-                break;
-            case find_vf_file_callback:
-                ftemp = kpse_find_file(s, kpse_ovf_format, 0);
-                if (ftemp == NULL)
-                    ftemp = kpse_find_file(s, kpse_vf_format, 0);
-                break;
-            case find_cidmap_file_callback:
-                ftemp = kpse_find_file(s, kpse_cid_format, 0);
-                break;
-            default:
-                printf("luatex_find_file(): do not know how to handle file %s of type %d\n", s, callback_index);
-                break;
-        }
-    }
-    return ftemp;
-}
-
-/*tex
-
-    \LUATEX\ used to have private functions for these that did not use kpathsea,
-    but since the file paranoia tests have to come from kpathsea anyway, that is
-    no longer useful. The only downside to using luatex is that if one wants to
-    disable kpathsea via the Lua startup script, it is now an absolute
-    requirement that all file discovery callbacks are specified. Just using the
-    find_read_file, but not setting open_read_file, for example, does not work
-    any more if kpathsea is not to be used at all.
-
-*/
-
-#define openoutnameok(A) kpse_out_name_ok (A)
-#define openinnameok(A)  kpse_in_name_ok (A)
-
-/*tex
-
-    Open an input file F, using the kpathsea format FILEFMT and passing
-    |FOPEN_MODE| to fopen. The filename is in `fn'. We return whether or not the
-    open succeeded.
-
-*/
-
-boolean luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, const_string fopen_mode, boolean must_exist)
-{
-    /*tex We haven't found anything yet. */
-    string fname = NULL;
-    *f_ptr = NULL;
-    if (fullnameoffile)
-        free(fullnameoffile);
-    fullnameoffile = NULL;
-    fname = kpse_find_file(fn, (kpse_file_format_type) filefmt, must_exist);
-    if (fname) {
-        fullnameoffile = xstrdup(fname);
-        /*tex
-
-            If we found the file in the current directory, don't leave the `./'
-            at the beginning of `fn', since it looks dumb when `tex foo' says
-            `(./foo.tex ... )'. On the other hand, if the user said `tex ./foo',
-            and that's what we opened, then keep it -- the user specified it, so
-            we shouldn't remove it.
-
-        */
-        if (fname[0] == '.' && IS_DIR_SEP(fname[1]) && (fn[0] != '.' || !IS_DIR_SEP(fn[1]))) {
-            unsigned i = 0;
-            while (fname[i + 2] != 0) {
-                fname[i] = fname[i + 2];
-                i++;
-            }
-            fname[i] = 0;
-        }
-        /*tex This fopen is not allowed to fail. */
-        *f_ptr = xfopen(fname, fopen_mode);
-    }
-    if (*f_ptr) {
-        recorder_record_input(fname);
-    }
-    return *f_ptr != NULL;
-}
-
-boolean luatex_open_output(FILE ** f_ptr, const char *fn, const_string fopen_mode)
-{
-    char *fname;
-    boolean absolute = kpse_absolute_p(fn, false);
-    /*tex If we have an explicit output directory, use it. */
-    if (output_directory && !absolute) {
-        fname = concat3(output_directory, DIR_SEP_STRING, fn);
-    } else {
-        fname = xstrdup(fn);
-    }
-    /*tex Is the filename openable as given?  */
-    *f_ptr = fopen(fname, fopen_mode);
-    if (!*f_ptr) {
-        /*tex Can't open as given. Try the envvar.  */
-        string texmfoutput = kpse_var_value("TEXMFOUTPUT");
-        if (texmfoutput && *texmfoutput && !absolute) {
-            fname = concat3(texmfoutput, DIR_SEP_STRING, fn);
-            *f_ptr = fopen(fname, fopen_mode);
-        }
-    }
-    if (*f_ptr) {
-        recorder_record_output(fname);
-    }
-    free(fname);
-    return *f_ptr != NULL;
-}
-
-boolean lua_a_open_in(alpha_file * f, char *fn, int n)
-{
-    int k;
-    char *fnam;
-    int callback_id;
-    boolean ret = true;
-    boolean file_ok = true;
-    if (n == 0) {
-        input_file_callback_id[iindex] = 0;
-    } else {
-        read_file_callback_id[n] = 0;
-    }
-    if (*fn == '|')
-        fnam = fn;
-    else
-        fnam = luatex_find_read_file(fn, n, find_read_file_callback);
-    if (!fnam)
-        return false;
-    callback_id = callback_defined(open_read_file_callback);
-    if (callback_id > 0) {
-        k = run_and_save_callback(callback_id, "S->", fnam);
-        if (k > 0) {
-            ret = true;
-            if (n == 0)
-                input_file_callback_id[iindex] = k;
-            else
-                read_file_callback_id[n] = k;
-        } else {
-            /*tex read failed */
-            file_ok = false;
-        }
-    } else {
-        /*tex no read callback */
-        if (openinnameok(fnam)) {
-            ret = open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, (n == 0 ? true : false));
-        } else {
-            /*tex open failed */
-            file_ok = false;
-        }
-    }
-    if (!file_ok) {
-        ret = false;
-    }
-    return ret;
-}
-
-boolean lua_a_open_out(alpha_file * f, char *fn, int n)
-{
-    boolean test;
-    str_number fnam;
-    int callback_id;
-    boolean ret = false;
-    callback_id = callback_defined(find_write_file_callback);
-    if (callback_id > 0) {
-        fnam = 0;
-        test = run_callback(callback_id, "dS->s", n, fn, &fnam);
-        if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
-            /*tex
-
-                There is no message here because if that is needed the macro
-                package should do that in the callback code. As elsewhere,
-                messaging is left to \LUA\ then.
-
-            */
-            ret = open_outfile(f, fn, FOPEN_W_MODE);
-        }
-    } else {
-        if (openoutnameok(fn)) {
-            if (n > 0 && selector != term_only) {
-                /*tex
-
-                    This message to the log is for downward compatibility with
-                    other tex's as there are scripts out there that act on this
-                    message. An alternative is to let a macro package write an
-                    explicit message.
-
-                */
-                fprintf(log_file,"\n\\openout%i = %s\n",n-1,fn);
-             }
-             ret = open_out_or_pipe(f, fn, FOPEN_W_MODE);
-        }
-    }
-    return ret;
-}
-
-boolean lua_b_open_out(alpha_file * f, char *fn)
-{
-    boolean test;
-    str_number fnam;
-    int callback_id;
-    boolean ret = false;
-    callback_id = callback_defined(find_output_file_callback);
-    if (callback_id > 0) {
-        fnam = 0;
-        test = run_callback(callback_id, "S->s", fn, &fnam);
-        if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
-            ret = open_outfile(f, fn, FOPEN_WBIN_MODE);
-        }
-    } else {
-        if (openoutnameok(fn)) {
-            ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE);
-        }
-    }
-    return ret;
-}
-
-void lua_a_close_in(alpha_file f, int n)
-{
-    int callback_id;
-    if (n == 0)
-        callback_id = input_file_callback_id[iindex];
-    else
-        callback_id = read_file_callback_id[n];
-    if (callback_id > 0) {
-        run_saved_callback(callback_id, "close", "->");
-        destroy_saved_callback(callback_id);
-        if (n == 0)
-            input_file_callback_id[iindex] = 0;
-        else
-            read_file_callback_id[n] = 0;
-    } else {
-        close_file_or_pipe(f);
-    }
-}
-
-void lua_a_close_out(alpha_file f)
-{
-    close_file_or_pipe(f);
-}
-
-/*tex
-
-    Binary input and output are done with C's ordinary procedures, so we don't
-    have to make any other special arrangements for binary~I/O. Text output is
-    also easy to do with standard routines. The treatment of text input is more
-    difficult, however, because of the necessary translation to |ASCII_code|
-    values. \TeX's conventions should be efficient, and they should blend nicely
-    with the user's operating environment.
-
-    Input from text files is read one line at a time, using a routine called
-    |lua_input_ln|. This function is defined in terms of global variables called
-    |buffer|, |first|, and |last| that will be described in detail later; for
-    now, it suffices for us to know that |buffer| is an array of |ASCII_code|
-    values, and that |first| and |last| are indices into this array representing
-    the beginning and ending of a line of text.
-
-*/
-
-/*tex lines of characters being read */
-
-packed_ASCII_code *buffer;
-
-/*tex the first unused position in |buffer| */
-
-int first;
-
-/*tex end of the line just input to |buffer| */
-
-int last;
-
-/*tex largest index used in |buffer| */
-
-int max_buf_stack;
-
-/*tex
-
-    The |lua_input_ln| function brings the next line of input from the specified
-    file into available positions of the buffer array and returns the value
-    |true|, unless the file has already been entirely read, in which case it
-    returns |false| and sets |last:=first|. In general, the |ASCII_code| numbers
-    that represent the next line of the file are input into |buffer[first]|,
-    |buffer[first+1]|, \dots, |buffer[last-1]|; and the global variable |last| is
-    set equal to |first| plus the length of the line. Trailing blanks are removed
-    from the line; thus, either |last=first| (in which case the line was entirely
-    blank) or |buffer[last-1]<>" "|.
-
-    An overflow error is given, however, if the normal actions of |lua_input_ln|
-    would make |last>=buf_size|; this is done so that other parts of \TeX\ can
-    safely look at the contents of |buffer[last+1]| without overstepping the
-    bounds of the |buffer| array. Upon entry to |lua_input_ln|, the condition
-    |first<buf_size| will always hold, so that there is always room for an
-    ``empty'' line.
-
-    The variable |max_buf_stack|, which is used to keep track of how large the
-    |buf_size| parameter must be to accommodate the present job, is also kept up
-    to date by |lua_input_ln|.
-
-    If the |bypass_eoln| parameter is |true|, |lua_input_ln| will do a |get|
-    before looking at the first character of the line; this skips over an |eoln|
-    that was in |f^|. The procedure does not do a |get| when it reaches the end
-    of the line; therefore it can be used to acquire input from the user's
-    terminal as well as from ordinary text files.
-
-    Since the inner loop of |lua_input_ln| is part of \TeX's ``inner
-    loop''---each character of input comes in at this place---it is wise to
-    reduce system overhead by making use of special routines that read in an
-    entire array of characters at once, if such routines are available.
-
-*/
-
-boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln)
-{
-    boolean lua_result;
-    int last_ptr;
-    int callback_id;
-    /*tex Todo: variable can be removed: */
-    (void) bypass_eoln;
-    if (n == 0)
-        callback_id = input_file_callback_id[iindex];
-    else
-        callback_id = read_file_callback_id[n];
-    if (callback_id > 0) {
-        last = first;
-        last_ptr = first;
-        lua_result =
-            run_saved_callback(callback_id, "reader", "->l", &last_ptr);
-        if ((lua_result == true) && (last_ptr != 0)) {
-            last = last_ptr;
-            if (last > max_buf_stack)
-                max_buf_stack = last;
-        } else {
-            lua_result = false;
-        }
-    } else {
-        lua_result = input_ln(f, bypass_eoln);
-    }
-    if (lua_result == true) {
-        /*tex Fix up the input buffer using callbacks */
-        if (last >= first) {
-            callback_id = callback_defined(process_input_buffer_callback);
-            if (callback_id > 0) {
-                last_ptr = first;
-                lua_result =
-                    run_callback(callback_id, "l->l", (last - first),
-                                 &last_ptr);
-                if ((lua_result == true) && (last_ptr != 0)) {
-                    last = last_ptr;
-                    if (last > max_buf_stack)
-                        max_buf_stack = last;
-                }
-            }
-        }
-        return true;
-    }
-    return false;
-}
-
-/*tex
-
-    We need a special routine to read the first line of \TeX\ input from the
-    user's terminal. This line is different because it is read before we have
-    opened the transcript file; there is sort of a ``chicken and egg'' problem
-    here. If the user types `\.{\\input paper}' on the first line, or if some
-    macro invoked by that line does such an \.{\\input}, the transcript file will
-    be named `\.{paper.log}'; but if no \.{\\input} commands are performed during
-    the first line of terminal input, the transcript file will acquire its
-    default name `\.{texput.log}'. (The transcript file will not contain error
-    messages generated by the first line before the first \.{\\input} command.)
-
-    The first line is special also because it may be read before \TeX\ has input
-    a format file. In such cases, normal error messages cannot yet be given. The
-    following code uses concepts that will be explained later.
-
-    Different systems have different ways to get started. But regardless of what
-    conventions are adopted, the routine that initializes the terminal should
-    satisfy the following specifications:
-
-    \startitemize[n]
-
-        \startitem
-            It should open file |term_in| for input from the terminal. (The file
-            |term_out| will already be open for output to the terminal.)
-        \stopitem
-
-        \startitem
-            If the user has given a command line, this line should be considered
-            the first line of terminal input. Otherwise the user should be
-            prompted with `\.{**}', and the first line of input should be
-            whatever is typed in response.
-        \stopitem
-
-        \startitem
-            The first line of input, which might or might not be a command line,
-            should appear in locations |first| to |last-1| of the |buffer| array.
-        \stopitem
-
-        \startitem
-            The global variable |loc| should be set so that the character to be
-            read next by \TeX\ is in |buffer[loc]|. This character should not be
-            blank, and we should have |loc<last|.
-        \stopitem
-
-    \stopitemize
-
-    It may be necessary to prompt the user several times before a non-blank line
-    comes in. The prompt is `\.{**}' instead of the later `\.*' because the
-    meaning is slightly different: `\.{\\input}' need not be typed immediately
-    after~`\.{**}'.)
-
-    The following program does the required initialization. Iff anything has been
-    specified on the command line, then |t_open_in| will return with |last >
-    first|.
-
-*/
-
-boolean init_terminal(void)
-{
-    /*tex This gets the terminal input started. */
-    t_open_in();
-    if (last > first) {
-        iloc = first;
-        while ((iloc < last) && (buffer[iloc] == ' '))
-            incr(iloc);
-        if (iloc < last) {
-            return true;
-        }
-    }
-    while (1) {
-        wake_up_terminal();
-        fputs("**", term_out);
-        update_terminal();
-        if (!input_ln(term_in, true)) {
-            /*tex This shouldn't happen. */
-            fputs("\n! End of file on the terminal... why?\n", term_out);
-            return false;
-        }
-        iloc = first;
-        while ((iloc < last) && (buffer[iloc] == ' ')) {
-            incr(iloc);
-        }
-        /*tex Return unless the line was all blank. */
-        if (iloc < last) {
-            return true;
-        }
-        fputs("Please type the name of your input file.\n", term_out);
-    }
-}
-
-
-/*tex
-
-    Here is a procedure that asks the user to type a line of input, assuming that
-    the |selector| setting is either |term_only| or |term_and_log|. The input is
-    placed into locations |first| through |last-1| of the |buffer| array, and
-    echoed on the transcript file if appropriate.
-
-*/
-
-void term_input(void)
-{
-    /*tex Index into |buffer|: */
-    int k;
-    /*tex Now the user sees the prompt for sure: */
-    update_terminal();
-    if (!input_ln(term_in, true))
-        fatal_error("End of file on the terminal!");
-    /*tex The user's line ended with \.{<return>}: */
-    term_offset = 0;
-    /*tex Prepare to echo the input. */
-    decr(selector);
-    if (last != first) {
-        for (k = first; k <= last - 1; k++)
-            print_char(buffer[k]);
-    }
-    print_ln();
-    /*tex Restore previous status. */
-    incr(selector);
-}
-
-/*tex
-
-    It's time now to fret about file names. Besides the fact that different
-    operating systems treat files in different ways, we must cope with the fact
-    that completely different naming conventions are used by different groups of
-    people. The following programs show what is required for one particular
-    operating system; similar routines for other systems are not difficult to
-    devise.
-
-    \TeX\ assumes that a file name has three parts: the name proper; its
-    ``extension''; and a ``file area'' where it is found in an external file
-    system. The extension of an input file or a write file is assumed to be
-    `\.{.tex}' unless otherwise specified; it is `\.{.log}' on the transcript
-    file that records each run of \TeX; it is `\.{.tfm}' on the font metric files
-    that describe characters in the fonts \TeX\ uses; it is `\.{.dvi}' on the
-    output files that specify typesetting information; and it is `\.{.fmt}' on
-    the format files written by \.{INITEX} to initialize \TeX. The file area can
-    be arbitrary on input files, but files are usually output to the user's
-    current area. If an input file cannot be found on the specified area, \TeX\
-    will look for it on a special system area; this special area is intended for
-    commonly used input files like \.{webmac.tex}.
-
-    Simple uses of \TeX\ refer only to file names that have no explicit extension
-    or area. For example, a person usually says `\.{\\input} \.{paper}' or
-    `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input} \.{paper.new}'
-    or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file names are best,
-    because they make the \TeX\ source files portable; whenever a file name
-    consists entirely of letters and digits, it should be treated in the same way
-    by all implementations of \TeX. However, users need the ability to refer to
-    other files in their environment, especially when responding to error
-    messages concerning unopenable files; therefore we want to let them use the
-    syntax that appears in their favorite operating system.
-
-    The following procedures don't allow spaces to be part of file names; but
-    some users seem to like names that are spaced-out. System-dependent changes
-    to allow such things should probably be made with reluctance, and only when
-    an entire file name that includes spaces is ``quoted'' somehow.
-
-    Here are the global values that file names will be scanned into.
-
-*/
-
-/*tex name of file just scanned */
-
-str_number cur_name;
-
-/*tex file area just scanned, or \.{""} */
-
-str_number cur_area;
-
-/*tex file extension just scanned, or \.{""} */
-
-str_number cur_ext;
-
-/*tex
-
-    The file names we shall deal with have the following structure: If the name
-    contains `\./' or `\.:' (for Amiga only), the file area consists of all
-    characters up to and including the final such character; otherwise the file
-    area is null. If the remaining file name contains `\..', the file extension
-    consists of all such characters from the last `\..' to the end, otherwise the
-    file extension is null.
-
-    We can scan such file names easily by using two global variables that keep
-    track of the occurrences of area and extension delimiters:
-
-*/
-
-/*tex the most recent `\./', if any */
-
-pool_pointer area_delimiter;
-
-/*tex the relevant `\..', if any */
-
-pool_pointer ext_delimiter;
-
-/*tex
-
-    Input files that can't be found in the user's area may appear in a standard
-    system area called |TEX_area|. Font metric files whose areas are not given
-    explicitly are assumed to appear in a standard system area called
-    |TEX_font_area|. $\Omega$'s compiled translation process files whose areas
-    are not given explicitly are assumed to appear in a standard system area.
-    These system area names will, of course, vary from place to place.
-
-*/
-
-#define append_to_fn(A) do {                                    \
-        c=(A);                                                  \
-        if (c!='"') {                                           \
-            if (k<file_name_size) fn[k++]=(unsigned char)(c);   \
-        }                                                       \
-    } while (0)
-
-
-char *pack_file_name(str_number n, str_number a, str_number e)
-{
-    /*tex character being packed */
-    ASCII_code c;
-    /*tex index into |str_pool| */
-    unsigned char *j;
-    /*tex number of positions filled in |fn| */
-    int k = 0;
-    unsigned char *fn = xmallocarray(packed_ASCII_code, str_length(a) + str_length(n) + str_length(e) + 1);
-    for (j = str_string(a); j < str_string(a) + str_length(a); j++)
-        append_to_fn(*j);
-    for (j = str_string(n); j < str_string(n) + str_length(n); j++)
-        append_to_fn(*j);
-    for (j = str_string(e); j < str_string(e) + str_length(e); j++)
-        append_to_fn(*j);
-    fn[k] = 0;
-    return (char *) fn;
-}
-
-/*tex
-
-    A messier routine is also needed, since format file names must be scanned
-    before \TeX's string mechanism has been initialized. We shall use the global
-    variable |TEX_format_default| to supply the text for default system areas and
-    extensions related to format files.
-
-    Under \UNIX\ we don't give the area part, instead depending on the path
-    searching that will happen during file opening. Also, the length will be set
-    in the main program.
-
-*/
-
-char *TEX_format_default;
-
-
-/*tex
-
-    This part of the program becomes active when a ``virgin'' \TeX\ is trying to
-    get going, just after the preliminary initialization, or when the user is
-    substituting another format file by typing `\.\&' after the initial `\.{**}'
-    prompt. The buffer contains the first line of input in
-    |buffer[loc..(last-1)]|, where |loc<last| and |buffer[loc]<>" "|.
-
-*/
-
-char *open_fmt_file(void)
-{
-    /*tex The first space after the format file name: */
-    int j;
-    char *fmt = NULL;
-    int dist;
-    j = iloc;
-    if (buffer[iloc] == '&') {
-        incr(iloc);
-        j = iloc;
-        buffer[last] = ' ';
-        while (buffer[j] != ' ')
-            incr(j);
-        fmt = xmalloc((unsigned) (j - iloc + 1));
-        strncpy(fmt, (char *) (buffer + iloc), (size_t) (j - iloc));
-        fmt[j - iloc] = 0;
-        dist = (int) (strlen(fmt) - strlen(DUMP_EXT));
-        if (!(strstr(fmt, DUMP_EXT) == fmt + dist))
-            fmt = concat(fmt, DUMP_EXT);
-        if (zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE))
-            goto FOUND;
-        wake_up_terminal();
-        fprintf(stdout, "Sorry, I can't find the format `%s'; will try `%s'.\n",
-                fmt, TEX_format_default);
-        update_terminal();
-    }
-    /*tex Now pull out all the stops: try for the system \.{plain} file. */
-    fmt = TEX_format_default;
-    if (!zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) {
-        wake_up_terminal();
-        fprintf(stdout, "I can't find the format file `%s'!\n",
-                TEX_format_default);
-        return NULL;
-    }
-  FOUND:
-    iloc = j;
-    return fmt;
-}
-
-/*tex
-
-    The global variable |name_in_progress| is used to prevent recursive use of
-    |scan_file_name|, since the |begin_name| and other procedures communicate via
-    global variables. Recursion would arise only by devious tricks like
-    `\.{\\input\\input f}'; such attempts at sabotage must be thwarted.
-    Furthermore, |name_in_progress| prevents \.{\\input} from being initiated
-    when a font size specification is being scanned.
-
-    Another global variable, |job_name|, contains the file name that was first
-    \.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}'
-    and `\.{.fmt}' in the names of \TeX's output files.
-
-*/
-
-/*tex is a file name being scanned? */
-
-boolean name_in_progress;
-
-/*tex principal file name */
-
-str_number job_name;
-
-/*tex has the transcript file been opened? */
-
-boolean log_opened_global;
-
-/*tex
-
-    Initially |job_name=0|; it becomes nonzero as soon as the true name is known.
-    We have |job_name=0| if and only if the `\.{log}' file has not been opened,
-    except of course for a short time just after |job_name| has become nonzero.
-
-*/
-
-/*tex full name of the log file */
-
-unsigned char *texmf_log_name;
-
-/*tex
-
-    The |open_log_file| routine is used to open the transcript file and to help
-    it catch up to what has previously been printed on the terminal.
-
-*/
-
-void open_log_file(void)
-{
-    /*tex previous |selector| setting */
-    int old_setting;
-    /*tex index into |buffer| */
-    int k;
-    /*tex end of first input line */
-    int l;
-    char *fn;
-    old_setting = selector;
-    if (job_name == 0)
-        job_name = getjobname(maketexstring("texput"));
-    fn = pack_job_name(".fls");
-    recorder_change_filename(fn);
-    fn = pack_job_name(".log");
-    while (!lua_a_open_out(&log_file, fn, 0)) {
-        /*tex
-
-            Try to get a different log file name. Sometimes |open_log_file| is
-            called at awkward moments when \TeX\ is unable to print error
-            messages or even to |show_context|. The |prompt_file_name| routine
-            can result in a |fatal_error|, but the |error| routine will not be
-            invoked because |log_opened| will be false.
-
-            The normal idea of |batch_mode| is that nothing at all should be
-            written on the terminal. However, in the unusual case that no log
-            file could be opened, we make an exception and allow an explanatory
-            message to be seen.
-
-            Incidentally, the program always refers to the log file as a
-            `\.{transcript file}', because some systems cannot use the extension
-            `\.{.log}' for this file.
-        */
-        selector = term_only;
-        fn = prompt_file_name("transcript file name", ".log");
-    }
-    texmf_log_name = (unsigned char *) xstrdup(fn);
-    selector = log_only;
-    log_opened_global = true;
-    if (callback_defined(start_run_callback) == 0) {
-        /*tex Print the banner line, including current date and time. */
-        log_banner(luatex_version_string);
-        /*tex Make sure bottom level is in memory. */
-        input_stack[input_ptr] = cur_input;
-        tprint_nl("**");
-        /*tex The last position of first line. */
-        l = input_stack[0].limit_field;
-        if (buffer[l] == end_line_char_par) {
-            /*tex maybe also handle multichar endlinechar */
-            decr(l);
-        }
-        for (k = 1; k <= l; k++) {
-            print_char(buffer[k]);
-        }
-        /*tex now the transcript file contains the first line of input */
-        print_ln();
-    }
-    /*tex should be done always */
-    flush_loggable_info();
-    /*tex should be done always */
-    selector = old_setting + 2; 
-}
-
-/*tex
-
-    This function is needed by synctex to make its log appear in the right spot
-    when |output_directory| is set.
-
-*/
-
-char *get_full_log_name (void)
-{
-   if (output_directory) {
-       char *ret  = xmalloc(strlen((char *)texmf_log_name)+2+strlen(output_directory));
-       ret = strcpy(ret, output_directory);
-       strcat(ret, "/");
-       strcat(ret, (char *)texmf_log_name);
-       return ret;
-   } else {
-       return xstrdup((const char*)texmf_log_name);
-   }
-}
-
-/*tex Synctex uses this to get the anchored path of an input file. */
-
-char *luatex_synctex_get_current_name (void)
-{
-  char *pwdbuf = NULL, *ret;
-  if (kpse_absolute_p(fullnameoffile, false)) {
-     return xstrdup(fullnameoffile);
-  }
-  pwdbuf = xgetcwd();
-  ret = concat3(pwdbuf, DIR_SEP_STRING, fullnameoffile);
-  free(pwdbuf) ;
-  return ret;
-}
-
-/*tex
-
-    Let's turn now to the procedure that is used to initiate file reading when an
-    `\.{\\input}' command is being processed.
-
-*/
-
-void start_input(void)
-{
-    str_number temp_str;
-    char *fn;
-    do {
-        get_x_token();
-    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
-
-    back_input();
-    if (cur_cmd != left_brace_cmd) {
-        /*tex Set |cur_name| to desired file name. */
-        scan_file_name();
-    } else {
-        scan_file_name_toks();
-    }
-    fn = pack_file_name(cur_name, cur_area, cur_ext);
-    while (1) {
-        /*tex Set up |cur_file| and new level of input. */
-        begin_file_reading();
-        if (lua_a_open_in(&cur_file, fn, 0)) {
-            break;
-        }
-        /*tex Remove the level that didn't work. */
-        end_file_reading();
-        fn = prompt_file_name("input file name", "");
-    }
-    iname = maketexstring(fullnameoffile);
-    /*tex
-
-        Now that we have |fullnameoffile|, it is time to post-adjust |cur_name|
-        and |cur_ext| for trailing |.tex|.
-
-    */
-    {
-        char *n, *p;
-        n = p = fullnameoffile + strlen(fullnameoffile);
-        while (p>fullnameoffile) {
-            p--;
-            if (IS_DIR_SEP(*p)) {
-                break;
-            }
-        }
-        if (IS_DIR_SEP(*p)) {
-            p++;
-        }
-        while (n>fullnameoffile) {
-            n--;
-            if (*n == '.') {
-                break;
-            }
-        }
-        if (n>p) {
-            int q = *n;
-            cur_ext = maketexstring(n);
-            *n = 0;
-            cur_name = maketexstring(p);
-            *n = q;
-        }
-    }
-    source_filename_stack[in_open] = iname;
-    full_source_filename_stack[in_open] = xstrdup(fullnameoffile);
-    /*tex We can try to conserve string pool space now. */
-    temp_str = search_string(iname);
-    if (temp_str > 0) {
-        flush_str(iname);
-        iname = temp_str;
-    }
-    if (job_name == 0) {
-        job_name = getjobname(cur_name);
-        open_log_file();
-    }
-    /*tex
-
-        |open_log_file| doesn't |show_context|, so |limit| and |loc| needn't be
-        set to meaningful values yet.
-
-    */
-    report_start_file(filetype_tex,fullnameoffile);
-    incr(open_parens);
-    update_terminal();
-    istate = new_line;
-    /*tex Prepare new file {\sl Sync\TeX} information. */
-    if (! synctex_get_no_files()) {
-        /*tex Give control to the {\sl Sync\TeX} controller. */
-        synctexstartinput();
-    }
-    /*tex
-
-        Read the first line of the new file. Here we have to remember to tell the
-        |lua_input_ln| routine not to start with a |get|. If the file is empty,
-        it is considered to contain a single blank line.
-
-    */
-    line = 1;
-    if (lua_input_ln(cur_file, 0, false)) {
-        ;
-    }
-    firm_up_the_line();
-    if (end_line_char_inactive)
-        decr(ilimit);
-    else
-        buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
-    first = ilimit + 1;
-    iloc = istart;
-}
-
-/*tex
-
-    Because the format is zipped we read and write dump files through zlib.
-    Earlier versions recast |*f| from |FILE *| to |gzFile|, but there is no
-    guarantee that these have the same size, so a static variable is needed.
-
-*/
-
-static gzFile gz_fmtfile = NULL;
-
-/*tex
-
-    As distributed, the dump files are architecture dependent; specifically,
-    BigEndian and LittleEndian architectures produce different files. These
-    routines always output BigEndian files. This still does not guarantee them to
-    be architecture-independent, because it is possible to make a format that
-    dumps a glue ratio, i.e., a floating-point number. Fortunately, none of the
-    standard formats do that.
-
-*/
-
-#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
-
-/*tex
-
-    This macro is always invoked as a statement. It assumes a variable `temp'.
-
-*/
-
-#  define SWAP(x, y) do { temp = x; x = y; y = temp; } while (0)
-
-/*tex
-
-    Make the NITEMS items pointed at by P, each of size SIZE, be the
-    opposite-endianness of whatever they are now.
-
-*/
-
-static void swap_items(char *pp, int nitems, int size)
-{
-    char temp;
-    unsigned total = (unsigned) (nitems * size);
-    char *q = xmalloc(total);
-    char *p = q;
-    memcpy(p,pp,total);
-    /*tex
-
-        Since `size' does not change, we can write a while loop for each case,
-        and avoid testing `size' for each time.
-
-    */
-    switch (size) {
-        case 16:
-            /*tex
-
-                16-byte items happen on the DEC Alpha machine when we are not doing
-                sharable memory dumps.
-
-            */
-            while (nitems--) {
-                SWAP(p[0], p[15]);
-                SWAP(p[1], p[14]);
-                SWAP(p[2], p[13]);
-                SWAP(p[3], p[12]);
-                SWAP(p[4], p[11]);
-                SWAP(p[5], p[10]);
-                SWAP(p[6], p[9]);
-                SWAP(p[7], p[8]);
-                p += size;
-            }
-            break;
-
-        case 12:
-            while (nitems--) {
-                SWAP(p[0], p[11]);
-                SWAP(p[1], p[10]);
-                SWAP(p[2], p[9]);
-                SWAP(p[3], p[8]);
-                SWAP(p[4], p[7]);
-                SWAP(p[5], p[6]);
-                p += size;
-            }
-            break;
-
-        case 8:
-            while (nitems--) {
-                SWAP(p[0], p[7]);
-                SWAP(p[1], p[6]);
-                SWAP(p[2], p[5]);
-                SWAP(p[3], p[4]);
-                p += size;
-            }
-            break;
-
-        case 4:
-            while (nitems--) {
-                SWAP(p[0], p[3]);
-                SWAP(p[1], p[2]);
-                p += size;
-            }
-            break;
-
-        case 2:
-            while (nitems--) {
-                SWAP(p[0], p[1]);
-                p += size;
-            }
-            break;
-        case 1:
-            /*tex Nothing to do. */
-            break;
-        default:
-            FATAL1("Can't swap a %d-byte item for (un)dumping", size);
-    }
-    memcpy(pp,q,total);
-    xfree(q);
-}
-#endif
-
-/*tex
-
-    That second swap is to make sure following calls don't get confused in the
-    case of |dump_things|.
-
-*/
-
-void do_zdump(char *p, int item_size, int nitems, FILE * out_file)
-{
-    int err;
-    (void) out_file;
-    if (nitems == 0)
-        return;
-#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
-    swap_items(p, nitems, item_size);
-#endif
-    if (gzwrite(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) !=
-        item_size * nitems) {
-        fprintf(stderr, "! Could not write %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err));
-        uexit(1);
-    }
-#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
-    swap_items(p, nitems, item_size);
-#endif
-}
-
-void do_zundump(char *p, int item_size, int nitems, FILE * in_file)
-{
-    int err;
-    (void) in_file;
-    if (nitems == 0)
-        return;
-    if (gzread(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) <= 0) {
-        fprintf(stderr, "Could not undump %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err));
-        uexit(1);
-    }
-#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
-    swap_items(p, nitems, item_size);
-#endif
-}
-
-/*tex
-
-    Tests has shown that a level 3 compression is the most optimal tradeoff
-    between file size and load time.
-
-*/
-
-#define COMPRESSION "R3"
-
-boolean zopen_w_input(FILE ** f, const char *fname, int format, const_string fopen_mode)
-{
-    int callbackid;
-    int res;
-    char *fnam;
-    callbackid = callback_defined(find_format_file_callback);
-    if (callbackid > 0) {
-        res = run_callback(callbackid, "S->R", fname, &fnam);
-        if (res && fnam && strlen(fnam) > 0) {
-            *f = fopen(fnam, fopen_mode);
-            if (*f == NULL) {
-                return 0;
-            }
-        } else {
-            return 0;
-        }
-    } else {
-        res = luatex_open_input(f, fname, format, fopen_mode, true);
-    }
-    if (res) {
-        gz_fmtfile = gzdopen(fileno(*f), "rb" COMPRESSION);
-    }
-    return res;
-}
-
-boolean zopen_w_output(FILE ** f, const char *s, const_string fopen_mode)
-{
-    int res = 1;
-    if (luainit) {
-        *f = fopen(s, fopen_mode);
-        if (*f == NULL) {
-            return 0;
-        }
-    } else {
-        res = luatex_open_output(f, s, fopen_mode);
-    }
-    if (res) {
-        gz_fmtfile = gzdopen(fileno(*f), "wb" COMPRESSION);
-    }
-    return res;
-}
-
-void zwclose(FILE * f)
-{
-    (void) f;
-    gzclose(gz_fmtfile);
-}
-
-/*tex Create the \DVI\ or \PDF\ file. */
-
-int open_outfile(FILE ** f, const char *name, const char *mode)
-{
-    FILE *res;
-    res = fopen(name, mode);
-    if (res != NULL) {
-        *f = res;
-        return 1;
-    }
-    return 0;
-}
-
-/*tex The caller should set |tfm_buffer=NULL| and |tfm_size=0|. */
-
-int readbinfile(FILE * f, unsigned char **tfm_buffer, int *tfm_size)
-{
-    void *buf;
-    int size;
-    if (fseek(f, 0, SEEK_END) == 0) {
-        size = (int) ftell(f);
-        if (size > 0) {
-            buf = xmalloc((unsigned) size);
-            if (fseek(f, 0, SEEK_SET) == 0) {
-                if (fread((void *) buf, (size_t) size, 1, f) == 1) {
-                    *tfm_buffer = (unsigned char *) buf;
-                    *tfm_size = size;
-                    return 1;
-                }
-            }
-        } else {
-            *tfm_buffer = NULL;
-            *tfm_size = 0;
-            return 1;
-        }
-    }
-    /*tex Either seek failed or we have a zero-sized file. */
-    return 0;
-}
-
-/*tex
-
-    Like |os.execute()|, the |runpopen()| function is called only when
-    |shellenabledp == 1|. Unlike |os.execute()| we write errors to stderr, since
-    we have nowhere better to use; and of course we return a file handle (or
-    NULL) instead of a status indicator.
-
-*/
-
-static FILE *runpopen(char *cmd, const char *mode)
-{
-    FILE *f = NULL;
-    char *safecmd = NULL;
-    char *cmdname = NULL;
-    int allow;
-#ifdef WIN32
-    char *pp;
-
-    for (pp = cmd; *pp; pp++) {
-      if (*pp == '\'') *pp = '"';
-    }
-#endif
-    /*tex If |restrictedshell| is zero, any command is allowed. */
-    if (restrictedshell == 0) {
-        allow = 1;
-    } else {
-        const char *thecmd = cmd;
-        allow = shell_cmd_is_allowed(thecmd, &safecmd, &cmdname);
-    }
-    if (allow == 1)
-        f = popen(cmd, mode);
-    else if (allow == 2)
-        f = popen(safecmd, mode);
-    else if (allow == -1)
-        fprintf(stderr, "\nrunpopen quotation error in command line: %s\n", cmd);
-    else
-        fprintf(stderr, "\nrunpopen command not allowed: %s\n", cmdname);
-    if (safecmd)
-        free(safecmd);
-    if (cmdname)
-        free(cmdname);
-    return f;
-}
-
-/*tex
-
-    The code that implements |popen()| needs an array for tracking possible pipe
-    file pointers, because these need to be closed using |pclose()|.
-
-*/
-
-#define NUM_PIPES 16
-static FILE *pipes[NUM_PIPES];
-
-#ifdef WIN32
-FILE *Poptr;
-#endif
-
-boolean open_in_or_pipe(FILE ** f_ptr, char *fn, int filefmt, const_string fopen_mode, boolean must_exist)
-{
-    string fname = NULL;
-    int i;
-    /*tex
-
-        Opening a read pipe is straightforward, only have to skip past the pipe
-        symbol in the file name. filename quoting is assumed to happen elsewhere
-        (it does :-))
-
-    */
-    if (shellenabledp && *fn == '|') {
-        /*tex The user requested a pipe. */
-        *f_ptr = NULL;
-        fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
-        strcpy(fname, fn);
-        if (fullnameoffile)
-            free(fullnameoffile);
-        fullnameoffile = xstrdup(fname);
-        recorder_record_input(fname + 1);
-        *f_ptr = runpopen(fname + 1, "r");
-        free(fname);
-        for (i = 0; i < NUM_PIPES; i++) {
-            if (pipes[i] == NULL) {
-                pipes[i] = *f_ptr;
-                break;
-            }
-        }
-        if (*f_ptr)
-            setvbuf(*f_ptr, (char *) NULL, _IONBF, 0);
-#ifdef WIN32
-        Poptr = *f_ptr;
-#endif
-        return *f_ptr != NULL;
-    }
-    return luatex_open_input(f_ptr, fn, filefmt, fopen_mode, must_exist);
-}
-
-
-boolean open_out_or_pipe(FILE ** f_ptr, char *fn, const_string fopen_mode)
-{
-    string fname;
-    int i;
-    /*tex
-
-        Opening a write pipe takes a little bit more work, because TeX will
-        perhaps have appended ".tex". To avoid user confusion as much as
-        possible, this extension is stripped only when the command is a bare
-        word. Some small string trickery is needed to make sure the correct
-        number of bytes is free()-d afterwards.
-    */
-    if (shellenabledp && *fn == '|') {
-        /*tex The user requested a pipe. */
-        fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
-        strcpy(fname, fn);
-        if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) {
-            /*tex
-
-                \METAPOST\ and \METAFIONT\ currently do not use this code, but it
-                is better to be prepared. Hm, what has this todo with \LUATEX ?
-
-            */
-            if (STREQ((fname + strlen(fname) - 3), "tex"))
-                *(fname + strlen(fname) - 4) = 0;
-            *f_ptr = runpopen(fname + 1, "w");
-            *(fname + strlen(fname)) = '.';
-        } else {
-            *f_ptr = runpopen(fname + 1, "w");
-        }
-        recorder_record_output(fname + 1);
-        free(fname);
-        for (i = 0; i < NUM_PIPES; i++) {
-            if (pipes[i] == NULL) {
-                pipes[i] = *f_ptr;
-                break;
-            }
-        }
-        if (*f_ptr)
-            setvbuf(*f_ptr, (char *) NULL, _IONBF, 0);
-        return *f_ptr != NULL;
-    }
-    return luatex_open_output(f_ptr, fn, fopen_mode);
-}
-
-
-void close_file_or_pipe(FILE * f)
-{
-    int i;
-    if (shellenabledp) {
-        for (i = 0; i <= 15; i++) {
-            /*tex If this file was a pipe, |pclose()| it and return. */
-            if (pipes[i] == f) {
-                if (f) {
-                    pclose(f);
-#ifdef WIN32
-                    Poptr = NULL;
-#endif
-                }
-                pipes[i] = NULL;
-                return;
-            }
-        }
-    }
-    close_file(f);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/texfileio.w
@@ -0,0 +1,1389 @@
+% texfileio.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 <string.h>
+#include <kpathsea/absolute.h>
+
+@ The bane of portability is the fact that different operating systems treat
+input and output quite differently, perhaps because computer scientists
+have not given sufficient attention to this problem. People have felt somehow
+that input and output are not part of ``real'' programming. Well, it is true
+that some kinds of programming are more fun than others. With existing
+input/output conventions being so diverse and so messy, the only sources of
+joy in such parts of the code are the rare occasions when one can find a
+way to make the program a little less bad than it might have been. We have
+two choices, either to attack I/O now and get it over with, or to postpone
+I/O until near the end. Neither prospect is very attractive, so let's
+get it over with.
+
+The basic operations we need to do are (1)~inputting and outputting of
+text, to or from a file or the user's terminal; (2)~inputting and
+outputting of eight-bit bytes, to or from a file; (3)~instructing the
+operating system to initiate (``open'') or to terminate (``close'') input or
+output from a specified file; (4)~testing whether the end of an input
+file has been reached.
+
+\TeX\ needs to deal with two kinds of files.
+We shall use the term |alpha_file| for a file that contains textual data,
+and the term |byte_file| for a file that contains eight-bit binary information.
+These two types turn out to be the same on many computers, but
+sometimes there is a significant distinction, so we shall be careful to
+distinguish between them. Standard protocols for transferring
+such files from computer to computer, via high-speed networks, are
+now becoming available to more and more communities of users.
+
+The program actually makes use also of a third kind of file, called a
+|word_file|, when dumping and reloading base information for its own
+initialization.  We shall define a word file later; but it will be possible
+for us to specify simple operations on word files before they are defined.
+
+@ We finally did away with |nameoffile| and |namelength|, but the variables
+have to be kept otherwise there will be link errors from |openclose.c| in
+the web2c library
+
+@c
+char *nameoffile;
+int namelength;
+
+
+@ When input files are opened via a callback, they will also be read using
+callbacks. for that purpose, the |open_read_file_callback| returns an
+integer to uniquely identify a callback table. This id replaces the file
+point |f| in this case, because the input does not have to be a file
+in the traditional sense.
+
+Signalling this fact is achieved by having two arrays of integers.
+
+@c
+int *input_file_callback_id;
+int read_file_callback_id[17];
+
+@ Handle -output-directory.
+
+We assume that it is OK to look here first.  Possibly it
+would be better to replace lookups in "." with lookups in the
+|output_directory| followed by "." but to do this requires much more
+invasive surgery in libkpathsea.
+
+@c
+static char *find_in_output_directory(const char *s)
+{
+    if (output_directory && !kpse_absolute_p(s, false)) {
+        FILE *f_ptr;
+        char *ftemp = concat3(output_directory, DIR_SEP_STRING, s);
+        f_ptr = fopen(ftemp, "rb");     /* this code is used for input files only */
+        if (f_ptr) {
+            fclose(f_ptr);
+            return ftemp;
+        } else {
+            free(ftemp);
+
+        }
+    }
+    return NULL;
+}
+
+@ find an \.{\\input} or \.{\\read} file. |n| differentiates between those case.
+
+@c
+int kpse_available(const char *m) {
+    if (!kpse_init) {
+          fprintf(stdout,"missing kpse replacement callback '%s', quitting\n",m);
+          exit(1);
+    }
+    return 1 ;
+}
+
+char *luatex_find_read_file(const char *s, int n, int callback_index)
+{
+    char *ftemp = NULL;
+    int callback_id = callback_defined(callback_index);
+    if (callback_id > 0) {
+        (void) run_callback(callback_id, "dS->R", n, s, &ftemp);
+    } else if (kpse_available("find_read_file")) {
+        /* use kpathsea here */
+        ftemp = find_in_output_directory(s);
+        if (!ftemp)
+            ftemp = kpse_find_file(s, kpse_tex_format, 1);
+    }
+    if (ftemp) {
+        if (fullnameoffile)
+            free(fullnameoffile);
+        fullnameoffile = xstrdup(ftemp);
+    }
+    return ftemp;
+}
+
+@ find other files types
+@c
+char *luatex_find_file(const char *s, int callback_index)
+{
+    char *ftemp = NULL;
+    int callback_id = callback_defined(callback_index);
+    if (callback_id > 0) {
+        (void) run_callback(callback_id, "S->R", s, &ftemp);
+
+    } else if (kpse_available("find_read_file")) {
+        /* use kpathsea here */
+        switch (callback_index) {
+        case find_enc_file_callback:
+            ftemp = kpse_find_file(s, kpse_enc_format, 0);
+            break;
+        case find_map_file_callback:
+            ftemp = kpse_find_file(s, kpse_fontmap_format, 0);
+            break;
+        case find_type1_file_callback:
+            ftemp = kpse_find_file(s, kpse_type1_format, 0);
+            break;
+        case find_truetype_file_callback:
+            ftemp = kpse_find_file(s, kpse_truetype_format, 0);
+            break;
+        case find_opentype_file_callback:
+            ftemp = kpse_find_file(s, kpse_opentype_format, 0);
+            if (ftemp == NULL)
+                ftemp = kpse_find_file(s, kpse_truetype_format, 0);
+            break;
+        case find_data_file_callback:
+            ftemp = find_in_output_directory(s);
+            if (!ftemp)
+                ftemp = kpse_find_file(s, kpse_tex_format, 1);
+            break;
+        case find_font_file_callback:
+            ftemp = kpse_find_file(s, kpse_ofm_format, 1);
+            if (ftemp == NULL)
+                ftemp = kpse_find_file(s, kpse_tfm_format, 1);
+            break;
+        case find_vf_file_callback:
+            ftemp = kpse_find_file(s, kpse_ovf_format, 0);
+            if (ftemp == NULL)
+                ftemp = kpse_find_file(s, kpse_vf_format, 0);
+            break;
+        case find_cidmap_file_callback:
+            ftemp = kpse_find_file(s, kpse_cid_format, 0);
+            break;
+        default:
+            printf
+                ("luatex_find_file(): do not know how to handle file %s of type %d\n",
+                 s, callback_index);
+            break;
+        }
+    }
+    return ftemp;
+}
+
+
+@  LuaTeX used to have private functions for these that did not use kpathsea,
+but since the file paranoia tests have to come from kpathsea anyway, that is no
+longer useful. The only downside to using luatex is that if one wants to disable
+kpathsea via the Lua startup script, it is now an absolute requirement that all
+file discovery callbacks are specified. Just using the find_read_file, but not
+setting open_read_file, for example, does not work any more if kpathsea is not
+to be used at all.
+
+@c
+#define openoutnameok(A)  kpse_out_name_ok (A)
+#define openinnameok(A)  kpse_in_name_ok (A)
+
+@  Open an input file F, using the kpathsea format FILEFMT and passing
+   |FOPEN_MODE| to fopen.  The filename is in `fn'.  We return whether or
+   not the open succeeded.
+
+@c
+boolean
+luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt,
+                  const_string fopen_mode, boolean must_exist)
+{
+    string fname = NULL;
+    /* We havent found anything yet. */
+    *f_ptr = NULL;
+    if (fullnameoffile)
+        free(fullnameoffile);
+    fullnameoffile = NULL;
+    fname = kpse_find_file(fn, (kpse_file_format_type) filefmt, must_exist);
+    if (fname) {
+        fullnameoffile = xstrdup(fname);
+        /* If we found the file in the current directory, don't leave
+           the `./' at the beginning of `fn', since it looks
+           dumb when `tex foo' says `(./foo.tex ... )'.  On the other
+           hand, if the user said `tex ./foo', and that's what we
+           opened, then keep it -- the user specified it, so we
+           shouldn't remove it.  */
+        if (fname[0] == '.' && IS_DIR_SEP(fname[1])
+            && (fn[0] != '.' || !IS_DIR_SEP(fn[1]))) {
+            unsigned i = 0;
+            while (fname[i + 2] != 0) {
+                fname[i] = fname[i + 2];
+                i++;
+            }
+            fname[i] = 0;
+        }
+        /* This fopen is not allowed to fail. */
+        *f_ptr = xfopen(fname, fopen_mode);
+    }
+    if (*f_ptr) {
+        recorder_record_input(fname);
+    }
+    return *f_ptr != NULL;
+}
+
+@ @c
+boolean luatex_open_output(FILE ** f_ptr, const char *fn,
+                           const_string fopen_mode)
+{
+    char *fname;
+    boolean absolute = kpse_absolute_p(fn, false);
+
+    /* If we have an explicit output directory, use it. */
+    if (output_directory && !absolute) {
+        fname = concat3(output_directory, DIR_SEP_STRING, fn);
+    } else {
+        fname = xstrdup(fn);
+    }
+
+    /* Is the filename openable as given?  */
+    *f_ptr = fopen(fname, fopen_mode);
+
+    if (!*f_ptr) {
+        /* Can't open as given.  Try the envvar.  */
+        string texmfoutput = kpse_var_value("TEXMFOUTPUT");
+
+        if (texmfoutput && *texmfoutput && !absolute) {
+            fname = concat3(texmfoutput, DIR_SEP_STRING, fn);
+            *f_ptr = fopen(fname, fopen_mode);
+        }
+    }
+    if (*f_ptr) {
+        recorder_record_output(fname);
+    }
+    free(fname);
+    return *f_ptr != NULL;
+}
+
+
+@ @c
+boolean lua_a_open_in(alpha_file * f, char *fn, int n)
+{
+    int k;
+    char *fnam;                 /* string returned by find callback */
+    int callback_id;
+    boolean ret = true;         /* return value */
+    boolean file_ok = true;     /* the status so far  */
+    if (n == 0) {
+        input_file_callback_id[iindex] = 0;
+    } else {
+        read_file_callback_id[n] = 0;
+    }
+    if (*fn == '|')
+        fnam = fn;
+    else
+        fnam = luatex_find_read_file(fn, n, find_read_file_callback);
+    if (!fnam)
+        return false;
+    callback_id = callback_defined(open_read_file_callback);
+    if (callback_id > 0) {
+        k = run_and_save_callback(callback_id, "S->", fnam);
+        if (k > 0) {
+            ret = true;
+            if (n == 0)
+                input_file_callback_id[iindex] = k;
+            else
+                read_file_callback_id[n] = k;
+        } else {
+            file_ok = false;    /* read failed */
+        }
+    } else {                    /* no read callback */
+        if (openinnameok(fnam)) {
+            ret =
+                open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE,
+                                (n == 0 ? true : false));
+        } else {
+            file_ok = false;    /* open failed */
+        }
+    }
+    if (!file_ok) {
+        ret = false;
+    }
+    return ret;
+}
+
+
+@ @c
+boolean lua_a_open_out(alpha_file * f, char *fn, int n)
+{
+    boolean test;
+    str_number fnam;
+    int callback_id;
+    boolean ret = false;
+    callback_id = callback_defined(find_write_file_callback);
+    if (callback_id > 0) {
+        fnam = 0;
+        test = run_callback(callback_id, "dS->s", n, fn, &fnam);
+        if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
+            /* There is no message here because if that is needed the macro package */
+            /* should do that in the callback code. As elsewhere, messaging is left */
+            /* to lua then. */
+            ret = open_outfile(f, fn, FOPEN_W_MODE);
+        }
+    } else {
+        if (openoutnameok(fn)) {
+            if (n > 0 && selector != term_only) {
+                /* This message to the log is for downward compatibility with other tex's  */
+                /* as there are scripts out there that act on this message. An alternative */
+                /* is to let a macro package write an explicit message. */
+                fprintf(log_file,"\n\\openout%i = %s\n",n-1,fn);
+             }
+             ret = open_out_or_pipe(f, fn, FOPEN_W_MODE);
+        }
+    }
+    return ret;
+}
+
+
+@ @c
+boolean lua_b_open_out(alpha_file * f, char *fn)
+{
+    boolean test;
+    str_number fnam;
+    int callback_id;
+    boolean ret = false;
+    callback_id = callback_defined(find_output_file_callback);
+    if (callback_id > 0) {
+        fnam = 0;
+        test = run_callback(callback_id, "S->s", fn, &fnam);
+        if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
+            ret = open_outfile(f, fn, FOPEN_WBIN_MODE);
+        }
+    } else {
+        if (openoutnameok(fn)) {
+            ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE);
+        }
+    }
+    return ret;
+}
+
+@ @c
+void lua_a_close_in(alpha_file f, int n)
+{                               /* close a text file */
+    int callback_id;
+    if (n == 0)
+        callback_id = input_file_callback_id[iindex];
+    else
+        callback_id = read_file_callback_id[n];
+    if (callback_id > 0) {
+        run_saved_callback(callback_id, "close", "->");
+        destroy_saved_callback(callback_id);
+        if (n == 0)
+            input_file_callback_id[iindex] = 0;
+        else
+            read_file_callback_id[n] = 0;
+    } else {
+        close_file_or_pipe(f);
+    }
+}
+
+@ @c
+void lua_a_close_out(alpha_file f)
+{                               /* close a text file */
+    close_file_or_pipe(f);
+}
+
+
+@ Binary input and output are done with C's ordinary
+procedures, so we don't have to make any other special arrangements for
+binary~I/O. Text output is also easy to do with standard routines.
+The treatment of text input is more difficult, however, because
+of the necessary translation to |ASCII_code| values.
+\TeX's conventions should be efficient, and they should
+blend nicely with the user's operating environment.
+
+Input from text files is read one line at a time, using a routine called
+|lua_input_ln|. This function is defined in terms of global variables called
+|buffer|, |first|, and |last| that will be described in detail later; for
+now, it suffices for us to know that |buffer| is an array of |ASCII_code|
+values, and that |first| and |last| are indices into this array
+representing the beginning and ending of a line of text.
+
+@c
+packed_ASCII_code *buffer;      /* lines of characters being read */
+int first;                      /* the first unused position in |buffer| */
+int last;                       /* end of the line just input to |buffer| */
+int max_buf_stack;              /* largest index used in |buffer| */
+
+
+@ The |lua_input_ln| function brings the next line of input from the specified
+file into available positions of the buffer array and returns the value
+|true|, unless the file has already been entirely read, in which case it
+returns |false| and sets |last:=first|.  In general, the |ASCII_code|
+numbers that represent the next line of the file are input into
+|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
+global variable |last| is set equal to |first| plus the length of the
+line. Trailing blanks are removed from the line; thus, either |last=first|
+(in which case the line was entirely blank) or |buffer[last-1]<>" "|.
+
+An overflow error is given, however, if the normal actions of |lua_input_ln|
+would make |last>=buf_size|; this is done so that other parts of \TeX\
+can safely look at the contents of |buffer[last+1]| without overstepping
+the bounds of the |buffer| array. Upon entry to |lua_input_ln|, the condition
+|first<buf_size| will always hold, so that there is always room for an
+``empty'' line.
+
+The variable |max_buf_stack|, which is used to keep track of how large
+the |buf_size| parameter must be to accommodate the present job, is
+also kept up to date by |lua_input_ln|.
+
+If the |bypass_eoln| parameter is |true|, |lua_input_ln| will do a |get|
+before looking at the first character of the line; this skips over
+an |eoln| that was in |f^|. The procedure does not do a |get| when it
+reaches the end of the line; therefore it can be used to acquire input
+from the user's terminal as well as from ordinary text files.
+
+Since the inner loop of |lua_input_ln| is part of \TeX's ``inner loop''---each
+character of input comes in at this place---it is wise to reduce system
+overhead by making use of special routines that read in an entire array
+of characters at once, if such routines are available.
+@^inner loop@>
+
+@c
+boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln)
+{
+    boolean lua_result;
+    int last_ptr;
+    int callback_id;
+    (void) bypass_eoln;         /* todo: variable can be removed */
+    if (n == 0)
+        callback_id = input_file_callback_id[iindex];
+    else
+        callback_id = read_file_callback_id[n];
+    if (callback_id > 0) {
+        last = first;
+        last_ptr = first;
+        lua_result =
+            run_saved_callback(callback_id, "reader", "->l", &last_ptr);
+        if ((lua_result == true) && (last_ptr != 0)) {
+            last = last_ptr;
+            if (last > max_buf_stack)
+                max_buf_stack = last;
+        } else {
+            lua_result = false;
+        }
+    } else {
+        lua_result = input_ln(f, bypass_eoln);
+    }
+    if (lua_result == true) {
+        /* Fix up the input buffer using callbacks */
+        if (last >= first) {
+            callback_id = callback_defined(process_input_buffer_callback);
+            if (callback_id > 0) {
+                last_ptr = first;
+                lua_result =
+                    run_callback(callback_id, "l->l", (last - first),
+                                 &last_ptr);
+                if ((lua_result == true) && (last_ptr != 0)) {
+                    last = last_ptr;
+                    if (last > max_buf_stack)
+                        max_buf_stack = last;
+                }
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+
+@ We need a special routine to read the first line of \TeX\ input from
+the user's terminal. This line is different because it is read before we
+have opened the transcript file; there is sort of a ``chicken and
+egg'' problem here. If the user types `\.{\\input paper}' on the first
+line, or if some macro invoked by that line does such an \.{\\input},
+the transcript file will be named `\.{paper.log}'; but if no \.{\\input}
+commands are performed during the first line of terminal input, the transcript
+file will acquire its default name `\.{texput.log}'. (The transcript file
+will not contain error messages generated by the first line before the
+first \.{\\input} command.)
+@.texput@>
+
+The first line is special also because it may be read before \TeX\ has
+input a format file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+
+@ Different systems have different ways to get started. But regardless of
+what conventions are adopted, the routine that initializes the terminal
+should satisfy the following specifications:
+
+\yskip\textindent{1)}It should open file |term_in| for input from the
+  terminal. (The file |term_out| will already be open for output to the
+  terminal.)
+
+\textindent{2)}If the user has given a command line, this line should be
+  considered the first line of terminal input. Otherwise the
+  user should be prompted with `\.{**}', and the first line of input
+  should be whatever is typed in response.
+
+\textindent{3)}The first line of input, which might or might not be a
+  command line, should appear in locations |first| to |last-1| of the
+  |buffer| array.
+
+\textindent{4)}The global variable |loc| should be set so that the
+  character to be read next by \TeX\ is in |buffer[loc]|. This
+  character should not be blank, and we should have |loc<last|.
+
+\yskip\noindent(It may be necessary to prompt the user several times
+before a non-blank line comes in. The prompt is `\.{**}' instead of the
+later `\.*' because the meaning is slightly different: `\.{\\input}' need
+not be typed immediately after~`\.{**}'.)
+
+The following program does the required initialization.
+Iff anything has been specified on the command line, then |t_open_in|
+will return with |last > first|.
+@^system dependencies@>
+
+@c
+boolean init_terminal(void)
+{                               /* gets the terminal input started */
+    t_open_in();
+    if (last > first) {
+        iloc = first;
+        while ((iloc < last) && (buffer[iloc] == ' '))
+            incr(iloc);
+        if (iloc < last) {
+            return true;
+        }
+    }
+    while (1) {
+        wake_up_terminal();
+        fputs("**", term_out);
+        update_terminal();
+        if (!input_ln(term_in, true)) {
+            /* this shouldn't happen */
+            fputs("\n! End of file on the terminal... why?\n", term_out);
+            return false;
+        }
+        iloc = first;
+        while ((iloc < last) && (buffer[iloc] == ' '))
+            incr(iloc);
+        if (iloc < last) {
+            return true;        /* return unless the line was all blank */
+        }
+        fputs("Please type the name of your input file.\n", term_out);
+    }
+}
+
+
+@ Here is a procedure that asks the user to type a line of input,
+assuming that the |selector| setting is either |term_only| or |term_and_log|.
+The input is placed into locations |first| through |last-1| of the
+|buffer| array, and echoed on the transcript file if appropriate.
+
+@c
+void term_input(void)
+{                               /* gets a line from the terminal */
+    int k;                      /* index into |buffer| */
+    update_terminal();          /* now the user sees the prompt for sure */
+    if (!input_ln(term_in, true))
+        fatal_error("End of file on the terminal!");
+    term_offset = 0;            /* the user's line ended with \.{<return>} */
+    decr(selector);             /* prepare to echo the input */
+    if (last != first) {
+        for (k = first; k <= last - 1; k++)
+            print_char(buffer[k]);
+    }
+    print_ln();
+    incr(selector);             /* restore previous status */
+}
+
+
+@ It's time now to fret about file names.  Besides the fact that different
+operating systems treat files in different ways, we must cope with the
+fact that completely different naming conventions are used by different
+groups of people. The following programs show what is required for one
+particular operating system; similar routines for other systems are not
+difficult to devise.
+@^fingers@>
+@^system dependencies@>
+
+\TeX\ assumes that a file name has three parts: the name proper; its
+``extension''; and a ``file area'' where it is found in an external file
+system.  The extension of an input file or a write file is assumed to be
+`\.{.tex}' unless otherwise specified; it is `\.{.log}' on the
+transcript file that records each run of \TeX; it is `\.{.tfm}' on the font
+metric files that describe characters in the fonts \TeX\ uses; it is
+`\.{.dvi}' on the output files that specify typesetting information; and it
+is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX.
+The file area can be arbitrary on input files, but files are usually
+output to the user's current area.  If an input file cannot be
+found on the specified area, \TeX\ will look for it on a special system
+area; this special area is intended for commonly used input files like
+\.{webmac.tex}.
+
+Simple uses of \TeX\ refer only to file names that have no explicit
+extension or area. For example, a person usually says `\.{\\input} \.{paper}'
+or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input}
+\.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file
+names are best, because they make the \TeX\ source files portable;
+whenever a file name consists entirely of letters and digits, it should be
+treated in the same way by all implementations of \TeX. However, users
+need the ability to refer to other files in their environment, especially
+when responding to error messages concerning unopenable files; therefore
+we want to let them use the syntax that appears in their favorite
+operating system.
+
+The following procedures don't allow spaces to be part of
+file names; but some users seem to like names that are spaced-out.
+System-dependent changes to allow such things should probably
+be made with reluctance, and only when an entire file name that
+includes spaces is ``quoted'' somehow.
+
+Here are the global values that file names will be scanned into.
+
+@c
+str_number cur_name;            /* name of file just scanned */
+str_number cur_area;            /* file area just scanned, or \.{""} */
+str_number cur_ext;             /* file extension just scanned, or \.{""} */
+
+
+@ The file names we shall deal with have the
+following structure:  If the name contains `\./' or `\.:'
+(for Amiga only), the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null.  If the remaining file name contains
+`\..', the file extension consists of all such characters from the last
+`\..' to the end, otherwise the file extension is null.
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@c
+pool_pointer area_delimiter;    /* the most recent `\./', if any */
+pool_pointer ext_delimiter;     /* the relevant `\..', if any */
+
+
+@ Input files that can't be found in the user's area may appear in a standard
+system area called |TEX_area|. Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|TEX_font_area|.  $\Omega$'s compiled translation process files whose areas
+are not given explicitly are assumed to appear in a standard system area.
+These system area names will, of course, vary from place to place.
+
+@c
+#define append_to_fn(A) do {                                    \
+        c=(A);                                                  \
+        if (c!='"') {                                           \
+            if (k<file_name_size) fn[k++]=(unsigned char)(c);   \
+        }                                                       \
+    } while (0)
+
+
+char *pack_file_name(str_number n, str_number a, str_number e)
+{
+    ASCII_code c;               /* character being packed */
+    unsigned char *j;           /* index into |str_pool| */
+    int k = 0;                  /* number of positions filled in |fn| */
+    unsigned char *fn = xmallocarray(packed_ASCII_code,
+                                     str_length(a) + str_length(n) +
+                                     str_length(e) + 1);
+    for (j = str_string(a); j < str_string(a) + str_length(a); j++)
+        append_to_fn(*j);
+    for (j = str_string(n); j < str_string(n) + str_length(n); j++)
+        append_to_fn(*j);
+    for (j = str_string(e); j < str_string(e) + str_length(e); j++)
+        append_to_fn(*j);
+    fn[k] = 0;
+    return (char *) fn;
+}
+
+
+
+@ A messier routine is also needed, since format file names must be scanned
+before \TeX's string mechanism has been initialized. We shall use the
+global variable |TEX_format_default| to supply the text for default system areas
+and extensions related to format files.
+@^system dependencies@>
+
+Under {\mc UNIX} we don't give the area part, instead depending
+on the path searching that will happen during file opening.  Also, the
+length will be set in the main program.
+
+@c
+char *TEX_format_default;
+
+
+@ This part of the program becomes active when a ``virgin'' \TeX\ is trying to get going,
+just after the preliminary initialization, or when the user is substituting another
+format file by typing `\.\&' after the initial `\.{**}' prompt.  The buffer
+contains the first line of input in |buffer[loc..(last-1)]|, where
+|loc<last| and |buffer[loc]<>" "|.
+
+@c
+char *open_fmt_file(void)
+{
+    int j;                      /* the first space after the format file name */
+    char *fmt = NULL;
+    int dist;
+    j = iloc;
+    if (buffer[iloc] == '&') {
+        incr(iloc);
+        j = iloc;
+        buffer[last] = ' ';
+        while (buffer[j] != ' ')
+            incr(j);
+        fmt = xmalloc((unsigned) (j - iloc + 1));
+        strncpy(fmt, (char *) (buffer + iloc), (size_t) (j - iloc));
+        fmt[j - iloc] = 0;
+        dist = (int) (strlen(fmt) - strlen(DUMP_EXT));
+        if (!(strstr(fmt, DUMP_EXT) == fmt + dist))
+            fmt = concat(fmt, DUMP_EXT);
+        if (zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE))
+            goto FOUND;
+        wake_up_terminal();
+        fprintf(stdout, "Sorry, I can't find the format `%s'; will try `%s'.\n",
+                fmt, TEX_format_default);
+        update_terminal();
+    }
+    /* now pull out all the stops: try for the system \.{plain} file */
+    fmt = TEX_format_default;
+    if (!zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) {
+        wake_up_terminal();
+        fprintf(stdout, "I can't find the format file `%s'!\n",
+                TEX_format_default);
+        return NULL;
+    }
+  FOUND:
+    iloc = j;
+    return fmt;
+}
+
+
+@ The global variable |name_in_progress| is used to prevent recursive
+use of |scan_file_name|, since the |begin_name| and other procedures
+communicate via global variables. Recursion would arise only by
+devious tricks like `\.{\\input\\input f}'; such attempts at sabotage
+must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input}
+@^recursion@>
+from being initiated when a font size specification is being scanned.
+
+Another global variable, |job_name|, contains the file name that was first
+\.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}'
+and `\.{.fmt}' in the names of \TeX's output files.
+
+@c
+boolean name_in_progress;       /* is a file name being scanned? */
+str_number job_name;            /* principal file name */
+boolean log_opened_global;             /* has the transcript file been opened? */
+
+
+@ Initially |job_name=0|; it becomes nonzero as soon as the true name is known.
+We have |job_name=0| if and only if the `\.{log}' file has not been opened,
+except of course for a short time just after |job_name| has become nonzero.
+
+@c
+unsigned char *texmf_log_name;  /* full name of the log file */
+
+@ The |open_log_file| routine is used to open the transcript file and to help
+it catch up to what has previously been printed on the terminal.
+
+@c
+void open_log_file(void)
+{
+    int old_setting;            /* previous |selector| setting */
+    int k;                      /* index into |buffer| */
+    int l;                      /* end of first input line */
+    char *fn;
+    old_setting = selector;
+    if (job_name == 0)
+        job_name = getjobname(maketexstring("texput")); /* TODO */
+    fn = pack_job_name(".fls");
+    recorder_change_filename(fn);
+    fn = pack_job_name(".log");
+    while (!lua_a_open_out(&log_file, fn, 0)) {
+        /* Try to get a different log file name */
+        /* Sometimes |open_log_file| is called at awkward moments when \TeX\ is
+           unable to print error messages or even to |show_context|.
+           The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
+           routine will not be invoked because |log_opened| will be false.
+
+           The normal idea of |batch_mode| is that nothing at all should be written
+           on the terminal. However, in the unusual case that
+           no log file could be opened, we make an exception and allow
+           an explanatory message to be seen.
+
+           Incidentally, the program always refers to the log file as a `\.{transcript
+           file}', because some systems cannot use the extension `\.{.log}' for
+           this file.
+         */
+        selector = term_only;
+        fn = prompt_file_name("transcript file name", ".log");
+    }
+    texmf_log_name = (unsigned char *) xstrdup(fn);
+    selector = log_only;
+    log_opened_global = true;
+    if (callback_defined(start_run_callback) == 0) {
+        /* Print the banner line, including current date and time */
+        log_banner(luatex_version_string);
+
+        input_stack[input_ptr] = cur_input;     /* make sure bottom level is in memory */
+        tprint_nl("**");
+        l = input_stack[0].limit_field; /* last position of first line */
+        if (buffer[l] == end_line_char_par)
+            decr(l);            /* TODO: multichar endlinechar */
+        for (k = 1; k <= l; k++)
+            print_char(buffer[k]);
+        print_ln();             /* now the transcript file contains the first line of input */
+    }
+    flush_loggable_info();      /* should be done always */
+    selector = old_setting + 2; /* |log_only| or |term_and_log| */
+}
+
+@ This function is needed by synctex to make its log appear in the right
+spot when |output_directory| is set.
+
+@c
+char *get_full_log_name (void)
+{
+   if (output_directory) {
+       char *ret  = xmalloc(strlen((char *)texmf_log_name)+2+strlen(output_directory));
+       ret = strcpy(ret, output_directory);
+       strcat(ret, "/");
+       strcat(ret, (char *)texmf_log_name);
+       return ret;
+   } else {
+       return xstrdup((const char*)texmf_log_name);
+   }
+}
+
+@ Synctex uses this to get the anchored path of an input file.
+
+@c
+char *luatex_synctex_get_current_name (void)
+{
+  char *pwdbuf = NULL, *ret;
+  if (kpse_absolute_p(fullnameoffile, false)) {
+     return xstrdup(fullnameoffile);
+  }
+  pwdbuf = xgetcwd();
+  ret = concat3(pwdbuf, DIR_SEP_STRING, fullnameoffile);
+  free(pwdbuf) ;
+  return ret;
+}
+
+
+@ Let's turn now to the procedure that is used to initiate file reading
+when an `\.{\\input}' command is being processed.
+
+@c
+void start_input(void)
+{                               /* \TeX\ will \.{\\input} something */
+    str_number temp_str;
+    char *fn;
+    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 */
+    } else {
+        scan_file_name_toks();
+    }
+    fn = pack_file_name(cur_name, cur_area, cur_ext);
+    while (1) {
+        begin_file_reading();   /* set up |cur_file| and new level of input */
+        if (lua_a_open_in(&cur_file, fn, 0))
+            break;
+        end_file_reading();     /* remove the level that didn't work */
+        fn = prompt_file_name("input file name", "");
+    }
+    iname = maketexstring(fullnameoffile);
+    /* Now that we have |fullnameoffile|, it is time to post-adjust
+      |cur_name| and |cur_ext| for trailing |.tex| */
+    {
+	char *n, *p;
+	n = p = fullnameoffile + strlen(fullnameoffile);
+	while (p>fullnameoffile) {
+	    p--;
+            if (IS_DIR_SEP(*p)) {
+	        break;
+            }
+	}
+	if (IS_DIR_SEP(*p)) {
+	    p++;
+	}
+	while (n>fullnameoffile) {
+	    n--;
+            if (*n == '.') {
+	        break;
+            }
+	}
+	if (n>p) {
+	    int q = *n;
+	    cur_ext = maketexstring(n);
+	    *n = 0;
+	    cur_name = maketexstring(p);
+	    *n = q;
+        }
+    }
+
+
+    source_filename_stack[in_open] = iname;
+    full_source_filename_stack[in_open] = xstrdup(fullnameoffile);
+    /* we can try to conserve string pool space now */
+    temp_str = search_string(iname);
+    if (temp_str > 0) {
+        flush_str(iname);
+        iname = temp_str;
+    }
+    if (job_name == 0) {
+        job_name = getjobname(cur_name);
+        open_log_file();
+    }
+    /* |open_log_file| doesn't |show_context|, so |limit|
+       and |loc| needn't be set to meaningful values yet */
+    report_start_file(filetype_tex,fullnameoffile);
+    incr(open_parens);
+    update_terminal();
+    istate = new_line;
+    /* Prepare new file {\sl Sync\TeX} information */
+    if (! synctex_get_no_files()) {
+        synctexstartinput();        /* Give control to the {\sl Sync\TeX} controller */
+    }
+    /* Read the first line of the new file */
+    /* Here we have to remember to tell the |lua_input_ln| routine not to
+       start with a |get|. If the file is empty, it is considered to
+       contain a single blank line. */
+    line = 1;
+    if (lua_input_ln(cur_file, 0, false)) {
+        ;
+    }
+    firm_up_the_line();
+    if (end_line_char_inactive)
+        decr(ilimit);
+    else
+        buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+    first = ilimit + 1;
+    iloc = istart;
+}
+
+@ Read and write dump files through zlib
+
+@ Earlier versions recast |*f| from |FILE *| to |gzFile|, but there is
+no guarantee that these have the same size, so a static variable
+is needed.
+
+@c
+static gzFile gz_fmtfile = NULL;
+
+@ As distributed, the dump files are
+architecture dependent; specifically, BigEndian and LittleEndian
+architectures produce different files.  These routines always output
+BigEndian files.  This still does not guarantee them to be
+architecture-independent, because it is possible to make a format
+that dumps a glue ratio, i.e., a floating-point number.  Fortunately,
+none of the standard formats do that.
+
+@c
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+
+/* This macro is always invoked as a statement.  It assumes a variable
+   `temp'.  */
+#  define SWAP(x, y) do { temp = x; x = y; y = temp; } while (0)
+
+/* Make the NITEMS items pointed at by P, each of size SIZE, be the
+   opposite-endianness of whatever they are now.  */
+static void
+swap_items(char *pp, int nitems, int size)
+{
+    char temp;
+    unsigned total = (unsigned) (nitems * size);
+    char *q = xmalloc(total);
+    char *p = q;
+    memcpy(p,pp,total);
+    /* Since `size' does not change, we can write a while loop for each
+       case, and avoid testing `size' for each time.  */
+    switch (size) {
+        /* 16-byte items happen on the DEC Alpha machine when we are not
+           doing sharable memory dumps.  */
+    case 16:
+        while (nitems--) {
+            SWAP(p[0], p[15]);
+            SWAP(p[1], p[14]);
+            SWAP(p[2], p[13]);
+            SWAP(p[3], p[12]);
+            SWAP(p[4], p[11]);
+            SWAP(p[5], p[10]);
+            SWAP(p[6], p[9]);
+            SWAP(p[7], p[8]);
+            p += size;
+        }
+        break;
+
+    case 12:
+        while (nitems--) {
+            SWAP(p[0], p[11]);
+            SWAP(p[1], p[10]);
+            SWAP(p[2], p[9]);
+            SWAP(p[3], p[8]);
+            SWAP(p[4], p[7]);
+            SWAP(p[5], p[6]);
+            p += size;
+        }
+        break;
+
+    case 8:
+        while (nitems--) {
+            SWAP(p[0], p[7]);
+            SWAP(p[1], p[6]);
+            SWAP(p[2], p[5]);
+            SWAP(p[3], p[4]);
+            p += size;
+        }
+        break;
+
+    case 4:
+        while (nitems--) {
+            SWAP(p[0], p[3]);
+            SWAP(p[1], p[2]);
+            p += size;
+        }
+        break;
+
+    case 2:
+        while (nitems--) {
+            SWAP(p[0], p[1]);
+            p += size;
+        }
+        break;
+
+    case 1:
+        /* Nothing to do.  */
+        break;
+
+    default:
+        FATAL1("Can't swap a %d-byte item for (un)dumping", size);
+    }
+    memcpy(pp,q,total);
+    xfree(q);
+}
+#endif                          /* not WORDS_BIGENDIAN and not NO_DUMP_SHARE */
+
+@ That second swap is to make sure following calls don't get
+confused in the case of |dump_things|.
+
+@c
+void do_zdump(char *p, int item_size, int nitems, FILE * out_file)
+{
+    int err;
+    (void) out_file;
+    if (nitems == 0)
+        return;
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+    swap_items(p, nitems, item_size);
+#endif
+    if (gzwrite(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) !=
+        item_size * nitems) {
+        fprintf(stderr, "! Could not write %d %d-byte item(s): %s.\n", nitems,
+                item_size, gzerror(gz_fmtfile, &err));
+        uexit(1);
+    }
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+    swap_items(p, nitems, item_size);
+#endif
+}
+
+@ @c
+void do_zundump(char *p, int item_size, int nitems, FILE * in_file)
+{
+    int err;
+    (void) in_file;
+    if (nitems == 0)
+        return;
+    if (gzread(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) <= 0) {
+        fprintf(stderr, "Could not undump %d %d-byte item(s): %s.\n",
+                nitems, item_size, gzerror(gz_fmtfile, &err));
+        uexit(1);
+    }
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+    swap_items(p, nitems, item_size);
+#endif
+}
+
+@ @c
+#define COMPRESSION "R3"
+
+boolean zopen_w_input(FILE ** f, const char *fname, int format,
+                      const_string fopen_mode)
+{
+    int callbackid;
+    int res;
+    char *fnam;
+    callbackid = callback_defined(find_format_file_callback);
+    if (callbackid > 0) {
+        res = run_callback(callbackid, "S->R", fname, &fnam);
+        if (res && fnam && strlen(fnam) > 0) {
+            *f = fopen(fnam, fopen_mode);
+            if (*f == NULL) {
+                return 0;
+            }
+        } else {
+            return 0;
+        }
+    } else {
+        res = luatex_open_input(f, fname, format, fopen_mode, true);
+    }
+    if (res) {
+        gz_fmtfile = gzdopen(fileno(*f), "rb" COMPRESSION);
+    }
+    return res;
+}
+
+@ @c
+boolean zopen_w_output(FILE ** f, const char *s, const_string fopen_mode)
+{
+    int res = 1;
+    if (luainit) {
+        *f = fopen(s, fopen_mode);
+        if (*f == NULL) {
+            return 0;
+        }
+    } else {
+        res = luatex_open_output(f, s, fopen_mode);
+    }
+    if (res) {
+        gz_fmtfile = gzdopen(fileno(*f), "wb" COMPRESSION);
+    }
+    return res;
+}
+
+@ @c
+void zwclose(FILE * f)
+{
+    (void) f;
+    gzclose(gz_fmtfile);
+}
+
+@  create the dvi or pdf file
+@c
+int open_outfile(FILE ** f, const char *name, const char *mode)
+{
+    FILE *res;
+    res = fopen(name, mode);
+    if (res != NULL) {
+        *f = res;
+        return 1;
+    }
+    return 0;
+}
+
+
+@ the caller should set |tfm_buffer=NULL| and |tfm_size=0|
+@c
+int readbinfile(FILE * f, unsigned char **tfm_buffer, int *tfm_size)
+{
+    void *buf;
+    int size;
+    if (fseek(f, 0, SEEK_END) == 0) {
+        size = (int) ftell(f);
+        if (size > 0) {
+            buf = xmalloc((unsigned) size);
+            if (fseek(f, 0, SEEK_SET) == 0) {
+                if (fread((void *) buf, (size_t) size, 1, f) == 1) {
+                    *tfm_buffer = (unsigned char *) buf;
+                    *tfm_size = size;
+                    return 1;
+                }
+            }
+        } else {
+            *tfm_buffer = NULL;
+            *tfm_size = 0;
+            return 1;
+        }
+    }                           /* seek failed, or zero-sized file */
+    return 0;
+}
+
+@ Like |os.execute()|, the |runpopen()| function is called only when
+|shellenabledp == 1|. Unlike |os.execute()| we write errors to stderr, since we
+have nowhere better to use; and of course we return a file handle (or NULL)
+instead of a status indicator.
+
+@c
+static FILE *runpopen(char *cmd, const char *mode)
+{
+    FILE *f = NULL;
+    char *safecmd = NULL;
+    char *cmdname = NULL;
+    int allow;
+
+#ifdef WIN32
+    char *pp;
+
+    for (pp = cmd; *pp; pp++) {
+      if (*pp == '\'') *pp = '"';
+    }
+#endif
+
+    /* If restrictedshell == 0, any command is allowed. */
+    if (restrictedshell == 0) {
+        allow = 1;
+    } else {
+        const char *thecmd = cmd;
+        allow = shell_cmd_is_allowed(thecmd, &safecmd, &cmdname);
+    }
+    if (allow == 1)
+        f = popen(cmd, mode);
+    else if (allow == 2)
+        f = popen(safecmd, mode);
+    else if (allow == -1)
+        fprintf(stderr, "\nrunpopen quotation error in command line: %s\n",
+                cmd);
+    else
+        fprintf(stderr, "\nrunpopen command not allowed: %s\n", cmdname);
+
+    if (safecmd)
+        free(safecmd);
+    if (cmdname)
+        free(cmdname);
+    return f;
+}
+
+@  piped I/O
+
+
+@ The code that implements |popen()| needs an array for tracking
+   possible pipe file pointers, because these need to be
+   closed using |pclose()|.
+
+@c
+#define NUM_PIPES 16
+static FILE *pipes[NUM_PIPES];
+
+#ifdef WIN32
+FILE *Poptr;
+#endif
+
+boolean open_in_or_pipe(FILE ** f_ptr, char *fn, int filefmt,
+                        const_string fopen_mode, boolean must_exist)
+{
+    string fname = NULL;
+    int i;                      /* iterator */
+
+    /* opening a read pipe is straightforward, only have to
+       skip past the pipe symbol in the file name. filename
+       quoting is assumed to happen elsewhere (it does :-)) */
+
+    if (shellenabledp && *fn == '|') {
+        /* the user requested a pipe */
+        *f_ptr = NULL;
+        fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
+        strcpy(fname, fn);
+        if (fullnameoffile)
+            free(fullnameoffile);
+        fullnameoffile = xstrdup(fname);
+        recorder_record_input(fname + 1);
+        *f_ptr = runpopen(fname + 1, "r");
+        free(fname);
+        for (i = 0; i < NUM_PIPES; i++) {
+            if (pipes[i] == NULL) {
+                pipes[i] = *f_ptr;
+                break;
+            }
+        }
+        if (*f_ptr)
+            setvbuf(*f_ptr, (char *) NULL, _IONBF, 0);
+#ifdef WIN32
+        Poptr = *f_ptr;
+#endif
+
+        return *f_ptr != NULL;
+    }
+
+    return luatex_open_input(f_ptr, fn, filefmt, fopen_mode, must_exist);
+}
+
+
+boolean open_out_or_pipe(FILE ** f_ptr, char *fn, const_string fopen_mode)
+{
+    string fname;
+    int i;                      /* iterator */
+
+    /* opening a write pipe takes a little bit more work, because TeX
+       will perhaps have appended ".tex".  To avoid user confusion as
+       much as possible, this extension is stripped only when the command
+       is a bare word.  Some small string trickery is needed to make
+       sure the correct number of bytes is free()-d afterwards */
+
+    if (shellenabledp && *fn == '|') {
+        /* the user requested a pipe */
+        fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
+        strcpy(fname, fn);
+        if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) {
+            /* mp and mf currently do not use this code, but it
+               is better to be prepared */
+            if (STREQ((fname + strlen(fname) - 3), "tex"))
+                *(fname + strlen(fname) - 4) = 0;
+            *f_ptr = runpopen(fname + 1, "w");
+            *(fname + strlen(fname)) = '.';
+        } else {
+            *f_ptr = runpopen(fname + 1, "w");
+        }
+        recorder_record_output(fname + 1);
+        free(fname);
+
+        for (i = 0; i < NUM_PIPES; i++) {
+            if (pipes[i] == NULL) {
+                pipes[i] = *f_ptr;
+                break;
+            }
+        }
+
+        if (*f_ptr)
+            setvbuf(*f_ptr, (char *) NULL, _IONBF, 0);
+
+        return *f_ptr != NULL;
+    }
+
+    return luatex_open_output(f_ptr, fn, fopen_mode);
+}
+
+
+void close_file_or_pipe(FILE * f)
+{
+    int i;                      /* iterator */
+
+    if (shellenabledp) {
+        for (i = 0; i <= 15; i++) {
+        /* if this file was a pipe, |pclose()| it and return */
+            if (pipes[i] == f) {
+                if (f) {
+                    pclose(f);
+#ifdef WIN32
+                    Poptr = NULL;
+#endif
+                }
+                pipes[i] = NULL;
+                return;
+            }
+        }
+    }
+    close_file(f);
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/texmath.w
@@ -0,0 +1,2592 @@
+% 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
+#define math_class_to_type(target,source) \
+    switch (source) { \
+        case 0: target = ord_noad_type; break; \
+        case 1: target = op_noad_type_normal; break; \
+        case 2: target = bin_noad_type; break; \
+        case 3: target = rel_noad_type; break; \
+        case 4: target = open_noad_type; break; \
+        case 5: target = close_noad_type; break; \
+        case 6: target = punct_noad_type; break; \
+    }
+
+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 {
+            math_class_to_type(subtype(p),mval.class_value);
+        }
+        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
+static void do_sub_sup(int no)
+{
+    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);
+        }
+        if (no) {
+            noadoptions(tail) = noadoptions(tail) | noad_option_no_super_script ;
+        }
+        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);
+        }
+        if (no) {
+            noadoptions(tail) = noadoptions(tail) | noad_option_no_sub_script ;
+        }
+        q = new_node(math_char_node, 0);
+        subscr(tail) = q;
+        (void) scan_math(subscr(tail), sub_style(m_style));
+    }
+}
+
+void sub_sup(void)
+{
+    do_sub_sup(0);
+}
+
+void no_sub_sup(void)
+{
+    do_sub_sup(1);
+}
+
+
+@ 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();
+                math_class_to_type(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;
+        delimitersamesize(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) \
+    if (g > 0) { \
+        switch (display_skip_mode_par) { \
+            case 0 : /* normal tex | always */ \
+            case 1 : /* always */ \
+                tail_append(new_param_glue(g)); \
+                break; \
+            case 2 : /* non-zero */ \
+                if (! glue_is_zero(glue_par(g))) \
+                    tail_append(new_param_glue(g)); \
+                break; \
+            case 3: /* ignore */ \
+                break; \
+            default: /* > 3 reserved for future use */ \
+                tail_append(new_param_glue(g)); \
+                break; \
+        } \
+    }
+
+#define inject_display_skip_after(g) \
+    if (g > 0) { \
+        switch (display_skip_mode_par) { \
+            case 0 : /* normal tex | always */ \
+            case 1 : /* always */ \
+                tail_append(new_param_glue(g)); \
+                break; \
+            case 2 : /* non-zero */ \
+                if (! glue_is_zero(glue_par(g))) \
+                    tail_append(new_param_glue(g)); \
+                break; \
+            case 3: /* ignore */ \
+                break; \
+            default: /* > 3 reserved for future use */ \
+                tail_append(new_param_glue(g)); \
+                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);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/texmath.c
+++ /dev/null
@@ -1,2723 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#define mode     mode_par
-#define tail     tail_par
-#define head     head_par
-#define dir_save dirs_par
-
-/*tex
-
-    Concerning display skips, \TEX\ normally always inserts before and only after
-    when larger than zero. THis can ow be controlled with |\mathdisplayskipmode|:
-
-    \starttabulate
-    \NC 0 \NC normal \TEX \NC \NR
-    \NC 1 \NC always      \NC \NR
-    \NC 2 \NC non-zero    \NC \NR
-    \NC 3 \NC ignore      \NC \NR
-    \stoptabulate
-
-*/
-
-#define back_error(A,B) do {                    \
-    OK_to_interrupt=false;                      \
-    back_input();                               \
-    OK_to_interrupt=true;                       \
-    tex_error(A,B);                             \
-  } while (0)
-
-int scan_math(pointer, int);
-int scan_math_style(pointer, int);
-pointer fin_mlist(pointer);
-
-/*tex
-
-    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|.
-
-    \startitemize
-
-        \startitem
-            |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.
-        \stopitem
-
-        \startitem
-            |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.)
-        \stopitem
-
-        \startitem
-            |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.
-        \stopitem
-
-        \startitem
-            |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.
-        \stopitem
-
-        \startitem
-            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).
-        \stopitem
-
-    \startitemize
-
-*/
-
-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);
-}
-
-/*tex
-
-    Sometimes it is necessary to destroy an mlist. The following subroutine
-    empties the current list, assuming that |abs(mode)=mmode|.
-
-*/
-
-void flush_math(void)
-{
-    flush_node_list(vlink(head));
-    flush_node_list(incompleat_noad_par);
-    vlink(head) = null;
-    tail = head;
-    incompleat_noad_par = null;
-}
-
-/*tex Before we can do anything in math mode, we need fonts. */
-
-#define MATHFONTSTACK  8
-#define MATHFONTDEFAULT 0
-
-static sa_tree math_fam_head = NULL;
-
-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);
-    }
-}
-
-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);
-            /*tex 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)--;
-    }
-}
-
-/*tex Parameters */
-
-#define MATHPARAMSTACK  8
-#define MATHPARAMDEFAULT undefined_math_parameter
-
-static sa_tree math_param_head = NULL;
-
-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;
-}
-
-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);
-            /*tex 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)--;
-    }
-}
-
-/*tex Saving and unsaving of both: */
-
-void unsave_math_data(int gl)
-{
-    unsave_math_fam_data(gl);
-    unsave_math_param_data(gl);
-}
-
-/*tex Dumping and undumping: */
-
-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");
-}
-
-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;
-}
-
-/*tex
-
-    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}.
-
-    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
-
-*/
-
-pointer new_noad(void)
-{
-    pointer p;
-    p = new_node(simple_noad, ord_noad_type);
-    /*tex All noad fields are zero after this. */
-    return p;
-}
-
-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;
-}
-
-/*tex
-
-    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.
-
-*/
-
-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
-};
-
-pointer new_style(small_number s)
-{
-    m_style = s;
-    return new_node(style_node, s);
-}
-
-/*tex
-
-    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.
-
-*/
-
-static pointer new_choice(void)
-{
-    return new_node(choice_node, 0);
-}
-
-/*tex
-
-    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\}\}\}\}\$}'.
-
-*/
-
-void display_normal_noad(pointer p);
-void display_fence_noad(pointer p);
-void display_fraction_noad(pointer p);
-
-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;
-    }
-}
-
-/*tex Here are some simple routines used in the display of noads. */
-
-static void print_fam_and_char(pointer p)
-{
-    tprint_esc("fam");
-    print_int(math_fam(p));
-    print_char(' ');
-    print(math_character(p));
-}
-
-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) {
-        /*tex This should never happen. */
-        print_int(-1);
-    } else if (small_fam(p) < 16 && large_fam(p) < 16 && small_char(p) < 256 && large_char(p) < 256) {
-        /*tex Traditional tex style. */
-        a = small_fam(p) * 256 + small_char(p);
-        a = a * 0x1000 + large_fam(p) * 256 + large_char(p);
-        print_qhex(a);
-    } else if ((large_fam(p) == 0 && large_char(p) == 0) || small_char(p) > 65535 || large_char(p) > 65535) {
-        /*tex \LUATEX\ style. */
-        print_qhex(small_fam(p));
-        print_qhex(small_char(p));
-    }
-}
-
-/*tex
-
-    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).
-
-*/
-
-static void print_subsidiary_data(pointer p, ASCII_code c)
-{
-    if ((int) cur_length >= depth_threshold) {
-        if (p != null)
-            tprint(" []");
-    } else {
-        /*tex Include |c| in the recursion history. */
-        append_char(c);
-        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;
-            }
-        }
-        /*tex Remove |c| from the recursion history. */
-        flush_char();
-    }
-}
-
-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), '_');
-}
-
-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));
-}
-
-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), '/');
-}
-
-/*tex
-
-    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|.
-
-*/
-
-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);
-}
-
-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);
-}
-
-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);
-}
-
-void enter_display_math(void);
-
-/*tex
-
-    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.
-
-*/
-
-void init_math(void)
-{
-    if (cur_cmd == math_shift_cmd) {
-        /*tex |get_x_token| would fail on \.{\\ifmmode}\thinspace! */
-        get_token();
-        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();
-    }
-}
-
-/*tex
-
-    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.
-
-*/
-
-void start_eq_no(void)
-{
-    set_saved_record(0, saved_eqno, 0, cur_chr);
-    incr(save_ptr);
-    enter_ordinary_math();
-}
-
-/*tex
-
-    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.
-
-*/
-
-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);
-}
-
-/*tex
-
-    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.
-
-*/
-
-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;
-}
-
-/*tex
-
-    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|.
-
-*/
-
-void enter_display_math(void)
-{
-    /*tex new or partial |pre_display_size| */
-    scaled w;
-    /*tex new |display_width| */
-    scaled l;
-    /*tex new |display_indent| */
-    scaled s;
-    pointer p;
-    /*tex scope of paragraph shape specification */
-    int n;
-    /*tex
-
-        `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' or the 2nd of \.{\$\${ }\$\$}
-        \.{\$\${ }\$\$}
-
-    */
-    if (head == tail ||
-        (vlink(head) == tail &&
-         type(tail) == local_par_node && vlink(tail) == null)) {
-        if (vlink(head) == tail) {
-            /*tex
-
-                |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);
-    }
-    /*tex
-
-        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();
-    }
-}
-
-/*tex
-
-    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}).
-
-*/
-
-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) {
-        /*tex
-
-            \.{\\delcode}, this is the easiest
-
-        */
-        scan_int();
-        /*tex  "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) {
-        /*tex
-
-            \.{\\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) {
-        /*tex
-
-           \.{\\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 {
-        /*tex 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;
-}
-
-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));
-}
-
-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) {
-        /*tex \.{\\mathcode}: "TFCC */
-        scan_int();
-        if (cur_val > 0x8000) {
-            /*tex 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) {
-        /*tex <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) {
-        /*tex
-
-            "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 {
-        /*tex Something's gone wrong. */
-        confusion("unknown_extcode");
-    }
-    d.class_value = mcls;
-    d.family_value = mfam;
-    d.character_value = mchr;
-    return d;
-}
-
-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));
-}
-
-/*tex This reads in a delcode when actually a mathcode is needed. */
-
-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;
-}
-
-/*tex
-
-    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|.
-
-*/
-
-#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)
-{
-    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) {
-            /*tex 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:
-        /*tex
-
-            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;
-}
-
-/*tex
-
-    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.
-
-*/
-
-#define math_class_to_type(target,source) \
-    switch (source) { \
-        case 0: target = ord_noad_type; break; \
-        case 1: target = op_noad_type_normal; break; \
-        case 2: target = bin_noad_type; break; \
-        case 3: target = rel_noad_type; break; \
-        case 4: target = open_noad_type; break; \
-        case 5: target = close_noad_type; break; \
-        case 6: target = punct_noad_type; break; \
-    }
-
-void set_math_char(mathcodeval mval)
-{
-    pointer p;
-    if (mval.class_value == 8) {
-        /*tex 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 {
-            math_class_to_type(subtype(p),mval.class_value);
-        }
-        vlink(tail) = p;
-        tail = p;
-    }
-}
-
-/*tex
-
-    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.
-
-*/
-
-void math_char_in_text(mathcodeval mval)
-{
-    pointer p;
-    if (mval.class_value == 8) {
-        /*tex 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;
-    }
-}
-
-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);
-}
-
-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);
-}
-
-/*tex
-
-     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.
-
-*/
-
-static void scan_delimiter(pointer p, int r)
-{
-    delcodeval dval = { 0, 0, 0, 0, 0 };
-    if (r == tex_mathcode) {
-        /*tex \.{\\radical} */
-        dval = do_scan_extdef_del_code(tex_mathcode, true);
-    } else if (r == umath_mathcode) {
-        /*tex \.{\\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) {
-                /*tex \.{\\delimiter} */
-                dval = do_scan_extdef_del_code(tex_mathcode, true);
-            } else if (cur_chr == 1) {
-                /*tex \.{\\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;
-}
-
-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)
-        /*tex \.{\\radical} */
-        scan_delimiter(left_delimiter(tail), tex_mathcode);
-    else if (chr_code == 1)
-        /*tex \.{\\Uradical} */
-        scan_delimiter(left_delimiter(tail), umath_mathcode);
-    else if (chr_code == 2)
-        /*tex \.{\\Uroot} */
-        scan_delimiter(left_delimiter(tail), umath_mathcode);
-    else if (chr_code == 3)
-        /*tex \.{\\Uunderdelimiter} */
-        scan_delimiter(left_delimiter(tail), umath_mathcode);
-    else if (chr_code == 4)
-        /*tex \.{\\Uoverdelimiter} */
-        scan_delimiter(left_delimiter(tail), umath_mathcode);
-    else if (chr_code == 5)
-        /*tex \.{\\Udelimiterunder} */
-        scan_delimiter(left_delimiter(tail), umath_mathcode);
-    else if (chr_code == 6)
-        /*tex \.{\\Udelimiterover} */
-        scan_delimiter(left_delimiter(tail), umath_mathcode);
-    else if (chr_code == 7)
-        /*tex \.{\\Uhextensible} */
-        scan_delimiter(left_delimiter(tail), umath_mathcode);
-    else
-        confusion("math_radical");
-    if (chr_code == 7) {
-        /*tex type will change */
-        q = new_node(sub_box_node, 0);
-        nucleus(tail) = q;
-        return;
-    } else if (chr_code == 2) {
-        /*tex
-
-            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));
-    }
-}
-
-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) {
-        /*tex \.{\\mathaccent} */
-        t = scan_mathchar(tex_mathcode);
-    } else if (cur_chr == 1) {
-        /*tex \.{\\Umathaccent} */
-        if (scan_keyword("fixed")) {
-            /*tex top */
-            subtype(tail) = 1;
-            t = scan_mathchar(umath_mathcode);
-        } else if (scan_keyword("both")) {
-            /*tex 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")) {
-            /*tex bottom */
-            if (scan_keyword("fixed")) {
-                subtype(tail) = 2;
-            }
-            b = scan_mathchar(umath_mathcode);
-        } else if (scan_keyword("top")) {
-            /*tex 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 {
-            /*tex 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));
-}
-
-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;
-}
-
-/*tex
-
-    The routine that scans the four mlists of a \.{\\mathchoice} is very much
-    like the routine that builds discretionary nodes.
-
-*/
-
-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();
-}
-
-void build_choices(void)
-{
-    pointer p;
-    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;
-    }
-    set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1));
-    push_math(math_choice_group, (prev_style + 2));
-    scan_left_brace();
-}
-
-/*tex
-
-    Subscripts and superscripts are attached to the previous nucleus by the
-    action procedure called |sub_sup|.
-
-*/
-
-static void do_sub_sup(int no)
-{
-    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) {
-        /*tex |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);
-        }
-        if (no) {
-            noadoptions(tail) = noadoptions(tail) | noad_option_no_super_script ;
-        }
-        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);
-        }
-        if (no) {
-            noadoptions(tail) = noadoptions(tail) | noad_option_no_sub_script ;
-        }
-        q = new_node(math_char_node, 0);
-        subscr(tail) = q;
-        (void) scan_math(subscr(tail), sub_style(m_style));
-    }
-}
-
-void sub_sup(void)
-{
-    do_sub_sup(0);
-}
-
-void no_sub_sup(void)
-{
-    do_sub_sup(1);
-}
-
-/*tex
-
-    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.
-
-*/
-
-void math_fraction(void)
-{
-    /*tex The type of generalized fraction we are scanning: */
-    halfword c;
-    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;
-        }
-    }
-}
-
-/*tex
-
-    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.
-
-*/
-
-pointer fin_mlist(pointer p)
-{
-    pointer q;
-    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");
-            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;
-}
-
-/*tex
-
-    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.
-
-*/
-
-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 && vlink(p) == null) {
-        if (type(p) == simple_noad) {
-            if (subscr(p) == null && supscr(p) == null) {
-                /*tex (subtype(p) == ord_noad_type) */
-                int flatten = 0;
-                int modepar = math_flatten_mode_par;
-                switch (subtype(p)) {
-                    case ord_noad_type :
-                        flatten = (modepar & 1) == 1;
-                        break;
-                    case bin_noad_type :
-                        flatten = (modepar & 2) == 2;
-                        break;
-                    case rel_noad_type :
-                        flatten = (modepar & 4) == 4;
-                        break;
-                    case punct_noad_type :
-                        flatten = (modepar & 8) == 8;
-                        break;
-                    case inner_noad_type :
-                        flatten = (modepar & 16) == 16;
-                        break;
-                    default:
-                        break;
-                }
-                if (flatten) {
-                    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) && 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);
-        /*tex restart */
-    }
-}
-
-/*tex
-
-    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}.
-
-*/
-
-void math_left_right(void)
-{
-    /*tex |left_noad_side| .. |right_noad_side| */
-    halfword t;
-    /*tex new noad */
-    pointer p;
-    /*tex resulting mlist */
-    pointer q;
-    /*tex temporary */
-    pointer r;
-    halfword ht = 0;
-    halfword dp = 0;
-    halfword options = 0;
-    halfword type = -1 ;
-    t = cur_chr;
-    if (t > 10) {
-        /*tex 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();
-                math_class_to_type(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;
-        delimitersamesize(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
-
-    \TeX\ gets to the following part of the program when the first `\.\$' ending
-    a display has been scanned.
-
-*/
-
-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);
-    }
-}
-
-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;
-    /*tex 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();
-    }
-}
-
-/*tex
-
-    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).
-
-    \starttabulate
-    \NC \type {p} \NC points to the mlist for the formula \NC \NR
-    \NC \type {a} \NC is either |null| or it points to a box containing the equation number \NC \NR
-    \NC \type {l} \NC is true if there was an \.{\\leqno}/ (so |a| is a horizontal box) \NC \NR
-    \stoptabulate
-
-*/
-
-#define inject_display_skip_before(g) \
-    if (g > 0) { \
-        switch (display_skip_mode_par) { \
-            case 0 : \
-                /*tex normal tex | always */ \
-            case 1 : \
-                /*tex always */ \
-                tail_append(new_param_glue(g)); \
-                break; \
-            case 2 : \
-                /*tex non-zero */ \
-                if (! glue_is_zero(glue_par(g))) \
-                    tail_append(new_param_glue(g)); \
-                break; \
-            case 3: \
-                /*tex ignore */ \
-                break; \
-            default: \
-                /*tex > 3 reserved for future use */ \
-                tail_append(new_param_glue(g)); \
-                break; \
-        } \
-    }
-
-#define inject_display_skip_after(g) \
-    if (g > 0) { \
-        switch (display_skip_mode_par) { \
-            case 0 : \
-                /*tex normal tex | always */ \
-            case 1 : \
-                /*tex always */ \
-                tail_append(new_param_glue(g)); \
-                break; \
-            case 2 : \
-                /*tex non-zero */ \
-                if (! glue_is_zero(glue_par(g))) \
-                    tail_append(new_param_glue(g)); \
-                break; \
-            case 3: \
-                /*tex ignore */ \
-                break; \
-            default: \
-                /*tex > 3 reserved for future use */ \
-                tail_append(new_param_glue(g)); \
-                break; \
-        } \
-    }
-
-static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
-{
-    /*tex box containing the equation */
-    pointer eq_box;
-    /*tex width of the equation */
-    scaled eq_w;
-    /*tex width of the line */
-    scaled line_w;
-    /*tex width of equation number */
-    scaled eqno_w;
-    /*tex width of equation number plus space to separate from equation */
-    scaled eqno_w2;
-    /*tex move the line right this much */
-    scaled line_s;
-    /*tex displacement of equation in the line */
-    scaled d;
-    /*tex glue parameter codes for before and after */
-    small_number g1, g2;
-    /*tex kern nodes used to position the display */
-    pointer r,s;
-    /*tex tail of adjustment list */
-    pointer t;
-    /*tex tail of pre-adjustment list */
-    pointer pre_t;
-    /*tex true if the math and surrounding text dirs are opposed */
-    boolean swap_dir;
-    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;
-    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;
-        /*tex build_attribute_list(eqno_box); */
-    }
-    if (eq_w + eqno_w2 > line_w) {
-        /*tex
-
-            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;
-            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;
-                build_attribute_list(eq_box);
-            }
-        }
-        eq_w = width(eq_box);
-    }
-    /*tex
-
-        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)) {
-        /*tex 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) {
-        /*tex 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;
-    }
-    /*tex
-
-        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) {
-                    /*tex TRT + TLT + \eqno: (swap_dir=true,  math_direction_par=TLT, l=true) */
-                    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 {
-                    /*tex TLT + TRT + \eqno: (swap_dir=true,  math_direction_par=TRT, l=true) */
-                    try_couple_nodes(eqno_box,r);
-                    try_couple_nodes(r,eq_box);
-                }
-            } else {
-                if (math_direction_par==dir_TLT) {
-                    /*tex TLT + TLT + \leqno: (swap_dir=false, math_direction_par=TLT, l=true) */
-                    s = new_kern(width(r) + eqno_w);
-                } else {
-                    /*tex TRT + TRT + \leqno: (swap_dir=false, math_direction_par=TRT, l=true) */
-                    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) {
-                    /*tex TRT + TLT + \leqno: (swap_dir=true,  math_direction_par=TLT, l=false) */
-                } else {
-                    /*tex TLT + TRT + \leqno: (swap_dir=true,  math_direction_par=TRT, l=false) */
-                }
-                try_couple_nodes(eq_box,r);
-                try_couple_nodes(r,eqno_box);
-            } else {
-                if (math_direction_par==dir_TLT) {
-                    /*tex TLT + TLT + \eqno: (swap_dir=false, math_direction_par=TLT, l=false) */
-                    s = new_kern(d);
-                } else {
-                    /*tex TRT + TRT + \eqno: (swap_dir=false, math_direction_par=TRT, l=false) */
-                    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;
-    }
-    /*tex 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) {
-        /*tex migrating material comes after equation number */
-        vlink(tail) = vlink(adjust_head);
-        alink(adjust_tail) = alink(tail);
-        tail = t;
-    }
-    if (pre_t != pre_adjust_head) {
-        vlink(tail) = vlink(pre_adjust_head);
-        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();
-}
-
-void after_math(void)
-{
-    /*tex |mmode| or |-mmode| */
-    int m;
-    /*tex the formula */
-    pointer p;
-    /*tex box containing equation number */
-    pointer a = null;
-    /*tex `\.{\\leqno}' instead of `\.{\\eqno}' */
-    boolean l = false;
-    m = mode;
-    /*tex this pops the nest */
-    p = fin_mlist(null);
-    if (cur_cmd == math_shift_cs_cmd &&
-        (cur_chr == text_style || cur_chr == display_style)) {
-        you_cant();
-    }
-    if (mode == -m) {
-        /*tex 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();
-        /*tex now |cur_group=math_shift_group| */
-        decr(save_ptr);
-        assert(saved_type(0) == saved_eqno);
-        if (saved_value(0) == 1)
-            l = true;
-        m = mode;
-        p = fin_mlist(null);
-
-    }
-    if (m < 0) {
-        /*tex
-
-            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));
-        /*tex begin mathskip code */
-        switch (math_skip_mode) {
-            case 0 :
-                /*tex 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 :
-                /*tex always left */
-            case 3 :
-                /*tex always both */
-            case 6 :
-                /*tex only when skip */
-                copy_glue_values(tail,math_skip_par);
-                surround(tail) = 0;
-                break ;
-            case 2 :
-                /*tex only right */
-                surround(tail) = 0;
-                break ;
-            case 4 :
-                /*tex ignore, obey marthsurround */
-                break ;
-            case 5:
-                /*tex all spacing disabled */
-                surround(tail) = 0;
-                break ;
-        }
-        /*tex 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));
-            subtype(tail) = cancel_dir;
-        }
-        dir_math_save = false;
-        tail_append(new_math(math_surround_par, after));
-        /*tex begin mathskip code */
-        switch (math_skip_mode) {
-            case 0 :
-                /*tex 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 :
-                /*tex always right */
-            case 3 :
-                /*tex always both */
-            case 6 :
-                /*tex only when skip */
-                copy_glue_values(tail,math_skip_par);
-                surround(tail) = 0;
-                break ;
-            case 1 :
-                /*tex only left */
-                surround(tail) = 0;
-                break ;
-            case 4 :
-                /*tex ignore, obey marthsurround */
-                break ;
-            case 5:
-                /*tex all spacing disabled */
-                surround(tail) = 0;
-                break ;
-        }
-        /*tex 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));
-    }
-}
-
-/*tex
-
-    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.
-
-*/
-
-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();
-}
-
-/*tex Interface to \.{\\Umath} and \.{\\mathstyle}: */
-
-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));
-}
-
-void print_math_style(void)
-{
-    if (abs(mode) == mmode)
-        print_int(m_style);
-    else
-        print_int(-1);
-}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/texnodes.c
+++ /dev/null
@@ -1,4770 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-/*tex
-
-    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.
-
-*/
-
-#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;
-
-/*tex Used in font and lang: */
-
-int fix_node_lists = 1;
-
-/*tex Defined below. */
-
-halfword slow_get_node(int s);
-
-#define fake_node       100
-#define fake_node_size  2
-#define fake_node_name "fake"
-
-#define variable_node_size 2
-
-/*tex
-
-    The following definitions are used for keys at the \LUA\ end and
-    provide an efficient way to share hashed strings.
-
-*/
-
-# define init_node_key(target,n,key) \
-    target[n].lua  = luaS_##key##_index; \
-    target[n].name = luaS_##key##_ptr;
-
-# define init_field_key(target,n,key) \
-    target[n].lua  = luaS_##key##_index; \
-    target[n].name = luaS_##key##_ptr;
-
-# define init_field_nop(target,n) \
-    target[n].lua  = 0; \
-    target[n].name = NULL;
-
-/*tex The fields of nodes. */
-
-field_info node_fields_accent[10];
-field_info node_fields_adjust[3];
-field_info node_fields_attribute[3];
-field_info node_fields_attribute_list[1];
-field_info node_fields_boundary[3];
-field_info node_fields_choice[6];
-field_info node_fields_delim[6];
-field_info node_fields_dir[4];
-field_info node_fields_disc[6];
-field_info node_fields_fence[8];
-field_info node_fields_fraction[10];
-field_info node_fields_glue[8];
-field_info node_fields_glue_spec[6];
-field_info node_fields_glyph[16];
-field_info node_fields_insert[7];
-field_info node_fields_inserting[9];
-field_info node_fields_kern[4];
-field_info node_fields_list[11];
-field_info node_fields_local_par[9];
-field_info node_fields_margin_kern[4];
-field_info node_fields_mark[4];
-field_info node_fields_math[8];
-field_info node_fields_math_char[4];
-field_info node_fields_math_text_char[4];
-field_info node_fields_noad[5];
-field_info node_fields_penalty[3];
-field_info node_fields_radical[9];
-field_info node_fields_rule[9];
-field_info node_fields_splitup[6];
-field_info node_fields_style[3];
-field_info node_fields_sub_box[3];
-field_info node_fields_sub_mlist[3];
-field_info node_fields_unset[12];
-
-/*tex The fields of whatsit nodes. */
-
-field_info node_fields_whatsit_close[3];
-field_info node_fields_whatsit_late_lua[6];
-field_info node_fields_whatsit_open[6];
-field_info node_fields_whatsit_save_pos[2];
-field_info node_fields_whatsit_special[3];
-field_info node_fields_whatsit_user_defined[5];
-field_info node_fields_whatsit_write[4];
-
-field_info node_fields_whatsit_pdf_action[7];
-field_info node_fields_whatsit_pdf_annot[7];
-field_info node_fields_whatsit_pdf_colorstack[5];
-field_info node_fields_whatsit_pdf_dest[10];
-field_info node_fields_whatsit_pdf_end_link[2];
-field_info node_fields_whatsit_pdf_end_thread[2];
-field_info node_fields_whatsit_pdf_literal[4];
-field_info node_fields_whatsit_pdf_refobj[3];
-field_info node_fields_whatsit_pdf_restore[2];
-field_info node_fields_whatsit_pdf_save[2];
-field_info node_fields_whatsit_pdf_setmatrix[3];
-field_info node_fields_whatsit_pdf_start_link[8];
-field_info node_fields_whatsit_pdf_start_thread[8];
-field_info node_fields_whatsit_pdf_thread[8];
-
-/*tex The values of fields. */
-
-subtype_info node_values_dir[] = {
-    { 0,  NULL, 0 },
-    { 1,  NULL, 0 },
-    { 2,  NULL, 0 },
-    { 3,  NULL, 0 },
-    { -1, NULL, 0 },
-};
-
-subtype_info node_values_pdf_destination[] = {
-    { 0,  NULL, 0 },
-    { 1,  NULL, 0 },
-    { 2,  NULL, 0 },
-    { 3,  NULL, 0 },
-    { 4,  NULL, 0 },
-    { 5,  NULL, 0 },
-    { 6,  NULL, 0 },
-    { 7,  NULL, 0 },
-    { -1, NULL, 0 },
-};
-
-subtype_info node_values_pdf_action[] = {
-    { 0,  NULL, 0 },
-    { 1,  NULL, 0 },
-    { 2,  NULL, 0 },
-    { 3,  NULL, 0 },
-    { -1, NULL, 0 },
-};
-
-subtype_info node_values_pdf_window[] = {
-    { 0,  NULL, 0 },
-    { 1,  NULL, 0 },
-    { 2,  NULL, 0 },
-    { -1, NULL, 0 },
-};
-
-subtype_info node_values_color_stack[] = {
-    { 0,  NULL, 0 },
-    { 1,  NULL, 0 },
-    { 2,  NULL, 0 },
-    { 3,  NULL, 0 },
-    { -1, NULL, 0 },
-};
-
-subtype_info node_values_fill[] = {
-    { normal, NULL, 0 },
-    { sfi,    NULL, 0 },
-    { fil,    NULL, 0 },
-    { fill,   NULL, 0 },
-    { filll,  NULL, 0 },
-    { -1,     NULL, 0 },
-};
-
-subtype_info node_values_pdf_literal[] = {
-    { set_origin,    NULL, 0 },
-    { direct_page,   NULL, 0 },
-    { direct_always, NULL, 0 },
-    { direct_raw,    NULL, 0 },
-    { direct_text,   NULL, 0 },
-    { direct_font,   NULL, 0 },
-    { scan_special,  NULL, 0 },
-    { -1,            NULL, 0 },
-};
-
-subtype_info other_values_page_states[] = {
-    { 0,  NULL, 0 },
-    { 1,  NULL, 0 },
-    { 2,  NULL, 0 },
-    { -1, NULL, 0 },
-};
-
-/*tex The subtypes of nodes (most have one). */
-
-subtype_info node_subtypes_dir[] = {
-    { normal_dir, NULL, 0 },
-    { cancel_dir, NULL, 0 },
-    { -1,         NULL, 0 },
-};
-
-subtype_info node_subtypes_glue[] = {
-    { user_skip_glue,                NULL, 0 },
-    { line_skip_glue,                NULL, 0 },
-    { baseline_skip_glue,            NULL, 0 },
-    { par_skip_glue,                 NULL, 0 },
-    { above_display_skip_glue,       NULL, 0 },
-    { below_display_skip_glue,       NULL, 0 },
-    { above_display_short_skip_glue, NULL, 0 },
-    { below_display_short_skip_glue, NULL, 0 },
-    { left_skip_glue,                NULL, 0 },
-    { right_skip_glue,               NULL, 0 },
-    { top_skip_glue,                 NULL, 0 },
-    { split_top_skip_glue,           NULL, 0 },
-    { tab_skip_glue,                 NULL, 0 },
-    { space_skip_glue,               NULL, 0 },
-    { xspace_skip_glue,              NULL, 0 },
-    { par_fill_skip_glue,            NULL, 0 },
-    { math_skip_glue,                NULL, 0 },
-    { thin_mu_skip_glue,             NULL, 0 },
-    { med_mu_skip_glue,              NULL, 0 },
-    { thick_mu_skip_glue,            NULL, 0 },
-    /* math */
-    { cond_math_glue,                NULL, 0 },
-    { mu_glue,                       NULL, 0 },
-    /* leaders */
-    { a_leaders,                     NULL, 0 },
-    { c_leaders,                     NULL, 0 },
-    { x_leaders,                     NULL, 0 },
-    { g_leaders,                     NULL, 0 },
-    { -1,                            NULL, 0 },
-};
-
-/*tex
-
-    Math glue and leaders have special numbers. At some point we might decide to
-    move them down so best don't use hard coded numbers!
-
-*/
-
-subtype_info node_subtypes_mathglue[] = { /* 98+ */
-    { cond_math_glue, NULL, 0 },
-    { mu_glue,        NULL, 0 },
-    { -1,             NULL, 0 },
-};
-
-subtype_info node_subtypes_leader[] = { /* 100+ */
-    { a_leaders, NULL, 0 },
-    { c_leaders, NULL, 0 },
-    { x_leaders, NULL, 0 },
-    { g_leaders, NULL, 0 },
-    { -1,        NULL, 0 },
-};
-
-subtype_info node_subtypes_boundary[] = {
-    { cancel_boundary,     NULL, 0 },
-    { user_boundary,       NULL, 0 },
-    { protrusion_boundary, NULL, 0 },
-    { word_boundary,       NULL, 0 },
-    { -1,                  NULL, 0 },
-};
-
-subtype_info node_subtypes_penalty[] = {
-    { user_penalty,            NULL, 0 },
-    { linebreak_penalty,       NULL, 0 },
-    { line_penalty,            NULL, 0 },
-    { word_penalty,            NULL, 0 },
-    { final_penalty,           NULL, 0 },
-    { noad_penalty,            NULL, 0 },
-    { before_display_penalty,  NULL, 0 },
-    { after_display_penalty,   NULL, 0 },
-    { equation_number_penalty, NULL, 0 },
-    { -1,                      NULL, 0 },
-};
-
-subtype_info node_subtypes_kern[] = {
-    { font_kern,     NULL, 0 },
-    { explicit_kern, NULL, 0 },
-    { accent_kern,   NULL, 0 },
-    { italic_kern,   NULL, 0 },
-    { -1,            NULL, 0 },
-};
-
-subtype_info node_subtypes_rule[] = {
-    { normal_rule,        NULL, 0 },
-    { box_rule,           NULL, 0 },
-    { image_rule,         NULL, 0 },
-    { empty_rule,         NULL, 0 },
-    { user_rule,          NULL, 0 },
-    { math_over_rule,     NULL, 0 },
-    { math_under_rule,    NULL, 0 },
-    { math_fraction_rule, NULL, 0 },
-    { math_radical_rule,  NULL, 0 },
-    { outline_rule,       NULL, 0 },
-    { -1,                 NULL, 0 },
-};
-
-subtype_info node_subtypes_glyph[] = {
-    { glyph_unset,     NULL, 0 },
-    { glyph_character, NULL, 0 },
-    { glyph_ligature,  NULL, 0 },
-    { glyph_ghost,     NULL, 0 },
-    { glyph_left,      NULL, 0 },
-    { glyph_right,     NULL, 0 },
-    { -1,              NULL, 0 },
-};
-
-subtype_info node_subtypes_disc[] = {
-    { discretionary_disc, NULL, 0 },
-    { explicit_disc,      NULL, 0 },
-    { automatic_disc,     NULL, 0 },
-    { syllable_disc,      NULL, 0 },
-    { init_disc,          NULL, 0 },
-    { select_disc,        NULL, 0 },
-    { -1,                 NULL, 0 },
-};
-
-subtype_info node_subtypes_marginkern[] = {
-    { left_side,  NULL, 0 },
-    { right_side, NULL, 0 },
-    { -1,         NULL, 0 },
-};
-
-subtype_info node_subtypes_list[] = {
-    { unknown_list,         NULL, 0 },
-    { line_list,            NULL, 0 },
-    { hbox_list,            NULL, 0 },
-    { indent_list,          NULL, 0 },
-    { align_row_list,       NULL, 0 },
-    { align_cell_list,      NULL, 0 },
-    { equation_list,        NULL, 0 },
-    { equation_number_list, NULL, 0 },
-    { -1,                   NULL, 0 },
-};
-
-subtype_info node_subtypes_adjust[] = {
-    { 0,  NULL, 0 },
-    { 1,  NULL, 0 },
-    { -1, NULL, 0 },
-};
-
-subtype_info node_subtypes_math[] = {
-    { before, NULL, 0 },
-    { after,  NULL, 0 },
-    { -1,     NULL, 0 },
-};
-
-subtype_info node_subtypes_noad[] = {
-    { ord_noad_type,          NULL, 0 },
-    { op_noad_type_normal,    NULL, 0 },
-    { op_noad_type_limits,    NULL, 0 },
-    { op_noad_type_no_limits, NULL, 0 },
-    { bin_noad_type,          NULL, 0 },
-    { rel_noad_type,          NULL, 0 },
-    { open_noad_type,         NULL, 0 },
-    { close_noad_type,        NULL, 0 },
-    { punct_noad_type,        NULL, 0 },
-    { inner_noad_type,        NULL, 0 },
-    { under_noad_type,        NULL, 0 },
-    { over_noad_type,         NULL, 0 },
-    { vcenter_noad_type,      NULL, 0 },
-    { -1,                     NULL, 0 },
-};
-
-subtype_info node_subtypes_radical[] = {
-    { radical_noad_type,         NULL, 0 },
-    { uradical_noad_type,        NULL, 0 },
-    { uroot_noad_type,           NULL, 0 },
-    { uunderdelimiter_noad_type, NULL, 0 },
-    { uoverdelimiter_noad_type,  NULL, 0 },
-    { udelimiterunder_noad_type, NULL, 0 },
-    { udelimiterover_noad_type,  NULL, 0 },
-    { -1,                        NULL, 0 },
-};
-
-subtype_info node_subtypes_accent[] = {
-    { bothflexible_accent, NULL, 0 },
-    { fixedtop_accent,     NULL, 0 },
-    { fixedbottom_accent,  NULL, 0 },
-    { fixedboth_accent,    NULL, 0 },
-    { -1,                  NULL, 0 },
-};
-
-subtype_info node_subtypes_fence[] = {
-    { unset_noad_side,  NULL, 0 },
-    { left_noad_side,   NULL, 0 },
-    { middle_noad_side, NULL, 0 },
-    { right_noad_side,  NULL, 0 },
-    { no_noad_side,     NULL, 0 },
-    { -1,               NULL, 0 },
-};
-
-/*tes This brings all together */
-
-node_info node_data[] = {
-    { hlist_node,          box_node_size,         node_subtypes_list,       node_fields_list,                          NULL,  1, 0 },
-    { vlist_node,          box_node_size,         node_subtypes_list,       node_fields_list,                          NULL,  2, 0 },
-    { rule_node,           rule_node_size,        node_subtypes_rule,       node_fields_rule,                          NULL,  3, 0 },
-    { ins_node,            ins_node_size,         NULL,                     node_fields_insert,                        NULL,  4, 0 },
-    { mark_node,           mark_node_size,        NULL,                     node_fields_mark,                          NULL,  5, 0 },
-    { adjust_node,         adjust_node_size,      node_subtypes_adjust,     node_fields_adjust,                        NULL,  6, 0 },
-    { boundary_node,       boundary_node_size,    node_subtypes_boundary,   node_fields_boundary,                      NULL, -1, 0 },
-    { disc_node,           disc_node_size,        node_subtypes_disc,       node_fields_disc,                          NULL,  8, 0 },
-    { whatsit_node,        -1,                    NULL,                     NULL,                                      NULL,  9, 0 },
-    { local_par_node,      local_par_size,        NULL,                     node_fields_local_par,                     NULL, -1, 0 },
-    { dir_node,            dir_node_size,         node_subtypes_dir,        node_fields_dir,                           NULL, -1, 0 },
-    { math_node,           math_node_size,        node_subtypes_math,       node_fields_math,                          NULL, 10, 0 },
-    { glue_node,           glue_node_size,        node_subtypes_glue,       node_fields_glue,                          NULL, 11, 0 },
-    { kern_node,           kern_node_size,        node_subtypes_kern,       node_fields_kern,                          NULL, 12, 0 },
-    { penalty_node,        penalty_node_size,     node_subtypes_penalty,    node_fields_penalty,                       NULL, 13, 0 },
-    { unset_node,          box_node_size,         NULL,                     node_fields_unset,                         NULL, 14, 0 },
-    { style_node,          style_node_size,       NULL,                     node_fields_style,                         NULL, 15, 0 },
-    { choice_node,         style_node_size,       NULL,                     node_fields_choice,                        NULL, 15, 0 },
-    { simple_noad,         noad_size,             node_subtypes_noad,       node_fields_noad,                          NULL, 15, 0 },
-    { radical_noad,        radical_noad_size,     node_subtypes_radical,    node_fields_radical,                       NULL, 15, 0 },
-    { fraction_noad,       fraction_noad_size,    NULL,                     node_fields_fraction,                      NULL, 15, 0 },
-    { accent_noad,         accent_noad_size,      node_subtypes_accent,     node_fields_accent,                        NULL, 15, 0 },
-    { fence_noad,          fence_noad_size,       node_subtypes_fence,      node_fields_fence,                         NULL, 15, 0 },
-    { math_char_node,      math_kernel_node_size, NULL,                     node_fields_math_char,                     NULL, 15, 0 },
-    { sub_box_node,        math_kernel_node_size, NULL,                     node_fields_sub_box,                       NULL, 15, 0 },
-    { sub_mlist_node,      math_kernel_node_size, NULL,                     node_fields_sub_mlist,                     NULL, 15, 0 },
-    { math_text_char_node, math_kernel_node_size, NULL,                     node_fields_math_text_char,                NULL, 15, 0 },
-    { delim_node,          math_shield_node_size, NULL,                     node_fields_delim,                         NULL, 15, 0 },
-    { margin_kern_node,    margin_kern_node_size, node_subtypes_marginkern, node_fields_margin_kern,                   NULL, -1, 0 },
-    { glyph_node,          glyph_node_size,       node_subtypes_glyph,      node_fields_glyph,                         NULL,  0, 0 },
-    { align_record_node,   box_node_size,         NULL,                     NULL,                                      NULL, -1, 0 },
-    { pseudo_file_node,    pseudo_file_node_size, NULL,                     NULL,                                      NULL, -1, 0 },
-    { pseudo_line_node,    variable_node_size,    NULL,                     NULL,                                      NULL, -1, 0 },
-    { inserting_node,      page_ins_node_size,    NULL,                     node_fields_inserting,                     NULL, -1, 0 },
-    { split_up_node,       page_ins_node_size,    NULL,                     node_fields_splitup,                       NULL, -1, 0 },
-    { expr_node,           expr_node_size,        NULL,                     NULL,                                      NULL, -1, 0 },
-    { nesting_node,        nesting_node_size,     NULL,                     NULL,                                      NULL, -1, 0 },
-    { span_node,           span_node_size,        NULL,                     NULL,                                      NULL, -1, 0 },
-    { attribute_node,      attribute_node_size,   NULL,                     node_fields_attribute,                     NULL, -1, 0 },
-    { glue_spec_node,      glue_spec_size,        NULL,                     node_fields_glue_spec,                     NULL, -1, 0 },
-    { attribute_list_node, attribute_node_size,   NULL,                     node_fields_attribute_list,                NULL, -1, 0 },
-    { temp_node,           temp_node_size,        NULL,                     NULL,                                      NULL, -1, 0 },
-    { align_stack_node,    align_stack_node_size, NULL,                     NULL,                                      NULL, -1, 0 },
-    { movement_node,       movement_node_size,    NULL,                     NULL,                                      NULL, -1, 0 },
-    { if_node,             if_node_size,          NULL,                     NULL,                                      NULL, -1, 0 },
-    { unhyphenated_node,   active_node_size,      NULL,                     NULL,                                      NULL, -1, 0 },
-    { hyphenated_node,     active_node_size,      NULL,                     NULL,                                      NULL, -1, 0 },
-    { delta_node,          delta_node_size,       NULL,                     NULL,                                      NULL, -1, 0 },
-    { passive_node,        passive_node_size,     NULL,                     NULL,                                      NULL, -1, 0 },
-    { shape_node,          variable_node_size,    NULL,                     NULL,                                      NULL, -1, 0 },
-    { -1,                 -1,                     NULL,                     NULL,                                      NULL, -1, 0 }
-};
-
-void l_set_node_data(void) {
-    init_node_key(node_data, hlist_node,          hlist)
-    init_node_key(node_data, vlist_node,          vlist)
-    init_node_key(node_data, rule_node,           rule)
-    init_node_key(node_data, ins_node,            ins)
-    init_node_key(node_data, mark_node,           mark)
-    init_node_key(node_data, adjust_node,         adjust)
-    init_node_key(node_data, boundary_node,       boundary)
-    init_node_key(node_data, disc_node,           disc)
-    init_node_key(node_data, whatsit_node,        whatsit)
-    init_node_key(node_data, local_par_node,      local_par)
-    init_node_key(node_data, dir_node,            dir)
-    init_node_key(node_data, math_node,           math)
-    init_node_key(node_data, glue_node,           glue)
-    init_node_key(node_data, kern_node,           kern)
-    init_node_key(node_data, penalty_node,        penalty)
-    init_node_key(node_data, unset_node,          unset)
-    init_node_key(node_data, style_node,          style)
-    init_node_key(node_data, choice_node,         choice)
-    init_node_key(node_data, simple_noad,         noad)
-    init_node_key(node_data, radical_noad,        radical)
-    init_node_key(node_data, fraction_noad,       fraction)
-    init_node_key(node_data, accent_noad,         accent)
-    init_node_key(node_data, fence_noad,          fence)
-    init_node_key(node_data, math_char_node,      math_char)
-    init_node_key(node_data, sub_box_node,        sub_box)
-    init_node_key(node_data, sub_mlist_node,      sub_mlist)
-    init_node_key(node_data, math_text_char_node, math_text_char)
-    init_node_key(node_data, delim_node,          delim)
-    init_node_key(node_data, margin_kern_node,    margin_kern)
-    init_node_key(node_data, glyph_node,          glyph)
-    init_node_key(node_data, align_record_node,   align_record)
-    init_node_key(node_data, pseudo_file_node,    pseudo_file)
-    init_node_key(node_data, pseudo_line_node,    pseudo_line)
-    init_node_key(node_data, inserting_node,      page_insert)
-    init_node_key(node_data, split_up_node,       split_insert)
-    init_node_key(node_data, expr_node,           expr_stack)
-    init_node_key(node_data, nesting_node,        nested_list)
-    init_node_key(node_data, span_node,           span)
-    init_node_key(node_data, attribute_node,      attribute)
-    init_node_key(node_data, glue_spec_node,      glue_spec)
-    init_node_key(node_data, attribute_list_node, attribute_list)
-    init_node_key(node_data, temp_node,           temp)
-    init_node_key(node_data, align_stack_node,    align_stack)
-    init_node_key(node_data, movement_node,       movement_stack)
-    init_node_key(node_data, if_node,             if_stack)
-    init_node_key(node_data, unhyphenated_node,   unhyphenated)
-    init_node_key(node_data, hyphenated_node,     hyphenated)
-    init_node_key(node_data, delta_node,          delta)
-    init_node_key(node_data, passive_node,        passive)
-    init_node_key(node_data, shape_node,          shape)
-
-    init_node_key(node_subtypes_dir, normal_dir, normal)
-    init_node_key(node_subtypes_dir, cancel_dir, cancel)
-
-    init_node_key(node_subtypes_glue, user_skip_glue,                userskip)
-    init_node_key(node_subtypes_glue, line_skip_glue,                lineskip)
-    init_node_key(node_subtypes_glue, baseline_skip_glue,            baselineskip)
-    init_node_key(node_subtypes_glue, par_skip_glue,                 parskip)
-    init_node_key(node_subtypes_glue, above_display_skip_glue,       abovedisplayskip)
-    init_node_key(node_subtypes_glue, below_display_skip_glue,       belowdisplayskip)
-    init_node_key(node_subtypes_glue, above_display_short_skip_glue, abovedisplayshortskip)
-    init_node_key(node_subtypes_glue, below_display_short_skip_glue, belowdisplayshortskip)
-    init_node_key(node_subtypes_glue, left_skip_glue,                leftskip)
-    init_node_key(node_subtypes_glue, right_skip_glue,               rightskip)
-    init_node_key(node_subtypes_glue, top_skip_glue,                 topskip)
-    init_node_key(node_subtypes_glue, split_top_skip_glue,           splittopskip)
-    init_node_key(node_subtypes_glue, tab_skip_glue,                 tabskip)
-    init_node_key(node_subtypes_glue, space_skip_glue,               spaceskip)
-    init_node_key(node_subtypes_glue, xspace_skip_glue,              xspaceskip)
-    init_node_key(node_subtypes_glue, par_fill_skip_glue,            parfillskip)
-    init_node_key(node_subtypes_glue, math_skip_glue,                mathskip)
-    init_node_key(node_subtypes_glue, thin_mu_skip_glue,             thinmuskip)
-    init_node_key(node_subtypes_glue, med_mu_skip_glue,              medmuskip)
-    init_node_key(node_subtypes_glue, thick_mu_skip_glue,            thickmuskip)
-    init_node_key(node_subtypes_glue, thick_mu_skip_glue + 1,        conditionalmathskip)
-    init_node_key(node_subtypes_glue, thick_mu_skip_glue + 2,        muglue)
-    init_node_key(node_subtypes_glue, thick_mu_skip_glue + 3,        leaders)
-    init_node_key(node_subtypes_glue, thick_mu_skip_glue + 4,        cleaders)
-    init_node_key(node_subtypes_glue, thick_mu_skip_glue + 5,        xleaders)
-    init_node_key(node_subtypes_glue, thick_mu_skip_glue + 6,        gleaders)
-
-    init_node_key(node_subtypes_mathglue, 0, conditionalmathskip)
-    init_node_key(node_subtypes_mathglue, 1, muglue)
-
-    init_node_key(node_subtypes_leader, 0, leaders)
-    init_node_key(node_subtypes_leader, 1, cleaders)
-    init_node_key(node_subtypes_leader, 2, xleaders)
-    init_node_key(node_subtypes_leader, 3, gleaders)
-
-    init_node_key(node_subtypes_boundary, cancel_boundary,     cancel)
-    init_node_key(node_subtypes_boundary, user_boundary,       user)
-    init_node_key(node_subtypes_boundary, protrusion_boundary, protrusion)
-    init_node_key(node_subtypes_boundary, word_boundary,       word)
-
-    init_node_key(node_subtypes_penalty, user_penalty,            userpenalty)
-    init_node_key(node_subtypes_penalty, linebreak_penalty,       linebreakpenalty)
-    init_node_key(node_subtypes_penalty, line_penalty,            linepenalty)
-    init_node_key(node_subtypes_penalty, word_penalty,            wordpenalty)
-    init_node_key(node_subtypes_penalty, final_penalty,           finalpenalty)
-    init_node_key(node_subtypes_penalty, noad_penalty,            noadpenalty)
-    init_node_key(node_subtypes_penalty, before_display_penalty,  beforedisplaypenalty)
-    init_node_key(node_subtypes_penalty, after_display_penalty,   afterdisplaypenalty)
-    init_node_key(node_subtypes_penalty, equation_number_penalty, equationnumberpenalty)
-
-    init_node_key(node_subtypes_kern, font_kern,     fontkern)
-    init_node_key(node_subtypes_kern, explicit_kern, userkern)
-    init_node_key(node_subtypes_kern, accent_kern,   accentkern)
-    init_node_key(node_subtypes_kern, italic_kern,   italiccorrection)
-
-    init_node_key(node_subtypes_rule, normal_rule,        normal)
-    init_node_key(node_subtypes_rule, box_rule,           box)
-    init_node_key(node_subtypes_rule, image_rule,         image)
-    init_node_key(node_subtypes_rule, empty_rule,         empty)
-    init_node_key(node_subtypes_rule, user_rule,          user)
-    init_node_key(node_subtypes_rule, math_over_rule,     over)
-    init_node_key(node_subtypes_rule, math_under_rule,    under)
-    init_node_key(node_subtypes_rule, math_fraction_rule, fraction)
-    init_node_key(node_subtypes_rule, math_radical_rule,  radical)
-    init_node_key(node_subtypes_rule, outline_rule,       outline)
-
-    init_node_key(node_subtypes_glyph, 0, unset)
-    init_node_key(node_subtypes_glyph, 1, character)
-    init_node_key(node_subtypes_glyph, 2, ligature)
-    init_node_key(node_subtypes_glyph, 3, ghost)
-    init_node_key(node_subtypes_glyph, 4, left)
-    init_node_key(node_subtypes_glyph, 5, right)
-
-    init_node_key(node_subtypes_disc, discretionary_disc, discretionary)
-    init_node_key(node_subtypes_disc, explicit_disc,      explicit)
-    init_node_key(node_subtypes_disc, automatic_disc,     automatic)
-    init_node_key(node_subtypes_disc, syllable_disc,      regular)
-    init_node_key(node_subtypes_disc, init_disc,          first)
-    init_node_key(node_subtypes_disc, select_disc,        second)
-
-    init_node_key(node_subtypes_fence, unset_noad_side,  unset)
-    init_node_key(node_subtypes_fence, left_noad_side,   left)
-    init_node_key(node_subtypes_fence, middle_noad_side, middle)
-    init_node_key(node_subtypes_fence, right_noad_side,  right)
-    init_node_key(node_subtypes_fence, no_noad_side,     no)
-
-    init_node_key(node_subtypes_list, unknown_list,         unknown)
-    init_node_key(node_subtypes_list, line_list,            line)
-    init_node_key(node_subtypes_list, hbox_list,            box)
-    init_node_key(node_subtypes_list, indent_list,          indent)
-    init_node_key(node_subtypes_list, align_row_list,       alignment)
-    init_node_key(node_subtypes_list, align_cell_list,      cell)
-    init_node_key(node_subtypes_list, equation_list,        equation)
-    init_node_key(node_subtypes_list, equation_number_list, equationnumber)
-
-    init_node_key(node_subtypes_math, before, beginmath)
-    init_node_key(node_subtypes_math, after,  endmath)
-
-    init_node_key(node_subtypes_marginkern, left_side,  left)
-    init_node_key(node_subtypes_marginkern, right_side, right)
-
-    init_node_key(node_subtypes_adjust, 0, normal)
-    init_node_key(node_subtypes_adjust, 1, pre)
-
-    init_node_key(node_subtypes_noad, ord_noad_type,          ord)
-    init_node_key(node_subtypes_noad, op_noad_type_normal,    opdisplaylimits)
-    init_node_key(node_subtypes_noad, op_noad_type_limits,    oplimits)
-    init_node_key(node_subtypes_noad, op_noad_type_no_limits, opnolimits)
-    init_node_key(node_subtypes_noad, bin_noad_type,          bin)
-    init_node_key(node_subtypes_noad, rel_noad_type,          rel)
-    init_node_key(node_subtypes_noad, open_noad_type,         open)
-    init_node_key(node_subtypes_noad, close_noad_type,        close)
-    init_node_key(node_subtypes_noad, punct_noad_type,        punct)
-    init_node_key(node_subtypes_noad, inner_noad_type,        inner)
-    init_node_key(node_subtypes_noad, under_noad_type,        under)
-    init_node_key(node_subtypes_noad, over_noad_type,         over)
-    init_node_key(node_subtypes_noad, vcenter_noad_type,      vcenter)
-
-    init_node_key(node_subtypes_radical, radical_noad_type,         radical)
-    init_node_key(node_subtypes_radical, uradical_noad_type,        uradical)
-    init_node_key(node_subtypes_radical, uroot_noad_type,           uroot)
-    init_node_key(node_subtypes_radical, uunderdelimiter_noad_type, uunderdelimiter)
-    init_node_key(node_subtypes_radical, uoverdelimiter_noad_type,  uoverdelimiter)
-    init_node_key(node_subtypes_radical, udelimiterunder_noad_type, udelimiterunder)
-    init_node_key(node_subtypes_radical, udelimiterover_noad_type,  udelimiterover)
-
-    init_node_key(node_subtypes_accent, bothflexible_accent, bothflexible)
-    init_node_key(node_subtypes_accent, fixedtop_accent,     fixedtop)
-    init_node_key(node_subtypes_accent, fixedbottom_accent,  fixedbottom)
-    init_node_key(node_subtypes_accent, fixedboth_accent,    fixedboth)
-
-    init_node_key(node_values_fill, normal, normal)
-    init_node_key(node_values_fill, sfi,    fi)
-    init_node_key(node_values_fill, fil,    fil)
-    init_node_key(node_values_fill, fill,   fill)
-    init_node_key(node_values_fill, filll,  filll)
-
-    init_node_key(node_values_dir, 0, TLT)
-    init_node_key(node_values_dir, 1, TRT)
-    init_node_key(node_values_dir, 2, LTL)
-    init_node_key(node_values_dir, 3, RTT)
-
-    init_node_key(other_values_page_states, 0, empty)
-    init_node_key(other_values_page_states, 1, box_there)
-    init_node_key(other_values_page_states, 2, inserts_only)
-
-    init_field_key(node_fields_accent, 0, attr);
-    init_field_key(node_fields_accent, 1, nucleus);
-    init_field_key(node_fields_accent, 2, sub);
-    init_field_key(node_fields_accent, 3, sup);
-    init_field_key(node_fields_accent, 4, accent);
-    init_field_key(node_fields_accent, 5, bot_accent);
-    init_field_key(node_fields_accent, 6, top_accent);
-    init_field_key(node_fields_accent, 7, overlay_accent);
-    init_field_key(node_fields_accent, 8, fraction);
-    init_field_nop(node_fields_accent, 9);
-
-    init_field_key(node_fields_adjust, 0, attr);
-    init_field_key(node_fields_adjust, 1, head);
-    init_field_nop(node_fields_adjust, 2);
-
-    init_field_key(node_fields_attribute, 0, number);
-    init_field_key(node_fields_attribute, 1, value);
-    init_field_nop(node_fields_attribute, 2);
-
-    init_field_nop(node_fields_attribute_list,0);
-
-    init_field_key(node_fields_boundary, 0, attr);
-    init_field_key(node_fields_boundary, 1, value);
-    init_field_nop(node_fields_boundary, 2);
-
-    init_field_key(node_fields_choice, 0, attr);
-    init_field_key(node_fields_choice, 1, display);
-    init_field_key(node_fields_choice, 2, text);
-    init_field_key(node_fields_choice, 3, script);
-    init_field_key(node_fields_choice, 4, scriptscript);
-    init_field_nop(node_fields_choice, 5);
-
-    init_field_key(node_fields_delim, 0, attr);
-    init_field_key(node_fields_delim, 1, small_fam);
-    init_field_key(node_fields_delim, 2, small_char);
-    init_field_key(node_fields_delim, 3, large_fam);
-    init_field_key(node_fields_delim, 4, large_char);
-    init_field_nop(node_fields_delim, 5);
-
-    init_field_key(node_fields_dir, 0, attr);
-    init_field_key(node_fields_dir, 1, dir);
-    init_field_key(node_fields_dir, 2, level);
-    init_field_nop(node_fields_dir, 3);
-
-    init_field_key(node_fields_disc, 0, attr);
-    init_field_key(node_fields_disc, 1, pre);
-    init_field_key(node_fields_disc, 2, post);
-    init_field_key(node_fields_disc, 3, replace);
-    init_field_key(node_fields_disc, 4, penalty);
-    init_field_nop(node_fields_disc, 5);
-
-    init_field_key(node_fields_fence, 0, attr);
-    init_field_key(node_fields_fence, 1, delim);
-    init_field_key(node_fields_fence, 2, italic);
-    init_field_key(node_fields_fence, 3, height);
-    init_field_key(node_fields_fence, 4, depth);
-    init_field_key(node_fields_fence, 5, options);
-    init_field_key(node_fields_fence, 6, class);
-    init_field_nop(node_fields_fence, 7);
-
-    init_field_key(node_fields_fraction, 0, attr);
-    init_field_key(node_fields_fraction, 1, width);
-    init_field_key(node_fields_fraction, 2, num);
-    init_field_key(node_fields_fraction, 3, denom);
-    init_field_key(node_fields_fraction, 4, left);
-    init_field_key(node_fields_fraction, 5, right);
-    init_field_key(node_fields_fraction, 6, middle);
-    init_field_key(node_fields_fraction, 7, fam);
-    init_field_key(node_fields_fraction, 8, options);
-    init_field_nop(node_fields_fraction, 9);
-
-    init_field_key(node_fields_glue, 0, attr);
-    init_field_key(node_fields_glue, 1, leader);
-    init_field_key(node_fields_glue, 2, width);
-    init_field_key(node_fields_glue, 3, stretch);
-    init_field_key(node_fields_glue, 4, shrink);
-    init_field_key(node_fields_glue, 5, stretch_order);
-    init_field_key(node_fields_glue, 6, shrink_order);
-    init_field_nop(node_fields_glue, 7);
-
-    init_field_key(node_fields_glue_spec, 0, width);
-    init_field_key(node_fields_glue_spec, 1, stretch);
-    init_field_key(node_fields_glue_spec, 2, shrink);
-    init_field_key(node_fields_glue_spec, 3, stretch_order);
-    init_field_key(node_fields_glue_spec, 4, shrink_order);
-    init_field_nop(node_fields_glue_spec, 5);
-
-    init_field_key(node_fields_glyph,  0, attr);
-    init_field_key(node_fields_glyph,  1, char);
-    init_field_key(node_fields_glyph,  2, font);
-    init_field_key(node_fields_glyph,  3, lang);
-    init_field_key(node_fields_glyph,  4, left);
-    init_field_key(node_fields_glyph,  5, right);
-    init_field_key(node_fields_glyph,  6, uchyph);
-    init_field_key(node_fields_glyph,  7, components);
-    init_field_key(node_fields_glyph,  8, xoffset);
-    init_field_key(node_fields_glyph,  9, yoffset);
-    init_field_key(node_fields_glyph, 10, width);
-    init_field_key(node_fields_glyph, 11, height);
-    init_field_key(node_fields_glyph, 12, depth);
-    init_field_key(node_fields_glyph, 13, expansion_factor);
-    init_field_key(node_fields_glyph, 14, data);
-    init_field_nop(node_fields_glyph, 15);
-
-    init_field_key(node_fields_insert, 0, attr);
-    init_field_key(node_fields_insert, 1, cost);
-    init_field_key(node_fields_insert, 2, depth);
-    init_field_key(node_fields_insert, 3, height);
-    init_field_key(node_fields_insert, 4, spec);
-    init_field_key(node_fields_insert, 5, head);
-    init_field_nop(node_fields_insert, 6);
-
-    init_field_key(node_fields_inserting, 0, height);
-    init_field_key(node_fields_inserting, 1, last_ins_ptr);
-    init_field_key(node_fields_inserting, 2, best_ins_ptr);
-    init_field_key(node_fields_inserting, 3, width);
-    init_field_key(node_fields_inserting, 4, stretch);
-    init_field_key(node_fields_inserting, 5, shrink);
-    init_field_key(node_fields_inserting, 6, stretch_order);
-    init_field_key(node_fields_inserting, 7, shrink_order);
-    init_field_nop(node_fields_inserting, 8);
-
-    init_field_key(node_fields_kern, 0, attr);
-    init_field_key(node_fields_kern, 1, kern);
-    init_field_key(node_fields_kern, 2, expansion_factor);
-    init_field_nop(node_fields_kern, 3);
-
-    init_field_key(node_fields_list,  0, attr);
-    init_field_key(node_fields_list,  1, width);
-    init_field_key(node_fields_list,  2, depth);
-    init_field_key(node_fields_list,  3, height);
-    init_field_key(node_fields_list,  4, dir);
-    init_field_key(node_fields_list,  5, shift);
-    init_field_key(node_fields_list,  6, glue_order);
-    init_field_key(node_fields_list,  7, glue_sign);
-    init_field_key(node_fields_list,  8, glue_set);
-    init_field_key(node_fields_list,  9, head);
-    init_field_nop(node_fields_list, 10);
-
-    init_field_key(node_fields_local_par, 0, attr);
-    init_field_key(node_fields_local_par, 1, pen_inter);
-    init_field_key(node_fields_local_par, 2, pen_broken);
-    init_field_key(node_fields_local_par, 3, dir);
-    init_field_key(node_fields_local_par, 4, box_left);
-    init_field_key(node_fields_local_par, 5, box_left_width);
-    init_field_key(node_fields_local_par, 6, box_right);
-    init_field_key(node_fields_local_par, 7, box_right_width);
-    init_field_nop(node_fields_local_par, 8);
-
-    init_field_key(node_fields_margin_kern, 0, attr);
-    init_field_key(node_fields_margin_kern, 1, width);
-    init_field_key(node_fields_margin_kern, 2, glyph);
-    init_field_nop(node_fields_margin_kern, 3);
-
-    init_field_key(node_fields_mark, 0, attr);
-    init_field_key(node_fields_mark, 1, class);
-    init_field_key(node_fields_mark, 2, mark);
-    init_field_nop(node_fields_mark, 3);
-
-    init_field_key(node_fields_math, 0, attr);
-    init_field_key(node_fields_math, 1, surround);
-    init_field_key(node_fields_math, 2, width);
-    init_field_key(node_fields_math, 3, stretch);
-    init_field_key(node_fields_math, 4, shrink);
-    init_field_key(node_fields_math, 5, stretch_order);
-    init_field_key(node_fields_math, 6, shrink_order);
-    init_field_nop(node_fields_math, 7);
-
-    init_field_key(node_fields_math_char, 0, attr);
-    init_field_key(node_fields_math_char, 1, fam);
-    init_field_key(node_fields_math_char, 2, char);
-    init_field_nop(node_fields_math_char, 3);
-
-    init_field_key(node_fields_math_text_char, 0, attr);
-    init_field_key(node_fields_math_text_char, 1, fam);
-    init_field_key(node_fields_math_text_char, 2, char);
-    init_field_nop(node_fields_math_text_char, 3);
-
-    init_field_key(node_fields_noad, 0, attr);
-    init_field_key(node_fields_noad, 1, nucleus);
-    init_field_key(node_fields_noad, 2, sub);
-    init_field_key(node_fields_noad, 3, sup);
-    init_field_nop(node_fields_noad, 4);
-
-    init_field_key(node_fields_penalty, 0, attr);
-    init_field_key(node_fields_penalty, 1, penalty);
-    init_field_nop(node_fields_penalty, 2);
-
-    init_field_key(node_fields_radical, 0, attr);
-    init_field_key(node_fields_radical, 1, nucleus);
-    init_field_key(node_fields_radical, 2, sub);
-    init_field_key(node_fields_radical, 3, sup);
-    init_field_key(node_fields_radical, 4, left);
-    init_field_key(node_fields_radical, 5, degree);
-    init_field_key(node_fields_radical, 6, width);
-    init_field_key(node_fields_radical, 7, options);
-    init_field_nop(node_fields_radical, 8);
-
-    init_field_key(node_fields_rule, 0, attr);
-    init_field_key(node_fields_rule, 1, width);
-    init_field_key(node_fields_rule, 2, depth);
-    init_field_key(node_fields_rule, 3, height);
-    init_field_key(node_fields_rule, 4, dir);
-    init_field_key(node_fields_rule, 5, index);
-    init_field_key(node_fields_rule, 6, left);
-    init_field_key(node_fields_rule, 7, right);
-    init_field_nop(node_fields_rule, 8);
-
-    init_field_key(node_fields_splitup, 0, height);
-    init_field_key(node_fields_splitup, 1, last_ins_ptr);
-    init_field_key(node_fields_splitup, 2, best_ins_ptr);
-    init_field_key(node_fields_splitup, 3, broken_ptr);
-    init_field_key(node_fields_splitup, 4, broken_ins);
-    init_field_nop(node_fields_splitup, 5);
-
-    init_field_key(node_fields_style, 0, attr);
-    init_field_key(node_fields_style, 1, style);
-    init_field_nop(node_fields_style, 2);
-
-    init_field_key(node_fields_sub_box, 0, attr);
-    init_field_key(node_fields_sub_box, 1, head);
-    init_field_nop(node_fields_sub_box, 2);
-
-    init_field_key(node_fields_sub_mlist, 0, attr);
-    init_field_key(node_fields_sub_mlist, 1, head);
-    init_field_nop(node_fields_sub_mlist, 2);
-
-    init_field_key(node_fields_unset,  0, attr);
-    init_field_key(node_fields_unset,  1, width);
-    init_field_key(node_fields_unset,  2, depth);
-    init_field_key(node_fields_unset,  3, height);
-    init_field_key(node_fields_unset,  4, dir);
-    init_field_key(node_fields_unset,  5, shrink);
-    init_field_key(node_fields_unset,  6, glue_order);
-    init_field_key(node_fields_unset,  7, glue_sign);
-    init_field_key(node_fields_unset,  8, stretch);
-    init_field_key(node_fields_unset,  9, span);
-    init_field_key(node_fields_unset, 10, head);
-    init_field_nop(node_fields_unset, 11);
-
-}
-
-node_info whatsit_node_data[] = {
-
-    /*tex These are always there. The fake nodes are historical. */
-
-    { open_node,             open_node_size,           NULL, node_fields_whatsit_open,             NULL, -1, 0 },
-    { write_node,            write_node_size,          NULL, node_fields_whatsit_write,            NULL, -1, 0 },
-    { close_node,            close_node_size,          NULL, node_fields_whatsit_close,            NULL, -1, 0 },
-    { special_node,          special_node_size,        NULL, node_fields_whatsit_special,          NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-    { save_pos_node,         save_pos_node_size,       NULL, node_fields_whatsit_save_pos,         NULL, -1, 0 },
-    { late_lua_node,         late_lua_node_size,       NULL, node_fields_whatsit_late_lua,         NULL, -1, 0 },
-    { user_defined_node,     user_defined_node_size,   NULL, node_fields_whatsit_user_defined,     NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-    { fake_node,             fake_node_size,           NULL, NULL,                                 NULL, -1, 0 },
-
-    /*tex Here starts the \DVI\ backend section, todo: a separate list. */
-
-    /*tex {\em There is nothing here.} */
-
-    /*tex Here starts the \PDF\ backend section, todo: a separate list.  */
-
-    { pdf_literal_node,      write_node_size,          NULL, node_fields_whatsit_pdf_literal,      NULL, -1, 0 },
-    { pdf_refobj_node,       pdf_refobj_node_size,     NULL, node_fields_whatsit_pdf_refobj,       NULL, -1, 0 },
-    { pdf_annot_node,        pdf_annot_node_size,      NULL, node_fields_whatsit_pdf_annot,        NULL, -1, 0 },
-    { pdf_start_link_node,   pdf_annot_node_size,      NULL, node_fields_whatsit_pdf_start_link,   NULL, -1, 0 },
-    { pdf_end_link_node,     pdf_end_link_node_size,   NULL, node_fields_whatsit_pdf_end_link,     NULL, -1, 0 },
-    { pdf_dest_node,         pdf_dest_node_size,       NULL, node_fields_whatsit_pdf_dest,         NULL, -1, 0 },
-    { pdf_action_node,       pdf_action_size,          NULL, node_fields_whatsit_pdf_action,       NULL, -1, 0 },
-    { pdf_thread_node,       pdf_thread_node_size,     NULL, node_fields_whatsit_pdf_thread,       NULL, -1, 0 },
-    { pdf_start_thread_node, pdf_thread_node_size,     NULL, node_fields_whatsit_pdf_start_thread, NULL, -1, 0 },
-    { pdf_end_thread_node,   pdf_end_thread_node_size, NULL, node_fields_whatsit_pdf_end_thread,   NULL, -1, 0 },
-    { pdf_thread_data_node,  pdf_thread_node_size,     NULL, NULL,                                 NULL, -1, 0 },
-    { pdf_link_data_node,    pdf_annot_node_size,      NULL, NULL,                                 NULL, -1, 0 },
-    { pdf_colorstack_node,   pdf_colorstack_node_size, NULL, node_fields_whatsit_pdf_colorstack,   NULL, -1, 0 },
-    { pdf_setmatrix_node,    pdf_setmatrix_node_size,  NULL, node_fields_whatsit_pdf_setmatrix,    NULL, -1, 0 },
-    { pdf_save_node,         pdf_save_node_size,       NULL, node_fields_whatsit_pdf_save,         NULL, -1, 0 },
-    { pdf_restore_node,      pdf_restore_node_size,    NULL, node_fields_whatsit_pdf_restore,      NULL, -1, 0 },
-
-    /*tex That's it. */
-
-    { -1,                    -1,                       NULL, NULL,                                 NULL, -1, 0 },
-
-};
-
-void l_set_whatsit_data(void) {
-    init_node_key(whatsit_node_data, open_node,         open)
-    init_node_key(whatsit_node_data, write_node,        write)
-    init_node_key(whatsit_node_data, close_node,        close)
-    init_node_key(whatsit_node_data, special_node,      special)
-    init_node_key(whatsit_node_data, save_pos_node,     save_pos)
-    init_node_key(whatsit_node_data, late_lua_node,     late_lua)
-    init_node_key(whatsit_node_data, user_defined_node, user_defined)
-
-    init_field_key(node_fields_whatsit_close, 0, attr);
-    init_field_key(node_fields_whatsit_close, 1, stream);
-    init_field_nop(node_fields_whatsit_close, 2);
-
-    init_field_key(node_fields_whatsit_late_lua, 0, attr);
-    init_field_key(node_fields_whatsit_late_lua, 1, reg);
-    init_field_key(node_fields_whatsit_late_lua, 2, data);
-    init_field_key(node_fields_whatsit_late_lua, 3, name);
-    init_field_key(node_fields_whatsit_late_lua, 4, string);
-    init_field_nop(node_fields_whatsit_late_lua, 5);
-
-    init_field_key(node_fields_whatsit_open, 0, attr);
-    init_field_key(node_fields_whatsit_open, 1, stream);
-    init_field_key(node_fields_whatsit_open, 2, name);
-    init_field_key(node_fields_whatsit_open, 3, area);
-    init_field_key(node_fields_whatsit_open, 4, ext);
-    init_field_nop(node_fields_whatsit_open, 5);
-
-    init_field_key(node_fields_whatsit_save_pos, 0, attr);
-    init_field_nop(node_fields_whatsit_save_pos, 1);
-
-    init_field_key(node_fields_whatsit_special, 0, attr);
-    init_field_key(node_fields_whatsit_special, 1, data);
-    init_field_nop(node_fields_whatsit_special, 2);
-
-    init_field_key(node_fields_whatsit_user_defined, 0, attr);
-    init_field_key(node_fields_whatsit_user_defined, 1, user_id);
-    init_field_key(node_fields_whatsit_user_defined, 2, type);
-    init_field_key(node_fields_whatsit_user_defined, 3, value);
-    init_field_nop(node_fields_whatsit_user_defined, 4);
-
-    init_field_key(node_fields_whatsit_write, 0, attr);
-    init_field_key(node_fields_whatsit_write, 1, stream);
-    init_field_key(node_fields_whatsit_write, 2, data);
-    init_field_nop(node_fields_whatsit_write, 3);
-
-    init_node_key(whatsit_node_data, pdf_literal_node,     pdf_literal)
-    init_node_key(whatsit_node_data, pdf_refobj_node,      pdf_refobj)
-    init_node_key(whatsit_node_data, pdf_annot_node,       pdf_annot)
-    init_node_key(whatsit_node_data, pdf_start_link_node,  pdf_start_link)
-    init_node_key(whatsit_node_data, pdf_end_link_node,    pdf_end_link)
-    init_node_key(whatsit_node_data, pdf_dest_node,        pdf_dest)
-    init_node_key(whatsit_node_data, pdf_action_node,      pdf_action)
-    init_node_key(whatsit_node_data, pdf_thread_node,      pdf_thread)
-    init_node_key(whatsit_node_data, pdf_start_thread_node,pdf_start_thread)
-    init_node_key(whatsit_node_data, pdf_end_thread_node,  pdf_end_thread)
-    init_node_key(whatsit_node_data, pdf_thread_data_node, pdf_thread_data)
-    init_node_key(whatsit_node_data, pdf_link_data_node,   pdf_link_data)
-    init_node_key(whatsit_node_data, pdf_colorstack_node,  pdf_colorstack)
-    init_node_key(whatsit_node_data, pdf_setmatrix_node,   pdf_setmatrix)
-    init_node_key(whatsit_node_data, pdf_save_node,        pdf_save)
-    init_node_key(whatsit_node_data, pdf_restore_node,     pdf_restore)
-
-    init_node_key(node_values_pdf_destination, 0, xyz)
-    init_node_key(node_values_pdf_destination, 1, fit)
-    init_node_key(node_values_pdf_destination, 2, fith)
-    init_node_key(node_values_pdf_destination, 3, fitv)
-    init_node_key(node_values_pdf_destination, 4, fitb)
-    init_node_key(node_values_pdf_destination, 5, fitbh)
-    init_node_key(node_values_pdf_destination, 6, fitbv)
-    init_node_key(node_values_pdf_destination, 7, fitr)
-
-    init_node_key(node_values_pdf_literal, set_origin,    origin)
-    init_node_key(node_values_pdf_literal, direct_page,   page)
-    init_node_key(node_values_pdf_literal, direct_always, always)
-    init_node_key(node_values_pdf_literal, direct_raw,    raw)
-    init_node_key(node_values_pdf_literal, direct_text,   text)
-    init_node_key(node_values_pdf_literal, direct_font,   font)
-    init_node_key(node_values_pdf_literal, scan_special,  special)
-
-    init_node_key(node_values_pdf_action, 0, page)
-    init_node_key(node_values_pdf_action, 1, goto)
-    init_node_key(node_values_pdf_action, 2, thread)
-    init_node_key(node_values_pdf_action, 3, user)
-
-    init_node_key(node_values_pdf_window, 0, unset)
-    init_node_key(node_values_pdf_window, 1, new)
-    init_node_key(node_values_pdf_window, 2, nonew)
-
-    init_node_key(node_values_color_stack, 0, set)
-    init_node_key(node_values_color_stack, 1, push)
-    init_node_key(node_values_color_stack, 2, pop)
-    init_node_key(node_values_color_stack, 3, current)
-
-    init_field_key(node_fields_whatsit_pdf_action, 0, action_type);
-    init_field_key(node_fields_whatsit_pdf_action, 1, named_id);
-    init_field_key(node_fields_whatsit_pdf_action, 2, action_id);
-    init_field_key(node_fields_whatsit_pdf_action, 3, file);
-    init_field_key(node_fields_whatsit_pdf_action, 4, new_window);
-    init_field_key(node_fields_whatsit_pdf_action, 5, data);
-    init_field_nop(node_fields_whatsit_pdf_action, 6);
-
-    init_field_key(node_fields_whatsit_pdf_annot, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_annot, 1, width);
-    init_field_key(node_fields_whatsit_pdf_annot, 2, depth);
-    init_field_key(node_fields_whatsit_pdf_annot, 3, height);
-    init_field_key(node_fields_whatsit_pdf_annot, 4, objnum);
-    init_field_key(node_fields_whatsit_pdf_annot, 5, data);
-    init_field_nop(node_fields_whatsit_pdf_annot, 6);
-
-    init_field_key(node_fields_whatsit_pdf_colorstack, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_colorstack, 1, stack);
-    init_field_key(node_fields_whatsit_pdf_colorstack, 2, cmd);
-    init_field_key(node_fields_whatsit_pdf_colorstack, 3, data);
-    init_field_nop(node_fields_whatsit_pdf_colorstack, 4);
-
-    init_field_key(node_fields_whatsit_pdf_dest, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_dest, 1, width);
-    init_field_key(node_fields_whatsit_pdf_dest, 2, depth);
-    init_field_key(node_fields_whatsit_pdf_dest, 3, height);
-    init_field_key(node_fields_whatsit_pdf_dest, 4, named_id);
-    init_field_key(node_fields_whatsit_pdf_dest, 5, dest_id);
-    init_field_key(node_fields_whatsit_pdf_dest, 6, dest_type);
-    init_field_key(node_fields_whatsit_pdf_dest, 7, xyz_zoom);
-    init_field_key(node_fields_whatsit_pdf_dest, 8, objnum);
-    init_field_nop(node_fields_whatsit_pdf_dest, 9);
-
-    init_field_key(node_fields_whatsit_pdf_end_link, 0, attr);
-    init_field_nop(node_fields_whatsit_pdf_end_link, 1);
-
-    init_field_key(node_fields_whatsit_pdf_end_thread, 0, attr);
-    init_field_nop(node_fields_whatsit_pdf_end_thread, 1);
-
-    init_field_key(node_fields_whatsit_pdf_literal, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_literal, 1, mode);
-    init_field_key(node_fields_whatsit_pdf_literal, 2, data);
-    init_field_nop(node_fields_whatsit_pdf_literal, 3);
-
-    init_field_key(node_fields_whatsit_pdf_refobj, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_refobj, 1, objnum);
-    init_field_nop(node_fields_whatsit_pdf_refobj, 2);
-
-    init_field_key(node_fields_whatsit_pdf_restore, 0, attr);
-    init_field_nop(node_fields_whatsit_pdf_restore, 1);
-
-    init_field_key(node_fields_whatsit_pdf_save, 0, attr);
-    init_field_nop(node_fields_whatsit_pdf_save, 1);
-
-    init_field_key(node_fields_whatsit_pdf_setmatrix, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_setmatrix, 1, data);
-    init_field_nop(node_fields_whatsit_pdf_setmatrix, 2);
-
-    init_field_key(node_fields_whatsit_pdf_start_link, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_start_link, 1, width);
-    init_field_key(node_fields_whatsit_pdf_start_link, 2, depth);
-    init_field_key(node_fields_whatsit_pdf_start_link, 3, height);
-    init_field_key(node_fields_whatsit_pdf_start_link, 4, objnum);
-    init_field_key(node_fields_whatsit_pdf_start_link, 5, link_attr);
-    init_field_key(node_fields_whatsit_pdf_start_link, 6, action);
-    init_field_nop(node_fields_whatsit_pdf_start_link, 7);
-
-    init_field_key(node_fields_whatsit_pdf_start_thread, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_start_thread, 1, width);
-    init_field_key(node_fields_whatsit_pdf_start_thread, 2, depth);
-    init_field_key(node_fields_whatsit_pdf_start_thread, 3, height);
-    init_field_key(node_fields_whatsit_pdf_start_thread, 4, named_id);
-    init_field_key(node_fields_whatsit_pdf_start_thread, 5, thread_id);
-    init_field_key(node_fields_whatsit_pdf_start_thread, 6, thread_attr);
-    init_field_nop(node_fields_whatsit_pdf_start_thread, 7);
-
-    init_field_key(node_fields_whatsit_pdf_thread, 0, attr);
-    init_field_key(node_fields_whatsit_pdf_thread, 1, width);
-    init_field_key(node_fields_whatsit_pdf_thread, 2, depth);
-    init_field_key(node_fields_whatsit_pdf_thread, 3, height);
-    init_field_key(node_fields_whatsit_pdf_thread, 4, named_id);
-    init_field_key(node_fields_whatsit_pdf_thread, 5, thread_id);
-    init_field_key(node_fields_whatsit_pdf_thread, 6, thread_attr);
-    init_field_nop(node_fields_whatsit_pdf_thread, 7);
-
-}
-
-#define last_whatsit_node pdf_restore_node
-
-/*tex
-
-    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:
-
-    \startitemize
-
-        \startitem
-            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.
-        \stopitem
-
-        \startitem
-            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.
-        \stopitem
-
-    \stopitemize
-
-    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.
-
-*/
-
-int lua_properties_level         = 0 ;
-int lua_properties_enabled       = 0 ;
-int lua_properties_use_metatable = 0 ;
-
-/*tex
-
-    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.
-
-*/
-
-#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)
-
-/*tex No setting is needed: */
-
-#define lua_properties_set(target) do { \
-} while(0)
-
-/*tex 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)
-
-/*tex
-
-    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:
-
-    \starttyping
-    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)
-    \stoptyping
-
-*/
-
-/*tex Isn't there a faster way to metatable? */
-
-/*tex
-
-    \starttyping
-    #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)
-    \stoptyping
-
-*/
-
-/*tex
-
-    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)
-
-/*tex Here end the property handlers. */
-
-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;
-}
-
-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); }
-
-/*tex hm, we can just pass |p| then. */
-
-#define check_token_ref(p) { \
-    if (type(p) == whatsit_node) { \
-        formatted_error("nodes","fuzzy token cleanup in whatsit node with type %s and subtype %i",node_data[type(p)].name,subtype(p)); \
-    } else { \
-        formatted_error("nodes","fuzzy token cleanup in node with type %s",node_data[type(p)].name); \
-    } \
-}
-
-#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;
-}
-
-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;
-}
-
-/*tex
-
-    Because of the 5-10\% overhead that \SYNTEX\ creates some options have been
-    implemented controlled by |synctex_anyway_mode|.
-
-    \startabulate
-    \NC \type {1} \NC all but glyphs  \NC \NR
-    \NC \type {2} \NC also glyphs     \NC \NR
-    \NC \type {3} \NC glyphs and glue \NC \NR
-    \NC \type {4} \NC only glyphs     \NC \NR
-    \stoptabulate
-
-*/
-
-static halfword synctex_anyway_mode = 0;
-static halfword synctex_line_field  = 0;
-static halfword synctex_no_files    = 0;
-
-void synctex_set_mode(int m)
-{
-    synctex_anyway_mode = m;
-};
-
-int synctex_get_mode(void)
-{
-    return synctex_anyway_mode;
-};
-
-void synctex_set_no_files(int f)
-{
-    synctex_no_files = f;
-};
-
-int synctex_get_no_files(void)
-{
-    return (int) synctex_no_files ;
-};
-
-void synctex_set_tag(int t)
-{
-    cur_input.synctex_tag_field = t;
-};
-
-int synctex_get_tag(void)
-{
-    return (int) cur_input.synctex_tag_field;
-};
-
-int synctex_get_line(void)
-{
-    return (int) synctex_line_field;
-};
-
-static int forced_tag  = 0;
-static int forced_line = 0;
-
-void synctex_force_tag(int t)
-{
-    forced_tag = t;
-};
-
-void synctex_force_line(int t)
-{
-    forced_line = t;
-};
-
-void synctex_set_line(int l)
-{
-    synctex_line_field = l;
-};
-
-/*tex |if_stack| is called a lot so maybe optimize that one. */
-
-halfword new_node(int i, int j)
-{
-    int s = get_node_size(i, j);
-    halfword n = get_node(s);
-    /*tex
-
-        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:
-            /*tex
-
-                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;
-        case fraction_noad:
-            fraction_fam(n) = -1;
-            break;
-        case simple_noad:
-            noad_fam(n) = -1;
-            break;
-        default:
-            break;
-    }
-    if (synctex_anyway_mode) {
-        /*tex See table above. */
-        switch (i) {
-            case glyph_node:
-                if (synctex_anyway_mode > 1) {
-                    synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-                    synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-                }
-                break;
-            case glue_node:
-                if (synctex_anyway_mode < 4) {
-                    synctex_tag_glue(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-                    synctex_line_glue(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-                }
-                break;
-            case kern_node:
-                if (synctex_anyway_mode < 3) {
-                    synctex_tag_kern(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-                    synctex_line_kern(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-                }
-                break;
-            case hlist_node:
-            case vlist_node:
-            case unset_node:
-                /*tex Rather useless: */
-                if (synctex_anyway_mode < 3) {
-                    synctex_tag_box(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-                    synctex_line_box(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-                }
-                break;
-            case rule_node:
-                if (synctex_anyway_mode < 3) {
-                    synctex_tag_rule(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-                    synctex_line_rule(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-                }
-                break;
-            case math_node:
-                /*tex Noads probably make more sense but let's not change that. */
-                if (synctex_anyway_mode < 3) {
-                    synctex_tag_math(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-                    synctex_line_math(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-                }
-                break;
-        }
-    } else if (synctex_par) {
-        /*tex Handle the \SYNTEX\ extension. */
-        switch (i) {
-            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;
-            case math_node:
-                synctex_tag_math(n) = cur_input.synctex_tag_field;
-                synctex_line_math(n) = line;
-                break;
-        }
-    }
-    /*tex Take care of attributes. */
-    if (nodetype_has_attributes(i)) {
-        build_attribute_list(n);
-        /*tex No need for |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)));
-    if (synctex_anyway_mode > 1) {
-        synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-        synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-    }
-    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)));
-    if (synctex_anyway_mode > 1) {
-        synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-        synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-    }
-    type(n) = glyph_node;
-    subtype(n) = 0;
-    build_attribute_list(n);
-    /*tex No need for |lua_properties_set|. */
-    return n;
-}
-
-/*tex
-
-    This makes a duplicate of the node list that starts at |p| and returns a
-    pointer to the new list.
-
-*/
-
-halfword do_copy_node_list(halfword p, halfword end)
-{
-    /*tex previous position in new list */
-    halfword q = null;
-    /*tex head of the list */
-    halfword h = null;
-    register halfword s ;
-    /*tex saves stack and time */
-    lua_properties_push;
-    while (p != end) {
-        s = copy_node(p);
-        if (h == null) {
-            h = s;
-        } else {
-            couple_nodes(q, s);
-        }
-        q = s;
-        p = vlink(p);
-    }
-    /*tex saves stack and time */
-    lua_properties_pop;
-    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)
-
-/*tex Make a dupe of a single node. */
-
-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)
-{
-    /*tex current node being fabricated for new list */
-    halfword r;
-    /*tex whatsit subtype */
-    halfword w;
-    /*tex type of node */
-    halfword t;
-    /*tex a helper variable for copying into variable mem  */
-    register halfword s;
-    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));
-    /*tex A possible speedup:
-
-    \starttyping
-    if t == glue_spec) {
-        return r;
-    }
-    \stoptyping
-
-    */
-    if (synctex_anyway_mode) {
-        /*tex Not:
-
-        \starttyping
-        if (t == glyph_node) {
-            if (synctex_anyway_mode > 1) {
-                synctex_tag_glyph(r) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
-                synctex_line_glyph(r) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
-            }
-        }
-        \stoptyping
-        */
-    } else if (synctex_par) {
-        /*tex 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:
-            break;
-        case dir_node:
-            break;
-        case local_par_node:
-            copy_sub_list(local_box_left(r),local_box_left(p));
-            copy_sub_list(local_box_right(r),local_box_right(p));
-        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;
-}
-
-#define free_sub_list(source) if (source != null) flush_node_list(source);
-#define free_sub_node(source) if (source != null) flush_node(source);
-
-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':
-                /*tex |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){
-        /*tex 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:
-            /*tex 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:
-            /*tex This allows free-ing of lua-allocated glue specs. */
-            break ;
-        case dir_node:
-            break;
-        case local_par_node:
-            free_sub_list(local_box_left(p));
-            free_sub_list(local_box_right(p));
-            break;
-        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:
-            /*tex 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:
-        case math_char_node:
-        case math_text_char_node:
-            /*tex Nothing to do. */
-            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;
-}
-
-/*tex Erase the list of nodes starting at |pp|. */
-
-void flush_node_list(halfword pp)
-{
-    register halfword p = pp;
-    if (p == null) {
-        /*tex Legal, but no-op. */
-        return;
-    }
-    if (free_error(p))
-        return;
-    /*tex Saves stack and time. */
-    lua_properties_push;
-    while (p != null) {
-        register halfword q = vlink(p);
-        flush_node(p);
-        p = q;
-    }
-    /*tex Saves stack and time. */
-    lua_properties_pop;
-}
-
-static void check_node_wrapup_core(halfword p)
-{
-    switch (subtype(p)) {
-        /*tex Frontend code. */
-        case special_node:
-            check_token_ref(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(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(p);
-            break;
-        case pdf_colorstack_node:
-            if (pdf_colorstack_cmd(p) <= colorstack_data)
-                check_token_ref(p);
-            break;
-        case pdf_setmatrix_node:
-            check_token_ref(p);
-            break;
-        case late_lua_node:
-            if (late_lua_name(p) > 0)
-                check_token_ref(p);
-            if (late_lua_type(p) == normal)
-                check_token_ref(p);
-            break;
-        case pdf_annot_node:
-            check_token_ref(p);
-            break;
-        case pdf_start_link_node:
-            if (pdf_link_attr(p) != null)
-                check_token_ref(p);
-            check_action_ref(pdf_link_action(p));
-            break;
-        case pdf_dest_node:
-            if (pdf_dest_named_id(p) > 0)
-                check_token_ref(p);
-            break;
-        case pdf_thread_node:
-        case pdf_start_thread_node:
-            if (pdf_thread_named_id(p) > 0)
-                check_token_ref(p);
-            if (pdf_thread_attr(p) != null)
-                check_token_ref(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 local_par_node:
-            dorangetest(p, local_box_left(p), var_mem_max);
-            dorangetest(p, local_box_right(p), var_mem_max);
-            break;
-        /*tex
-
-        There is no need for useless cases:
-
-        \starttyping
-        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:
-            break;
-        default:
-            fprintf(stdout, "check_node: type is %d\n", type(p));
-        \stoptyping
-
-        */
-    }
-}
-
-halfword fix_node_list(halfword head)
-{
-    halfword next, tail;
-    if (head == null)
-        return null;
-    tail = head;
-    next = vlink(head);
-    while (next != null) {
-        alink(next) = tail;
-        tail = next;
-        next = vlink(tail);
-    }
-    return tail;
-}
-
-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;
-            /*tex Maintain usage statistics. */
-            var_used += s;
-            return r;
-        }
-        /*tex This is the end of the \quote {inner loop}. */
-        return slow_get_node(s);
-    } else {
-        normal_error("nodes","there is a problem in getting a node, case 1");
-        return null;
-    }
-}
-
-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 {
-        /*tex 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;
-    }
-    /*tex Maintain statistics. */
-    var_used -= s;
-}
-
-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;
-}
-
-/*tex
-
-    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.
-
-*/
-
-#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;
-
-    /*tex 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);
-
-    /*tex 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);
-}
-
-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);
-}
-
-/*tex
-
-    It makes sense to enlarge the varmem array immediately.
-
-*/
-
-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) {
-        /*tex Todo: is it 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;
-    }
-}
-
-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) {
-            /*tex 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) {
-                /*tex The 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;
-                /*tex Maintain usage statistics. */
-                var_used += s;
-                /*tex This is the only exit. */
-                return r;
-            } else {
-                normal_error("nodes","there is a problem in getting a node, case 2");
-                return null;
-            }
-        } else {
-            /*tex 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);
-                    }
-                }
-            }
-            /*tex 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
-            /*tex Todo: is it 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;
-    }
-}
-
-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;
-}
-
-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;
-}
-
-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;
-        }
-    }
-    /*tex A newline, if needed: */
-    print_nlp();
-}
-
-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;
-}
-
-/* Now comes some attribute stuff. */
-
-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;
-}
-
-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);
-        /*tex 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;
-}
-
-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;
-}
-
-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;
-    }
-}
-
-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 ;
-}
-
-void reassign_attribute(halfword n, halfword new)
-{
-    halfword old;
-    old = node_attr(n);
-    if (new == null) {
-        /*tex There is nothing to assign but we need to check for an old value. */
-        if (old != null) {
-            /*tex This also nulls |attr| field of |n|. */
-            delete_attribute_ref(old);
-        }
-    } else if (old == null) {
-        /*tex Nothing is assigned so we just do that now. */
-        assign_attribute_ref(n,new);
-    } else if (old != new) {
-        /*tex Something is assigned so we need to clean up and assign then. */
-        delete_attribute_ref(old);
-        assign_attribute_ref(n,new);
-    }
-    /*tex The same value so there is no need to assign and change the refcount. */
-    node_attr(n) = new ;
-}
-
-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);
-            }
-            /*tex 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);
-    }
-}
-
-/*tex Here |p| is an attr list head, or zero. */
-
-halfword do_set_attribute(halfword p, int i, int val)
-{
-    register halfword q;
-    register int j = 0;
-    if (p == null) {
-        /*tex 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) {
-                /*tex There is no need to do anything. */
-                return q;
-            }
-            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 {
-            /*tex 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 ;
-    }
-}
-
-void set_attribute(halfword n, int i, int val)
-{
-    register halfword p;
-    register int j = 0;
-    /*tex Not all nodes can have an attribute list. */
-    if (!nodetype_has_attributes(type(n)))
-        return;
-    /*tex 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;
-    }
-    /*tex 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);
-        }
-        /*tex If found |j| has now the position and we assume a sorted list ! */
-        p = node_attr(n);
-        if (attr_list_ref(p) == 0 ) {
-            /*tex 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);
-            /*tex The still dangling list gets ref count 1. */
-            attr_list_ref(p) = 1;
-        } else if (attr_list_ref(p) == 1) {
-            /*tex This can really happen! */
-            if (p == attr_list_cache) {
-                /*tex
-
-                    We can invalidate the cache setting with |attr_list_cache =
-                    cache_disabled| or or save the list, as done below.
-
-                */
-                p = copy_attribute_list(p);
-                node_attr(n) = p;
-                /*tex The copied list gets ref count 1. */
-                attr_list_ref(p) = 1;
-            }
-        } else {
-            /*tex The list is used multiple times so we make a copy. */
-            p = copy_attribute_list(p);
-            /*tex We decrement the ref count or the original. */
-            delete_attribute_ref(node_attr(n));
-            node_attr(n) = p;
-            /*tex The copied list gets ref count 1. */
-            attr_list_ref(p) = 1;
-        }
-        /*tex We go to position |j| in the list. */
-        while (j-- > 0)
-            p = vlink(p);
-        /*tex 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 {
-            /*tex 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");
-    }
-}
-
-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;
-        /*tex 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;
-    }
-}
-
-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;
-}
-
-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;
-    }
-}
-
-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));
-}
-
-/*tex
-
-    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.
-
-*/
-
-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('-');
-}
-
-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;
-    }
-}
-
-/*tex
-
-    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.
-
-*/
-
-#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)
-
-/*tex Print a node list symbolically: */
-
-void show_node_list(int p)
-{
-    /*tex The number of items already printed at this level: */
-    int n = 0;
-    halfword w;
-    /*tex A glue ratio, as a floating point number: */
-    real g;
-    if ((int) cur_length > depth_threshold) {
-        if (p > null) {
-            /*tex Indicate that there's been some truncation. */
-            tprint(" []");
-        }
-        return;
-    }
-    while (p != null) {
-        print_ln();
-        print_current_string();
-        /*tex Display the nesting history. */
-        if (tracing_online_par < -2)
-            print_int(p);
-        incr(n);
-        if (n > breadth_max) {
-            /*tex Time to stop. */
-            tprint("etc.");
-            return;
-        }
-        /*tex Display node |p|. */
-        if (is_char_node(p)) {
-            print_font_and_char(p);
-            if (is_ligature(p)) {
-                /*tex 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:
-                    /*tex 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) {
-                        /*tex 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 {
-                        /*tex
-
-                            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_par(box_dir(p));
-                    }
-                    /*tex Recursive call: */
-                    node_list_display(list_ptr(p));
-                    break;
-                case rule_node:
-                    /*tex 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:
-                    /*tex 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));
-                    /*tex Recursive call. */
-                    node_list_display(ins_ptr(p));
-                    break;
-                case dir_node:
-                    if (subtype(p) == cancel_dir) {
-                        tprint_esc("enddir");
-                    } else {
-                        tprint_esc("begindir");
-                    }
-                    print_char(' ');
-                    print_dir_par(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:
-                    /*tex Display glue |p|. */
-                    if (subtype(p) >= a_leaders) {
-                        /*tex 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);
-                        /*tex Recursive call: */
-                        node_list_display(leader_ptr(p));
-                    } 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:
-                    /*tex Display kern |p| */
-                    if (subtype(p) != mu_glue) {
-                        tprint_esc("kern");
-                        /*tex
-
-                        \starttyping
-                        if (subtype(p) != normal) {
-                            print_char(' ');
-                        }
-                        \stoptyping
-
-                        */
-                        print_scaled(width(p));
-                        if (subtype(p) == font_kern)
-                            tprint(" (font)");
-                        else if (subtype(p) == italic_kern)
-                            tprint(" (italic)");
-                        else if (subtype(p) == accent_kern)
-                            tprint(" (accent)");
-                    } else {
-                        tprint_esc("mkern");
-                        print_scaled(width(p));
-                        tprint("mu");
-                    }
-                    break;
-                case math_node:
-                    /*tex 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:
-                    /*tex Display penalty |p|. */
-                    tprint_esc("penalty ");
-                    print_int(penalty(p));
-                    break;
-                case disc_node:
-                    /*tex
-
-                        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 ...
-
-                        \starttyping
-                        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();
-                        \stoptyping
-
-                        has become:
-
-                    */
-                    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:
-                    /*tex 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:
-                    /*tex Display adjustment |p|. */
-                    tprint_esc("vadjust");
-                    if (subtype(p) != 0)
-                        tprint(" pre ");
-                    /*tex Recursive call. */
-                    node_list_display(adjust_ptr(p));
-                    break;
-                case glue_spec_node:
-                    tprint("<glue_spec ");
-                    print_spec(p, NULL);
-                    tprint(">");
-                    break;
-                default:
-                    show_math_node(p);
-                    break;
-            }
-        }
-        p = vlink(p);
-    }
-}
-
-/*tex
-
-    This routine finds the 'base' width of a horizontal box, using the same logic
-    that \TeX82 used for \.{\\predisplaywidth}.
-
-*/
-
-static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width)
-{
-    /*tex increment to |v| */
-    scaled d;
-    /*tex calculated |size| */
-    scaled w = -max_dimen;
-    /*tex |w| plus possible glue amount */
-    scaled v = initial_width;
-    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:
-                /*tex 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:
-                /*tex Begin mathskip code. */
-                if (glue_is_zero(p)) {
-                    d = surround(p);
-                    break;
-                } else {
-                    /*tex Fall through. */
-                }
-                /*tex End mathskip code. */
-            case glue_node:
-                /*tex
-
-                    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)
-{
-    /*tex
-
-        Often this is the same as:
-
-        \starttyping
-        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));
-        \stoptyping
-    */
-    return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width);
-}
-
-halfword tail_of_list(halfword p)
-{
-    halfword q = p;
-    while (vlink(q) != null)
-        q = vlink(q);
-    return q;
-}
-
-int var_used;
-
-/*tex
-
-    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.
-
-*/
-
-/*tex The maximum assigned attribute id: */
-
-int max_used_attr;
-halfword attr_list_cache;
-
-/*tex
-
-    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|.
-
-*/
-
-/*tex Create a new box node. */
-
-halfword new_null_box(void)
-{
-    halfword p = new_node(hlist_node, min_quarterword);
-    box_dir(p) = text_direction_par;
-    return p;
-}
-
-/*tex
-
-    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.
-
-*/
-
-halfword new_rule(int s)
-{
-    halfword p = new_node(rule_node,s);
-    return p;
-}
-
-/*tex
-
-    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|).
-
-*/
-
-halfword new_glyph(int f, int c)
-{
-    halfword p = null;
-    if ((f == 0) || (char_exists(f, c))) {
-        p = new_glyph_node();
-        set_to_glyph(p);
-        font(p) = f;
-        character(p) = c;
-    }
-    return p;
-}
-
-/*tex
-
-    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|.
-
-*/
-
-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;
-    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;
-}
-
-/*tex
-
-    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.
-
-*/
-
-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;
-}
-
-/*tex
-
-    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|.
-
-    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.
-
-*/
-
-halfword new_disc(void)
-{
-    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, 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}.
-
-*/
-
-halfword new_math(scaled w, int s)
-{
-    halfword p = new_node(math_node, s);
-    surround(p) = w;
-    return p;
-}
-
-/*tex
-
-    \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,
-    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.
-
-*/
-
-halfword new_spec(halfword q)
-{
-    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 {
-        /*tex Alternatively we can issue a warning. */
-        return copy_node(zero_glue);
-    }
-}
-
-/*tex
-
-    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}.
-
-*/
-
-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;
-}
-
-/*tex
-
-    Glue nodes that are more or less anonymous are created by |new_glue|, whose
-    argument points to a glue specification.
-
-*/
-
-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;
-}
-
-/*tex
-
-    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.
-
-*/
-
-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;
-}
-
-/*tex
-
-    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.
-
-*/
-
-halfword new_kern(scaled w)
-{
-    halfword p = new_node(kern_node, normal);
-    width(p) = w;
-    return p;
-}
-
-/*tex
-
-    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.
-
-*/
-
-halfword new_penalty(int m, int s)
-{
-    halfword p = new_node(penalty_node, 0);
-    penalty(p) = m;
-    subtype(p) = s;
-    return p;
-}
-
-/*tex
-
-    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'. 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.
-
-*/
-
-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;
-    /*tex Callback with node passed: */
-    callback_id = callback_defined(insert_local_par_callback);
-    if (callback_id > 0) {
-        /*tex Todo: use general mechanism/wrapper. */
-        int sfix = lua_gettop(Luas);
-        if (!get_callback(Luas, callback_id)) {
-            lua_settop(Luas, sfix);
-        } else {
-            int i;
-            nodelist_to_lua(Luas, p);
-            lua_push_local_par_mode(Luas,mode)
-            /*tex 2 arg, 0 result */
-            i = lua_pcall(Luas, 2, 0, 0);
-            if (i != 0) {
-                lua_gc(Luas, LUA_GCCOLLECT, 0);
-                Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
-            }
-            lua_settop(Luas, sfix);
-        }
-    }
-    return p;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/texnodes.w
@@ -0,0 +1,3910 @@
+% 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); }
+
+/* hm, we can just pass p then */
+
+#define check_token_ref(p) { \
+    if (type(p) == whatsit_node) { \
+        formatted_error("nodes","fuzzy token cleanup in whatsit node with type %s and subtype %i",node_data[type(p)].name,subtype(p)); \
+    } else { \
+        formatted_error("nodes","fuzzy token cleanup in node with type %s",node_data[type(p)].name); \
+    } \
+}
+
+#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
+static halfword synctex_anyway_mode = 0; /* 2 also glyphs */
+static halfword synctex_line_field  = 0;
+static halfword synctex_no_files    = 0;
+
+void synctex_set_mode(int m)
+{
+    synctex_anyway_mode = m;
+};
+
+int synctex_get_mode(void)
+{
+    return synctex_anyway_mode;
+};
+
+void synctex_set_no_files(int f)
+{
+    synctex_no_files = f;
+};
+
+int synctex_get_no_files(void)
+{
+    return (int) synctex_no_files ;
+};
+
+void synctex_set_tag(int t)
+{
+    cur_input.synctex_tag_field = t;
+};
+
+int synctex_get_tag(void)
+{
+    return (int) cur_input.synctex_tag_field;
+};
+
+
+int synctex_get_line(void)
+{
+    return (int) synctex_line_field;
+};
+
+static int forced_tag  = 0;
+static int forced_line = 0;
+
+void synctex_force_tag(int t)
+{
+    forced_tag = t;
+};
+
+void synctex_force_line(int t)
+{
+    forced_line = t;
+};
+
+void synctex_set_line(int l)
+{
+    synctex_line_field = l;
+};
+
+@ @c
+/* if_stack is called a lot so maybe optimize */
+
+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_anyway_mode) {
+        switch (i) {
+            /* 1 = all but glyphs  */
+            /* 2 = also glyphs     */
+            /* 3 = glyphs and glue */
+            /* 4 = only glyphs     */
+            case glyph_node:
+                if (synctex_anyway_mode > 1) {
+                    synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+                    synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+                }
+                break;
+            case glue_node:
+                if (synctex_anyway_mode < 4) {
+                    synctex_tag_glue(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+                    synctex_line_glue(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+                }
+                break;
+            case kern_node:
+                if (synctex_anyway_mode < 3) {
+                    synctex_tag_kern(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+                    synctex_line_kern(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+                }
+                break;
+            case hlist_node:
+            case vlist_node:
+            case unset_node: /* useless */
+                if (synctex_anyway_mode < 3) {
+                    synctex_tag_box(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+                    synctex_line_box(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+                }
+                break;
+            case rule_node:
+                if (synctex_anyway_mode < 3) {
+                    synctex_tag_rule(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+                    synctex_line_rule(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+                }
+                break;
+            case math_node: /* noads probably make more sense */
+                if (synctex_anyway_mode < 3) {
+                    synctex_tag_math(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+                    synctex_line_math(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+                }
+                break;
+        }
+    } else if (synctex_par) {
+        /* handle synctex extension */
+        switch (i) {
+            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;
+            case math_node:
+                synctex_tag_math(n) = cur_input.synctex_tag_field;
+                synctex_line_math(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)));
+    if (synctex_anyway_mode > 1) {
+        synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+        synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+    }
+    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)));
+    if (synctex_anyway_mode > 1) {
+        synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+        synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+    }
+    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_anyway_mode) {
+        /*
+        if (t == glyph_node) {
+            if (synctex_anyway_mode > 1) {
+                synctex_tag_glyph(r) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+                synctex_line_glyph(r) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+            }
+        }
+        */
+    } else 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(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(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(p);
+            break;
+        case pdf_colorstack_node:
+            if (pdf_colorstack_cmd(p) <= colorstack_data)
+                check_token_ref(p);
+            break;
+        case pdf_setmatrix_node:
+            check_token_ref(p);
+            break;
+        case late_lua_node:
+            if (late_lua_name(p) > 0)
+                check_token_ref(p);
+            if (late_lua_type(p) == normal)
+                check_token_ref(p);
+            break;
+        case pdf_annot_node:
+            check_token_ref(p);
+            break;
+        case pdf_start_link_node:
+            if (pdf_link_attr(p) != null)
+                check_token_ref(p);
+            check_action_ref(pdf_link_action(p));
+            break;
+        case pdf_dest_node:
+            if (pdf_dest_named_id(p) > 0)
+                check_token_ref(p);
+            break;
+        case pdf_thread_node:
+        case pdf_start_thread_node:
+            if (pdf_thread_named_id(p) > 0)
+                check_token_ref(p);
+            if (pdf_thread_attr(p) != null)
+                check_token_ref(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
+halfword fix_node_list(halfword head)
+{
+    halfword next, tail;
+    if (head == null)
+        return null;
+    tail = head;
+    next = vlink(head);
+    while (next != null) {
+        alink(next) = tail;
+        tail = next;
+        next = vlink(tail);
+    }
+    return tail;
+}
+
+@ @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) == font_kern)
+                        tprint(" (font)");
+                    else if (subtype(p) == italic_kern)
+                        tprint(" (italic)");
+                    else if (subtype(p) == accent_kern)
+                        tprint(" (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;
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/textcodes.w
@@ -0,0 +1,490 @@
+% textcodes.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"
+
+@ catcodes @c
+
+#define CATCODESTACK       8
+#define CATCODEDEFAULT    12
+#define CATCODE_MAX    32767
+
+static sa_tree *catcode_heads = NULL;
+static int catcode_max = 0;
+static unsigned char *catcode_valid = NULL;
+
+void set_cat_code(int h, int n, halfword v, quarterword gl)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_tree s = catcode_heads[h];
+    if (h > catcode_max)
+        catcode_max = h;
+    if (s == NULL) {
+        sa_value.int_value = CATCODEDEFAULT;
+        s = new_sa_tree(CATCODESTACK, 1, sa_value);
+        catcode_heads[h] = s;
+    }
+    sa_value.int_value = (int) v;
+    set_sa_item(s, n, sa_value, gl);
+}
+
+halfword get_cat_code(int h, int n)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_tree s = catcode_heads[h];
+    if (h > catcode_max)
+        catcode_max = h;
+    if (s == NULL) {
+        sa_value.int_value = CATCODEDEFAULT;
+        s = new_sa_tree(CATCODESTACK, 1, sa_value);
+        catcode_heads[h] = s;
+    }
+    return (halfword) get_sa_item(s, n).int_value;
+}
+
+void unsave_cat_codes(int h, quarterword gl)
+{
+    int k;
+    if (h > catcode_max)
+        catcode_max = h;
+    for (k = 0; k <= catcode_max; k++) {
+        if (catcode_heads[k] != NULL)
+            restore_sa_stack(catcode_heads[k], gl);
+    }
+}
+
+static void initializecatcodes(void)
+{
+    sa_tree_item sa_value = { 0 };
+    catcode_max = 0;
+    catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1));
+    catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1));
+    memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1));
+    memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1));
+    catcode_valid[0] = 1;
+    sa_value.int_value = CATCODEDEFAULT;
+    catcode_heads[0] = new_sa_tree(CATCODESTACK, 1, sa_value);
+}
+
+static void dumpcatcodes(void)
+{
+    int total = 0;
+    int k;
+    for (k = 0; k <= catcode_max; k++) {
+        if (catcode_valid[k]) {
+            total++;
+        }
+    }
+    dump_int(catcode_max);
+    dump_int(total);
+    for (k = 0; k <= catcode_max; k++) {
+        if (catcode_valid[k]) {
+            dump_int(k);
+            dump_sa_tree(catcode_heads[k],"catcodes");
+        }
+    }
+}
+
+static void undumpcatcodes(void)
+{
+    int total, k, x;
+    xfree(catcode_heads);
+    xfree(catcode_valid);
+    catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1));
+    catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1));
+    memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1));
+    memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1));
+    undump_int(catcode_max);
+    undump_int(total);
+    for (k = 0; k < total; k++) {
+        undump_int(x);
+        catcode_heads[x] = undump_sa_tree("catcodes");
+        catcode_valid[x] = 1;
+    }
+}
+
+int valid_catcode_table(int h)
+{
+    if (h <= CATCODE_MAX && h >= 0 && catcode_valid[h]) {
+        return 1;
+    }
+    return 0;
+}
+
+void copy_cat_codes(int from, int to)
+{
+    if (from < 0 || from > CATCODE_MAX || catcode_valid[from] == 0) {
+        uexit(1);
+    }
+    if (to > catcode_max)
+        catcode_max = to;
+    destroy_sa_tree(catcode_heads[to]);
+    catcode_heads[to] = copy_sa_tree(catcode_heads[from]);
+    catcode_valid[to] = 1;
+}
+
+void initex_cat_codes(int h)
+{
+    int k;
+    if (h > catcode_max)
+        catcode_max = h;
+    destroy_sa_tree(catcode_heads[h]);
+    catcode_heads[h] = NULL;
+    set_cat_code(h, '\r', car_ret_cmd, 1);
+    set_cat_code(h, ' ', spacer_cmd, 1);
+    set_cat_code(h, '\\', escape_cmd, 1);
+    set_cat_code(h, '%', comment_cmd, 1);
+    set_cat_code(h, 127, invalid_char_cmd, 1);
+    set_cat_code(h, 0, ignore_cmd, 1);
+    set_cat_code(h, 0xFEFF, ignore_cmd, 1);
+    for (k = 'A'; k <= 'Z'; k++) {
+        set_cat_code(h, k, letter_cmd, 1);
+        set_cat_code(h, k + 'a' - 'A', letter_cmd, 1);
+    }
+    catcode_valid[h] = 1;
+}
+
+static void freecatcodes(void)
+{
+    int k;
+    for (k = 0; k <= catcode_max; k++) {
+        if (catcode_valid[k]) {
+            destroy_sa_tree(catcode_heads[k]);
+        }
+    }
+    xfree(catcode_heads);
+    xfree(catcode_valid);
+}
+
+@ lccodes @c
+
+#define LCCODESTACK   8
+#define LCCODEDEFAULT 0
+
+static sa_tree lccode_head = NULL;
+
+void set_lc_code(int n, halfword v, quarterword gl)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_value.int_value = (int) v;
+    set_sa_item(lccode_head, n, sa_value, gl);
+}
+
+halfword get_lc_code(int n)
+{
+    return (halfword) get_sa_item(lccode_head, n).int_value;
+}
+
+static void unsavelccodes(quarterword gl)
+{
+    restore_sa_stack(lccode_head, gl);
+}
+
+static void initializelccodes(void)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_value.int_value = LCCODEDEFAULT;
+    lccode_head = new_sa_tree(LCCODESTACK, 1, sa_value);
+}
+
+static void dumplccodes(void)
+{
+    dump_sa_tree(lccode_head,"lccodes");
+}
+
+static void undumplccodes(void)
+{
+    lccode_head = undump_sa_tree("lccodes");
+}
+
+static void freelccodes(void)
+{
+    destroy_sa_tree(lccode_head);
+}
+
+@ uccodes @c
+
+#define UCCODESTACK   8
+#define UCCODEDEFAULT 0
+
+static sa_tree uccode_head = NULL;
+
+void set_uc_code(int n, halfword v, quarterword gl)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_value.int_value = (int) v;
+    set_sa_item(uccode_head, n, sa_value, gl);
+}
+
+halfword get_uc_code(int n)
+{
+    return (halfword) get_sa_item(uccode_head, n).int_value;
+}
+
+static void unsaveuccodes(quarterword gl)
+{
+    restore_sa_stack(uccode_head, gl);
+}
+
+static void initializeuccodes(void)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_value.int_value = UCCODEDEFAULT;
+    uccode_head = new_sa_tree(UCCODESTACK, 1, sa_value);
+}
+
+static void dumpuccodes(void)
+{
+    dump_sa_tree(uccode_head,"uccodes");
+}
+
+static void undumpuccodes(void)
+{
+    uccode_head = undump_sa_tree("uccodes");
+}
+
+static void freeuccodes(void)
+{
+    destroy_sa_tree(uccode_head);
+}
+
+@ sfcodes @c
+
+#define SFCODESTACK      8
+#define SFCODEDEFAULT 1000
+
+static sa_tree sfcode_head = NULL;
+
+void set_sf_code(int n, halfword v, quarterword gl)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_value.int_value = (int) v;
+    set_sa_item(sfcode_head, n, sa_value, gl);
+}
+
+halfword get_sf_code(int n)
+{
+    return (halfword) get_sa_item(sfcode_head, n).int_value;
+}
+
+static void unsavesfcodes(quarterword gl)
+{
+    restore_sa_stack(sfcode_head, gl);
+}
+
+static void initializesfcodes(void)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_value.int_value = SFCODEDEFAULT;
+    sfcode_head = new_sa_tree(SFCODESTACK, 1, sa_value);
+}
+
+static void dumpsfcodes(void)
+{
+    dump_sa_tree(sfcode_head,"sfcodes");
+}
+
+static void undumpsfcodes(void)
+{
+    sfcode_head = undump_sa_tree("sfcodes");
+}
+
+static void freesfcodes(void)
+{
+    destroy_sa_tree(sfcode_head);
+}
+
+@ hjcodes @c
+
+#define HJCODESTACK       8
+#define HJCODEDEFAULT     0
+#define HJCODE_MAX    16383 /* number of languages */
+
+static sa_tree *hjcode_heads = NULL;
+static int hjcode_max = 0;
+static unsigned char *hjcode_valid = NULL;
+
+@ Here we set codes but we don't initialize from lccodes.
+
+@c void set_hj_code(int h, int n, halfword v, quarterword gl)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_tree s = hjcode_heads[h];
+    if (h > hjcode_max)
+        hjcode_max = h;
+    if (s == NULL) {
+        sa_value.int_value = HJCODEDEFAULT;
+        s = new_sa_tree(HJCODESTACK, 1, sa_value);
+        hjcode_heads[h] = s;
+    }
+    sa_value.int_value = (int) v;
+    set_sa_item(s, n, sa_value, gl);
+}
+
+@ We just return the lccodes when nothing is set.
+
+@c halfword get_hj_code(int h, int n)
+{
+    sa_tree s = hjcode_heads[h];
+    if (s == NULL) {
+        s = lccode_head;
+    }
+    return (halfword) get_sa_item(s, n).int_value;
+}
+
+@ We don't restore as we can have more languages in a paragraph
+and hyphenation takes place at a later stage so we would get
+weird grouping side effects .. so, one can overload settings
+but management is then upto the used, so no:
+
+@c
+/*
+    static void unsavehjcodes(quarterword gl) { }
+*/
+
+static void initializehjcodes(void)
+{
+    /*
+        sa_tree_item sa_value = { 0 };
+    */
+    hjcode_max = 0;
+    hjcode_heads = Mxmalloc_array(sa_tree, (HJCODE_MAX + 1));
+    hjcode_valid = Mxmalloc_array(unsigned char, (HJCODE_MAX + 1));
+    memset(hjcode_heads, 0, sizeof(sa_tree) * (HJCODE_MAX + 1));
+    memset(hjcode_valid, 0, sizeof(unsigned char) * (HJCODE_MAX + 1));
+    /*
+        hjcode_valid[0] = 1;
+        sa_value.int_value = HJCODEDEFAULT;
+        hjcode_heads[0] = new_sa_tree(HJCODESTACK, 1, sa_value);
+    */
+}
+
+void hj_codes_from_lc_codes(int h)
+{
+    sa_tree_item sa_value = { 0 };
+    sa_tree s = hjcode_heads[h];
+    if (s != NULL) {
+        destroy_sa_tree(s);
+    } else if (h > hjcode_max) {
+        hjcode_max = h;
+    }
+    if (s == NULL) {
+        sa_value.int_value = HJCODEDEFAULT;
+        s = new_sa_tree(HJCODESTACK, 1, sa_value);
+        hjcode_heads[h] = s;
+    }
+    hjcode_heads[h] = copy_sa_tree(lccode_head);
+    hjcode_valid[h] = 1;
+}
+
+static void dumphjcodes(void)
+{
+    int total = 0;
+    int k;
+    for (k = 0; k <= hjcode_max; k++) {
+        if (hjcode_valid[k]) {
+            total++;
+        }
+    }
+    dump_int(hjcode_max);
+    dump_int(total);
+    for (k = 0; k <= hjcode_max; k++) {
+        if (hjcode_valid[k]) {
+            dump_int(k);
+            dump_sa_tree(hjcode_heads[k],"hjcodes");
+        }
+    }
+}
+
+static void undumphjcodes(void)
+{
+    int total, k, x;
+    xfree(hjcode_heads);
+    xfree(hjcode_valid);
+    hjcode_heads = Mxmalloc_array(sa_tree, (HJCODE_MAX + 1));
+    hjcode_valid = Mxmalloc_array(unsigned char, (HJCODE_MAX + 1));
+    memset(hjcode_heads, 0, sizeof(sa_tree) * (HJCODE_MAX + 1));
+    memset(hjcode_valid, 0, sizeof(unsigned char) * (HJCODE_MAX + 1));
+    undump_int(hjcode_max);
+    undump_int(total);
+    for (k = 0; k < total; k++) {
+        undump_int(x);
+        hjcode_heads[x] = undump_sa_tree("hjcodes");
+        hjcode_valid[x] = 1;
+    }
+}
+
+static void freehjcodes(void)
+{
+    int k;
+    for (k = 0; k <= hjcode_max; k++) {
+        if (hjcode_valid[k]) {
+            destroy_sa_tree(hjcode_heads[k]);
+        }
+    }
+    xfree(hjcode_heads);
+    xfree(hjcode_valid);
+}
+
+/* management */
+
+void unsave_text_codes(quarterword grouplevel)
+{
+    unsavelccodes(grouplevel);
+    unsaveuccodes(grouplevel);
+    unsavesfcodes(grouplevel);
+}
+
+void initialize_text_codes(void)
+{
+    initializecatcodes();
+    initializelccodes();
+    initializeuccodes();
+    initializesfcodes();
+    initializehjcodes();
+}
+
+void free_text_codes(void)
+{
+    freecatcodes();
+    freelccodes();
+    freeuccodes();
+    freesfcodes();
+    freehjcodes();
+}
+
+void dump_text_codes(void)
+{
+    dumpcatcodes();
+    dumplccodes();
+    dumpuccodes();
+    dumpsfcodes();
+    dumphjcodes();
+}
+
+void undump_text_codes(void)
+{
+    undumpcatcodes();
+    undumplccodes();
+    undumpuccodes();
+    undumpsfcodes();
+    undumphjcodes();
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/textcodes.c
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
-
-textcodes.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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-Contrary to traditional \TEX\ we have catcode tables so that we can switch
-catcode regimes very fast. We can have many such regimes and they're stored in
-trees.
-
-*/
-
-#define CATCODESTACK       8
-#define CATCODEDEFAULT    12
-#define CATCODE_MAX    32767
-
-static sa_tree *catcode_heads = NULL;
-static int catcode_max = 0;
-static unsigned char *catcode_valid = NULL;
-
-void set_cat_code(int h, int n, halfword v, quarterword gl)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_tree s = catcode_heads[h];
-    if (h > catcode_max)
-        catcode_max = h;
-    if (s == NULL) {
-        sa_value.int_value = CATCODEDEFAULT;
-        s = new_sa_tree(CATCODESTACK, 1, sa_value);
-        catcode_heads[h] = s;
-    }
-    sa_value.int_value = (int) v;
-    set_sa_item(s, n, sa_value, gl);
-}
-
-halfword get_cat_code(int h, int n)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_tree s = catcode_heads[h];
-    if (h > catcode_max)
-        catcode_max = h;
-    if (s == NULL) {
-        sa_value.int_value = CATCODEDEFAULT;
-        s = new_sa_tree(CATCODESTACK, 1, sa_value);
-        catcode_heads[h] = s;
-    }
-    return (halfword) get_sa_item(s, n).int_value;
-}
-
-void unsave_cat_codes(int h, quarterword gl)
-{
-    int k;
-    if (h > catcode_max)
-        catcode_max = h;
-    for (k = 0; k <= catcode_max; k++) {
-        if (catcode_heads[k] != NULL)
-            restore_sa_stack(catcode_heads[k], gl);
-    }
-}
-
-static void initializecatcodes(void)
-{
-    sa_tree_item sa_value = { 0 };
-    catcode_max = 0;
-    catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1));
-    catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1));
-    memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1));
-    memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1));
-    catcode_valid[0] = 1;
-    sa_value.int_value = CATCODEDEFAULT;
-    catcode_heads[0] = new_sa_tree(CATCODESTACK, 1, sa_value);
-}
-
-static void dumpcatcodes(void)
-{
-    int total = 0;
-    int k;
-    for (k = 0; k <= catcode_max; k++) {
-        if (catcode_valid[k]) {
-            total++;
-        }
-    }
-    dump_int(catcode_max);
-    dump_int(total);
-    for (k = 0; k <= catcode_max; k++) {
-        if (catcode_valid[k]) {
-            dump_int(k);
-            dump_sa_tree(catcode_heads[k],"catcodes");
-        }
-    }
-}
-
-static void undumpcatcodes(void)
-{
-    int total, k, x;
-    xfree(catcode_heads);
-    xfree(catcode_valid);
-    catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1));
-    catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1));
-    memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1));
-    memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1));
-    undump_int(catcode_max);
-    undump_int(total);
-    for (k = 0; k < total; k++) {
-        undump_int(x);
-        catcode_heads[x] = undump_sa_tree("catcodes");
-        catcode_valid[x] = 1;
-    }
-}
-
-int valid_catcode_table(int h)
-{
-    if (h <= CATCODE_MAX && h >= 0 && catcode_valid[h]) {
-        return 1;
-    }
-    return 0;
-}
-
-void copy_cat_codes(int from, int to)
-{
-    if (from < 0 || from > CATCODE_MAX || catcode_valid[from] == 0) {
-        uexit(1);
-    }
-    if (to > catcode_max)
-        catcode_max = to;
-    destroy_sa_tree(catcode_heads[to]);
-    catcode_heads[to] = copy_sa_tree(catcode_heads[from]);
-    catcode_valid[to] = 1;
-}
-
-void initex_cat_codes(int h)
-{
-    int k;
-    if (h > catcode_max)
-        catcode_max = h;
-    destroy_sa_tree(catcode_heads[h]);
-    catcode_heads[h] = NULL;
-    set_cat_code(h, '\r', car_ret_cmd, 1);
-    set_cat_code(h, ' ', spacer_cmd, 1);
-    set_cat_code(h, '\\', escape_cmd, 1);
-    set_cat_code(h, '%', comment_cmd, 1);
-    set_cat_code(h, 127, invalid_char_cmd, 1);
-    set_cat_code(h, 0, ignore_cmd, 1);
-    set_cat_code(h, 0xFEFF, ignore_cmd, 1);
-    for (k = 'A'; k <= 'Z'; k++) {
-        set_cat_code(h, k, letter_cmd, 1);
-        set_cat_code(h, k + 'a' - 'A', letter_cmd, 1);
-    }
-    catcode_valid[h] = 1;
-}
-
-static void freecatcodes(void)
-{
-    int k;
-    for (k = 0; k <= catcode_max; k++) {
-        if (catcode_valid[k]) {
-            destroy_sa_tree(catcode_heads[k]);
-        }
-    }
-    xfree(catcode_heads);
-    xfree(catcode_valid);
-}
-
-/*tex
-
-The lowercase mapping codes are also stored in a tree.
-
-*/
-
-#define LCCODESTACK   8
-#define LCCODEDEFAULT 0
-
-static sa_tree lccode_head = NULL;
-
-void set_lc_code(int n, halfword v, quarterword gl)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_value.int_value = (int) v;
-    set_sa_item(lccode_head, n, sa_value, gl);
-}
-
-halfword get_lc_code(int n)
-{
-    return (halfword) get_sa_item(lccode_head, n).int_value;
-}
-
-static void unsavelccodes(quarterword gl)
-{
-    restore_sa_stack(lccode_head, gl);
-}
-
-static void initializelccodes(void)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_value.int_value = LCCODEDEFAULT;
-    lccode_head = new_sa_tree(LCCODESTACK, 1, sa_value);
-}
-
-static void dumplccodes(void)
-{
-    dump_sa_tree(lccode_head,"lccodes");
-}
-
-static void undumplccodes(void)
-{
-    lccode_head = undump_sa_tree("lccodes");
-}
-
-static void freelccodes(void)
-{
-    destroy_sa_tree(lccode_head);
-}
-
-/*tex
-
-And the uppercase mapping codes are again stored in a tree.
-
-*/
-
-#define UCCODESTACK   8
-#define UCCODEDEFAULT 0
-
-static sa_tree uccode_head = NULL;
-
-void set_uc_code(int n, halfword v, quarterword gl)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_value.int_value = (int) v;
-    set_sa_item(uccode_head, n, sa_value, gl);
-}
-
-halfword get_uc_code(int n)
-{
-    return (halfword) get_sa_item(uccode_head, n).int_value;
-}
-
-static void unsaveuccodes(quarterword gl)
-{
-    restore_sa_stack(uccode_head, gl);
-}
-
-static void initializeuccodes(void)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_value.int_value = UCCODEDEFAULT;
-    uccode_head = new_sa_tree(UCCODESTACK, 1, sa_value);
-}
-
-static void dumpuccodes(void)
-{
-    dump_sa_tree(uccode_head,"uccodes");
-}
-
-static void undumpuccodes(void)
-{
-    uccode_head = undump_sa_tree("uccodes");
-}
-
-static void freeuccodes(void)
-{
-    destroy_sa_tree(uccode_head);
-}
-
-/*tex
-
-By now it will be no surprise that the space factors get stored in a tree.
-
-*/
-
-#define SFCODESTACK      8
-#define SFCODEDEFAULT 1000
-
-static sa_tree sfcode_head = NULL;
-
-void set_sf_code(int n, halfword v, quarterword gl)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_value.int_value = (int) v;
-    set_sa_item(sfcode_head, n, sa_value, gl);
-}
-
-halfword get_sf_code(int n)
-{
-    return (halfword) get_sa_item(sfcode_head, n).int_value;
-}
-
-static void unsavesfcodes(quarterword gl)
-{
-    restore_sa_stack(sfcode_head, gl);
-}
-
-static void initializesfcodes(void)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_value.int_value = SFCODEDEFAULT;
-    sfcode_head = new_sa_tree(SFCODESTACK, 1, sa_value);
-}
-
-static void dumpsfcodes(void)
-{
-    dump_sa_tree(sfcode_head,"sfcodes");
-}
-
-static void undumpsfcodes(void)
-{
-    sfcode_head = undump_sa_tree("sfcodes");
-}
-
-static void freesfcodes(void)
-{
-    destroy_sa_tree(sfcode_head);
-}
-
-/*tex
-
-The hyphenation codes are indeed stored in a tree and are used instead of
-lowercase codes when deciding what characters to take into acccount when
-hyphenating. They are bound to upto |HJCODE_MAX| languages.
-
-*/
-
-#define HJCODESTACK       8
-#define HJCODEDEFAULT     0
-#define HJCODE_MAX    16383
-
-static sa_tree *hjcode_heads = NULL;
-static int hjcode_max = 0;
-static unsigned char *hjcode_valid = NULL;
-
-/*tex
-
-Here we set codes but we don't initialize from lccodes.
-
-*/
-
-void set_hj_code(int h, int n, halfword v, quarterword gl)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_tree s = hjcode_heads[h];
-    if (h > hjcode_max)
-        hjcode_max = h;
-    if (s == NULL) {
-        sa_value.int_value = HJCODEDEFAULT;
-        s = new_sa_tree(HJCODESTACK, 1, sa_value);
-        hjcode_heads[h] = s;
-    }
-    sa_value.int_value = (int) v;
-    set_sa_item(s, n, sa_value, gl);
-}
-
-/*tex
-
-We just return the lccodes when nothing is set.
-
-*/
-
-halfword get_hj_code(int h, int n)
-{
-    sa_tree s = hjcode_heads[h];
-    if (s == NULL) {
-        s = lccode_head;
-    }
-    return (halfword) get_sa_item(s, n).int_value;
-}
-
-/*tex
-
-We don't restore as we can have more languages in a paragraph and hyphenation
-takes place at a later stage so we would get weird grouping side effects .. so,
-one can overload settings but management is then upto the used, so no:
-
-*/
-
-/*
-    static void unsavehjcodes(quarterword gl) { }
-*/
-
-static void initializehjcodes(void)
-{
-    /*
-        sa_tree_item sa_value = { 0 };
-    */
-    hjcode_max = 0;
-    hjcode_heads = Mxmalloc_array(sa_tree, (HJCODE_MAX + 1));
-    hjcode_valid = Mxmalloc_array(unsigned char, (HJCODE_MAX + 1));
-    memset(hjcode_heads, 0, sizeof(sa_tree) * (HJCODE_MAX + 1));
-    memset(hjcode_valid, 0, sizeof(unsigned char) * (HJCODE_MAX + 1));
-    /*
-        hjcode_valid[0] = 1;
-        sa_value.int_value = HJCODEDEFAULT;
-        hjcode_heads[0] = new_sa_tree(HJCODESTACK, 1, sa_value);
-    */
-}
-
-void hj_codes_from_lc_codes(int h)
-{
-    sa_tree_item sa_value = { 0 };
-    sa_tree s = hjcode_heads[h];
-    if (s != NULL) {
-        destroy_sa_tree(s);
-    } else if (h > hjcode_max) {
-        hjcode_max = h;
-    }
-    if (s == NULL) {
-        sa_value.int_value = HJCODEDEFAULT;
-        s = new_sa_tree(HJCODESTACK, 1, sa_value);
-        hjcode_heads[h] = s;
-    }
-    hjcode_heads[h] = copy_sa_tree(lccode_head);
-    hjcode_valid[h] = 1;
-}
-
-static void dumphjcodes(void)
-{
-    int total = 0;
-    int k;
-    for (k = 0; k <= hjcode_max; k++) {
-        if (hjcode_valid[k]) {
-            total++;
-        }
-    }
-    dump_int(hjcode_max);
-    dump_int(total);
-    for (k = 0; k <= hjcode_max; k++) {
-        if (hjcode_valid[k]) {
-            dump_int(k);
-            dump_sa_tree(hjcode_heads[k],"hjcodes");
-        }
-    }
-}
-
-static void undumphjcodes(void)
-{
-    int total, k, x;
-    xfree(hjcode_heads);
-    xfree(hjcode_valid);
-    hjcode_heads = Mxmalloc_array(sa_tree, (HJCODE_MAX + 1));
-    hjcode_valid = Mxmalloc_array(unsigned char, (HJCODE_MAX + 1));
-    memset(hjcode_heads, 0, sizeof(sa_tree) * (HJCODE_MAX + 1));
-    memset(hjcode_valid, 0, sizeof(unsigned char) * (HJCODE_MAX + 1));
-    undump_int(hjcode_max);
-    undump_int(total);
-    for (k = 0; k < total; k++) {
-        undump_int(x);
-        hjcode_heads[x] = undump_sa_tree("hjcodes");
-        hjcode_valid[x] = 1;
-    }
-}
-
-static void freehjcodes(void)
-{
-    int k;
-    for (k = 0; k <= hjcode_max; k++) {
-        if (hjcode_valid[k]) {
-            destroy_sa_tree(hjcode_heads[k]);
-        }
-    }
-    xfree(hjcode_heads);
-    xfree(hjcode_valid);
-}
-
-/*tex
-
-The public management functions.
-
-*/
-
-void unsave_text_codes(quarterword grouplevel)
-{
-    unsavelccodes(grouplevel);
-    unsaveuccodes(grouplevel);
-    unsavesfcodes(grouplevel);
-}
-
-void initialize_text_codes(void)
-{
-    initializecatcodes();
-    initializelccodes();
-    initializeuccodes();
-    initializesfcodes();
-    initializehjcodes();
-}
-
-void free_text_codes(void)
-{
-    freecatcodes();
-    freelccodes();
-    freeuccodes();
-    freesfcodes();
-    freehjcodes();
-}
-
-void dump_text_codes(void)
-{
-    dumpcatcodes();
-    dumplccodes();
-    dumpuccodes();
-    dumpsfcodes();
-    dumphjcodes();
-}
-
-void undump_text_codes(void)
-{
-    undumpcatcodes();
-    undumplccodes();
-    undumpuccodes();
-    undumpsfcodes();
-    undumphjcodes();
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/tex/textoken.w
@@ -0,0 +1,3541 @@
+% textoken.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"
+
+@ @c
+#define detokenized_line() (line_catcode_table==NO_CAT_TABLE)
+
+/*
+#define do_get_cat_code(a,b) do { \
+    if (line_catcode_table<=-0xFF) \
+      a= - line_catcode_table - 0xFF ; \
+    else if (line_catcode_table!=DEFAULT_CAT_TABLE) \
+      a=get_cat_code(line_catcode_table,b); \
+    else \
+      a=get_cat_code(cat_code_table_par,b); \
+  } while (0)
+*/
+
+#define do_get_cat_code(a,b) do { \
+    if (line_catcode_table==DEFAULT_CAT_TABLE) \
+      a=get_cat_code(cat_code_table_par,b); \
+    else if (line_catcode_table>-0xFF) \
+      a=get_cat_code(line_catcode_table,b); \
+    else \
+      a= - line_catcode_table - 0xFF ; \
+  } while (0)
+
+
+@ The \TeX\ system does nearly all of its own memory allocation, so that it can
+readily be transported into environments that do not have automatic facilities
+for strings, garbage collection, etc., and so that it can be in control of what
+error messages the user receives. The dynamic storage requirements of \TeX\ are
+handled by providing two large arrays called |fixmem| and |varmem| in which
+consecutive blocks of words are used as nodes by the \TeX\ routines.
+
+Pointer variables are indices into this array, or into another array called
+|eqtb| that will be explained later. A pointer variable might also be a special
+flag that lies outside the bounds of |mem|, so we allow pointers to assume any
+|halfword| value. The minimum halfword value represents a null pointer. \TeX\
+does not assume that |mem[null]| exists.
+
+@ Locations in |fixmem| are used for storing one-word records; a conventional
+\.{AVAIL} stack is used for allocation in this array.
+
+@c
+smemory_word *fixmem;           /* the big dynamic storage area */
+unsigned fix_mem_min;           /* the smallest location of one-word memory in use */
+unsigned fix_mem_max;           /* the largest location of one-word memory in use */
+
+@ In order to study the memory requirements of particular applications, it is
+possible to prepare a version of \TeX\ that keeps track of current and maximum
+memory usage. When code between the delimiters |@!stat| $\ldots$ |tats| is not
+commented out, \TeX\ will run a bit slower but it will report these statistics
+when |tracing_stats| is sufficiently large.
+
+@c
+int var_used, dyn_used;         /* how much memory is in use */
+
+halfword avail;                 /* head of the list of available one-word nodes */
+unsigned fix_mem_end;           /* the last one-word node used in |mem| */
+
+halfword garbage;               /* head of a junk list, write only */
+halfword temp_token_head;       /* head of a temporary list of some kind */
+halfword hold_token_head;       /* head of a temporary list of another kind */
+halfword omit_template;         /* a constant token list */
+halfword null_list;             /* permanently empty list */
+halfword backup_head;           /* head of token list built by |scan_keyword| */
+
+@ @c
+void initialize_tokens(void)
+{
+    halfword p;
+    avail = null;
+    fix_mem_end = 0;
+    p = get_avail();
+    temp_token_head = p;
+    set_token_info(temp_token_head, 0);
+    p = get_avail();
+    hold_token_head = p;
+    set_token_info(hold_token_head, 0);
+    p = get_avail();
+    omit_template = p;
+    set_token_info(omit_template, 0);
+    p = get_avail();
+    null_list = p;
+    set_token_info(null_list, 0);
+    p = get_avail();
+    backup_head = p;
+    set_token_info(backup_head, 0);
+    p = get_avail();
+    garbage = p;
+    set_token_info(garbage, 0);
+    dyn_used = 0;               /* initialize statistics */
+}
+
+@ The function |get_avail| returns a pointer to a new one-word node whose |link|
+field is null. However, \TeX\ will halt if there is no more room left.
+@^inner loop@>
+
+If the available-space list is empty, i.e., if |avail=null|, we try first to
+increase |fix_mem_end|. If that cannot be done, i.e., if
+|fix_mem_end=fix_mem_max|, we try to reallocate array |fixmem|. If, that doesn't
+work, we have to quit.
+
+@c
+halfword get_avail(void)
+{                               /* single-word node allocation */
+    unsigned p;                 /* the new node being got */
+    unsigned t;
+    p = (unsigned) avail;       /* get top location in the |avail| stack */
+    if (p != null) {
+        avail = token_link(avail);      /* and pop it off */
+    } else if (fix_mem_end < fix_mem_max) {     /* or go into virgin territory */
+        incr(fix_mem_end);
+        p = fix_mem_end;
+    } else {
+        smemory_word *new_fixmem;       /* the big dynamic storage area */
+        t = (fix_mem_max / 5);
+        new_fixmem =
+            fixmemcast(realloc
+                       (fixmem, sizeof(smemory_word) * (fix_mem_max + t + 1)));
+        if (new_fixmem == NULL) {
+            runaway();          /* if memory is exhausted, display possible runaway text */
+            overflow("token memory size", fix_mem_max);
+        } else {
+            fixmem = new_fixmem;
+        }
+        memset(voidcast(fixmem + fix_mem_max + 1), 0, t * sizeof(smemory_word));
+        fix_mem_max += t;
+        p = ++fix_mem_end;
+    }
+    token_link(p) = null;       /* provide an oft-desired initialization of the new node */
+    incr(dyn_used);             /* maintain statistics */
+    return (halfword) p;
+}
+
+@ The procedure |flush_list(p)| frees an entire linked list of one-word nodes
+that starts at position |p|.
+@^inner loop@>
+
+@c
+void flush_list(halfword p)
+{                               /* makes list of single-word nodes available */
+    halfword q, r;              /* list traversers */
+    if (p != null) {
+        r = p;
+        do {
+            q = r;
+            r = token_link(r);
+            decr(dyn_used);
+        } while (r != null);    /* now |q| is the last node on the list */
+        token_link(q) = avail;
+        avail = p;
+    }
+}
+
+@ A \TeX\ token is either a character or a control sequence, and it is @^token@>
+represented internally in one of two ways: (1)~A character whose ASCII code
+number is |c| and whose command code is |m| is represented as the number
+$2^{21}m+c$; the command code is in the range |1<=m<=14|. (2)~A control sequence
+whose |eqtb| address is |p| is represented as the number |cs_token_flag+p|. Here
+|cs_token_flag=@t$2^{25}-1$@>| is larger than $2^{21}m+c$, yet it is small enough
+that |cs_token_flag+p< max_halfword|; thus, a token fits comfortably in a
+halfword.
+
+A token |t| represents a |left_brace| command if and only if
+|t<left_brace_limit|; it represents a |right_brace| command if and only if we
+have |left_brace_limit<=t<right_brace_limit|; and it represents a |match| or
+|end_match| command if and only if |match_token<=t<=end_match_token|. The
+following definitions take care of these token-oriented constants and a few
+others.
+
+@ A token list is a singly linked list of one-word nodes in |mem|, where each
+word contains a token and a link. Macro definitions, output-routine definitions,
+marks, \.{\\write} texts, and a few other things are remembered by \TeX\ in the
+form of token lists, usually preceded by a node with a reference count in its
+|token_ref_count| field. The token stored in location |p| is called |info(p)|.
+
+Three special commands appear in the token lists of macro definitions. When
+|m=match|, it means that \TeX\ should scan a parameter for the current macro;
+when |m=end_match|, it means that parameter matching should end and \TeX\ should
+start reading the macro text; and when |m=out_param|, it means that \TeX\ should
+insert parameter number |c| into the text at this point.
+
+The enclosing \.{\char'173} and \.{\char'175} characters of a macro definition
+are omitted, but the final right brace of an output routine is included at the
+end of its token list.
+
+Here is an example macro definition that illustrates these conventions. After
+\TeX\ processes the text
+
+$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$
+
+the definition of \.{\\mac} is represented as a token list containing
+
+$$\def\,{\hskip2pt}
+\vbox{\halign{\hfil#\hfil\cr
+(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ ,
+\.{\\b}, |end_match|,\cr
+|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#,
+|other_char|\,\.1,\cr
+|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$
+
+The procedure |scan_toks| builds such token lists, and |macro_call| does the
+parameter matching. @^reference counts@>
+
+Examples such as $$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$ explain why reference
+counts would be needed even if \TeX\ had no \.{\\let} operation: When the token
+list for \.{\\m} is being read, the redefinition of \.{\\m} changes the |eqtb|
+entry before the token list has been fully consumed, so we dare not simply
+destroy a token list when its control sequence is being redefined.
+
+If the parameter-matching part of a definition ends with `\.{\#\{}', the
+corresponding token list will have `\.\{' just before the `|end_match|' and also
+at the very end. The first `\.\{' is used to delimit the parameter; the second
+one keeps the first from disappearing.
+
+The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in symbolic form,
+including the expansion of a macro or mark.
+
+@c
+void print_meaning(void)
+{
+    /* remap \mathchar onto \Umathchar */
+/*
+    if (cur_cmd == math_given_cmd) {
+        cur_cmd = xmath_given_cmd ;
+    }
+*/
+    print_cmd_chr((quarterword) cur_cmd, cur_chr);
+    if (cur_cmd >= call_cmd) {
+        print_char(':');
+        print_ln();
+        token_show(cur_chr);
+    } else {
+        /* Show the meaning of a mark node */
+        if ((cur_cmd == top_bot_mark_cmd) && (cur_chr < marks_code)) {
+            print_char(':');
+            print_ln();
+            switch (cur_chr) {
+                case first_mark_code:
+                    token_show(first_mark(0));
+                    break;
+                case bot_mark_code:
+                    token_show(bot_mark(0));
+                    break;
+                case split_first_mark_code:
+                    token_show(split_first_mark(0));
+                    break;
+                case split_bot_mark_code:
+                    token_show(split_bot_mark(0));
+                    break;
+                default:
+                    token_show(top_mark(0));
+                    break;
+            }
+        }
+    }
+}
+
+@ The procedure |show_token_list|, which prints a symbolic form of the token list
+that starts at a given node |p|, illustrates these conventions. The token list
+being displayed should not begin with a reference count. However, the procedure
+is intended to be robust, so that if the memory links are awry or if |p| is not
+really a pointer to a token list, nothing catastrophic will happen.
+
+An additional parameter |q| is also given; this parameter is either null or it
+points to a node in the token list where a certain magic computation takes place
+that will be explained later. (Basically, |q| is non-null when we are printing
+the two-line context information at the time of an error message; |q| marks the
+place corresponding to where the second line should begin.)
+
+For example, if |p| points to the node containing the first \.a in the token list
+above, then |show_token_list| will print the string $$\hbox{`\.{a\#1\#2\ \\b\
+->\#1\\-a\ \#\#1\#2\ \#2}';}$$ and if |q| points to the node containing the
+second \.a, the magic computation will be performed just before the second \.a is
+printed.
+
+The generation will stop, and `\.{\\ETC.}' will be printed, if the length of
+printing exceeds a given limit~|l|. Anomalous entries are printed in the form of
+control sequences that are not followed by a blank space, e.g., `\.{\\BAD.}';
+this cannot be confused with actual control sequences because a real control
+sequence named \.{BAD} would come out `\.{\\BAD\ }'.
+
+@c
+#define not_so_bad(p) \
+    switch (m) { \
+        case assign_int_cmd: \
+            if (c >= (backend_int_base) && c <= (backend_int_last)) \
+                p("[internal backend integer]"); \
+            break; \
+        case assign_dimen_cmd: \
+            if (c >= (backend_dimen_base) && c <= (backend_dimen_last)) \
+                p("[internal backend dimension]"); \
+            break; \
+        case assign_toks_cmd: \
+            if (c >= (backend_toks_base) && c <= (backend_toks_last)) \
+                p("[internal backend tokenlist]"); \
+            break; \
+        default: \
+            p("BAD"); \
+            break; \
+    }
+
+void show_token_list(int p, int q, int l)
+{
+    int m, c;                    /* pieces of a token */
+    ASCII_code match_chr = '#';  /* character used in a `|match|' */
+    ASCII_code n = '0';          /* the highest parameter number, as an ASCII digit */
+    tally = 0;
+    if (l < 0)
+        l = 0x3FFFFFFF;
+    while ((p != null) && (tally < l)) {
+        if (p == q) {
+            /* Do magic computation */
+            set_trick_count();
+        }
+        /* Display token |p|, and |return| if there are problems */
+        if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) {
+            tprint_esc("CLOBBERED.");
+            return;
+        }
+        if (token_info(p) >= cs_token_flag) {
+            if (!((inhibit_par_tokens) && (token_info(p) == par_token)))
+                print_cs(token_info(p) - cs_token_flag);
+        } else {
+            m = token_cmd(token_info(p));
+            c = token_chr(token_info(p));
+            if (token_info(p) < 0) {
+                tprint_esc("BAD");
+            } else {
+                /*
+                    Display the token $(|m|,|c|)$
+
+                    The procedure usually ``learns'' the character code used for macro
+                    parameters by seeing one in a |match| command before it runs into any
+                    |out_param| commands.
+                */
+                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:
+                        print(c);
+                        break;
+                    case mac_param_cmd:
+                        if (!in_lua_escape && (is_in_csname==0))
+                            print(c);
+                        print(c);
+                        break;
+                    case out_param_cmd:
+                        print(match_chr);
+                        if (c <= 9) {
+                            print_char(c + '0');
+                        } else {
+                            print_char('!');
+                            return;
+                        }
+                        break;
+                    case match_cmd:
+                        match_chr = c;
+                        print(c);
+                        incr(n);
+                        print_char(n);
+                        if (n > '9')
+                            return;
+                        break;
+                    case end_match_cmd:
+                        if (c == 0)
+                            tprint("->");
+                        break;
+                    default:
+                        not_so_bad(tprint);
+                        break;
+                }
+            }
+        }
+        p = token_link(p);
+    }
+    if (p != null)
+        tprint_esc("ETC.");
+}
+
+@ @c
+#define do_buffer_to_unichar(a,b) do { \
+    a = (halfword)str2uni(buffer+b); \
+    b += utf8_size(a); \
+} while (0)
+
+@ Here's the way we sometimes want to display a token list, given a pointer to
+its reference count; the pointer may be null.
+
+@c
+void token_show(halfword p)
+{
+    if (p != null)
+        show_token_list(token_link(p), null, 10000000);
+}
+
+@ |delete_token_ref|, is called when a pointer to a token list's reference count
+is being removed. This means that the token list should disappear if the
+reference count was |null|, otherwise the count should be decreased by one.
+@^reference counts@>
+
+@ |p| points to the reference count of a token list that is losing one
+reference.
+
+@c
+void delete_token_ref(halfword p)
+{
+    if (token_ref_count(p) == 0)
+        flush_list(p);
+    else
+        decr(token_ref_count(p));
+}
+
+@ @c
+int get_char_cat_code(int curchr)
+{
+    int a;
+    do_get_cat_code(a,curchr);
+    return a;
+}
+
+@ @c
+static void invalid_character_error(void)
+{
+    const char *hlp[] = {
+        "A funny symbol that I can't read has just been input.",
+        "Continue, and I'll forget that it ever happened.",
+        NULL
+    };
+    deletions_allowed = false;
+    tex_error("Text line contains an invalid character", hlp);
+    deletions_allowed = true;
+}
+
+@ @c
+static boolean process_sup_mark(void);  /* below */
+
+static int scan_control_sequence(void); /* below */
+
+typedef enum {
+    next_line_ok,
+    next_line_return,
+    next_line_restart
+} next_line_retval;
+
+static next_line_retval next_line(void); /* below */
+
+@ In case you are getting bored, here is a slightly less trivial routine: Given a
+string of lowercase letters, like `\.{pt}' or `\.{plus}' or `\.{width}', the
+|scan_keyword| routine checks to see whether the next tokens of input match this
+string. The match must be exact, except that uppercase letters will match their
+lowercase counterparts; uppercase equivalents are determined by subtracting
+|"a"-"A"|, rather than using the |uc_code| table, since \TeX\ uses this routine
+only for its own limited set of keywords.
+
+If a match is found, the characters are effectively removed from the input and
+|true| is returned. Otherwise |false| is returned, and the input is left
+essentially unchanged (except for the fact that some macros may have been
+expanded, etc.). @^inner loop@>
+
+@c
+boolean scan_keyword(const char *s)
+{                               /* look for a given string */
+    halfword p;                 /* tail of the backup list */
+    halfword q;                 /* new node being added to the token list via |store_new_token| */
+    const char *k;              /* index into |str_pool| */
+    halfword save_cur_cs = cur_cs;
+    if (strlen(s) == 0)        /* was assert (strlen(s) > 1); */
+      return false ;           /* but not with newtokenlib  zero keyword simply doesn't match  */
+    p = backup_head;
+    token_link(p) = null;
+    k = s;
+    while (*k) {
+        get_x_token();      /* recursion is possible here */
+        if ((cur_cs == 0) && ((cur_chr == *k) || (cur_chr == *k - 'a' + 'A'))) {
+            store_new_token(cur_tok);
+            k++;
+        } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) {
+            /*
+                crashes on some alignments:
+
+                if (p != backup_head) {
+                    q = get_avail();
+                    token_info(q) = cur_tok;
+                    token_link(q) = null;
+                    token_link(p) = q;
+                    begin_token_list(token_link(backup_head), backed_up);
+                } else {
+                    back_input();
+                }
+            */
+            back_input();
+            if (p != backup_head) {
+                begin_token_list(token_link(backup_head), backed_up);
+            }
+            /*  */
+            cur_cs = save_cur_cs;
+            return false;
+        }
+    }
+    if (token_link(backup_head) != null)
+        flush_list(token_link(backup_head));
+    cur_cs = save_cur_cs;
+    return true;
+}
+
+@ We can not return |undefined_control_sequence| under some conditions
+ (inside |shift_case|, for example). This needs thinking.
+
+@c
+
+/*
+    halfword active_to_cs(int curchr, int force)
+    {
+        halfword curcs;
+        char *a, *b;
+        char *utfbytes = xmalloc(8);
+        int nncs = no_new_control_sequence;
+        a = (char *) uni2str(0xFFFF);
+        utfbytes = strcpy(utfbytes, a);
+        if (force)
+            no_new_control_sequence = false;
+        if (curchr > 0) {
+            b = (char *) uni2str((unsigned) curchr);
+            utfbytes = strcat(utfbytes, b);
+            free(b);
+            curcs = string_lookup(utfbytes, strlen(utfbytes));
+        } else {
+            utfbytes[3] = '\0';
+            curcs = string_lookup(utfbytes, 4);
+        }
+        no_new_control_sequence = nncs;
+        free(a);
+        free(utfbytes);
+        return curcs;
+    }
+*/
+
+/*static char * FFFF = "\xEF\xBF\xBF";*/ /* 0xFFFF */
+
+halfword active_to_cs(int curchr, int force)
+{
+    halfword curcs;
+    int nncs = no_new_control_sequence;
+    if (force) {
+        no_new_control_sequence = false;
+    }
+    if (curchr > 0) {
+        char *b = (char *) uni2str((unsigned) curchr);
+        char *utfbytes = xmalloc(8);
+        utfbytes = strcpy(utfbytes, "\xEF\xBF\xBF");
+        utfbytes = strcat(utfbytes, b);
+        free(b);
+        curcs = string_lookup(utfbytes, utf8_size(curchr)+3);
+        free(utfbytes);
+    } else {
+        curcs = string_lookup("\xEF\xBF\xBF", 4); /* 0xFFFF ... why not 3 ? */
+    }
+    no_new_control_sequence = nncs;
+    return curcs;
+}
+
+/*
+
+    static unsigned char *uni2csstr(unsigned unic)
+    {
+        unsigned char *buf = xmalloc(8);
+        unsigned char *pt = buf;
+        *pt++ = 239; *pt++ = 191; *pt++ = 191; // 0xFFFF
+        if (unic < 0x80)
+            *pt++ = (unsigned char) unic;
+        else if (unic < 0x800) {
+            *pt++ = (unsigned char) (0xc0 | (unic >> 6));
+            *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
+        } else if (unic >= 0x110000) {
+            *pt++ = (unsigned char) (unic - 0x110000);
+        } else if (unic < 0x10000) {
+            *pt++ = (unsigned char) (0xe0 | (unic >> 12));
+            *pt++ = (unsigned char) (0x80 | ((unic >> 6) & 0x3f));
+            *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
+        } else {
+            int u, z, y, x;
+            unsigned val = unic - 0x10000;
+            u = (int) (((val & 0xf0000) >> 16) + 1);
+            z = (int) ((val & 0x0f000) >> 12);
+            y = (int) ((val & 0x00fc0) >> 6);
+            x = (int) (val & 0x0003f);
+            *pt++ = (unsigned char) (0xf0 | (u >> 2));
+            *pt++ = (unsigned char) (0x80 | ((u & 3) << 4) | z);
+            *pt++ = (unsigned char) (0x80 | y);
+            *pt++ = (unsigned char) (0x80 | x);
+        }
+        *pt = '\0';
+        return buf;
+    }
+
+    halfword active_to_cs(int curchr, int force)
+    {
+        halfword curcs;
+        int nncs = no_new_control_sequence;
+        if (force) {
+            no_new_control_sequence = false;
+        }
+        if (curchr > 0) {
+            char * utfbytes = (char *) uni2csstr((unsigned) curchr);
+            curcs = string_lookup(utfbytes, utf8_size(curchr)+3);
+            free(utfbytes);
+        } else {
+            curcs = string_lookup(FFFF, 4); // 0xFFFF ... why not 3 ?
+        }
+        no_new_control_sequence = nncs;
+        return curcs;
+    }
+
+*/
+
+@ TODO this function should listen to \.{\\escapechar}
+
+@ prints a control sequence
+
+@c
+static char *cs_to_string(halfword p)
+{
+    const char *s;
+    char *sh;
+    int k = 0;
+    static char ret[256] = { 0 };
+    if (p == 0 || p == null_cs) {
+        ret[k++] = '\\';
+        s = "csname";
+        while (*s) {
+            ret[k++] = *s++;
+        }
+        ret[k++] = '\\';
+        s = "endcsname";
+        while (*s) {
+            ret[k++] = *s++;
+        }
+        ret[k] = 0;
+
+    } else {
+        str_number txt = cs_text(p);
+        sh = makecstring(txt);
+        s = sh;
+        if (is_active_cs(txt)) {
+            s = s + 3;
+            while (*s) {
+                ret[k++] = *s++;
+            }
+            ret[k] = 0;
+        } else {
+            ret[k++] = '\\';
+            while (*s) {
+                ret[k++] = *s++;
+            }
+            ret[k] = 0;
+        }
+        free(sh);
+    }
+    return (char *) ret;
+}
+
+@ TODO this is a quick hack, will be solved differently soon
+
+@c
+static char *cmd_chr_to_string(int cmd, int chr)
+{
+    char *s;
+    str_number str;
+    int sel = selector;
+    selector = new_string;
+    print_cmd_chr((quarterword) cmd, chr);
+    str = make_string();
+    s = makecstring(str);
+    selector = sel;
+    flush_str(str);
+    return s;
+}
+
+@ The heart of \TeX's input mechanism is the |get_next| procedure, which we shall
+develop in the next few sections of the program. Perhaps we shouldn't actually
+call it the ``heart,'' however, because it really acts as \TeX's eyes and mouth,
+reading the source files and gobbling them up. And it also helps \TeX\ to
+regurgitate stored token lists that are to be processed again. @^eyes and mouth@>
+
+The main duty of |get_next| is to input one token and to set |cur_cmd| and
+|cur_chr| to that token's command code and modifier. Furthermore, if the input
+token is a control sequence, the |eqtb| location of that control sequence is
+stored in |cur_cs|; otherwise |cur_cs| is set to zero.
+
+Underlying this simple description is a certain amount of complexity because of
+all the cases that need to be handled. However, the inner loop of |get_next| is
+reasonably short and fast.
+
+When |get_next| is asked to get the next token of a \.{\\read} line,
+it sets |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens
+appear on that line. (There might not be any tokens at all, if the
+|end_line_char| has |ignore| as its catcode.)
+
+The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity is
+needed because a blank line of input is supposed to be exactly equivalent to the
+appearance of \.{\\par}; we must set |cur_cs:=par_loc| when detecting a blank
+line.
+
+@c
+halfword par_loc;   /* location of `\.{\\par}' in |eqtb| */
+halfword par_token; /* token representing `\.{\\par}' */
+
+@ Parts |get_next| are executed more often than any other instructions of \TeX.
+@^mastication@>@^inner loop@>
+
+The global variable |force_eof| is normally |false|; it is set |true| by an
+\.{\\endinput} command. |luacstrings| is the number of lua print statements
+waiting to be input, it is changed by |luatokencall|.
+
+@c
+boolean force_eof; /* should the next \.{\\input} be aborted early? */
+int luacstrings;   /* how many lua strings are waiting to be input? */
+
+@ If the user has set the |pausing| parameter to some positive value, and if
+nonstop mode has not been selected, each line of input is displayed on the
+terminal and the transcript file, followed by `\.{=>}'. \TeX\ waits for a
+response. If the response is simply |carriage_return|, the line is accepted as it
+stands, otherwise the line typed is used instead of the line in the file.
+
+@c
+void firm_up_the_line(void)
+{
+    int k;                      /* an index into |buffer| */
+    ilimit = last;
+    if (pausing_par > 0) {
+        if (interaction > nonstop_mode) {
+            wake_up_terminal();
+            print_ln();
+            if (istart < ilimit) {
+                for (k = istart; k <= ilimit - 1; k++)
+                    print_char(buffer[k]);
+            }
+            first = ilimit;
+            prompt_input("=>"); /* wait for user response */
+            if (last > first) {
+                for (k = first; k < +last - 1; k++)     /* move line down in buffer */
+                    buffer[k + istart - first] = buffer[k];
+                ilimit = istart + last - first;
+            }
+        }
+    }
+}
+
+@ Before getting into |get_next|, let's consider the subroutine that is called
+when an `\.{\\outer}' control sequence has been scanned or when the end of a file
+has been reached. These two cases are distinguished by |cur_cs|, which is zero at
+the end of a file.
+
+@c
+void check_outer_validity(void)
+{
+    halfword p;                 /* points to inserted token list */
+    halfword q;                 /* auxiliary pointer */
+    if (suppress_outer_error_par)
+        return;
+    if (scanner_status != normal) {
+        deletions_allowed = false;
+        /* Back up an outer control sequence so that it can be reread; */
+        /* An outer control sequence that occurs in a \.{\\read} will not be reread,
+           since the error recovery for \.{\\read} is not very powerful. */
+        if (cur_cs != 0) {
+            if ((istate == token_list) || (iname < 1) || (iname > 17)) {
+                p = get_avail();
+                token_info(p) = cs_token_flag + cur_cs;
+                begin_token_list(p, backed_up); /* prepare to read the control sequence again */
+            }
+            cur_cmd = spacer_cmd;
+            cur_chr = ' ';      /* replace it by a space */
+        }
+        if (scanner_status > skipping) {
+            const char *errhlp[] = {
+                "I suspect you have forgotten a `}', causing me",
+                "to read past where you wanted me to stop.",
+                "I'll try to recover; but if the error is serious,",
+                "you'd better type `E' or `X' now and fix your file.",
+                NULL
+            };
+            char errmsg[256];
+            const char *startmsg;
+            const char *scannermsg;
+            /* Tell the user what has run away and try to recover */
+            runaway();          /* print a definition, argument, or preamble */
+            if (cur_cs == 0) {
+                startmsg = "File ended";
+            } else {
+                cur_cs = 0;
+                startmsg = "Forbidden control sequence found";
+            }
+            /* Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}',
+               and insert tokens that should lead to recovery; */
+            /* The recovery procedure can't be fully understood without knowing more
+               about the \TeX\ routines that should be aborted, but we can sketch the
+               ideas here:  For a runaway definition we will insert a right brace; for a
+               runaway preamble, we will insert a special \.{\\cr} token and a right
+               brace; and for a runaway argument, we will set |long_state| to
+               |outer_call| and insert \.{\\par}. */
+            p = get_avail();
+            switch (scanner_status) {
+            case defining:
+                scannermsg = "definition";
+                token_info(p) = right_brace_token + '}';
+                break;
+            case matching:
+                scannermsg = "use";
+                token_info(p) = par_token;
+                long_state = outer_call_cmd;
+                break;
+            case aligning:
+                scannermsg = "preamble";
+                token_info(p) = right_brace_token + '}';
+                q = p;
+                p = get_avail();
+                token_link(p) = q;
+                token_info(p) = cs_token_flag + frozen_cr;
+                align_state = -1000000;
+                break;
+            case absorbing:
+                scannermsg = "text";
+                token_info(p) = right_brace_token + '}';
+                break;
+            default:           /* can't happen */
+                scannermsg = "unknown";
+                break;
+            }                   /*there are no other cases */
+            begin_token_list(p, inserted);
+            snprintf(errmsg, 255, "%s while scanning %s of %s",
+                     startmsg, scannermsg, cs_to_string(warning_index));
+            tex_error(errmsg, errhlp);
+        } else {
+            char errmsg[256];
+            const char *errhlp_no[] = {
+                "The file ended while I was skipping conditional text.",
+                "This kind of error happens when you say `\\if...' and forget",
+                "the matching `\\fi'. I've inserted a `\\fi'; this might work.",
+                NULL
+            };
+            const char *errhlp_cs[] = {
+                "A forbidden control sequence occurred in skipped text.",
+                "This kind of error happens when you say `\\if...' and forget",
+                "the matching `\\fi'. I've inserted a `\\fi'; this might work.",
+                NULL
+            };
+            const char **errhlp = (const char **) errhlp_no;
+            char *ss;
+            if (cur_cs != 0) {
+                errhlp = errhlp_cs;
+                cur_cs = 0;
+            }
+            ss = cmd_chr_to_string(if_test_cmd, cur_if);
+            snprintf(errmsg, 255, "Incomplete %s; all text was ignored after line %d",
+                 ss, (int) skip_line);
+            free(ss);
+            /* Incomplete \\if... */
+            cur_tok = cs_token_flag + frozen_fi;
+            /* back up one inserted token and call |error| */
+            {
+                OK_to_interrupt = false;
+                back_input();
+                token_type = inserted;
+                OK_to_interrupt = true;
+                tex_error(errmsg, errhlp);
+            }
+        }
+        deletions_allowed = true;
+    }
+}
+
+@ @c
+
+#if 0
+
+/*
+    The other variant gives less clutter in tracing cache usage when profiling and for
+    some files (like the manual) also a bit of a speedup.
+*/
+
+static boolean get_next_file(void)
+{
+  SWITCH:
+    if (iloc <= ilimit) {
+        /* current line not yet finished */
+        do_buffer_to_unichar(cur_chr, iloc);
+
+      RESWITCH:
+        if (detokenized_line()) {
+            cur_cmd = (cur_chr == ' ' ? 10 : 12);
+        } else {
+            do_get_cat_code(cur_cmd, cur_chr);
+        }
+        /*
+            Change state if necessary, and |goto switch| if the current
+            character should be ignored, or |goto reswitch| if the current
+            character changes to another;
+
+            The following 48-way switch accomplishes the scanning quickly, assuming
+            that a decent C compiler has translated the code. Note that the numeric
+            values for |mid_line|, |skip_blanks|, and |new_line| are spaced
+            apart from each other by |max_char_code+1|, so we can add a character's
+            command code to the state to get a single number that characterizes both.
+
+            Remark [ls/hh]: checking performance indicated that this switch was the
+            cause of many branch prediction errors but changing it to:
+
+                c = istate + cur_cmd;
+                if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) {
+                    return true;
+                } else if (c >= new_line) {
+                    switch (c) {
+                    }
+                } else if (c >= skip_blanks) {
+                    switch (c) {
+                    }
+                } else if (c >= mid_line) {
+                    switch (c) {
+                    }
+                } else {
+                    istate = mid_line;
+                    return true;
+                }
+
+            gives as many prediction errors. So, we can indeed assume that the compiler
+            does the right job, or that there is simply no other way.
+        */
+
+        switch (istate + cur_cmd) {
+            case mid_line + ignore_cmd:
+            case skip_blanks + ignore_cmd:
+            case new_line + ignore_cmd:
+            case skip_blanks + spacer_cmd:
+            case new_line + spacer_cmd:
+                /* Cases where character is ignored */
+                goto SWITCH;
+                break;
+            case mid_line + escape_cmd:
+            case new_line + escape_cmd:
+            case skip_blanks + escape_cmd:
+                /* Scan a control sequence ...; */
+                istate = (unsigned char) scan_control_sequence();
+                if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                    check_outer_validity();
+                break;
+            case mid_line + active_char_cmd:
+            case new_line + active_char_cmd:
+            case skip_blanks + active_char_cmd:
+                /* Process an active-character  */
+                cur_cs = active_to_cs(cur_chr, false);
+                cur_cmd = eq_type(cur_cs);
+                cur_chr = equiv(cur_cs);
+                istate = mid_line;
+                if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                    check_outer_validity();
+                break;
+            case mid_line + sup_mark_cmd:
+            case new_line + sup_mark_cmd:
+            case skip_blanks + sup_mark_cmd:
+                /* If this |sup_mark| starts */
+                if (process_sup_mark())
+                    goto RESWITCH;
+                else
+                    istate = mid_line;
+                break;
+            case mid_line + invalid_char_cmd:
+            case new_line + invalid_char_cmd:
+            case skip_blanks + invalid_char_cmd:
+                /* Decry the invalid character and |goto restart|; */
+                invalid_character_error();
+                return false; /* because state may be |token_list| now */
+                break;
+            case mid_line + spacer_cmd:
+                /* Enter |skip_blanks| state, emit a space; */
+                istate = skip_blanks;
+                cur_chr = ' ';
+                break;
+            case mid_line + car_ret_cmd:
+                /*
+                    Finish line, emit a space. When a character of type |spacer| gets through, its
+                    character code is changed to $\.{"\ "}=040$. This means that the ASCII codes
+                    for tab and space, and for the space inserted at the end of a line, will be
+                    treated alike when macro parameters are being matched. We do this since such
+                    characters are indistinguishable on most computer terminal displays.
+                 */
+                iloc = ilimit + 1;
+                cur_cmd = spacer_cmd;
+                cur_chr = ' ';
+                break;
+            case skip_blanks + car_ret_cmd:
+            case mid_line + comment_cmd:
+            case new_line + comment_cmd:
+            case skip_blanks + comment_cmd:
+                /* Finish line, |goto switch|; */
+                iloc = ilimit + 1;
+                goto SWITCH;
+                break;
+            case new_line + car_ret_cmd:
+                /* Finish line, emit a \.{\\par}; */
+                iloc = ilimit + 1;
+                cur_cs = par_loc;
+                cur_cmd = eq_type(cur_cs);
+                cur_chr = equiv(cur_cs);
+                if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                    check_outer_validity();
+                break;
+            case skip_blanks + left_brace_cmd:
+            case new_line + left_brace_cmd:
+                istate = mid_line;
+                /* fall through */
+            case mid_line + left_brace_cmd:
+                align_state++;
+                break;
+            case skip_blanks + right_brace_cmd:
+            case new_line + right_brace_cmd:
+                istate = mid_line;
+                /* fall through */
+            case mid_line + right_brace_cmd:
+                align_state--;
+                break;
+            case mid_line + math_shift_cmd:
+            case mid_line + tab_mark_cmd:
+            case mid_line + mac_param_cmd:
+            case mid_line + sub_mark_cmd:
+            case mid_line + letter_cmd:
+            case mid_line + other_char_cmd:
+                break;
+            /*
+            case skip_blanks + math_shift:
+            case skip_blanks + tab_mark:
+            case skip_blanks + mac_param:
+            case skip_blanks + sub_mark:
+            case skip_blanks + letter:
+            case skip_blanks + other_char:
+            case new_line    + math_shift:
+            case new_line    + tab_mark:
+            case new_line    + mac_param:
+            case new_line    + sub_mark:
+            case new_line    + letter:
+            case new_line    + other_char:
+            */
+            default:
+                istate = mid_line;
+                break;
+        }
+    } else {
+        if (iname != 21)
+            istate = new_line;
+        /*
+           Move to next line of file,
+           or |goto restart| if there is no next line,
+           or |return| if a \.{\\read} line has finished;
+         */
+        do {
+            next_line_retval r = next_line();
+            if (r == next_line_return) {
+                return true;
+            } else if (r == next_line_restart) {
+                return false;
+            }
+        } while (0);
+        check_interrupt();
+        goto SWITCH;
+    }
+    return true;
+}
+
+#else
+
+/* 10 times less Bim in callgrind */
+
+/*
+    escape_cmd left_brace_cmd right_brace_cmd math_shift_cmd
+    tab_mark_cmd car_ret_cmd mac_param_cmd sup_mark_cmd
+    sub_mark_cmd ignore_cmd spacer_cmd letter_cmd
+    other_char_cmd active_char_cmd comment_cmd invalid_char_cmd
+*/
+
+static boolean get_next_file(void)
+{
+    int c = 0;
+  SWITCH:
+    if (iloc <= ilimit) {
+        /* current line not yet finished */
+        do_buffer_to_unichar(cur_chr, iloc);
+      RESWITCH:
+        if (detokenized_line()) {
+            cur_cmd = (cur_chr == ' ' ? 10 : 12);
+        } else {
+            do_get_cat_code(cur_cmd, cur_chr);
+        }
+        /*
+           Change state if necessary, and |goto switch| if the current
+           character should be ignored, or |goto reswitch| if the current
+           character changes to another;
+        */
+        c = istate + cur_cmd;
+        if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) {
+            return true;
+        } else if (c >= new_line) {
+            switch (c-new_line) {
+                case escape_cmd:
+                    istate = (unsigned char) scan_control_sequence();
+                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                        check_outer_validity();
+                    return true;
+                case left_brace_cmd:
+                    istate = mid_line;
+                    align_state++;
+                    return true;
+                case right_brace_cmd:
+                    istate = mid_line;
+                    align_state--;
+                    return true;
+                case math_shift_cmd:
+                    istate = mid_line;
+                    return true;
+                case tab_mark_cmd:
+                    istate = mid_line;
+                    return true;
+                case car_ret_cmd:
+                    /* Finish line, emit a \.{\\par}; */
+                    iloc = ilimit + 1;
+                    cur_cs = par_loc;
+                    cur_cmd = eq_type(cur_cs);
+                    cur_chr = equiv(cur_cs);
+                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                        check_outer_validity();
+                    return true;
+                case mac_param_cmd:
+                    istate = mid_line;
+                    return true;
+                case sup_mark_cmd:
+                    if (process_sup_mark())
+                        goto RESWITCH;
+                    else
+                        istate = mid_line;
+                    return true;
+                case sub_mark_cmd:
+                    istate = mid_line;
+                    return true;
+                case ignore_cmd:
+                    goto SWITCH;
+                    return true;
+                case spacer_cmd:
+                    /* Cases where character is ignored */
+                    goto SWITCH;
+                case letter_cmd:
+                    istate = mid_line;
+                    return true;
+                case other_char_cmd:
+                    istate = mid_line;
+                    return true;
+                case active_char_cmd:
+                    cur_cs = active_to_cs(cur_chr, false);
+                    cur_cmd = eq_type(cur_cs);
+                    cur_chr = equiv(cur_cs);
+                    istate = mid_line;
+                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                        check_outer_validity();
+                    return true;
+                case comment_cmd:
+                    iloc = ilimit + 1;
+                    goto SWITCH;
+                case invalid_char_cmd:
+                    invalid_character_error();
+                    return false; /* because state may be |token_list| now */
+                default:
+                    istate = mid_line;
+                    return true;
+            }
+        } else if (c >= skip_blanks) {
+            switch (c-skip_blanks) {
+                case escape_cmd:
+                    /* Scan a control sequence ...; */
+                    istate = (unsigned char) scan_control_sequence();
+                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                        check_outer_validity();
+                    return true;
+                case left_brace_cmd:
+                    istate = mid_line;
+                    align_state++;
+                    return true;
+                case right_brace_cmd:
+                    istate = mid_line;
+                    align_state--;
+                    return true;
+                case math_shift_cmd:
+                    istate = mid_line;
+                    return true;
+                case tab_mark_cmd:
+                    istate = mid_line;
+                    return true;
+                case car_ret_cmd:
+                    iloc = ilimit + 1;
+                    goto SWITCH;
+                case mac_param_cmd:
+                    istate = mid_line;
+                    return true;
+                case sup_mark_cmd:
+                    /* If this |sup_mark| starts */
+                    if (process_sup_mark())
+                        goto RESWITCH;
+                    else
+                        istate = mid_line;
+                    return true;
+                case sub_mark_cmd:
+                    istate = mid_line;
+                    return true;
+                case ignore_cmd:
+                    goto SWITCH;
+                case spacer_cmd:
+                    goto SWITCH;
+                case letter_cmd:
+                    istate = mid_line;
+                    return true;
+                case other_char_cmd:
+                    istate = mid_line;
+                    return true;
+                case active_char_cmd:
+                    cur_cs = active_to_cs(cur_chr, false);
+                    cur_cmd = eq_type(cur_cs);
+                    cur_chr = equiv(cur_cs);
+                    istate = mid_line;
+                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                        check_outer_validity();
+                    return true;
+                case comment_cmd:
+                    /* Finish line, |goto switch|; */
+                    iloc = ilimit + 1;
+                    goto SWITCH;
+                case invalid_char_cmd:
+                    /* Decry the invalid character and |goto restart|; */
+                    invalid_character_error();
+                    return false; /* because state may be |token_list| now */
+                default:
+                    istate = mid_line;
+                    return true;
+            }
+        } else if (c >= mid_line) {
+            switch (c-mid_line) {
+                case escape_cmd:
+                    istate = (unsigned char) scan_control_sequence();
+                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                        check_outer_validity();
+                    return true;
+                case left_brace_cmd:
+                    align_state++;
+                    return true;
+                case right_brace_cmd:
+                    align_state--;
+                    return true;
+                case math_shift_cmd:
+                    return true;
+                case tab_mark_cmd:
+                    return true;
+                case car_ret_cmd:
+                    /*
+                        Finish line, emit a space. When a character of type |spacer| gets through, its
+                        character code is changed to $\.{"\ "}=040$. This means that the ASCII codes
+                        for tab and space, and for the space inserted at the end of a line, will be
+                        treated alike when macro parameters are being matched. We do this since such
+                        characters are indistinguishable on most computer terminal displays.
+                     */
+                    iloc = ilimit + 1;
+                    cur_cmd = spacer_cmd;
+                    cur_chr = ' ';
+                    return true;
+                case mac_param_cmd:
+                    return true;
+                case sup_mark_cmd:
+                    if (process_sup_mark())
+                        goto RESWITCH;
+                    else
+                        istate = mid_line;
+                    return true;
+                case sub_mark_cmd:
+                    return true;
+                case ignore_cmd:
+                    goto SWITCH;
+                case spacer_cmd:
+                    /* Enter |skip_blanks| state, emit a space; */
+                    istate = skip_blanks;
+                    cur_chr = ' ';
+                    return true;
+                case letter_cmd:
+                    istate = mid_line;
+                    return true;
+                case other_char_cmd:
+                    istate = mid_line;
+                    return true;
+                case active_char_cmd:
+                    cur_cs = active_to_cs(cur_chr, false);
+                    cur_cmd = eq_type(cur_cs);
+                    cur_chr = equiv(cur_cs);
+                    istate = mid_line;
+                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+                        check_outer_validity();
+                    return true;
+                case comment_cmd:
+                    iloc = ilimit + 1;
+                    goto SWITCH;
+                case invalid_char_cmd:
+                    invalid_character_error();
+                    return false; /* because state may be |token_list| now */
+                default:
+                    istate = mid_line;
+                    return true;
+            }
+        } else {
+            istate = mid_line;
+            return true;
+        }
+    } else {
+        if (iname != 21) {
+            istate = new_line;
+        }
+        /*
+           Move to next line of file, or |goto restart| if there is no next line,
+           or |return| if a \.{\\read} line has finished;
+        */
+        do {
+            next_line_retval r = next_line();
+            if (r == next_line_return) {
+                return true;
+            } else if (r == next_line_restart) {
+                return false;
+            }
+        } while (0);
+        check_interrupt();
+        goto SWITCH;
+    }
+    return true;
+}
+
+#endif
+
+@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
+We only support a limited set:
+
+^^^^^^XXXXXX
+^^^^XXXXXX
+^^XX ^^<char>
+
+@c
+
+#define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f'))
+
+#define add_nybble(c) \
+    if (c<='9') { \
+        cur_chr=(cur_chr<<4)+c-'0'; \
+    } else { \
+        cur_chr=(cur_chr<<4)+c-'a'+10; \
+    }
+
+#define set_nybble(c) \
+    if (c<='9') { \
+        cur_chr=c-'0'; \
+    } else { \
+        cur_chr=c-'a'+10; \
+    }
+
+#define one_hex_to_cur_chr(c1) \
+    set_nybble(c1);
+
+#define two_hex_to_cur_chr(c1,c2) \
+    set_nybble(c1); \
+    add_nybble(c2);
+
+#define four_hex_to_cur_chr(c1,c2,c3,c4) \
+    two_hex_to_cur_chr(c1,c2); \
+    add_nybble(c3); \
+    add_nybble(c4);
+
+#define six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6) \
+    four_hex_to_cur_chr(c1,c2,c3,c4); \
+    add_nybble(c5); \
+    add_nybble(c6);
+
+static boolean process_sup_mark(void)
+{
+    if (cur_chr == buffer[iloc]) {
+        if (iloc < ilimit) {
+            if ((cur_chr == buffer[iloc + 1]) && (cur_chr == buffer[iloc + 2])) {
+                if ((cur_chr == buffer[iloc + 3]) && (cur_chr == buffer[iloc + 4])) {
+                    /* ^^^^^^XXXXXX */
+                    if ((iloc + 10) <= ilimit) {
+                        int c1 = buffer[iloc +  5];
+                        int c2 = buffer[iloc +  6];
+                        int c3 = buffer[iloc +  7];
+                        int c4 = buffer[iloc +  8];
+                        int c5 = buffer[iloc +  9];
+                        int c6 = buffer[iloc + 10];
+                        if (is_hex(c1) && is_hex(c2) && is_hex(c3) &&
+                            is_hex(c4) && is_hex(c5) && is_hex(c6)) {
+                            iloc = iloc + 11;
+                            six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6);
+                            return true;
+                        } else {
+                            tex_error("^^^^^^ needs six hex digits", NULL);
+                        }
+                    } else {
+                        tex_error("^^^^^^ needs six hex digits, end of input", NULL);
+                    }
+                } else {
+                    /* ^^^^XXXX */
+                    if ((iloc + 6) <= ilimit) {
+                        int c1 = buffer[iloc + 3];
+                        int c2 = buffer[iloc + 4];
+                        int c3 = buffer[iloc + 5];
+                        int c4 = buffer[iloc + 6];
+                        if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4)) {
+                            iloc = iloc + 7;
+                            four_hex_to_cur_chr(c1,c2,c3,c4);
+                            return true;
+                        } else {
+                            tex_error("^^^^ needs four hex digits", NULL);
+                        }
+                    } else {
+                        tex_error("^^^^ needs four hex digits, end of input", NULL);
+                    }
+                }
+            } else {
+                /* ^^XX */
+                if ((iloc + 2) <= ilimit) {
+                    int c1 = buffer[iloc + 1];
+                    int c2 = buffer[iloc + 2];
+                    if (is_hex(c1) && is_hex(c2)) {
+                        iloc = iloc + 3;
+                        two_hex_to_cur_chr(c1,c2);
+                        return true;
+                    }
+                }
+                /* go on, no error, good old tex */
+            }
+        }
+        /* the rest */
+        {
+            int c1 = buffer[iloc + 1];
+            if (c1 < 0200) {
+                iloc = iloc + 2;
+                if (is_hex(c1) && (iloc <= ilimit)) {
+                    int c2 = buffer[iloc];
+                    if (is_hex(c2)) {
+                        incr(iloc);
+                        two_hex_to_cur_chr(c1,c2);
+                        return true;
+                    }
+                }
+                cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+@ Control sequence names are scanned only when they appear in some line of a
+file; once they have been scanned the first time, their |eqtb| location serves as
+a unique identification, so \TeX\ doesn't need to refer to the original name any
+more except when it prints the equivalent in symbolic form.
+
+The program that scans a control sequence has been written carefully in order to
+avoid the blowups that might otherwise occur if a malicious user tried something
+like `\.{\\catcode\'15=0}'. The algorithm might look at |buffer[ilimit+1]|, but
+it never looks at |buffer[ilimit+2]|.
+
+If expanded characters like `\.{\^\^A}' or `\.{\^\^df}' appear in or just
+following a control sequence name, they are converted to single characters in the
+buffer and the process is repeated, slowly but surely.
+
+@c
+static boolean check_expanded_code(int *kk);    /* below */
+
+static int scan_control_sequence(void)
+{
+    int retval = mid_line;
+    if (iloc > ilimit) {
+        cur_cs = null_cs;       /* |state| is irrelevant in this case */
+    } else {
+        register int cat;       /* |cat_code(cur_chr)|, usually */
+        while (1) {
+            int k = iloc;
+            do_buffer_to_unichar(cur_chr, k);
+            do_get_cat_code(cat, cur_chr);
+            if (cat != letter_cmd || k > ilimit) {
+                retval = (cat == spacer_cmd ? skip_blanks : mid_line);
+                if (cat == sup_mark_cmd && check_expanded_code(&k))     /* If an expanded...; */
+                    continue;
+            } else {
+                retval = skip_blanks;
+                do {
+                    do_buffer_to_unichar(cur_chr, k);
+                    do_get_cat_code(cat, cur_chr);
+                } while (cat == letter_cmd && k <= ilimit);
+
+                if (cat == sup_mark_cmd && check_expanded_code(&k))     /* If an expanded...; */
+                    continue;
+                if (cat != letter_cmd) {
+                    /* backtrack one character which can be utf */
+                    /*
+                    decr(k);
+                    if (cur_chr > 0xFFFF)
+                        decr(k);
+                    if (cur_chr > 0x7FF)
+                        decr(k);
+                    if (cur_chr > 0x7F)
+                        decr(k);
+                    */
+                    if (cur_chr <= 0x7F) {
+                        k -= 1; /* in most cases */
+                    } else if (cur_chr > 0xFFFF) {
+                        k -= 4;
+                    } else if (cur_chr > 0x7FF) {
+                        k -= 3;
+                    } else /* if (cur_chr > 0x7F) */ {
+                        k -= 2;
+                    }
+                    /* now |k| points to first nonletter */
+                }
+            }
+            cur_cs = id_lookup(iloc, k - iloc);
+            iloc = k;
+            break;
+        }
+    }
+    cur_cmd = eq_type(cur_cs);
+    cur_chr = equiv(cur_cs);
+    return retval;
+}
+
+@ Whenever we reach the following piece of code, we will have
+|cur_chr=buffer[k-1]| and |k<=ilimit+1| and
+|cat=get_cat_code(cat_code_table,cur_chr)|. If an expanded code like \.{\^\^A} or
+\.{\^\^df} appears in |buffer[(k-1)..(k+1)]| or |buffer[(k-1)..(k+2)]|, we will
+store the corresponding code in |buffer[k-1]| and shift the rest of the buffer
+left two or three places.
+
+@c
+static boolean check_expanded_code(int *kk)
+{
+    int l;
+    int k = *kk;
+    int d = 1;
+    if (buffer[k] == cur_chr && k < ilimit) {
+        if ((cur_chr == buffer[k + 1]) && (cur_chr == buffer[k + 2])) {
+            if ((cur_chr == buffer[k + 3]) && (cur_chr == buffer[k + 4])) {
+                if ((k + 10) <= ilimit) {
+                    int c1 = buffer[k + 6 - 1];
+                    int c2 = buffer[k + 6];
+                    int c3 = buffer[k + 6 + 1];
+                    int c4 = buffer[k + 6 + 2];
+                    int c5 = buffer[k + 6 + 3];
+                    int c6 = buffer[k + 6 + 4];
+                    if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4) && is_hex(c5) && is_hex(c6)) {
+                        d = 6;
+                        six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6);
+                    } else {
+                        tex_error("^^^^^^ needs six hex digits", NULL);
+                    }
+                } else {
+                    tex_error("^^^^^^ needs six hex digits, end of input", NULL);
+                }
+            } else {
+                if ((k + 6) <= ilimit) {
+                    int c1 = buffer[k + 4 - 1];
+                    int c2 = buffer[k + 4];
+                    int c3 = buffer[k + 4 + 1];
+                    int c4 = buffer[k + 4 + 2];
+                    if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4)) {
+                        d = 4;
+                        four_hex_to_cur_chr(c1,c2,c3,c4);
+                    } else {
+                        tex_error("^^^^ needs four hex digits", NULL);
+                    }
+                } else {
+                    tex_error("^^^^ needs four hex digits, end of input", NULL);
+                }
+            }
+        } else {
+            int c1 = buffer[k + 1];
+            if (c1 < 0200) {
+                d = 1;
+                if (is_hex(c1) && (k + 2) <= ilimit) {
+                    int c2 = buffer[k + 2];
+                    if (is_hex(c2)) {
+                        d = 2;
+                        two_hex_to_cur_chr(c1,c2);
+                    } else {
+                        cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
+                    }
+                } else {
+                    cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
+                }
+            }
+        }
+        if (d > 2)
+            d = 2 * d - 1;
+        else
+            d++;
+        if (cur_chr <= 0x7F) {
+            buffer[k - 1] = (packed_ASCII_code) cur_chr;
+        } else if (cur_chr <= 0x7FF) {
+            buffer[k - 1] = (packed_ASCII_code) (0xC0 + cur_chr / 0x40);
+            k++;
+            d--;
+            buffer[k - 1] = (packed_ASCII_code) (0x80 + cur_chr % 0x40);
+        } else if (cur_chr <= 0xFFFF) {
+            buffer[k - 1] = (packed_ASCII_code) (0xE0 + cur_chr / 0x1000);
+            k++;
+            d--;
+            buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) / 0x40);
+            k++;
+            d--;
+            buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) % 0x40);
+        } else {
+            buffer[k - 1] = (packed_ASCII_code) (0xF0 + cur_chr / 0x40000);
+            k++;
+            d--;
+            buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x40000) / 0x1000);
+            k++;
+            d--;
+            buffer[k - 1] = (packed_ASCII_code) (0x80 + ((cur_chr % 0x40000) % 0x1000) / 0x40);
+            k++;
+            d--;
+            buffer[k - 1] = (packed_ASCII_code) (0x80 + ((cur_chr % 0x40000) % 0x1000) % 0x40);
+        }
+        l = k;
+        ilimit = ilimit - d;
+        while (l <= ilimit) {
+            buffer[l] = buffer[l + d];
+            l++;
+        }
+        *kk = k;
+        return true;
+    }
+    return false;
+}
+
+@ All of the easy branches of |get_next| have now been taken care of. There is
+one more branch.
+
+@c static next_line_retval next_line(void)
+{
+    boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */
+    if (iname > 17) {
+        /* Read next line of file into |buffer|, or |goto restart| if the file has ended */
+        incr(line);
+        first = istart;
+        if (!force_eof) {
+            if (iname <= 20) {
+                if (pseudo_input()) {   /* not end of file */
+                    firm_up_the_line(); /* this sets |ilimit| */
+                    line_catcode_table = DEFAULT_CAT_TABLE;
+                    if ((iname == 19) && (pseudo_lines(pseudo_files) == null))
+                        inhibit_eol = true;
+                } else if ((every_eof_par != null) && !eof_seen[iindex]) {
+                    ilimit = first - 1;
+                    eof_seen[iindex] = true; /* fake one empty line */
+                    if (iname != 19)
+                        begin_token_list(every_eof_par, every_eof_text);
+                    return next_line_restart;
+                } else {
+                    force_eof = true;
+                }
+            } else {
+                if (iname == 21) {
+                    if (luacstring_input()) { /* not end of strings  */
+                        firm_up_the_line();
+                        line_catcode_table = (short) luacstring_cattable();
+                        line_partial = (signed char) luacstring_partial();
+                        if (luacstring_final_line() || line_partial
+                            || line_catcode_table == NO_CAT_TABLE)
+                            inhibit_eol = true;
+                        if (!line_partial)
+                            istate = new_line;
+                    } else {
+                        force_eof = true;
+                    }
+                } else {
+                    if (lua_input_ln(cur_file, 0, true)) { /* not end of file */
+                        firm_up_the_line(); /* this sets |ilimit| */
+                        line_catcode_table = DEFAULT_CAT_TABLE;
+                    } else if ((every_eof_par != null) && (!eof_seen[iindex])) {
+                        ilimit = first - 1;
+                        eof_seen[iindex] = true; /* fake one empty line */
+                        begin_token_list(every_eof_par, every_eof_text);
+                        return next_line_restart;
+                    } else {
+                        force_eof = true;
+                    }
+                }
+            }
+        }
+        if (force_eof) {
+            if (tracing_nesting_par > 0)
+                if ((grp_stack[in_open] != cur_boundary) || (if_stack[in_open] != cond_ptr))
+                    if (!((iname == 19) || (iname == 21))) {
+                        /* give warning for some unfinished groups and/or conditionals */
+                        file_warning();
+                    }
+            if ((iname > 21) || (iname == 20)) {
+                report_stop_file(filetype_tex);
+                decr(open_parens);
+            }
+            force_eof = false;
+            /* lua input or \.{\\scantextokens} */
+            if (iname == 21 || iname == 19) {
+                end_file_reading();
+            } else {
+                end_file_reading();
+                if (! suppress_outer_error_par)
+                    check_outer_validity();
+            }
+            return next_line_restart;
+        }
+        if (inhibit_eol || end_line_char_inactive)
+            ilimit--;
+        else
+            buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+        first = ilimit + 1;
+        iloc = istart; /* ready to read */
+    } else {
+        if (!terminal_input) {
+            /* \.{\\read} line has ended */
+            cur_cmd = 0;
+            cur_chr = 0;
+            return next_line_return;    /* OUTER */
+        }
+        if (input_ptr > 0) {
+            /* text was inserted during error recovery */
+            end_file_reading();
+            return next_line_restart; /* resume previous level */
+        }
+        if (selector < log_only)
+            open_log_file();
+        if (interaction > nonstop_mode) {
+            if (end_line_char_inactive)
+                ilimit++;
+            if (ilimit == istart) {
+                /* previous line was empty */
+                tprint_nl("(Please type a command or say `\\end')");
+            }
+            print_ln();
+            first = istart;
+            prompt_input("*"); /* input on-line into |buffer| */
+            ilimit = last;
+            if (end_line_char_inactive)
+                ilimit--;
+            else
+                buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+            first = ilimit + 1;
+            iloc = istart;
+        } else {
+            /*
+                Nonstop mode, which is intended for overnight batch processing,
+                never waits for on-line input.
+            */
+            fatal_error("*** (job aborted, no legal \\end found)");
+        }
+    }
+    return next_line_ok;
+}
+
+@ Let's consider now what happens when |get_next| is looking at a token list.
+
+@c
+static boolean get_next_tokenlist(void)
+{
+    register halfword t = token_info(iloc);
+    iloc = token_link(iloc); /* move to next */
+    if (t >= cs_token_flag) {
+        /* a control sequence token */
+        cur_cs = t - cs_token_flag;
+        cur_cmd = eq_type(cur_cs);
+        if (cur_cmd >= outer_call_cmd) {
+            if (cur_cmd == dont_expand_cmd) {
+                /*
+                    Get the next token, suppressing expansion. The present point in the program
+                    is reached only when the |expand| routine has inserted a special marker into
+                    the input. In this special case, |token_info(iloc)| is known to be a control
+                    sequence token, and |token_link(iloc)=null|.
+                */
+                cur_cs = token_info(iloc) - cs_token_flag;
+                iloc = null;
+                cur_cmd = eq_type(cur_cs);
+                if (cur_cmd > max_command_cmd) {
+                    cur_cmd = relax_cmd;
+                    cur_chr = no_expand_flag;
+                    return true;
+                }
+            } else if (! suppress_outer_error_par) {
+                check_outer_validity();
+            }
+        }
+        cur_chr = equiv(cur_cs);
+    } else {
+        cur_cmd = token_cmd(t);
+        cur_chr = token_chr(t);
+        switch (cur_cmd) {
+            case left_brace_cmd:
+                align_state++;
+                break;
+            case right_brace_cmd:
+                align_state--;
+                break;
+            case out_param_cmd:
+                /* Insert macro parameter and |goto restart|; */
+                begin_token_list(param_stack[param_start + cur_chr - 1], parameter);
+                return false;
+                break;
+        }
+    }
+    return true;
+}
+
+@ Now we're ready to take the plunge into |get_next| itself. Parts of this
+routine are executed more often than any other instructions of \TeX.
+@^mastication@>@^inner loop@>
+
+@ sets |cur_cmd|, |cur_chr|, |cur_cs| to next token
+
+@c
+void get_next(void)
+{
+  RESTART:
+    cur_cs = 0;
+    if (istate != token_list) {
+        /* Input from external file, |goto restart| if no input found */
+        if (!get_next_file())
+            goto RESTART;
+    } else {
+        if (iloc == null) {
+            end_token_list();
+            goto RESTART;       /* list exhausted, resume previous level */
+        } else if (!get_next_tokenlist()) {
+            goto RESTART;       /* parameter needs to be expanded */
+        }
+    }
+    /* If an alignment entry has just ended, take appropriate action */
+    if ((cur_cmd == tab_mark_cmd || cur_cmd == car_ret_cmd) && align_state == 0) {
+        insert_vj_template();
+        goto RESTART;
+    }
+}
+
+@ Since |get_next| is used so frequently in \TeX, it is convenient to define
+three related procedures that do a little more:
+
+\yskip\hang|get_token| not only sets |cur_cmd| and |cur_chr|, it also sets
+|cur_tok|, a packed halfword version of the current token.
+
+\yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like |get_token|,
+but if the current token turns out to be a user-defined control sequence (i.e., a
+macro call), or a conditional, or something like \.{\\topmark} or
+\.{\\expandafter} or \.{\\csname}, it is eliminated from the input by beginning
+the expansion of the macro or the evaluation of the conditional.
+
+\yskip\hang|x_token| is like |get_x_token| except that it assumes that |get_next|
+has already been called.
+
+\yskip\noindent In fact, these three procedures account for almost every use of
+|get_next|.
+
+No new control sequences will be defined except during a call of |get_token|, or
+when \.{\\csname} compresses a token list, because |no_new_control_sequence| is
+always |true| at other times.
+
+@ sets |cur_cmd|, |cur_chr|, |cur_tok|
+
+@c
+void get_token(void)
+{
+    no_new_control_sequence = false;
+    get_next();
+    no_new_control_sequence = true;
+    if (cur_cs == 0)
+        cur_tok = token_val(cur_cmd, cur_chr);
+    else
+        cur_tok = cs_token_flag + cur_cs;
+}
+
+@ changes the string |s| to a token list
+
+@c
+halfword string_to_toks(const char *ss)
+{
+    halfword p; /* tail of the token list */
+    halfword q; /* new node being added to the token list via |store_new_token| */
+    halfword t; /* token being appended */
+    const char *s = ss;
+    const char *se = ss + strlen(s);
+    p = temp_token_head;
+    set_token_link(p, null);
+    while (s < se) {
+        t = (halfword) str2uni((const unsigned char *) s);
+        s += utf8_size(t);
+        if (t == ' ')
+            t = space_token;
+        else
+            t = other_token + t;
+        fast_store_new_token(t);
+    }
+    return token_link(temp_token_head);
+}
+
+@ The token lists for macros and for other things like \.{\\mark} and
+\.{\\output} and \.{\\write} are produced by a procedure called |scan_toks|.
+
+Before we get into the details of |scan_toks|, let's consider a much simpler
+task, that of converting the current string into a token list. The |str_toks|
+function does this; it classifies spaces as type |spacer| and everything else as
+type |other_char|.
+
+The token list created by |str_toks| begins at |link(temp_token_head)| and ends
+at the value |p| that is returned. (If |p=temp_token_head|, the list is empty.)
+
+|lua_str_toks| is almost identical, but it also escapes the three symbols that
+|lua| considers special while scanning a literal string
+
+@ changes the string |str_pool[b..pool_ptr]| to a token list
+
+@c
+halfword lua_str_toks(lstring b)
+{
+    halfword p;       /* tail of the token list */
+    halfword q;       /* new node being added to the token list via |store_new_token| */
+    halfword t;       /* token being appended */
+    unsigned char *k; /* index into string */
+    p = temp_token_head;
+    set_token_link(p, null);
+    k = (unsigned char *) b.s;
+    while (k < (unsigned char *) b.s + b.l) {
+        t = pool_to_unichar(k);
+        k += utf8_size(t);
+        if (t == ' ') {
+            t = space_token;
+        } else {
+            if ((t == '\\') || (t == '"') || (t == '\'') || (t == 10) || (t == 13))
+                fast_store_new_token(other_token + '\\');
+            if (t == 10)
+                t = 'n';
+            if (t == 13)
+                t = 'r';
+            t = other_token + t;
+        }
+        fast_store_new_token(t);
+    }
+    return p;
+}
+
+@ Incidentally, the main reason for wanting |str_toks| is the function
+|the_toks|, which has similar input/output characteristics.
+
+@ changes the string |str_pool[b..pool_ptr]| to a token list
+
+@c
+halfword str_toks(lstring s)
+{
+    halfword p;           /* tail of the token list */
+    halfword q;           /* new node being added to the token list via |store_new_token| */
+    halfword t;           /* token being appended */
+    unsigned char *k, *l; /* index into string */
+    p = temp_token_head;
+    set_token_link(p, null);
+    k = s.s;
+    l = k + s.l;
+    while (k < l) {
+        t = pool_to_unichar(k);
+        k += utf8_size(t);
+        if (t == ' ')
+            t = space_token;
+        else
+            t = other_token + t;
+        fast_store_new_token(t);
+    }
+    return p;
+}
+
+/*
+    hh: most of the converter is similar to the one i made for macro so at some point i
+    can make a helper; also todo: there is no need to go through the pool
+
+*/
+
+halfword str_scan_toks(int ct, lstring s)
+{                         /* changes the string |str_pool[b..pool_ptr]| to a token list */
+    halfword p;           /* tail of the token list */
+    halfword q;           /* new node being added to the token list via |store_new_token| */
+    halfword t;           /* token being appended */
+    unsigned char *k, *l; /* index into string */
+    int cc;
+    p = temp_token_head;
+    set_token_link(p, null);
+    k = s.s;
+    l = k + s.l;
+    while (k < l) {
+        t = pool_to_unichar(k);
+        k += utf8_size(t);
+        cc = get_cat_code(ct,t);
+            if (cc == 0) {
+                /* we have a potential control sequence so we check for it */
+                int _lname = 0 ;
+                int _s = 0 ;
+                int _c = 0 ;
+                halfword _cs = null ;
+                unsigned char *_name  = k ;
+                while (k < l) {
+                    t = (halfword) str2uni((const unsigned char *) k);
+                    _s = utf8_size(t);
+                    _c = get_cat_code(ct,t);
+                    if (_c == 11) {
+                        k += _s ;
+                        _lname = _lname + _s ;
+                    } else if (_c == 10) {
+                        /* we ignore a trailing space like normal scanning does */
+                        k += _s ;
+                        break ;
+                    } else {
+                        break ;
+                    }
+                }
+                if (_s > 0) {
+                    /* we have a potential \cs */
+                    _cs = string_lookup((const char *) _name, _lname);
+                    if (_cs == undefined_control_sequence) {
+                        /* let's play safe and backtrack */
+                        t = cc * (1<<21) + t ;
+                        k = _name ;
+                    } else {
+                        t = cs_token_flag + _cs;
+                    }
+                } else {
+                    /* just a character with some meaning, so \unknown becomes effectively */
+                    /* \\unknown assuming that \\ has some useful meaning of course        */
+                    t = cc * (1<<21) + t ;
+                    k = _name ;
+                }
+
+            } else {
+                /* whatever token, so for instance $x^2$ just works given a tex */
+                /* catcode regime */
+                t = cc * (1<<21) + t ;
+            }
+            fast_store_new_token(t);
+
+    }
+    return p;
+}
+
+@ Here's part of the |expand| subroutine that we are now ready to complete:
+
+@c
+void ins_the_toks(void)
+{
+    (void) the_toks();
+    ins_list(token_link(temp_token_head));
+}
+
+#define set_toks_register(n,t,g) { \
+    int a = (g>0) ? 4 : 0; \
+    halfword ref = get_avail();  \
+    set_token_ref_count(ref, 0); \
+    set_token_link(ref, token_link(t)); \
+    define(n + toks_base, call_cmd, ref); \
+}
+
+void combine_the_toks(int how)
+{
+    halfword nt;
+    get_x_token();
+    /* target */
+    if (cur_cmd == assign_toks_cmd) {
+        nt = equiv(cur_cs) - toks_base;
+        /* check range */
+    } else {
+        back_input();
+        scan_int();
+        nt = cur_val;
+    }
+    /* source */
+    do {
+        get_x_token();
+    } while (cur_cmd == spacer_cmd);
+    if (cur_cmd == left_brace_cmd) {
+        halfword x, source;
+        back_input();
+        x = scan_toks(false,how > 1); /* expanded or not */
+        source = def_ref;
+        /* action */
+        if (source != null) {
+            halfword target = toks(nt);
+            if (target == null) {
+                set_toks_register(nt,source,0);
+            } else {
+                halfword s = token_link(source);
+                if (s != null) {
+                    halfword t = token_link(target);
+                    if (t == null) {
+                        /* can this happen ? */
+                        set_token_link(target, s);
+                    } else if (odd(how)) {
+                        /* prepend */
+                        if (cur_level != eq_level_field(eqtb[toks_base+nt])) {
+                            halfword p = temp_token_head;
+                            halfword q;
+                            set_token_link(p, s); /* s = head, x = tail */
+                            p = x;
+                            while (t != null) {
+                                fast_store_new_token(token_info(t));
+                                t = token_link(t);
+                            }
+                            set_toks_register(nt,temp_token_head,0);
+                        } else {
+                            set_token_link(x,t);
+                            set_token_link(target,s);
+                        }
+                    } else {
+                        /* append */
+                        if (cur_level != eq_level_field(eqtb[toks_base+nt])) {
+                            halfword p = temp_token_head;
+                            halfword q;
+                            set_token_link(p, null);
+                            while (t != null) {
+                                fast_store_new_token(token_info(t));
+                                t = token_link(t);
+                            }
+                            set_token_link(p,s);
+                            set_toks_register(nt,temp_token_head,0);
+                        } else {
+                            while (token_link(t) != null) {
+                                t = token_link(t);
+                            }
+                            set_token_link(t,s);
+                        }
+                    }
+                }
+            }
+        }
+    } else {
+        halfword source, ns;
+        if (cur_cmd == assign_toks_cmd) {
+            ns = equiv(cur_cs) - toks_base;
+            /* check range */
+        } else {
+            back_input();
+            scan_int();
+            ns = cur_val;
+        }
+        /* action */
+        source = toks(ns);
+        if (source != null) {
+            halfword target = toks(nt);
+            if (target == null) {
+                equiv(toks_base+nt) = source;
+                equiv(toks_base+ns) = null;
+            } else {
+                halfword s = token_link(source);
+                if (s != null) {
+                    halfword t = token_link(target);
+                    if (t == null) {
+                        set_token_link(target, s);
+                    } else if (odd(how)) {
+                        /* prepend */
+                        halfword x = s;
+                        while (token_link(x) != null) {
+                            x = token_link(x);
+                        }
+                        set_token_link(x,t);
+                        set_token_link(target,s);
+                    } else {
+                        /* append */
+                        while (token_link(t) != null) {
+                            t = token_link(t);
+                        }
+                        set_token_link(t,s);
+                    }
+                     equiv(toks_base+ns) = null;
+                }
+            }
+        }
+    }
+}
+
+@ This routine, used in the next one, prints the job name, possibly modified by
+the |process_jobname| callback.
+
+@c
+static void print_job_name(void)
+{
+   if (job_name) {
+      char *s, *ss; /* C strings for jobname before and after processing */
+      int callback_id, lua_retval;
+      s = (char*)str_string(job_name);
+      callback_id = callback_defined(process_jobname_callback);
+      if (callback_id > 0) {
+        lua_retval = run_callback(callback_id, "S->S", s, &ss);
+        if ((lua_retval == true) && (ss != NULL))
+            s = ss;
+      }
+      tprint(s);
+   } else {
+      print(job_name);
+   }
+}
+
+@ Here is a routine that print the result of a convert command, using the
+argument |i|. It returns |false | if it does not know to print the code |c|. The
+function exists because lua code and tex code can both call it to convert
+something.
+
+@ Parse optional lua state integer, or an instance name to be stored in |sn| and
+get the next non-blank non-relax non-call token.
+
+@c
+
+int scan_lua_state(void)
+{
+    int sn = 0;
+    do {
+        get_x_token();
+    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+    back_input();
+    if (cur_cmd != left_brace_cmd) {
+        if (scan_keyword("name")) {
+            (void) scan_toks(false, true);
+            sn = def_ref;
+        } else {
+            scan_register_num();
+            if (get_lua_name(cur_val))
+                sn = (cur_val - 65536);
+        }
+    }
+    return sn;
+}
+
+@ The procedure |conv_toks| uses |str_toks| to insert the token list for
+|convert| functions into the scanner; `\.{\\outer}' control sequences are allowed
+to follow `\.{\\string}' and `\.{\\meaning}'.
+
+The extra temp string |u| is needed because |pdf_scan_ext_toks| incorporates any
+pending string in its output. In order to save such a pending string, we have to
+create a temporary string that is destroyed immediately after.
+
+@c
+#define push_selector { \
+    old_setting = selector; \
+    selector = new_string; \
+}
+
+#define pop_selector { \
+    selector = old_setting; \
+}
+
+static int do_variable_dvi(halfword c)
+{
+    return 0;
+}
+
+#define do_variable_backend_int(i) \
+    cur_cmd = assign_int_cmd; \
+    cur_val = backend_int_base + i; \
+    cur_tok = token_val(cur_cmd, cur_val); \
+    back_input();
+
+#define do_variable_backend_dimen(i) \
+    cur_cmd = assign_dimen_cmd; \
+    cur_val = backend_dimen_base + i; \
+    cur_tok = token_val(cur_cmd, cur_val); \
+    back_input();
+
+#define do_variable_backend_toks(i) \
+    cur_cmd = assign_toks_cmd; \
+    cur_val = backend_toks_base + i ; \
+    cur_tok = token_val(cur_cmd, cur_val); \
+    back_input();
+
+static int do_variable_pdf(halfword c)
+{
+         if (scan_keyword("compresslevel"))        { do_variable_backend_int(c_pdf_compress_level); }
+    else if (scan_keyword("decimaldigits"))        { do_variable_backend_int(c_pdf_decimal_digits); }
+    else if (scan_keyword("imageresolution"))      { do_variable_backend_int(c_pdf_image_resolution); }
+    else if (scan_keyword("pkresolution"))         { do_variable_backend_int(c_pdf_pk_resolution); }
+    else if (scan_keyword("uniqueresname"))        { do_variable_backend_int(c_pdf_unique_resname); }
+    else if (scan_keyword("majorversion"))         { do_variable_backend_int(c_pdf_major_version); }
+    else if (scan_keyword("minorversion"))         { do_variable_backend_int(c_pdf_minor_version); }
+    else if (scan_keyword("pagebox"))              { do_variable_backend_int(c_pdf_pagebox); }
+    else if (scan_keyword("inclusionerrorlevel"))  { do_variable_backend_int(c_pdf_inclusion_errorlevel); }
+    else if (scan_keyword("ignoreunknownimages"))  { do_variable_backend_int(c_pdf_ignore_unknown_images); }
+    else if (scan_keyword("gamma"))                { do_variable_backend_int(c_pdf_gamma); }
+    else if (scan_keyword("imageapplygamma"))      { do_variable_backend_int(c_pdf_image_apply_gamma); }
+    else if (scan_keyword("imagegamma"))           { do_variable_backend_int(c_pdf_image_gamma); }
+    else if (scan_keyword("imagehicolor"))         { do_variable_backend_int(c_pdf_image_hicolor); }
+    else if (scan_keyword("imageaddfilename"))     { do_variable_backend_int(c_pdf_image_addfilename); }
+    else if (scan_keyword("objcompresslevel"))     { do_variable_backend_int(c_pdf_obj_compress_level); }
+    else if (scan_keyword("inclusioncopyfonts"))   { do_variable_backend_int(c_pdf_inclusion_copy_font); }
+    else if (scan_keyword("gentounicode"))         { do_variable_backend_int(c_pdf_gen_tounicode); }
+    else if (scan_keyword("pkfixeddpi"))           { do_variable_backend_int(c_pdf_pk_fixed_dpi); }
+    else if (scan_keyword("suppressoptionalinfo")) { do_variable_backend_int(c_pdf_suppress_optional_info); }
+    else if (scan_keyword("omitcidset"))           { do_variable_backend_int(c_pdf_omit_cidset); }
+
+    else if (scan_keyword("horigin"))              { do_variable_backend_dimen(d_pdf_h_origin); }
+    else if (scan_keyword("vorigin"))              { do_variable_backend_dimen(d_pdf_v_origin); }
+    else if (scan_keyword("threadmargin"))         { do_variable_backend_dimen(d_pdf_thread_margin); }
+    else if (scan_keyword("destmargin"))           { do_variable_backend_dimen(d_pdf_dest_margin); }
+    else if (scan_keyword("linkmargin"))           { do_variable_backend_dimen(d_pdf_link_margin); }
+    else if (scan_keyword("xformmargin"))          { do_variable_backend_dimen(d_pdf_xform_margin); }
+
+    else if (scan_keyword("pageattr"))             { do_variable_backend_toks(t_pdf_page_attr); }
+    else if (scan_keyword("pageresources"))        { do_variable_backend_toks(t_pdf_page_resources); }
+    else if (scan_keyword("pagesattr"))            { do_variable_backend_toks(t_pdf_pages_attr); }
+    else if (scan_keyword("xformattr"))            { do_variable_backend_toks(t_pdf_xform_attr); }
+    else if (scan_keyword("xformresources"))       { do_variable_backend_toks(t_pdf_xform_resources); }
+    else if (scan_keyword("pkmode"))               { do_variable_backend_toks(t_pdf_pk_mode); }
+    else if (scan_keyword("trailerid"))            { do_variable_backend_toks(t_pdf_trailer_id); }
+
+    else
+        return 0;
+    return 1;
+}
+
+static int do_feedback_dvi(halfword c)
+{
+    return 0;
+}
+
+/* codes not really needed but cleaner when testing */
+
+#define pdftex_version  140 /* these values will not change any more */
+#define pdftex_revision "0" /* these values will not change any more */
+
+static int do_feedback_pdf(halfword c)
+{
+    int old_setting;            /* holds |selector| setting */
+    int save_scanner_status;    /* |scanner_status| upon entry */
+    halfword save_def_ref;      /* |def_ref| upon entry, important if inside `\.{\\message}' */
+    halfword save_warning_index;
+    boolean bool;               /* temp boolean */
+    str_number s;               /* first temp string */
+    int ff;                     /* for use with |set_ff| */
+    str_number u = 0;           /* third temp string, will become non-nil if a string is already being built */
+    char *str;                  /* color stack init str */
+
+    if (scan_keyword("lastlink")) {
+        push_selector;
+        print_int(pdf_last_link);
+        pop_selector;
+    } else if (scan_keyword("retval")) {
+        push_selector;
+        print_int(pdf_retval);
+        pop_selector;
+    } else if (scan_keyword("lastobj")) {
+        push_selector;
+        print_int(pdf_last_obj);
+        pop_selector;
+    } else if (scan_keyword("lastannot")) {
+        push_selector;
+        print_int(pdf_last_annot);
+        pop_selector;
+    } else if (scan_keyword("xformname")) {
+        scan_int();
+        check_obj_type(static_pdf, obj_type_xform, cur_val);
+        push_selector;
+        print_int(obj_info(static_pdf, cur_val));
+        pop_selector;
+    } else if (scan_keyword("creationdate")) {
+        ins_list(string_to_toks(getcreationdate(static_pdf)));
+        /* no further action */
+        return 2;
+    } else if (scan_keyword("fontname")) {
+        scan_font_ident();
+        if (cur_val == null_font)
+            normal_error("pdf backend", "invalid font identifier when asking 'fontname'");
+        pdf_check_vf(cur_val);
+        if (!font_used(cur_val))
+            pdf_init_font(static_pdf, cur_val);
+        push_selector;
+        set_ff(cur_val);
+        print_int(obj_info(static_pdf, pdf_font_num(ff)));
+        pop_selector;
+    } else if (scan_keyword("fontobjnum")) {
+        scan_font_ident();
+        if (cur_val == null_font)
+            normal_error("pdf backend", "invalid font identifier when asking 'objnum'");
+        pdf_check_vf(cur_val);
+        if (!font_used(cur_val))
+            pdf_init_font(static_pdf, cur_val);
+        push_selector;
+        set_ff(cur_val);
+        print_int(pdf_font_num(ff));
+        pop_selector;
+    } else if (scan_keyword("fontsize")) {
+        scan_font_ident();
+        if (cur_val == null_font)
+            normal_error("pdf backend", "invalid font identifier when asking 'fontsize'");
+        push_selector;
+        print_scaled(font_size(cur_val));
+        tprint("pt");
+        pop_selector;
+    } else if (scan_keyword("pageref")) {
+        scan_int();
+        if (cur_val <= 0)
+            normal_error("pdf backend", "invalid page number when asking 'pageref'");
+        push_selector;
+        print_int(pdf_get_obj(static_pdf, obj_type_page, cur_val, false));
+        pop_selector;
+    } else if (scan_keyword("colorstackinit")) {
+        bool = scan_keyword("page");
+        if (scan_keyword("direct"))
+            cur_val = direct_always;
+        else if (scan_keyword("page"))
+            cur_val = direct_page;
+        else if (scan_keyword("text"))
+            cur_val = direct_text;
+        else if (scan_keyword("raw"))
+            cur_val = direct_raw;
+        else if (scan_keyword("origin"))
+            cur_val = set_origin;
+        else
+            cur_val = set_origin;
+        save_scanner_status = scanner_status;
+        save_warning_index = warning_index;
+        save_def_ref = def_ref;
+        u = save_cur_string();
+        scan_toks(false, true);
+        s = tokens_to_string(def_ref);
+        delete_token_ref(def_ref);
+        def_ref = save_def_ref;
+        warning_index = save_warning_index;
+        scanner_status = save_scanner_status;
+        str = makecstring(s);
+        cur_val = newcolorstack(str, cur_val, bool);
+        free(str);
+        flush_str(s);
+        cur_val_level = int_val_level;
+        if (cur_val < 0) {
+            print_err("Too many color stacks");
+            help2("The number of color stacks is limited to 32768.",
+                  "I'll use the default color stack 0 here.");
+            error();
+            cur_val = 0;
+            restore_cur_string(u);
+        }
+        push_selector;
+        print_int(cur_val);
+        pop_selector;
+    } else if (scan_keyword("version")) {
+        push_selector;
+        print_int(pdftex_version);
+        pop_selector;
+    } else if (scan_keyword("revision")) {
+        ins_list(string_to_toks(pdftex_revision));
+        return 2;
+    } else {
+        return 0;
+    }
+    return 1;
+}
+
+void conv_toks(void)
+{
+    int old_setting;            /* holds |selector| setting */
+    halfword p, q;
+    int save_scanner_status;    /* |scanner_status| upon entry */
+    halfword save_def_ref;      /* |def_ref| upon entry, important if inside `\.{\\message}' */
+    halfword save_warning_index;
+    boolean bool;               /* temp boolean */
+    str_number s;               /* first temp string */
+    int sn;                     /* lua chunk name */
+    str_number u = 0;           /* third temp string, will become non-nil if a string is already being built */
+    int c = cur_chr;            /* desired type of conversion */
+    str_number str;
+    int i = 0;
+    /* Scan the argument for command |c| */
+    switch (c) {
+        case number_code:
+            scan_int();
+            push_selector;
+            print_int(cur_val);
+            pop_selector;
+            break;
+        case lua_function_code:
+            scan_int();
+            if (cur_val <= 0) {
+                normal_error("luafunction", "invalid number");
+            } else {
+                u = save_cur_string();
+                luacstrings = 0;
+                luafunctioncall(cur_val);
+                restore_cur_string(u);
+                if (luacstrings > 0)
+                    lua_string_start();
+            }
+            /* no further action */
+            return;
+            break;
+        case lua_code:
+            u = save_cur_string();
+            save_scanner_status = scanner_status;
+            save_def_ref = def_ref;
+            save_warning_index = warning_index;
+            sn = scan_lua_state();
+            scan_toks(false, true);
+            s = def_ref;
+            warning_index = save_warning_index;
+            def_ref = save_def_ref;
+            scanner_status = save_scanner_status;
+            luacstrings = 0;
+            luatokencall(s, sn);
+            delete_token_ref(s);
+            restore_cur_string(u);  /* TODO: check this, was different */
+            if (luacstrings > 0)
+                lua_string_start();
+            /* no further action */
+            return;
+            break;
+        case expanded_code:
+            save_scanner_status = scanner_status;
+            save_warning_index = warning_index;
+            save_def_ref = def_ref;
+            u = save_cur_string();
+            scan_toks(false, true);
+            warning_index = save_warning_index;
+            scanner_status = save_scanner_status;
+            ins_list(token_link(def_ref));
+            def_ref = save_def_ref;
+            restore_cur_string(u);
+            /* no further action */
+            return;
+            break;
+        case math_style_code:
+            push_selector;
+            print_math_style();
+            pop_selector;
+            break;
+        case string_code:
+            save_scanner_status = scanner_status;
+            scanner_status = normal;
+            get_token();
+            scanner_status = save_scanner_status;
+            push_selector;
+            if (cur_cs != 0)
+                sprint_cs(cur_cs);
+            else
+                print(cur_chr);
+            pop_selector;
+            break;
+        case cs_string_code:
+            save_scanner_status = scanner_status;
+            scanner_status = normal;
+            get_token();
+            scanner_status = save_scanner_status;
+            push_selector;
+            if (cur_cs != 0)
+                sprint_cs_name(cur_cs);
+            else
+                print(cur_chr);
+            pop_selector;
+            break;
+        case roman_numeral_code:
+            scan_int();
+            push_selector;
+            print_roman_int(cur_val);
+            pop_selector;
+            break;
+        case meaning_code:
+            save_scanner_status = scanner_status;
+            scanner_status = normal;
+            get_token();
+            scanner_status = save_scanner_status;
+            push_selector;
+            print_meaning();
+            pop_selector;
+            break;
+        case uchar_code:
+            scan_char_num();
+            push_selector;
+            print(cur_val);
+            pop_selector;
+            break;
+        case lua_escape_string_code:
+            {
+                lstring escstr;
+                int l = 0;
+                save_scanner_status = scanner_status;
+                save_def_ref = def_ref;
+                save_warning_index = warning_index;
+                scan_toks(false, true);
+                bool = in_lua_escape;
+                in_lua_escape = true;
+                escstr.s = (unsigned char *) tokenlist_to_cstring(def_ref, false, &l);
+                escstr.l = (unsigned) l;
+                in_lua_escape = bool;
+                delete_token_ref(def_ref);
+                def_ref = save_def_ref;
+                warning_index = save_warning_index;
+                scanner_status = save_scanner_status;
+                (void) lua_str_toks(escstr);
+                ins_list(token_link(temp_token_head));
+                free(escstr.s);
+                return;
+            }
+            /* no further action */
+            break;
+        case font_id_code:
+            scan_font_ident();
+            push_selector;
+            print_int(cur_val);
+            pop_selector;
+            break;
+        case font_name_code:
+            scan_font_ident();
+            push_selector;
+            append_string((unsigned char *) font_name(cur_val),(unsigned) strlen(font_name(cur_val)));
+            if (font_size(cur_val) != font_dsize(cur_val)) {
+                tprint(" at ");
+                print_scaled(font_size(cur_val));
+                tprint("pt");
+            }
+            pop_selector;
+            break;
+        case left_margin_kern_code:
+            scan_int();
+            if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node))
+                normal_error("marginkern", "a non-empty hbox expected");
+            push_selector;
+            p = list_ptr(box(cur_val));
+            while ((p != null) && (type(p) == glue_node)) {
+                p = vlink(p);
+            }
+            if ((p != null) && (type(p) == margin_kern_node) && (subtype(p) == left_side))
+                print_scaled(width(p));
+            else
+                print_char('0');
+            tprint("pt");
+            pop_selector;
+            break;
+        case right_margin_kern_code:
+            scan_int();
+            if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node))
+                normal_error("marginkern", "a non-empty hbox expected");
+            push_selector;
+            p = list_ptr(box(cur_val));
+            if (p != null) {
+                p = tail_of_list(p);
+                /*
+                    there can be a leftskip, rightskip, penalty and yes, also a disc node with a nesting
+                    node that points to glue spec ... and we don't want to analyze that messy lot
+                */
+                while ((p != null) && (type(p) == glue_node)) {
+                    p = alink(p);
+                }
+                if ((p != null) && ! ((type(p) == margin_kern_node) && (subtype(p) == right_side))) {
+                    if (type(p) == disc_node) {
+                        q = alink(p);
+                        if ((q != null) && ((type(q) == margin_kern_node) && (subtype(q) == right_side))) {
+                            p = q;
+                        } else {
+                            /*
+                                officially we should look in the replace but currently protrusion doesn't
+                                work anyway with "foo\discretionary{}{}{bar-} " (no following char) so we
+                                don't need it now
+                            */
+                        }
+                    }
+                }
+            }
+            if ((p != null) && (type(p) == margin_kern_node) && (subtype(p) == right_side))
+                print_scaled(width(p));
+            else
+                print_char('0');
+            tprint("pt");
+            pop_selector;
+            break;
+        case uniform_deviate_code:
+            scan_int();
+            push_selector;
+            print_int(unif_rand(cur_val));
+            pop_selector;
+            break;
+        case normal_deviate_code:
+            push_selector;
+            print_int(norm_rand());
+            pop_selector;
+            break;
+        case math_char_class_code:
+            {
+                mathcodeval mval;
+                scan_int();
+                mval = get_math_code(cur_val);
+                push_selector;
+                print_int(mval.class_value);
+                pop_selector;
+            }
+            break;
+        case math_char_fam_code:
+            {
+                mathcodeval mval;
+                scan_int();
+                mval = get_math_code(cur_val);
+                push_selector;
+                print_int(mval.family_value);
+                pop_selector;
+            }
+            break;
+        case math_char_slot_code:
+            {
+                mathcodeval mval;
+                scan_int();
+                mval = get_math_code(cur_val);
+                push_selector;
+                print_int(mval.character_value);
+                pop_selector;
+            }
+            break;
+        case insert_ht_code:
+            scan_register_num();
+            push_selector;
+            i = cur_val;
+            p = page_ins_head;
+            while (i >= subtype(vlink(p)))
+                p = vlink(p);
+            if (subtype(p) == i)
+                print_scaled(height(p));
+            else
+                print_char('0');
+            tprint("pt");
+            pop_selector;
+            break;
+        case job_name_code:
+            if (job_name == 0)
+                open_log_file();
+            push_selector;
+            print_job_name();
+            pop_selector;
+            break;
+        case format_name_code:
+            if (job_name == 0)
+                open_log_file();
+            push_selector;
+            print(format_name);
+            pop_selector;
+            break;
+        case luatex_banner_code:
+            push_selector;
+            tprint(luatex_banner);
+            pop_selector;
+            break;
+        case luatex_revision_code:
+            push_selector;
+            print(get_luatexrevision());
+            pop_selector;
+            break;
+        case etex_code:
+            push_selector;
+            tprint(eTeX_version_string);
+            pop_selector;
+            break;
+        case eTeX_revision_code:
+            push_selector;
+            tprint(eTeX_revision);
+            pop_selector;
+            break;
+        case font_identifier_code:
+            confusion("convert");
+            break;
+        default:
+            confusion("convert");
+            break;
+    }
+    str = make_string();
+    (void) str_toks(str_lstring(str));
+    flush_str(str);
+    ins_list(token_link(temp_token_head));
+}
+
+void do_feedback(void)
+{
+    int c = cur_chr;
+    str_number str;
+    int done = 1;
+    switch (c) {
+        case dvi_feedback_code:
+            if (get_o_mode() == OMODE_DVI) {
+                done = do_feedback_dvi(c);
+            } else {
+                tex_error("unexpected use of \\dvifeedback",null);
+                return ;
+            }
+            if (done==0) {
+                /* we recover */
+                normal_warning("dvi backend","unexpected use of \\dvifeedback");
+                return;
+            } else if (done==2) {
+                return;
+            }
+            break;
+        case pdf_feedback_code:
+            if (get_o_mode() == OMODE_PDF) {
+                done = do_feedback_pdf(c);
+            } else {
+                tex_error("unexpected use of \\pdffeedback",null);
+                return ;
+            }
+            if (done==0) {
+                /* we recover */
+                normal_warning("pdf backend","unexpected use of \\pdffeedback");
+                return;
+            } else if (done==2) {
+                return;
+            }
+            break;
+        default:
+            confusion("feedback");
+            break;
+    }
+    str = make_string();
+    (void) str_toks(str_lstring(str));
+    flush_str(str);
+    ins_list(token_link(temp_token_head));
+}
+
+void do_variable(void)
+{
+    int c = cur_chr;
+    int done = 1;
+    switch (c) {
+        case dvi_variable_code:
+            done = do_variable_dvi(c);
+            if (done==0) {
+                /* we recover */
+                normal_warning("dvi backend","unexpected use of \\dvivariable");
+            }
+            return;
+            break;
+        case pdf_variable_code:
+            done = do_variable_pdf(c);
+            if (done==0) {
+                /* we recover */
+                normal_warning("pdf backend","unexpected use of \\pdfvariable");
+            }
+            return;
+            break;
+        default:
+            confusion("variable");
+            break;
+    }
+}
+
+/*
+    The following code is not used as we can only set math options and not query them. If
+    an option is really important we will provide a proper variable. Most options are not
+    meant for users anyway but for development.
+*/
+
+/*
+
+#define do_mathoption_int(i) \
+    cur_cmd = assign_int_cmd; \
+    cur_val = mathoption_int_base + i; \
+    cur_tok = token_val(cur_cmd, cur_val); \
+    back_input();
+
+void do_mathoption(void)
+{
+         if (scan_keyword("old"))                    { do_mathoption_int(c_mathoption_no_italic_compensation_code); }
+         if (scan_keyword("noitaliccompensation"))   { do_mathoption_int(c_mathoption_no_char_italic_code); }
+    else if (scan_keyword("nocharitalic"))           { do_mathoption_int(c_mathoption_use_old_fraction_scaling_code); }
+    else if (scan_keyword("useoldfractionscaling"))  { do_mathoption_int(c_mathoption_old_code); }
+    else if (scan_keyword("umathcodemeaning"))       { do_mathoption_int(c_mathoption_umathcode_meaning_code); }
+}
+
+*/
+
+@ This boolean is keeping track of the lua string escape state
+@c
+boolean in_lua_escape;
+
+static int the_convert_string_dvi(halfword c, int i)
+{
+    return 0 ;
+}
+
+static int the_convert_string_pdf(halfword c, int i)
+{
+    int ff;
+    if (get_o_mode() != OMODE_PDF) {
+        return 0;
+    } else if (scan_keyword("lastlink")) {
+        print_int(pdf_last_link);
+    } else if (scan_keyword("retval")) {
+        print_int(pdf_retval);
+    } else if (scan_keyword("lastobj")) {
+        print_int(pdf_last_obj);
+    } else if (scan_keyword("lastannot")) {
+        print_int(pdf_last_annot);
+    } else if (scan_keyword("xformname")) {
+        print_int(obj_info(static_pdf, i));
+    } else if (scan_keyword("creationdate")) {
+        return 0;
+    } else if (scan_keyword("fontname")) {
+        set_ff(i);
+        print_int(obj_info(static_pdf, pdf_font_num(ff)));
+    } else if (scan_keyword("fontobjnum")) {
+        set_ff(i);
+        print_int(pdf_font_num(ff));
+    } else if (scan_keyword("fontsize")) {
+        print_scaled(font_size(i));
+        tprint("pt");
+    } else if (scan_keyword("pageref")) {
+        print_int(pdf_get_obj(static_pdf, obj_type_page, i, false));
+    } else if (scan_keyword("colorstackinit")) {
+        return 0;
+    } else {
+        return 0;
+    }
+    return 1;
+}
+
+str_number the_convert_string(halfword c, int i)
+{
+    int old_setting;            /* saved |selector| setting */
+    str_number ret = 0;
+    boolean done = true ;
+    old_setting = selector;
+    selector = new_string;
+    switch (c) {
+        case number_code:
+            print_int(i);
+            break;
+     /* case lua_function_code: */
+     /* case lua_code: */
+     /* case expanded_code: */
+        case math_style_code:
+            print_math_style();
+            break;
+     /* case string_code: */
+     /* case cs_string_code: */
+        case roman_numeral_code:
+            print_roman_int(i);
+            break;
+     /* case meaning_code: */
+        case uchar_code:
+            print(i);
+            break;
+     /* lua_escape_string_code: */
+        case font_id_code:
+            print_int(i);
+            break;
+        case font_name_code:
+            append_string((unsigned char *) font_name(i),(unsigned) strlen(font_name(i)));
+            if (font_size(i) != font_dsize(i)) {
+                tprint(" at ");
+                print_scaled(font_size(i));
+                tprint("pt");
+            }
+            break;
+     /* left_margin_kern_code: */
+     /* right_margin_kern_code: */
+        case uniform_deviate_code:
+            print_int(unif_rand(i));
+            break;
+        case normal_deviate_code:
+            print_int(norm_rand());
+            break;
+     /* math_char_class_code: */
+     /* math_char_fam_code: */
+     /* math_char_slot_code: */
+     /* insert_ht_code: */
+        case job_name_code:
+            print_job_name();
+            break;
+        case format_name_code:
+            print(format_name);
+            break;
+        case luatex_banner_code:
+            tprint(luatex_banner);
+            break;
+        case luatex_revision_code:
+            print(get_luatexrevision());
+            break;
+        case etex_code:
+            tprint(eTeX_version_string);
+            break;
+        case eTeX_revision_code:
+            tprint(eTeX_revision);
+            break;
+        case font_identifier_code:
+            print_font_identifier(i);
+            break;
+        /* backend: this might become obsolete */
+        case dvi_feedback_code:
+            done = the_convert_string_dvi(c,i);
+            break;
+        case pdf_feedback_code:
+            done = the_convert_string_pdf(c,i);
+            break;
+        /* done */
+        default:
+            done = false;
+            break;
+    }
+    if (done) {
+        ret = make_string();
+    }
+    selector = old_setting;
+    return ret;
+}
+
+@ Another way to create a token list is via the \.{\\read} command. The sixteen
+files potentially usable for reading appear in the following global variables.
+The value of |read_open[n]| will be |closed| if stream number |n| has not been
+opened or if it has been fully read; |just_open| if an \.{\\openin} but not a
+\.{\\read} has been done; and |normal| if it is open and ready to read the next
+line.
+
+@c
+FILE *read_file[16]; /* used for \.{\\read} */
+int read_open[17];   /* state of |read_file[n]| */
+
+void initialize_read(void)
+{
+    int k;
+    for (k = 0; k <= 16; k++)
+        read_open[k] = closed;
+}
+
+@ The |read_toks| procedure constructs a token list like that for any macro
+definition, and makes |cur_val| point to it. Parameter |r| points to the control
+sequence that will receive this token list.
+
+@c
+void read_toks(int n, halfword r, halfword j)
+{
+    halfword p; /* tail of the token list */
+    halfword q; /* new node being added to the token list via |store_new_token| */
+    int s;      /* saved value of |align_state| */
+    int m;      /* stream number */
+    scanner_status = defining;
+    warning_index = r;
+    p = get_avail();
+    def_ref = p;
+    set_token_ref_count(def_ref, 0);
+    p = def_ref;                /* the reference count */
+    store_new_token(end_match_token);
+    if ((n < 0) || (n > 15))
+        m = 16;
+    else
+        m = n;
+    s = align_state;
+    align_state = 1000000;      /* disable tab marks, etc. */
+    do {
+        /* Input and store tokens from the next line of the file */
+        begin_file_reading();
+        iname = m + 1;
+        if (read_open[m] == closed) {
+            /*
+                Input for \.{\\read} from the terminal
+
+                Here we input on-line into the |buffer| array, prompting the user explicitly
+                if |n>=0|.  The value of |n| is set negative so that additional prompts
+                will not be given in the case of multi-line input.
+            */
+            if (interaction > nonstop_mode) {
+                if (n < 0) {
+                    prompt_input("");
+                } else {
+                    wake_up_terminal();
+                    print_ln();
+                    sprint_cs(r);
+                    prompt_input(" =");
+                    n = -1;
+                }
+            } else {
+                fatal_error
+                    ("*** (cannot \\read from terminal in nonstop modes)");
+            }
+
+        } else if (read_open[m] == just_open) {
+            /*
+                Input the first line of |read_file[m]|
+
+                The first line of a file must be treated specially, since |lua_input_ln|
+                must be told not to start with |get|.
+            */
+            if (lua_input_ln(read_file[m], (m + 1), false)) {
+                read_open[m] = normal;
+            } else {
+                lua_a_close_in(read_file[m], (m + 1));
+                read_open[m] = closed;
+            }
+
+        } else {
+            /*
+                Input the next line of |read_file[m]|
+
+                An empty line is appended at the end of a |read_file|.
+            */
+            if (!lua_input_ln(read_file[m], (m + 1), true)) {
+                lua_a_close_in(read_file[m], (m + 1));
+                read_open[m] = closed;
+                if (align_state != 1000000) {
+                    runaway();
+                    print_err("File ended within \\read");
+                    help1("This \\read has unbalanced braces.");
+                    align_state = 1000000;
+                    error();
+                }
+            }
+
+        }
+        ilimit = last;
+        if (end_line_char_inactive)
+            decr(ilimit);
+        else
+            buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+        first = ilimit + 1;
+        iloc = istart;
+        istate = new_line;
+        /* Handle \.{\\readline} and |goto done|; */
+        if (j == 1) {
+            while (iloc <= ilimit) {
+                /* current line not yet finished */
+                do_buffer_to_unichar(cur_chr, iloc);
+                if (cur_chr == ' ')
+                    cur_tok = space_token;
+                else
+                    cur_tok = cur_chr + other_token;
+                store_new_token(cur_tok);
+            }
+        } else {
+            while (1) {
+                get_token();
+                if (cur_tok == 0) {
+                    /* |cur_cmd=cur_chr=0| will occur at the end of the line */
+                    break;
+                }
+                if (align_state < 1000000) {
+                    /* unmatched `\.\}' aborts the line */
+                    do {
+                        get_token();
+                    } while (cur_tok != 0);
+                    align_state = 1000000;
+                    break;
+                }
+                store_new_token(cur_tok);
+            }
+        }
+        end_file_reading();
+
+    } while (align_state != 1000000);
+    cur_val = def_ref;
+    scanner_status = normal;
+    align_state = s;
+}
+
+@ return a string from tokens list
+
+@c
+str_number tokens_to_string(halfword p)
+{
+    int old_setting;
+    if (selector == new_string)
+        normal_error("tokens","tokens_to_string() called while selector = new_string");
+    old_setting = selector;
+    selector = new_string;
+    show_token_list(token_link(p), null, -1);
+    selector = old_setting;
+    return make_string();
+}
+
+@ @c
+#define make_room(a)                     \
+    if ((unsigned)i+a+1>alloci) {        \
+        ret = xrealloc(ret,(alloci+64)); \
+        alloci = alloci + 64;            \
+    }
+
+#define append_i_byte(a) ret[i++] = (char)(a)
+
+#define Print_char(a) make_room(1); append_i_byte(a)
+
+#define Print_uchar(s) {                                       \
+    make_room(4);                                              \
+    if (s<=0x7F) {                                             \
+      append_i_byte(s);                                        \
+    } else if (s<=0x7FF) {                                     \
+      append_i_byte(0xC0 + (s / 0x40));                        \
+      append_i_byte(0x80 + (s % 0x40));                        \
+    } else if (s<=0xFFFF) {                                    \
+      append_i_byte(0xE0 + (s / 0x1000));                      \
+      append_i_byte(0x80 + ((s % 0x1000) / 0x40));             \
+      append_i_byte(0x80 + ((s % 0x1000) % 0x40));             \
+    } else if (s>=0x110000) {                                  \
+      append_i_byte(s-0x11000);                                \
+    } else {                                                   \
+      append_i_byte(0xF0 + (s / 0x40000));                     \
+      append_i_byte(0x80 + ((s % 0x40000) / 0x1000));          \
+      append_i_byte(0x80 + (((s % 0x40000) % 0x1000) / 0x40)); \
+      append_i_byte(0x80 + (((s % 0x40000) % 0x1000) % 0x40)); \
+    } }
+
+#define Print_esc(b) {                     \
+    const char *v = b;                     \
+    if (e>0 && e<STRING_OFFSET) {          \
+        Print_uchar (e);                   \
+    }                                      \
+    make_room(strlen(v));                  \
+    while (*v) { append_i_byte(*v); v++; } \
+  }
+
+#define Print_str(b) {                     \
+    const char *v = b;                     \
+    make_room(strlen(v));                  \
+    while (*v) { append_i_byte(*v); v++; } \
+  }
+
+#define is_cat_letter(a) \
+    (get_char_cat_code(pool_to_unichar(str_string((a)))) == 11)
+
+@ the actual token conversion in this function is now functionally equivalent to
+|show_token_list|, except that it always prints the whole token list. TODO: check
+whether this causes problems in the lua library.
+
+@c
+char *tokenlist_to_cstring(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;
+    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) {
+            Print_esc("CLOBBERED.");
+            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) {
+                    if (q == null_cs) {
+                        Print_esc("csname");
+                        Print_esc("endcsname");
+                    } else {
+                        Print_esc("IMPOSSIBLE.");
+                    }
+                } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) {
+                    Print_esc("IMPOSSIBLE.");
+                } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) {
+                    Print_esc("NONEXISTENT.");
+                } else {
+                    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) {
+                Print_esc("BAD");
+            } 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:
+                        Print_uchar(c);
+                        break;
+                    case mac_param_cmd:
+                        if (!in_lua_escape && (is_in_csname==0))
+                            Print_uchar(c);
+                        Print_uchar(c);
+                        break;
+                    case out_param_cmd:
+                        Print_uchar(match_chr);
+                        if (c <= 9) {
+                            Print_char(c + '0');
+                        } else {
+                            Print_char('!');
+                            goto EXIT;
+                        }
+                        break;
+                    case match_cmd:
+                        match_chr = c;
+                        Print_uchar(c);
+                        n++;
+                        Print_char(n);
+                        if (n > '9')
+                            goto EXIT;
+                        break;
+                    case end_match_cmd:
+                        if (c == 0) {
+                            Print_char('-');
+                            Print_char('>');
+                        }
+                        break;
+                    default:
+                        not_so_bad(Print_esc);
+                        break;
+                }
+            }
+        }
+        p = token_link(p);
+    }
+  EXIT:
+    ret[i] = '\0';
+    if (siz != NULL)
+        *siz = i;
+    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)
+{
+    int siz;
+    lstring *ret = xmalloc(sizeof(lstring));
+    ret->s = (unsigned char *) tokenlist_to_cstring(pp, inhibit_par, &siz);
+    ret->l = (size_t) siz;
+    return ret;
+}
+
+@ @c
+void free_lstring(lstring * ls)
+{
+    if (ls == NULL)
+        return;
+    if (ls->s != NULL)
+        free(ls->s);
+    free(ls);
+}
--- texlive-bin.orig/texk/web2c/luatexdir/tex/textoken.c
+++ /dev/null
@@ -1,3916 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-#include "ptexlib.h"
-
-#define detokenized_line() (line_catcode_table==NO_CAT_TABLE)
-
-#define do_get_cat_code(a,b) do { \
-    if (line_catcode_table==DEFAULT_CAT_TABLE) \
-      a=get_cat_code(cat_code_table_par,b); \
-    else if (line_catcode_table>-0xFF) \
-      a=get_cat_code(line_catcode_table,b); \
-    else \
-      a= - line_catcode_table - 0xFF ; \
-  } while (0)
-
-
-/*tex
-
-    The \TeX\ system does nearly all of its own memory allocation, so that it can
-    readily be transported into environments that do not have automatic
-    facilities for strings, garbage collection, etc., and so that it can be in
-    control of what error messages the user receives. The dynamic storage
-    requirements of \TeX\ are handled by providing two large arrays called
-    |fixmem| and |varmem| in which consecutive blocks of words are used as nodes
-    by the \TeX\ routines.
-
-    Pointer variables are indices into this array, or into another array called
-    |eqtb| that will be explained later. A pointer variable might also be a
-    special flag that lies outside the bounds of |mem|, so we allow pointers to
-    assume any |halfword| value. The minimum halfword value represents a null
-    pointer. \TeX\ does not assume that |mem[null]| exists.
-
-    Locations in |fixmem| are used for storing one-word records; a conventional
-    \.{AVAIL} stack is used for allocation in this array.
-
-*/
-
-/*tex the big dynamic storage area */
-
-smemory_word *fixmem;
-
-/*tex the smallest location of one-word memory in use */
-
-unsigned fix_mem_min;
-
-/*tex the largest location of one-word memory in use */
-
-unsigned fix_mem_max;
-
-/*tex
-
-    In order to study the memory requirements of particular applications, it is
-    possible to prepare a version of \TeX\ that keeps track of current and
-    maximum memory usage. When code between the delimiters |stat| $\ldots$
-    |tats| is not commented out, \TeX\ will run a bit slower but it will report
-    these statistics when |tracing_stats| is sufficiently large.
-
-*/
-
-/*tex how much memory is in use */
-
-int var_used, dyn_used;
-
-/*tex head of the list of available one-word nodes */
-
-halfword avail;
-
-/*tex the last one-word node used in |mem| */
-
-unsigned fix_mem_end;
-
-/*tex head of a junk list, write only */
-
-halfword garbage;
-
-/*tex head of a temporary list of some kind */
-
-halfword temp_token_head;
-
-/*tex head of a temporary list of another kind */
-
-halfword hold_token_head;
-
-/*tex a constant token list */
-
-halfword omit_template;
-
-/*tex permanently empty list */
-
-halfword null_list;
-
-/*tex head of token list built by |scan_keyword| */
-
-halfword backup_head;
-
-void initialize_tokens(void)
-{
-    halfword p;
-    avail = null;
-    fix_mem_end = 0;
-    p = get_avail();
-    temp_token_head = p;
-    set_token_info(temp_token_head, 0);
-    p = get_avail();
-    hold_token_head = p;
-    set_token_info(hold_token_head, 0);
-    p = get_avail();
-    omit_template = p;
-    set_token_info(omit_template, 0);
-    p = get_avail();
-    null_list = p;
-    set_token_info(null_list, 0);
-    p = get_avail();
-    backup_head = p;
-    set_token_info(backup_head, 0);
-    p = get_avail();
-    garbage = p;
-    set_token_info(garbage, 0);
-    dyn_used = 0;
-}
-
-/*tex
-
-    The function |get_avail| returns a pointer to a new one-word node whose
-    |link| field is null. However, \TeX\ will halt if there is no more room left.
-
-    If the available-space list is empty, i.e., if |avail=null|, we try first to
-    increase |fix_mem_end|. If that cannot be done, i.e., if
-    |fix_mem_end=fix_mem_max|, we try to reallocate array |fixmem|. If, that
-    doesn't work, we have to quit.
-
-    Single-word node allocation:
-*/
-
-halfword get_avail(void)
-{
-    /*tex The new node being got: */
-    unsigned p;
-    unsigned t;
-    /*tex Get top location in the |avail| stack. */
-    p = (unsigned) avail;
-    if (p != null) {
-        /*tex Pop it off. */
-        avail = token_link(avail);
-    } else if (fix_mem_end < fix_mem_max) {
-        /*tex Go into virgin territory. */
-        incr(fix_mem_end);
-        p = fix_mem_end;
-    } else {
-        /*tex The big dynamic storage area. */
-        smemory_word *new_fixmem;
-        t = (fix_mem_max / 5);
-        new_fixmem = fixmemcast(realloc(fixmem, sizeof(smemory_word) * (fix_mem_max + t + 1)));
-        if (new_fixmem == NULL) {
-            /*tex If memory is exhausted, display possible runaway text. */
-            runaway();
-            overflow("token memory size", fix_mem_max);
-        } else {
-            fixmem = new_fixmem;
-        }
-        memset(voidcast(fixmem + fix_mem_max + 1), 0, t * sizeof(smemory_word));
-        fix_mem_max += t;
-        p = ++fix_mem_end;
-    }
-    /*tex Provide an oft-desired initialization of the new node. */
-    token_link(p) = null;
-    /*tex Maintain statistics. */
-    incr(dyn_used);
-    return (halfword) p;
-}
-
-/*tex
-
-    The procedure |flush_list(p)| frees an entire linked list of one-word nodes
-    that starts at position |p|.
-
-    This makes list of single-word nodes available:
-
-*/
-
-void flush_list(halfword p)
-{
-    halfword q, r;
-    if (p != null) {
-        r = p;
-        do {
-            q = r;
-            r = token_link(r);
-            decr(dyn_used);
-        } while (r != null);
-        /*tex Now |q| is the last node on the list. */
-        token_link(q) = avail;
-        avail = p;
-    }
-}
-
-/*tex
-
-    A \TeX\ token is either a character or a control sequence, and it is
-    represented internally in one of two ways: (1)~A character whose ASCII code
-    number is |c| and whose command code is |m| is represented as the number
-    $2^{21}m+c$; the command code is in the range |1<=m<=14|. (2)~A control
-    sequence whose |eqtb| address is |p| is represented as the number
-    |cs_token_flag+p|. Here |cs_token_flag=t=| $2^{25}-1$ is larger than
-    $2^{21}m+c$, yet it is small enough that |cs_token_flag+p< max_halfword|;
-    thus, a token fits comfortably in a halfword.
-
-    A token |t| represents a |left_brace| command if and only if
-    |t<left_brace_limit|; it represents a |right_brace| command if and only if we
-    have |left_brace_limit<=t<right_brace_limit|; and it represents a |match| or
-    |end_match| command if and only if |match_token<=t<=end_match_token|. The
-    following definitions take care of these token-oriented constants and a few
-    others.
-
-    A token list is a singly linked list of one-word nodes in |mem|, where each
-    word contains a token and a link. Macro definitions, output-routine
-    definitions, marks, \.{\\write} texts, and a few other things are remembered
-    by \TeX\ in the form of token lists, usually preceded by a node with a
-    reference count in its |token_ref_count| field. The token stored in location
-    |p| is called |info(p)|.
-
-    Three special commands appear in the token lists of macro definitions. When
-    |m=match|, it means that \TeX\ should scan a parameter for the current macro;
-    when |m=end_match|, it means that parameter matching should end and \TeX\
-    should start reading the macro text; and when |m=out_param|, it means that
-    \TeX\ should insert parameter number |c| into the text at this point.
-
-    The enclosing \.{\char'173} and \.{\char'175} characters of a macro
-    definition are omitted, but the final right brace of an output routine is
-    included at the end of its token list.
-
-    Here is an example macro definition that illustrates these conventions. After
-    \TeX\ processes the text
-
-    \starttyping
-    \def\mac a#1#2 \b {#1\-a ##1#2 \#2\}
-    \stoptyping
-
-    the definition of \.{\\mac} is represented as a token list containing
-
-    \starttyping
-    (reference count) |letter|a |match|# |match|# |spacer| \b |end_match|
-    |out_param|1 \- |letter|a |spacer|, |mac_param|# |other_char|1
-    |out_param|2 |spacer| |out_param|2
-    \stoptyping
-
-    The procedure |scan_toks| builds such token lists, and |macro_call| does the
-    parameter matching.
-
-    Examples such as \type{\def\m{\def\m{a} b}} explain why reference
-    counts would be needed even if \TeX\ had no \.{\\let} operation: When the
-    token list for \.{\\m} is being read, the redefinition of \.{\\m} changes the
-    |eqtb| entry before the token list has been fully consumed, so we dare not
-    simply destroy a token list when its control sequence is being redefined.
-
-    If the parameter-matching part of a definition ends with `\.{\#\{}', the
-    corresponding token list will have `\.\{' just before the `|end_match|' and
-    also at the very end. The first `\.\{' is used to delimit the parameter; the
-    second one keeps the first from disappearing.
-
-    The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in symbolic
-    form, including the expansion of a macro or mark.
-
-*/
-
-void print_meaning(void)
-{
-    /*tex
-
-    This would make sense but some macro packages don't like it:
-
-    \starttyping
-    if (cur_cmd == math_given_cmd) {
-        cur_cmd = xmath_given_cmd ;
-    }
-    \stoptyping
-
-    */
-    print_cmd_chr((quarterword) cur_cmd, cur_chr);
-    if (cur_cmd >= call_cmd) {
-        print_char(':');
-        print_ln();
-        token_show(cur_chr);
-    } else {
-        /*tex Show the meaning of a mark node. */
-        if ((cur_cmd == top_bot_mark_cmd) && (cur_chr < marks_code)) {
-            print_char(':');
-            print_ln();
-            switch (cur_chr) {
-                case first_mark_code:
-                    token_show(first_mark(0));
-                    break;
-                case bot_mark_code:
-                    token_show(bot_mark(0));
-                    break;
-                case split_first_mark_code:
-                    token_show(split_first_mark(0));
-                    break;
-                case split_bot_mark_code:
-                    token_show(split_bot_mark(0));
-                    break;
-                default:
-                    token_show(top_mark(0));
-                    break;
-            }
-        }
-    }
-}
-
-/*tex
-
-    The procedure |show_token_list|, which prints a symbolic form of the token
-    list that starts at a given node |p|, illustrates these conventions. The
-    token list being displayed should not begin with a reference count. However,
-    the procedure is intended to be robust, so that if the memory links are awry
-    or if |p| is not really a pointer to a token list, nothing catastrophic will
-    happen.
-
-    An additional parameter |q| is also given; this parameter is either null or
-    it points to a node in the token list where a certain magic computation takes
-    place that will be explained later. (Basically, |q| is non-null when we are
-    printing the two-line context information at the time of an error message;
-    |q| marks the place corresponding to where the second line should begin.)
-
-    For example, if |p| points to the node containing the first \.a in the token
-    list above, then |show_token_list| will print the string $$\hbox{`\.{a\#1\#2\
-    \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$ and if |q| points to the node containing
-    the second \.a, the magic computation will be performed just before the
-    second \.a is printed.
-
-    The generation will stop, and `\.{\\ETC.}' will be printed, if the length of
-    printing exceeds a given limit~|l|. Anomalous entries are printed in the form
-    of control sequences that are not followed by a blank space, e.g.,
-    `\.{\\BAD.}'; this cannot be confused with actual control sequences because a
-    real control sequence named \.{BAD} would come out `\.{\\BAD\ }'.
-
-*/
-
-#define not_so_bad(p) \
-    switch (m) { \
-        case assign_int_cmd: \
-            if (c >= (backend_int_base) && c <= (backend_int_last)) \
-                p("[internal backend integer]"); \
-            break; \
-        case assign_dimen_cmd: \
-            if (c >= (backend_dimen_base) && c <= (backend_dimen_last)) \
-                p("[internal backend dimension]"); \
-            break; \
-        case assign_toks_cmd: \
-            if (c >= (backend_toks_base) && c <= (backend_toks_last)) \
-                p("[internal backend tokenlist]"); \
-            break; \
-        case node_cmd: \
-            p("[internal node pointer]"); \
-            break; \
-        case lua_call_cmd: \
-            p("[internal lua function call]"); \
-            break; \
-        case lua_expandable_call_cmd: \
-            p("[internal expandable lua function call]"); \
-            break; \
-        case lua_local_call_cmd: \
-            p("[internal local lua function call]"); \
-            break; \
-        default: \
-            p("BAD"); \
-            break; \
-    }
-
-void show_token_list(int p, int q, int l)
-{
-    /*tex pieces of a token */
-    int m, c;
-    /*tex character used in a `|match|' */
-    ASCII_code match_chr = '#';
-    /*tex the highest parameter number, as an ASCII digit */
-    ASCII_code n = '0';
-    tally = 0;
-    if (l < 0)
-        l = 0x3FFFFFFF;
-    while ((p != null) && (tally < l)) {
-        if (p == q) {
-            /*tex Do magic computation. */
-            set_trick_count();
-        }
-        /*tex Display token |p|, and |return| if there are problems. */
-        if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) {
-            tprint_esc("CLOBBERED.");
-            return;
-        }
-        if (token_info(p) >= cs_token_flag) {
-            if (!((inhibit_par_tokens) && (token_info(p) == par_token)))
-                print_cs(token_info(p) - cs_token_flag);
-        } else {
-            m = token_cmd(token_info(p));
-            c = token_chr(token_info(p));
-            if (token_info(p) < 0) {
-                tprint_esc("BAD");
-            } else {
-                /*
-                    Display the token \type {(|m|,|c|)}. The procedure usually
-                    ``learns'' the character code used for macro parameters by
-                    seeing one in a |match| command before it runs into any
-                    |out_param| commands.
-
-                */
-                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:
-                        print(c);
-                        break;
-                    case mac_param_cmd:
-                        if (!in_lua_escape && (is_in_csname==0))
-                            print(c);
-                        print(c);
-                        break;
-                    case out_param_cmd:
-                        print(match_chr);
-                        if (c <= 9) {
-                            print_char(c + '0');
-                        } else {
-                            print_char('!');
-                            return;
-                        }
-                        break;
-                    case match_cmd:
-                        match_chr = c;
-                        print(c);
-                        incr(n);
-                        print_char(n);
-                        if (n > '9')
-                            return;
-                        break;
-                    case end_match_cmd:
-                        if (c == 0)
-                            tprint("->");
-                        break;
-                    default:
-                        not_so_bad(tprint);
-                        break;
-                }
-            }
-        }
-        p = token_link(p);
-    }
-    if (p != null)
-        tprint_esc("ETC.");
-}
-
-#define do_buffer_to_unichar(a,b) do { \
-    a = (halfword)str2uni(buffer+b); \
-    b += utf8_size(a); \
-} while (0)
-
-/*tex
-
-    Here's the way we sometimes want to display a token list, given a pointer to
-    its reference count; the pointer may be null.
-
-*/
-
-void token_show(halfword p)
-{
-    if (p != null)
-        show_token_list(token_link(p), null, 10000000);
-}
-
-/*tex
-
-    |delete_token_ref|, is called when a pointer to a token list's reference
-    count is being removed. This means that the token list should disappear if
-    the reference count was |null|, otherwise the count should be decreased by
-    one.
-
-    |p| points to the reference count of a token list that is losing one
-    reference.
-
-*/
-
-void delete_token_ref(halfword p)
-{
-    if (token_ref_count(p) == 0)
-        flush_list(p);
-    else
-        decr(token_ref_count(p));
-}
-
-int get_char_cat_code(int curchr)
-{
-    int a;
-    do_get_cat_code(a,curchr);
-    return a;
-}
-
-static void invalid_character_error(void)
-{
-    const char *hlp[] = {
-        "A funny symbol that I can't read has just been input.",
-        "Continue, and I'll forget that it ever happened.",
-        NULL
-    };
-    deletions_allowed = false;
-    tex_error("Text line contains an invalid character", hlp);
-    deletions_allowed = true;
-}
-
-static boolean process_sup_mark(void);
-
-static int scan_control_sequence(void);
-
-typedef enum {
-    next_line_ok,
-    next_line_return,
-    next_line_restart
-} next_line_retval;
-
-static next_line_retval next_line(void);
-
-/*tex
-
-    In case you are getting bored, here is a slightly less trivial routine: Given
-    a string of lowercase letters, like `\.{pt}' or `\.{plus}' or `\.{width}',
-    the |scan_keyword| routine checks to see whether the next tokens of input
-    match this string. The match must be exact, except that uppercase letters
-    will match their lowercase counterparts; uppercase equivalents are determined
-    by subtracting |"a"-"A"|, rather than using the |uc_code| table, since \TeX\
-    uses this routine only for its own limited set of keywords.
-
-    If a match is found, the characters are effectively removed from the input
-    and |true| is returned. Otherwise |false| is returned, and the input is left
-    essentially unchanged (except for the fact that some macros may have been
-    expanded, etc.).
-
-*/
-
-boolean scan_keyword(const char *s)
-{
-    /*tex tail of the backup list */
-    halfword p;
-    /*tex new node being added to the token list via |store_new_token| */
-    halfword q;
-    /*tex index into |str_pool| */
-    const char *k;
-    halfword save_cur_cs = cur_cs;
-    if (strlen(s) == 0) {
-        /*tex but not with newtokenlib zero keyword simply doesn't match  */
-        return false ;
-    }
-    p = backup_head;
-    token_link(p) = null;
-    k = s;
-    while (*k) {
-        /*tex Recursion is possible here! */
-        get_x_token();
-        if ((cur_cs == 0) && ((cur_chr == *k) || (cur_chr == *k - 'a' + 'A'))) {
-            store_new_token(cur_tok);
-            k++;
-        } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) {
-            back_input();
-            if (p != backup_head) {
-                begin_token_list(token_link(backup_head), backed_up);
-            }
-            cur_cs = save_cur_cs;
-            return false;
-        }
-    }
-    if (token_link(backup_head) != null)
-        flush_list(token_link(backup_head));
-    cur_cs = save_cur_cs;
-    return true;
-}
-
-boolean scan_keyword_case_sensitive(const char *s)
-{
-    halfword p;
-    halfword q;
-    const char *k;
-    halfword save_cur_cs = cur_cs;
-    if (strlen(s) == 0)
-        return false ;
-    p = backup_head;
-    token_link(p) = null;
-    k = s;
-    while (*k) {
-        get_x_token();
-        if ((cur_cs == 0) && (cur_chr == *k)) {
-            store_new_token(cur_tok);
-            k++;
-        } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) {
-            back_input();
-            if (p != backup_head) {
-                begin_token_list(token_link(backup_head), backed_up);
-            }
-            cur_cs = save_cur_cs;
-            return false;
-        }
-    }
-    if (token_link(backup_head) != null)
-        flush_list(token_link(backup_head));
-    cur_cs = save_cur_cs;
-    return true;
-}
-
-/*tex
-
-    We can not return |undefined_control_sequence| under some conditions (inside
-    |shift_case|, for example). This needs thinking.
-
-*/
-
-/*static char * FFFF = "\xEF\xBF\xBF";*/ /* 0xFFFF */
-
-halfword active_to_cs(int curchr, int force)
-{
-    halfword curcs;
-    int nncs = no_new_control_sequence;
-    if (force) {
-        no_new_control_sequence = false;
-    }
-    if (curchr > 0) {
-        char *b = (char *) uni2str((unsigned) curchr);
-        char *utfbytes = xmalloc(8);
-        utfbytes = strcpy(utfbytes, "\xEF\xBF\xBF");
-        utfbytes = strcat(utfbytes, b);
-        free(b);
-        curcs = string_lookup(utfbytes, utf8_size(curchr)+3);
-        free(utfbytes);
-    } else {
-        /*tex 0xFFFF ... why not 3 ? */
-        curcs = string_lookup("\xEF\xBF\xBF", 4);
-    }
-    no_new_control_sequence = nncs;
-    return curcs;
-}
-
-/*
-
-    static unsigned char *uni2csstr(unsigned unic)
-    {
-        unsigned char *buf = xmalloc(8);
-        unsigned char *pt = buf;
-        *pt++ = 239; *pt++ = 191; *pt++ = 191; // 0xFFFF
-        if (unic < 0x80)
-            *pt++ = (unsigned char) unic;
-        else if (unic < 0x800) {
-            *pt++ = (unsigned char) (0xc0 | (unic >> 6));
-            *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
-        } else if (unic >= 0x110000) {
-            *pt++ = (unsigned char) (unic - 0x110000);
-        } else if (unic < 0x10000) {
-            *pt++ = (unsigned char) (0xe0 | (unic >> 12));
-            *pt++ = (unsigned char) (0x80 | ((unic >> 6) & 0x3f));
-            *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
-        } else {
-            int u, z, y, x;
-            unsigned val = unic - 0x10000;
-            u = (int) (((val & 0xf0000) >> 16) + 1);
-            z = (int) ((val & 0x0f000) >> 12);
-            y = (int) ((val & 0x00fc0) >> 6);
-            x = (int) (val & 0x0003f);
-            *pt++ = (unsigned char) (0xf0 | (u >> 2));
-            *pt++ = (unsigned char) (0x80 | ((u & 3) << 4) | z);
-            *pt++ = (unsigned char) (0x80 | y);
-            *pt++ = (unsigned char) (0x80 | x);
-        }
-        *pt = '\0';
-        return buf;
-    }
-
-    halfword active_to_cs(int curchr, int force)
-    {
-        halfword curcs;
-        int nncs = no_new_control_sequence;
-        if (force) {
-            no_new_control_sequence = false;
-        }
-        if (curchr > 0) {
-            char * utfbytes = (char *) uni2csstr((unsigned) curchr);
-            curcs = string_lookup(utfbytes, utf8_size(curchr)+3);
-            free(utfbytes);
-        } else {
-            curcs = string_lookup(FFFF, 4); // 0xFFFF ... why not 3 ?
-        }
-        no_new_control_sequence = nncs;
-        return curcs;
-    }
-
-*/
-
-/*tex
-
-    Maybe this function should listen to \.{\\escapechar} but we can do without.
-
-*/
-
-static char *cs_to_string(halfword p)
-{
-    const char *s;
-    char *sh;
-    int k = 0;
-    static char ret[256] = { 0 };
-    if (p == 0 || p == null_cs) {
-        ret[k++] = '\\';
-        s = "csname";
-        while (*s) {
-            ret[k++] = *s++;
-        }
-        ret[k++] = '\\';
-        s = "endcsname";
-        while (*s) {
-            ret[k++] = *s++;
-        }
-        ret[k] = 0;
-
-    } else {
-        str_number txt = cs_text(p);
-        sh = makecstring(txt);
-        s = sh;
-        if (is_active_cs(txt)) {
-            s = s + 3;
-            while (*s) {
-                ret[k++] = *s++;
-            }
-            ret[k] = 0;
-        } else {
-            ret[k++] = '\\';
-            while (*s) {
-                ret[k++] = *s++;
-            }
-            ret[k] = 0;
-        }
-        free(sh);
-    }
-    return (char *) ret;
-}
-
-/*tex This is sort of a hack. */
-
-static char *cmd_chr_to_string(int cmd, int chr)
-{
-    char *s;
-    str_number str;
-    int sel = selector;
-    selector = new_string;
-    print_cmd_chr((quarterword) cmd, chr);
-    str = make_string();
-    s = makecstring(str);
-    selector = sel;
-    flush_str(str);
-    return s;
-}
-
-/*tex
-
-    The heart of \TeX's input mechanism is the |get_next| procedure, which we
-    shall develop in the next few sections of the program. Perhaps we shouldn't
-    actually call it the ``heart,'' however, because it really acts as \TeX's
-    eyes and mouth, reading the source files and gobbling them up. And it also
-    helps \TeX\ to regurgitate stored token lists that are to be processed again.
-
-    The main duty of |get_next| is to input one token and to set |cur_cmd| and
-    |cur_chr| to that token's command code and modifier. Furthermore, if the
-    input token is a control sequence, the |eqtb| location of that control
-    sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero.
-
-    Underlying this simple description is a certain amount of complexity because
-    of all the cases that need to be handled. However, the inner loop of
-    |get_next| is reasonably short and fast.
-
-    When |get_next| is asked to get the next token of a \.{\\read} line, it sets
-    |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens appear on that
-    line. (There might not be any tokens at all, if the |end_line_char| has
-    |ignore| as its catcode.)
-
-    The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity is
-    needed because a blank line of input is supposed to be exactly equivalent to
-    the appearance of \.{\\par}; we must set |cur_cs:=par_loc| when detecting a
-    blank line.
-
-*/
-
-/*tex
-
-    The location of `\.{\\par}' in |eqtb| adn the token representing `\.{\\par}.
-
-*/
-
-halfword par_loc;
-halfword par_token;
-
-/*tex
-
-    Parts |get_next| are executed more often than any other instructions of \TeX.
-
-    The global variable |force_eof| is normally |false|; it is set |true| by an
-    \.{\\endinput} command. |luacstrings| is the number of lua print statements
-    waiting to be input, it is changed by |luatokencall|.
-
-
-*/
-
-/*tex Should the next \.{\\input} be aborted early? */
-boolean force_eof;
-
-/*tex How many lua strings are waiting to be input? */
-
-int luacstrings;
-
-/*tex
-
-    If the user has set the |pausing| parameter to some positive value, and if
-    nonstop mode has not been selected, each line of input is displayed on the
-    terminal and the transcript file, followed by `\.{=>}'. \TeX\ waits for a
-    response. If the response is simply |carriage_return|, the line is accepted
-    as it stands, otherwise the line typed is used instead of the line in the
-    file.
-
-*/
-
-void firm_up_the_line(void)
-{
-    int k;
-    ilimit = last;
-    if (pausing_par > 0) {
-        if (interaction > nonstop_mode) {
-            wake_up_terminal();
-            print_ln();
-            if (istart < ilimit) {
-                for (k = istart; k <= ilimit - 1; k++)
-                    print_char(buffer[k]);
-            }
-            first = ilimit;
-            prompt_input("=>");
-            if (last > first) {
-                /*tex Move line down in buffer. */
-                for (k = first; k < +last - 1; k++)
-                    buffer[k + istart - first] = buffer[k];
-                ilimit = istart + last - first;
-            }
-        }
-    }
-}
-
-/*tex
-
-    Before getting into |get_next|, let's consider the subroutine that is called
-    when an `\.{\\outer}' control sequence has been scanned or when the end of a
-    file has been reached. These two cases are distinguished by |cur_cs|, which
-    is zero at the end of a file.
-
-*/
-
-void check_outer_validity(void)
-{
-    /*tex points to inserted token list */
-    halfword p;
-    /*tex auxiliary pointer */
-    halfword q;
-    if (suppress_outer_error_par)
-        return;
-    if (scanner_status != normal) {
-        deletions_allowed = false;
-        /*tex
-
-            Back up an outer control sequence so that it can be reread. An
-            outer control sequence that occurs in a \.{\\read} will not be
-            reread, since the error recovery for \.{\\read} is not very powerful.
-
-        */
-        if (cur_cs != 0) {
-            if ((istate == token_list) || (iname < 1) || (iname > 17)) {
-                p = get_avail();
-                token_info(p) = cs_token_flag + cur_cs;
-                /*tex prepare to read the control sequence again */
-                begin_token_list(p, backed_up);
-            }
-            /*tex replace it by a space */
-            cur_cmd = spacer_cmd;
-            cur_chr = ' ';
-        }
-        if (scanner_status > skipping) {
-            const char *errhlp[] = {
-                "I suspect you have forgotten a `}', causing me",
-                "to read past where you wanted me to stop.",
-                "I'll try to recover; but if the error is serious,",
-                "you'd better type `E' or `X' now and fix your file.",
-                NULL
-            };
-            char errmsg[318];
-            const char *startmsg;
-            const char *scannermsg;
-            /*tex
-
-                Tell the user what has run away and try to recover Print a
-                definition, argument, or preamble.
-
-            */
-            runaway();
-            if (cur_cs == 0) {
-                startmsg = "File ended";
-            } else {
-                cur_cs = 0;
-                startmsg = "Forbidden control sequence found";
-            }
-            /*tex
-
-                Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or
-                `\.{text}', and insert tokens that should lead to recovery. The
-                recovery procedure can't be fully understood without knowing more
-                about the \TeX\ routines that should be aborted, but we can
-                sketch the ideas here: For a runaway definition we will insert a
-                right brace; for a runaway preamble, we will insert a special
-                \.{\\cr} token and a right brace; and for a runaway argument, we
-                will set |long_state| to |outer_call| and insert \.{\\par}.
-
-            */
-            p = get_avail();
-            switch (scanner_status) {
-                case defining:
-                    scannermsg = "definition";
-                    token_info(p) = right_brace_token + '}';
-                    break;
-                case matching:
-                    scannermsg = "use";
-                    token_info(p) = par_token;
-                    long_state = outer_call_cmd;
-                    break;
-                case aligning:
-                    scannermsg = "preamble";
-                    token_info(p) = right_brace_token + '}';
-                    q = p;
-                    p = get_avail();
-                    token_link(p) = q;
-                    token_info(p) = cs_token_flag + frozen_cr;
-                    align_state = -1000000;
-                    break;
-                case absorbing:
-                    scannermsg = "text";
-                    token_info(p) = right_brace_token + '}';
-                    break;
-                default:
-                    scannermsg = "unknown";
-                    break;
-            }
-            begin_token_list(p, inserted);
-            snprintf(errmsg, 318, "%s while scanning %s of %s", startmsg, scannermsg, cs_to_string(warning_index));
-            tex_error(errmsg, errhlp);
-        } else {
-            char errmsg[256];
-            const char *errhlp_no[] = {
-                "The file ended while I was skipping conditional text.",
-                "This kind of error happens when you say `\\if...' and forget",
-                "the matching `\\fi'. I've inserted a `\\fi'; this might work.",
-                NULL
-            };
-            const char *errhlp_cs[] = {
-                "A forbidden control sequence occurred in skipped text.",
-                "This kind of error happens when you say `\\if...' and forget",
-                "the matching `\\fi'. I've inserted a `\\fi'; this might work.",
-                NULL
-            };
-            const char **errhlp = (const char **) errhlp_no;
-            char *ss;
-            if (cur_cs != 0) {
-                errhlp = errhlp_cs;
-                cur_cs = 0;
-            }
-            ss = cmd_chr_to_string(if_test_cmd, cur_if);
-            snprintf(errmsg, 255, "Incomplete %s; all text was ignored after line %d",
-                 ss, (int) skip_line);
-            free(ss);
-            /*tex Incomplete |\if...| */
-            cur_tok = cs_token_flag + frozen_fi;
-            /*tex back up one inserted token and call |error|. */
-            {
-                OK_to_interrupt = false;
-                back_input();
-                token_type = inserted;
-                OK_to_interrupt = true;
-                tex_error(errmsg, errhlp);
-            }
-        }
-        deletions_allowed = true;
-    }
-}
-
-#if 0
-
-/*tex
-
-    The other variant gives less clutter in tracing cache usage when profiling
-    and for some files (like the manual) also a bit of a speedup.
-
-*/
-
-static boolean get_next_file(void)
-{
-  SWITCH:
-    if (iloc <= ilimit) {
-        /*tex current line not yet finished */
-        do_buffer_to_unichar(cur_chr, iloc);
-      RESWITCH:
-        if (detokenized_line()) {
-            cur_cmd = (cur_chr == ' ' ? 10 : 12);
-        } else {
-            do_get_cat_code(cur_cmd, cur_chr);
-        }
-        /*tex
-
-            Change state if necessary, and |goto switch| if the current
-            character should be ignored, or |goto reswitch| if the current
-            character changes to another;
-
-            The following 48-way switch accomplishes the scanning quickly,
-            assuming that a decent C compiler has translated the code. Note that
-            the numeric values for |mid_line|, |skip_blanks|, and |new_line| are
-            spaced apart from each other by |max_char_code+1|, so we can add a
-            character's command code to the state to get a single number that
-            characterizes both.
-
-            Remark [ls/hh]: checking performance indicated that this switch was
-            the cause of many branch prediction errors but changing it to:
-
-            \starttyping
-            c = istate + cur_cmd;
-            if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) {
-                return true;
-            } else if (c >= new_line) {
-                switch (c) {
-                }
-            } else if (c >= skip_blanks) {
-                switch (c) {
-                }
-            } else if (c >= mid_line) {
-                switch (c) {
-                }
-            } else {
-                istate = mid_line;
-                return true;
-            }
-            \stoptyping
-
-            gives as many prediction errors. So, we can indeed assume that the
-            compiler does the right job, or that there is simply no other way.
-
-        */
-
-        switch (istate + cur_cmd) {
-            case mid_line + ignore_cmd:
-            case skip_blanks + ignore_cmd:
-            case new_line + ignore_cmd:
-            case skip_blanks + spacer_cmd:
-            case new_line + spacer_cmd:
-                /*tex Cases where character is ignored. */
-                goto SWITCH;
-                break;
-            case mid_line + escape_cmd:
-            case new_line + escape_cmd:
-            case skip_blanks + escape_cmd:
-                /*tex Scan a control sequence. */
-                istate = (unsigned char) scan_control_sequence();
-                if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                    check_outer_validity();
-                break;
-            case mid_line + active_char_cmd:
-            case new_line + active_char_cmd:
-            case skip_blanks + active_char_cmd:
-                /*tex Process an active-character.  */
-                cur_cs = active_to_cs(cur_chr, false);
-                cur_cmd = eq_type(cur_cs);
-                cur_chr = equiv(cur_cs);
-                istate = mid_line;
-                if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                    check_outer_validity();
-                break;
-            case mid_line + sup_mark_cmd:
-            case new_line + sup_mark_cmd:
-            case skip_blanks + sup_mark_cmd:
-                /*tex If this |sup_mark| starts. */
-                if (process_sup_mark())
-                    goto RESWITCH;
-                else
-                    istate = mid_line;
-                break;
-            case mid_line + invalid_char_cmd:
-            case new_line + invalid_char_cmd:
-            case skip_blanks + invalid_char_cmd:
-                /*tex Decry the invalid character and |goto restart|. */
-                invalid_character_error();
-                /*tex Because state may be |token_list| now: */
-                return false;
-                break;
-            case mid_line + spacer_cmd:
-                /*tex Enter |skip_blanks| state, emit a space. */
-                istate = skip_blanks;
-                cur_chr = ' ';
-                break;
-            case mid_line + car_ret_cmd:
-                /*tex
-
-                    Finish line, emit a space. When a character of type |spacer|
-                    gets through, its character code is changed to $\.{"\
-                    "}=040$. This means that the ASCII codes for tab and space,
-                    and for the space inserted at the end of a line, will be
-                    treated alike when macro parameters are being matched. We do
-                    this since such characters are indistinguishable on most
-                    computer terminal displays.
-
-                 */
-                iloc = ilimit + 1;
-                cur_cmd = spacer_cmd;
-                cur_chr = ' ';
-                break;
-            case skip_blanks + car_ret_cmd:
-            case mid_line + comment_cmd:
-            case new_line + comment_cmd:
-            case skip_blanks + comment_cmd:
-                /*tex Finish line, |goto switch|; */
-                iloc = ilimit + 1;
-                goto SWITCH;
-                break;
-            case new_line + car_ret_cmd:
-                /*tex Finish line, emit a \.{\\par}; */
-                iloc = ilimit + 1;
-                cur_cs = par_loc;
-                cur_cmd = eq_type(cur_cs);
-                cur_chr = equiv(cur_cs);
-                if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                    check_outer_validity();
-                break;
-            case skip_blanks + left_brace_cmd:
-            case new_line + left_brace_cmd:
-                istate = mid_line;
-                /*tex Fall through. */
-            case mid_line + left_brace_cmd:
-                align_state++;
-                break;
-            case skip_blanks + right_brace_cmd:
-            case new_line + right_brace_cmd:
-                istate = mid_line;
-                /*tex Fall through. */
-            case mid_line + right_brace_cmd:
-                align_state--;
-                break;
-            case mid_line + math_shift_cmd:
-            case mid_line + tab_mark_cmd:
-            case mid_line + mac_param_cmd:
-            case mid_line + sub_mark_cmd:
-            case mid_line + letter_cmd:
-            case mid_line + other_char_cmd:
-                break;
-            /*
-            case skip_blanks + math_shift:
-            case skip_blanks + tab_mark:
-            case skip_blanks + mac_param:
-            case skip_blanks + sub_mark:
-            case skip_blanks + letter:
-            case skip_blanks + other_char:
-            case new_line    + math_shift:
-            case new_line    + tab_mark:
-            case new_line    + mac_param:
-            case new_line    + sub_mark:
-            case new_line    + letter:
-            case new_line    + other_char:
-            */
-            default:
-                istate = mid_line;
-                break;
-        }
-    } else {
-        if (iname != 21)
-            istate = new_line;
-        /*tex
-
-           Move to next line of file, or |goto restart| if there is no next line,
-           or |return| if a \.{\\read} line has finished;
-
-        */
-        do {
-            next_line_retval r = next_line();
-            if (r == next_line_return) {
-                return true;
-            } else if (r == next_line_restart) {
-                return false;
-            }
-        } while (0);
-        check_interrupt();
-        goto SWITCH;
-    }
-    return true;
-}
-
-#else
-
-/*tex
-
-    This variant gives 10 times less Bim in vallgrind!
-
-*/
-
-static boolean get_next_file(void)
-{
-    int c = 0;
-  SWITCH:
-    if (iloc <= ilimit) {
-        /*tex current line not yet finished */
-        do_buffer_to_unichar(cur_chr, iloc);
-      RESWITCH:
-        if (detokenized_line()) {
-            cur_cmd = (cur_chr == ' ' ? 10 : 12);
-        } else {
-            do_get_cat_code(cur_cmd, cur_chr);
-        }
-        /*tex
-
-           Change state if necessary, and |goto switch| if the current character
-           should be ignored, or |goto reswitch| if the current character changes
-           to another.
-
-        */
-        c = istate + cur_cmd;
-        if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) {
-            return true;
-        } else if (c >= new_line) {
-            switch (c-new_line) {
-                case escape_cmd:
-                    istate = (unsigned char) scan_control_sequence();
-                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                        check_outer_validity();
-                    return true;
-                case left_brace_cmd:
-                    istate = mid_line;
-                    align_state++;
-                    return true;
-                case right_brace_cmd:
-                    istate = mid_line;
-                    align_state--;
-                    return true;
-                case math_shift_cmd:
-                    istate = mid_line;
-                    return true;
-                case tab_mark_cmd:
-                    istate = mid_line;
-                    return true;
-                case car_ret_cmd:
-                    /*tex Finish line, emit a \.{\\par}. */
-                    iloc = ilimit + 1;
-                    cur_cs = par_loc;
-                    cur_cmd = eq_type(cur_cs);
-                    cur_chr = equiv(cur_cs);
-                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                        check_outer_validity();
-                    return true;
-                case mac_param_cmd:
-                    istate = mid_line;
-                    return true;
-                case sup_mark_cmd:
-                    if (process_sup_mark())
-                        goto RESWITCH;
-                    else
-                        istate = mid_line;
-                    return true;
-                case sub_mark_cmd:
-                    istate = mid_line;
-                    return true;
-                case ignore_cmd:
-                    goto SWITCH;
-                    return true;
-                case spacer_cmd:
-                    /*tex Cases where character is ignored. */
-                    goto SWITCH;
-                case letter_cmd:
-                    istate = mid_line;
-                    return true;
-                case other_char_cmd:
-                    istate = mid_line;
-                    return true;
-                case active_char_cmd:
-                    cur_cs = active_to_cs(cur_chr, false);
-                    cur_cmd = eq_type(cur_cs);
-                    cur_chr = equiv(cur_cs);
-                    istate = mid_line;
-                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                        check_outer_validity();
-                    return true;
-                case comment_cmd:
-                    iloc = ilimit + 1;
-                    goto SWITCH;
-                case invalid_char_cmd:
-                    /*tex Because state may be |token_list| now. */
-                    invalid_character_error();
-                    return false;
-                default:
-                    istate = mid_line;
-                    return true;
-            }
-        } else if (c >= skip_blanks) {
-            switch (c-skip_blanks) {
-                case escape_cmd:
-                    /*tex Scan a control sequence. */
-                    istate = (unsigned char) scan_control_sequence();
-                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                        check_outer_validity();
-                    return true;
-                case left_brace_cmd:
-                    istate = mid_line;
-                    align_state++;
-                    return true;
-                case right_brace_cmd:
-                    istate = mid_line;
-                    align_state--;
-                    return true;
-                case math_shift_cmd:
-                    istate = mid_line;
-                    return true;
-                case tab_mark_cmd:
-                    istate = mid_line;
-                    return true;
-                case car_ret_cmd:
-                    iloc = ilimit + 1;
-                    goto SWITCH;
-                case mac_param_cmd:
-                    istate = mid_line;
-                    return true;
-                case sup_mark_cmd:
-                    if (process_sup_mark())
-                        goto RESWITCH;
-                    else
-                        istate = mid_line;
-                    return true;
-                case sub_mark_cmd:
-                    istate = mid_line;
-                    return true;
-                case ignore_cmd:
-                    goto SWITCH;
-                case spacer_cmd:
-                    goto SWITCH;
-                case letter_cmd:
-                    istate = mid_line;
-                    return true;
-                case other_char_cmd:
-                    istate = mid_line;
-                    return true;
-                case active_char_cmd:
-                    cur_cs = active_to_cs(cur_chr, false);
-                    cur_cmd = eq_type(cur_cs);
-                    cur_chr = equiv(cur_cs);
-                    istate = mid_line;
-                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                        check_outer_validity();
-                    return true;
-                case comment_cmd:
-                    /*tex Finish line, |goto switch|. */
-                    iloc = ilimit + 1;
-                    goto SWITCH;
-                case invalid_char_cmd:
-                    /*tex Decry the invalid character and |goto restart|. */
-                    invalid_character_error();
-                    /*tex Because state may be |token_list| now. */
-                    return false;
-                default:
-                    istate = mid_line;
-                    return true;
-            }
-        } else if (c >= mid_line) {
-            switch (c-mid_line) {
-                case escape_cmd:
-                    istate = (unsigned char) scan_control_sequence();
-                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                        check_outer_validity();
-                    return true;
-                case left_brace_cmd:
-                    align_state++;
-                    return true;
-                case right_brace_cmd:
-                    align_state--;
-                    return true;
-                case math_shift_cmd:
-                    return true;
-                case tab_mark_cmd:
-                    return true;
-                case car_ret_cmd:
-                    /*tex
-
-                        Finish line, emit a space. When a character of type
-                        |spacer| gets through, its character code is changed to
-                        $\.{"\ "}=040$. This means that the ASCII codes for tab
-                        and space, and for the space inserted at the end of a
-                        line, will be treated alike when macro parameters are
-                        being matched. We do this since such characters are
-                        indistinguishable on most computer terminal displays.
-
-                    */
-                    iloc = ilimit + 1;
-                    cur_cmd = spacer_cmd;
-                    cur_chr = ' ';
-                    return true;
-                case mac_param_cmd:
-                    return true;
-                case sup_mark_cmd:
-                    if (process_sup_mark())
-                        goto RESWITCH;
-                    else
-                        istate = mid_line;
-                    return true;
-                case sub_mark_cmd:
-                    return true;
-                case ignore_cmd:
-                    goto SWITCH;
-                case spacer_cmd:
-                    /*tex Enter |skip_blanks| state, emit a space. */
-                    istate = skip_blanks;
-                    cur_chr = ' ';
-                    return true;
-                case letter_cmd:
-                    istate = mid_line;
-                    return true;
-                case other_char_cmd:
-                    istate = mid_line;
-                    return true;
-                case active_char_cmd:
-                    cur_cs = active_to_cs(cur_chr, false);
-                    cur_cmd = eq_type(cur_cs);
-                    cur_chr = equiv(cur_cs);
-                    istate = mid_line;
-                    if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
-                        check_outer_validity();
-                    return true;
-                case comment_cmd:
-                    iloc = ilimit + 1;
-                    goto SWITCH;
-                case invalid_char_cmd:
-                    /*tex Because state may be |token_list| now. */
-                    invalid_character_error();
-                    return false;
-                default:
-                    istate = mid_line;
-                    return true;
-            }
-        } else {
-            istate = mid_line;
-            return true;
-        }
-    } else {
-        if (iname != 21) {
-            istate = new_line;
-        }
-        /*tex
-
-            Move to next line of file, or |goto restart| if there is no next
-            line, or |return| if a \.{\\read} line has finished;
-
-        */
-        do {
-            next_line_retval r = next_line();
-            if (r == next_line_return) {
-                return true;
-            } else if (r == next_line_restart) {
-                return false;
-            }
-        } while (0);
-        check_interrupt();
-        goto SWITCH;
-    }
-    return true;
-}
-
-#endif
-
-/*tex
-
-    Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
-    We only support a limited set:
-
-    \starttyping
-    ^^^^^^XXXXXX
-    ^^^^XXXXXX
-    ^^XX ^^<char>
-    \stoptyping
-
-*/
-
-#define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f'))
-
-#define add_nybble(c) \
-    if (c<='9') { \
-        cur_chr=(cur_chr<<4)+c-'0'; \
-    } else { \
-        cur_chr=(cur_chr<<4)+c-'a'+10; \
-    }
-
-#define set_nybble(c) \
-    if (c<='9') { \
-        cur_chr=c-'0'; \
-    } else { \
-        cur_chr=c-'a'+10; \
-    }
-
-#define one_hex_to_cur_chr(c1) \
-    set_nybble(c1);
-
-#define two_hex_to_cur_chr(c1,c2) \
-    set_nybble(c1); \
-    add_nybble(c2);
-
-#define four_hex_to_cur_chr(c1,c2,c3,c4) \
-    two_hex_to_cur_chr(c1,c2); \
-    add_nybble(c3); \
-    add_nybble(c4);
-
-#define six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6) \
-    four_hex_to_cur_chr(c1,c2,c3,c4); \
-    add_nybble(c5); \
-    add_nybble(c6);
-
-static boolean process_sup_mark(void)
-{
-    if (cur_chr == buffer[iloc]) {
-        if (iloc < ilimit) {
-            if ((cur_chr == buffer[iloc + 1]) && (cur_chr == buffer[iloc + 2])) {
-                if ((cur_chr == buffer[iloc + 3]) && (cur_chr == buffer[iloc + 4])) {
-                    /*tex |^^^^^^XXXXXX| */
-                    if ((iloc + 10) <= ilimit) {
-                        int c1 = buffer[iloc +  5];
-                        int c2 = buffer[iloc +  6];
-                        int c3 = buffer[iloc +  7];
-                        int c4 = buffer[iloc +  8];
-                        int c5 = buffer[iloc +  9];
-                        int c6 = buffer[iloc + 10];
-                        if (is_hex(c1) && is_hex(c2) && is_hex(c3) &&
-                            is_hex(c4) && is_hex(c5) && is_hex(c6)) {
-                            iloc = iloc + 11;
-                            six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6);
-                            return true;
-                        } else {
-                            tex_error("^^^^^^ needs six hex digits", NULL);
-                        }
-                    } else {
-                        tex_error("^^^^^^ needs six hex digits, end of input", NULL);
-                    }
-                } else {
-                    /*tex |^^^^XXXX| */
-                    if ((iloc + 6) <= ilimit) {
-                        int c1 = buffer[iloc + 3];
-                        int c2 = buffer[iloc + 4];
-                        int c3 = buffer[iloc + 5];
-                        int c4 = buffer[iloc + 6];
-                        if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4)) {
-                            iloc = iloc + 7;
-                            four_hex_to_cur_chr(c1,c2,c3,c4);
-                            return true;
-                        } else {
-                            tex_error("^^^^ needs four hex digits", NULL);
-                        }
-                    } else {
-                        tex_error("^^^^ needs four hex digits, end of input", NULL);
-                    }
-                }
-            } else {
-                /*tex |^^XX| */
-                if ((iloc + 2) <= ilimit) {
-                    int c1 = buffer[iloc + 1];
-                    int c2 = buffer[iloc + 2];
-                    if (is_hex(c1) && is_hex(c2)) {
-                        iloc = iloc + 3;
-                        two_hex_to_cur_chr(c1,c2);
-                        return true;
-                    }
-                }
-                /*tex Go on, no error, good old \TEX . */
-            }
-        }
-        /*tex The rest. */
-        {
-            int c1 = buffer[iloc + 1];
-            if (c1 < 0200) {
-                iloc = iloc + 2;
-                if (is_hex(c1) && (iloc <= ilimit)) {
-                    int c2 = buffer[iloc];
-                    if (is_hex(c2)) {
-                        incr(iloc);
-                        two_hex_to_cur_chr(c1,c2);
-                        return true;
-                    }
-                }
-                cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-/*tex
-
-    Control sequence names are scanned only when they appear in some line of a
-    file; once they have been scanned the first time, their |eqtb| location
-    serves as a unique identification, so \TeX\ doesn't need to refer to the
-    original name any more except when it prints the equivalent in symbolic form.
-
-    The program that scans a control sequence has been written carefully in order
-    to avoid the blowups that might otherwise occur if a malicious user tried
-    something like `\.{\\catcode\'15=0}'. The algorithm might look at
-    |buffer[ilimit+1]|, but it never looks at |buffer[ilimit+2]|.
-
-    If expanded characters like `\.{\^\^A}' or `\.{\^\^df}' appear in or just
-    following a control sequence name, they are converted to single characters in
-    the buffer and the process is repeated, slowly but surely.
-
-*/
-
-static boolean check_expanded_code(int *kk);    /* below */
-
-static int scan_control_sequence(void)
-{
-    int retval = mid_line;
-    if (iloc > ilimit) {
-        /*tex |state| is irrelevant in this case. */
-        cur_cs = null_cs;
-    } else {
-        /*tex |cat_code(cur_chr)|, usually: */
-        register int cat;
-        while (1) {
-            int k = iloc;
-            do_buffer_to_unichar(cur_chr, k);
-            do_get_cat_code(cat, cur_chr);
-            if (cat != letter_cmd || k > ilimit) {
-                retval = (cat == spacer_cmd ? skip_blanks : mid_line);
-                /*tex If an expanded \unknown */
-                if (cat == sup_mark_cmd && check_expanded_code(&k))
-                    continue;
-            } else {
-                retval = skip_blanks;
-                do {
-                    do_buffer_to_unichar(cur_chr, k);
-                    do_get_cat_code(cat, cur_chr);
-                } while (cat == letter_cmd && k <= ilimit);
-                /*tex If an expanded \unknown */
-                if (cat == sup_mark_cmd && check_expanded_code(&k))
-                    continue;
-                if (cat != letter_cmd) {
-                    /*tex Backtrack one character which can be \UTF. */
-                    if (cur_chr <= 0x7F) {
-                        k -= 1; /* in most cases */
-                    } else if (cur_chr > 0xFFFF) {
-                        k -= 4;
-                    } else if (cur_chr > 0x7FF) {
-                        k -= 3;
-                    } else /* if (cur_chr > 0x7F) */ {
-                        k -= 2;
-                    }
-                    /*tex Now |k| points to first nonletter. */
-                }
-            }
-            cur_cs = id_lookup(iloc, k - iloc);
-            iloc = k;
-            break;
-        }
-    }
-    cur_cmd = eq_type(cur_cs);
-    cur_chr = equiv(cur_cs);
-    return retval;
-}
-
-/*tex
-
-    Whenever we reach the following piece of code, we will have
-    |cur_chr=buffer[k-1]| and |k<=ilimit+1| and
-    |cat=get_cat_code(cat_code_table,cur_chr)|. If an expanded code like
-    \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)..(k+1)]| or
-    |buffer[(k-1)..(k+2)]|, we will store the corresponding code in |buffer[k-1]|
-    and shift the rest of the buffer left two or three places.
-
-*/
-
-static boolean check_expanded_code(int *kk)
-{
-    int l;
-    int k = *kk;
-    int d = 1;
-    if (buffer[k] == cur_chr && k < ilimit) {
-        if ((cur_chr == buffer[k + 1]) && (cur_chr == buffer[k + 2])) {
-            if ((cur_chr == buffer[k + 3]) && (cur_chr == buffer[k + 4])) {
-                if ((k + 10) <= ilimit) {
-                    int c1 = buffer[k + 6 - 1];
-                    int c2 = buffer[k + 6];
-                    int c3 = buffer[k + 6 + 1];
-                    int c4 = buffer[k + 6 + 2];
-                    int c5 = buffer[k + 6 + 3];
-                    int c6 = buffer[k + 6 + 4];
-                    if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4) && is_hex(c5) && is_hex(c6)) {
-                        d = 6;
-                        six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6);
-                    } else {
-                        tex_error("^^^^^^ needs six hex digits", NULL);
-                    }
-                } else {
-                    tex_error("^^^^^^ needs six hex digits, end of input", NULL);
-                }
-            } else {
-                if ((k + 6) <= ilimit) {
-                    int c1 = buffer[k + 4 - 1];
-                    int c2 = buffer[k + 4];
-                    int c3 = buffer[k + 4 + 1];
-                    int c4 = buffer[k + 4 + 2];
-                    if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4)) {
-                        d = 4;
-                        four_hex_to_cur_chr(c1,c2,c3,c4);
-                    } else {
-                        tex_error("^^^^ needs four hex digits", NULL);
-                    }
-                } else {
-                    tex_error("^^^^ needs four hex digits, end of input", NULL);
-                }
-            }
-        } else {
-            int c1 = buffer[k + 1];
-            if (c1 < 0200) {
-                d = 1;
-                if (is_hex(c1) && (k + 2) <= ilimit) {
-                    int c2 = buffer[k + 2];
-                    if (is_hex(c2)) {
-                        d = 2;
-                        two_hex_to_cur_chr(c1,c2);
-                    } else {
-                        cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
-                    }
-                } else {
-                    cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
-                }
-            }
-        }
-        if (d > 2)
-            d = 2 * d - 1;
-        else
-            d++;
-        if (cur_chr <= 0x7F) {
-            buffer[k - 1] = (packed_ASCII_code) cur_chr;
-        } else if (cur_chr <= 0x7FF) {
-            buffer[k - 1] = (packed_ASCII_code) (0xC0 + cur_chr / 0x40);
-            k++;
-            d--;
-            buffer[k - 1] = (packed_ASCII_code) (0x80 + cur_chr % 0x40);
-        } else if (cur_chr <= 0xFFFF) {
-            buffer[k - 1] = (packed_ASCII_code) (0xE0 + cur_chr / 0x1000);
-            k++;
-            d--;
-            buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) / 0x40);
-            k++;
-            d--;
-            buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) % 0x40);
-        } else {
-            buffer[k - 1] = (packed_ASCII_code) (0xF0 + cur_chr / 0x40000);
-            k++;
-            d--;
-            buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x40000) / 0x1000);
-            k++;
-            d--;
-            buffer[k - 1] = (packed_ASCII_code) (0x80 + ((cur_chr % 0x40000) % 0x1000) / 0x40);
-            k++;
-            d--;
-            buffer[k - 1] = (packed_ASCII_code) (0x80 + ((cur_chr % 0x40000) % 0x1000) % 0x40);
-        }
-        l = k;
-        ilimit = ilimit - d;
-        while (l <= ilimit) {
-            buffer[l] = buffer[l + d];
-            l++;
-        }
-        *kk = k;
-        return true;
-    }
-    return false;
-}
-
-/*tex
-
-    All of the easy branches of |get_next| have now been taken care of. There is
-    one more branch.
-
-*/
-
-static next_line_retval next_line(void)
-{
-    /*tex A way to end a pseudo file without trailing space: */
-    boolean inhibit_eol = false;
-    if (iname > 17) {
-        /*tex
-
-            Read next line of file into |buffer|, or |goto restart| if the file
-            has ended.
-
-        */
-        incr(line);
-        first = istart;
-        if (!force_eof) {
-            if (iname <= 20) {
-                if (pseudo_input()) {
-                    /*tex Not end of file; set |ilimit|. */
-                    firm_up_the_line();
-                    line_catcode_table = DEFAULT_CAT_TABLE;
-                    if ((iname == 19) && (pseudo_lines(pseudo_files) == null))
-                        inhibit_eol = true;
-                } else if ((every_eof_par != null) && !eof_seen[iindex]) {
-                    ilimit = first - 1;
-                    /*tex Fake one empty line. */
-                    eof_seen[iindex] = true;
-                    if (iname != 19)
-                        begin_token_list(every_eof_par, every_eof_text);
-                    return next_line_restart;
-                } else {
-                    force_eof = true;
-                }
-            } else {
-                if (iname == 21) {
-                    halfword n = null;
-                    int t = luacstring_input(&n);
-                    switch (t) {
-                        case 0:
-                            force_eof = true;
-                            break;
-                        case 1:
-                            /*tex string */
-                            firm_up_the_line();
-                            line_catcode_table = (short) luacstring_cattable();
-                            line_partial = (signed char) luacstring_partial();
-                            if (luacstring_final_line() || line_partial || line_catcode_table == NO_CAT_TABLE)
-                                inhibit_eol = true;
-                            if (!line_partial)
-                                istate = new_line;
-                            break;
-                        case 2:
-                            /*tex token */
-                            cur_tok = n;
-                            back_input();
-                            /*tex Needs checking. */
-                            return next_line_restart;
-                            break;
-                        case 3:
-                            /*tex node */
-                            if (n < biggest_char) {
-                                /*tex |0x10FFFF == 1114111| */
-                                cur_tok = token_val(node_cmd, n);
-                                back_input();
-                                /*tex Needs checking. */
-                                return next_line_restart;
-                            } else {
-                                normal_warning("nodes","unable to store reference from lua in tex");
-                                force_eof = true;
-                            }
-                            break;
-                        default:
-                            force_eof = true;
-                            break;
-                    }
-                } else if (lua_input_ln(cur_file, 0, true)) {
-                    /*tex Not end of file, set |ilimit|. */
-                    firm_up_the_line();
-                    line_catcode_table = DEFAULT_CAT_TABLE;
-                } else if ((every_eof_par != null) && (!eof_seen[iindex])) {
-                    ilimit = first - 1;
-                    /* tex Fake one empty line. */
-                    eof_seen[iindex] = true;
-                    begin_token_list(every_eof_par, every_eof_text);
-                    return next_line_restart;
-                } else {
-                    force_eof = true;
-                }
-            }
-        }
-        if (force_eof) {
-            if (tracing_nesting_par > 0)
-                if ((grp_stack[in_open] != cur_boundary) || (if_stack[in_open] != cond_ptr))
-                    if (!((iname == 19) || (iname == 21))) {
-                        /*tex Give warning for some unfinished groups and/or conditionals. */
-                        file_warning();
-                    }
-            if ((iname > 21) || (iname == 20)) {
-                report_stop_file(filetype_tex);
-                decr(open_parens);
-            }
-            force_eof = false;
-            /*tex \LUA\ input or \.{\\scantextokens} */
-            if (iname == 21 || iname == 19) {
-                end_file_reading();
-            } else {
-                end_file_reading();
-                if (! suppress_outer_error_par)
-                    check_outer_validity();
-            }
-            return next_line_restart;
-        }
-        if (inhibit_eol || end_line_char_inactive)
-            ilimit--;
-        else
-            buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
-        first = ilimit + 1;
-        iloc = istart;
-        /*tex We're ready to read. */
-    } else {
-        if (!terminal_input) {
-            /*tex \.{\\read} line has ended */
-            cur_cmd = 0;
-            cur_chr = 0;
-            return next_line_return;
-        }
-        if (input_ptr > 0) {
-            /*tex Text was inserted during error recovery. */
-            end_file_reading();
-            /*tex Resume previous level. */
-            return next_line_restart;
-        }
-        if (selector < log_only)
-            open_log_file();
-        if (interaction > nonstop_mode) {
-            if (end_line_char_inactive)
-                ilimit++;
-            if (ilimit == istart) {
-                /*tex Previous line was empty. */
-                tprint_nl("(Please type a command or say `\\end')");
-            }
-            print_ln();
-            first = istart;
-            /*tex Input on-line into |buffer| */
-            prompt_input("*");
-            ilimit = last;
-            if (end_line_char_inactive)
-                ilimit--;
-            else
-                buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
-            first = ilimit + 1;
-            iloc = istart;
-        } else {
-            /*tex
-
-                Nonstop mode, which is intended for overnight batch processing,
-                never waits for on-line input.
-
-            */
-            fatal_error("*** (job aborted, no legal \\end found)");
-        }
-    }
-    return next_line_ok;
-}
-
-/*tex
-
-    Let's consider now what happens when |get_next| is looking at a token list.
-
-*/
-
-static boolean get_next_tokenlist(void)
-{
-    register halfword t = token_info(iloc);
-    /*tex Move to next. */
-    iloc = token_link(iloc);
-    if (t >= cs_token_flag) {
-        /*tex A control sequence token */
-        cur_cs = t - cs_token_flag;
-        cur_cmd = eq_type(cur_cs);
-        if (cur_cmd >= outer_call_cmd) {
-            if (cur_cmd == dont_expand_cmd) {
-                /*tex
-
-                    Get the next token, suppressing expansion. The present point
-                    in the program is reached only when the |expand| routine has
-                    inserted a special marker into the input. In this special
-                    case, |token_info(iloc)| is known to be a control sequence
-                    token, and |token_link(iloc)=null|.
-
-                */
-                cur_cs = token_info(iloc) - cs_token_flag;
-                iloc = null;
-                cur_cmd = eq_type(cur_cs);
-                if (cur_cmd > max_command_cmd) {
-                    cur_cmd = relax_cmd;
-                    cur_chr = no_expand_flag;
-                    return true;
-                }
-            } else if (! suppress_outer_error_par) {
-                check_outer_validity();
-            }
-        }
-        cur_chr = equiv(cur_cs);
-    } else {
-        cur_cmd = token_cmd(t);
-        cur_chr = token_chr(t);
-        switch (cur_cmd) {
-            case left_brace_cmd:
-                align_state++;
-                break;
-            case right_brace_cmd:
-                align_state--;
-                break;
-            case out_param_cmd:
-                /*tex Insert macro parameter and |goto restart|. */
-                begin_token_list(param_stack[param_start + cur_chr - 1], parameter);
-                return false;
-                break;
-        }
-    }
-    return true;
-}
-
-/*tex
-
-    Now we're ready to take the plunge into |get_next| itself. Parts of this
-    routine are executed more often than any other instructions of \TeX.
-
-    This sets |cur_cmd|, |cur_chr|, |cur_cs| to next token:
-
-*/
-
-void get_next(void)
-{
-  RESTART:
-    cur_cs = 0;
-    if (istate != token_list) {
-        /*tex Input from external file, |goto restart| if no input found. */
-        if (!get_next_file())
-            goto RESTART;
-    } else {
-        if (iloc == null) {
-            end_token_list();
-            /*tex List exhausted, resume previous level. */
-            goto RESTART;
-        } else if (!get_next_tokenlist()) {
-            /*tex Parameter needs to be expanded. */
-            goto RESTART;
-        }
-    }
-    /*tex If an alignment entry has just ended, take appropriate action. */
-    if ((cur_cmd == tab_mark_cmd || cur_cmd == car_ret_cmd) && align_state == 0) {
-        insert_vj_template();
-        goto RESTART;
-    }
-}
-
-/*tex
-
-    Since |get_next| is used so frequently in \TeX, it is convenient to define
-    three related procedures that do a little more:
-
-    \startitemize
-        \startitem
-            |get_token| not only sets |cur_cmd| and |cur_chr|, it also sets
-            |cur_tok|, a packed halfword version of the current token.
-        \stopitem
-        \startitem
-            |get_x_token|, meaning ``get an expanded token,'' is like
-            |get_token|, but if the current token turns out to be a user-defined
-            control sequence (i.e., a macro call), or a conditional, or something
-            like \.{\\topmark} or \.{\\expandafter} or \.{\\csname}, it is
-            eliminated from the input by beginning the expansion of the macro or
-            the evaluation of the conditional.
-        \stopitem
-        \startitem
-            |x_token| is like |get_x_token| except that it assumes that
-            |get_next| has already been called.
-        \stopitem
-    \stopitemize
-
-    In fact, these three procedures account for almost every use of |get_next|.
-    No new control sequences will be defined except during a call of |get_token|,
-    or when \.{\\csname} compresses a token list, because
-    |no_new_control_sequence| is always |true| at other times.
-
-    This sets |cur_cmd|, |cur_chr|, |cur_tok|:
-
-*/
-
-void get_token(void)
-{
-    no_new_control_sequence = false;
-    get_next();
-    no_new_control_sequence = true;
-    if (cur_cs == 0)
-        cur_tok = token_val(cur_cmd, cur_chr);
-    else
-        cur_tok = cs_token_flag + cur_cs;
-}
-
-/*tex This changes the string |s| to a token list. */
-
-halfword string_to_toks(const char *ss)
-{
-    /*tex tail of the token list */
-    halfword p;
-    /*tex new node being added to the token list via |store_new_token| */
-    halfword q;
-    /*tex token being appended */
-    halfword t;
-    const char *s = ss;
-    const char *se = ss + strlen(s);
-    p = temp_token_head;
-    set_token_link(p, null);
-    while (s < se) {
-        t = (halfword) str2uni((const unsigned char *) s);
-        s += utf8_size(t);
-        if (t == ' ')
-            t = space_token;
-        else
-            t = other_token + t;
-        fast_store_new_token(t);
-    }
-    return token_link(temp_token_head);
-}
-
-/*tex
-
-    The token lists for macros and for other things like \.{\\mark} and
-    \.{\\output} and \.{\\write} are produced by a procedure called |scan_toks|.
-
-    Before we get into the details of |scan_toks|, let's consider a much simpler
-    task, that of converting the current string into a token list. The |str_toks|
-    function does this; it classifies spaces as type |spacer| and everything else
-    as type |other_char|.
-
-    The token list created by |str_toks| begins at |link(temp_token_head)| and
-    ends at the value |p| that is returned. (If |p=temp_token_head|, the list is
-    empty.)
-
-    |lua_str_toks| is almost identical, but it also escapes the three symbols
-    that |lua| considers special while scanning a literal string.
-
-    This changes the string |str_pool[b..pool_ptr]| to a token list:
-
-*/
-
-halfword lua_str_toks(lstring b)
-{
-    /*tex tail of the token list */
-    halfword p;
-    /*tex new node being added to the token list via |store_new_token| */
-    halfword q;
-    /*tex token being appended */
-    halfword t;
-    /*tex index into string */
-    unsigned char *k;
-    p = temp_token_head;
-    set_token_link(p, null);
-    k = (unsigned char *) b.s;
-    while (k < (unsigned char *) b.s + b.l) {
-        t = pool_to_unichar(k);
-        k += utf8_size(t);
-        if (t == ' ') {
-            t = space_token;
-        } else {
-            if ((t == '\\') || (t == '"') || (t == '\'') || (t == 10) || (t == 13))
-                fast_store_new_token(other_token + '\\');
-            if (t == 10)
-                t = 'n';
-            if (t == 13)
-                t = 'r';
-            t = other_token + t;
-        }
-        fast_store_new_token(t);
-    }
-    return p;
-}
-
-/*tex
-
-    Incidentally, the main reason for wanting |str_toks| is the function
-    |the_toks|, which has similar input/output characteristics.
-
-    This changes the string |str_pool[b..pool_ptr]| to a token list:
-
-*/
-
-halfword str_toks(lstring s)
-{
-    /*tex tail of the token list */
-    halfword p;
-    /*tex new node being added to the token list via |store_new_token| */
-    halfword q;
-    /*tex token being appended */
-    halfword t;
-    /*tex index into string */
-    unsigned char *k, *l;
-    p = temp_token_head;
-    set_token_link(p, null);
-    k = s.s;
-    l = k + s.l;
-    while (k < l) {
-        t = pool_to_unichar(k);
-        k += utf8_size(t);
-        if (t == ' ')
-            t = space_token;
-        else
-            t = other_token + t;
-        fast_store_new_token(t);
-    }
-    return p;
-}
-
-/*tex
-
-    Most of the converter is similar to the one i made for macro so at some point
-    I can make a helper; also todo: there is no need to go through the pool.
-
-*/
-
-/*tex Change the string |str_pool[b..pool_ptr]| to a token list. */
-
-halfword str_scan_toks(int ct, lstring s)
-{
-    /*tex tail of the token list */
-    halfword p;
-    /*tex new node being added to the token list via |store_new_token| */
-    halfword q;
-    /*tex token being appended */
-    halfword t;
-    /*tex index into string */
-    unsigned char *k, *l;
-    int cc;
-    p = temp_token_head;
-    set_token_link(p, null);
-    k = s.s;
-    l = k + s.l;
-    while (k < l) {
-        t = pool_to_unichar(k);
-        k += utf8_size(t);
-        cc = get_cat_code(ct,t);
-            if (cc == 0) {
-                /*tex We have a potential control sequence so we check for it. */
-                int _lname = 0 ;
-                int _s = 0 ;
-                int _c = 0 ;
-                halfword _cs = null ;
-                unsigned char *_name  = k ;
-                while (k < l) {
-                    t = (halfword) str2uni((const unsigned char *) k);
-                    _s = utf8_size(t);
-                    _c = get_cat_code(ct,t);
-                    if (_c == 11) {
-                        k += _s ;
-                        _lname = _lname + _s ;
-                    } else if (_c == 10) {
-                        /*tex We ignore a trailing space like normal scanning does. */
-                        k += _s ;
-                        break ;
-                    } else {
-                        break ;
-                    }
-                }
-                if (_s > 0) {
-                    /*tex We have a potential |\cs|. */
-                    _cs = string_lookup((const char *) _name, _lname);
-                    if (_cs == undefined_control_sequence) {
-                        /* let's play safe and backtrack */
-                        t = cc * (1<<21) + t ;
-                        k = _name ;
-                    } else {
-                        t = cs_token_flag + _cs;
-                    }
-                } else {
-                    /*tex
-
-                        Just a character with some meaning, so |\unknown| becomes
-                        effectively |\unknown| assuming that |\\| has some useful
-                        meaning of course.
-
-                    */
-                    t = cc * (1<<21) + t ;
-                    k = _name ;
-                }
-
-            } else {
-                /*tex
-
-                    Whatever token, so for instance $x^2$ just works given a \TEX\
-                    catcode regime.
-
-                */
-                t = cc * (1<<21) + t ;
-            }
-            fast_store_new_token(t);
-
-    }
-    return p;
-}
-
-/*tex
-
-    Here's part of the |expand| subroutine that we are now ready to complete:
-
-*/
-
-void ins_the_toks(void)
-{
-    (void) the_toks();
-    ins_list(token_link(temp_token_head));
-}
-
-#define set_toks_register(n,t,g) do { \
-    int a = (g>0) ? 4 : 0; \
-    halfword ref = get_avail();  \
-    set_token_ref_count(ref, 0); \
-    set_token_link(ref, token_link(t)); \
-    define(n + toks_base, call_cmd, ref); \
-} while (0)
-
-#define append_copied_toks_list(s,t) do { \
-    halfword p; \
-    halfword q; \
-    p = temp_token_head; \
-    set_token_link(p, null); \
-    while (s != null) { \
-        fast_store_new_token(token_info(s)); \
-        s = token_link(s); \
-    } \
-    while (t != null) { \
-        fast_store_new_token(token_info(t)); \
-        t = token_link(t); \
-    } \
-   } while (0)
-
-
-/*tex
-
-   \starttabulate[|T||T||]
-   \NC 0 \NC \type {toksapp}   \NC 1 \NC \type {etoksapp} \NC \NR
-   \NC 2 \NC \type {tokspre}   \NC 3 \NC \type {etokspre} \NC \NR
-   \NC 4 \NC \type {gtoksapp}  \NC 5 \NC \type {xtoksapp} \NC \NR
-   \NC 6 \NC \type {gtokspre}  \NC 7 \NC \type {xtokspre} \NC \NR
-   \stoptabulate
-
-*/
-
-void combine_the_toks(int how)
-{
-    halfword source = null;
-    halfword target = null;
-    halfword append = (how == 0) || (how == 1) || (how == 4) || (how == 5);
-    halfword expand = odd(how);
-    halfword global = how > 3;
-    halfword nt, ns, s, t, p, q, h;
-    get_x_token();
-    /*tex The target. */
-    if (cur_cmd == assign_toks_cmd) {
-        /*tex Check range. */
-        nt = equiv(cur_cs) - toks_base;
-    } else {
-        back_input();
-        scan_register_num();
-        nt = cur_val;
-    }
-    /*tex The source. */
-    do {
-        get_x_token();
-    } while (cur_cmd == spacer_cmd);
-    if (cur_cmd == left_brace_cmd) {
-        back_input();
-        scan_toks(false,expand);
-        source = def_ref;
-        /*tex The action. */
-        if (source != null) {
-            target = toks(nt);
-            if (target == null) {
-                set_toks_register(nt,source,global);
-            } else {
-                s = token_link(source);
-                if (s != null) {
-                    t = token_link(target);
-                    if (t == null) {
-                        /*tex Can this happen? */
-                        set_token_link(target, s);
-                    } else if (append) {
-                        /*tex Append. */
-                        if (token_ref_count(target) == 0) {
-                            p = t;
-                            while (token_link(p) != null) {
-                                p = token_link(p);
-                            }
-                            while (s != null) {
-                                fast_store_new_token(token_info(s));
-                                s = token_link(s);
-                            }
-                        } else {
-                            token_ref_count(target)--;
-                            append_copied_toks_list(t,s);
-                            set_toks_register(nt,temp_token_head,global);
-                        }
-                    } else {
-                        /* prepend */
-                        if (token_ref_count(target) == 0) {
-                            h = null;
-                            p = null ;
-                            while (s != null) {
-                                fast_store_new_token(token_info(s));
-                                if (h == null) {
-                                    h = p;
-                                }
-                                s = token_link(s);
-                            }
-                            set_token_link(p,t);
-                            set_token_link(target,h);
-                        } else {
-                            token_ref_count(target)--;
-                            append_copied_toks_list(s,t);
-                            set_toks_register(nt,temp_token_head,global);
-                        }
-                    }
-                }
-            }
-        }
-    } else {
-        if (cur_cmd == assign_toks_cmd) {
-            ns = equiv(cur_cs) - toks_base;
-            /*tex Check range. */
-        } else {
-            scan_register_num();
-            ns = cur_val;
-        }
-        /*tex The action. */
-        source = toks(ns);
-        if (source != null) {
-            target = toks(nt);
-            if (target == null) {
-                /*tex The assign. */
-                token_ref_count(source)++;
-                equiv(toks_base+nt) = source;
-                return;
-            }
-            s = token_link(source);
-            t = token_link(target);
-            if (append) {
-                /*tex Append. */
-                if (token_ref_count(target) == 0) {
-                    p = t;
-                    while (token_link(p) != null) {
-                        p = token_link(p);
-                    }
-                    while (s != null) {
-                        fast_store_new_token(token_info(s));
-                        s = token_link(s);
-                    }
-                } else {
-                    token_ref_count(target)--;
-                    append_copied_toks_list(t,s);
-                    set_toks_register(nt,temp_token_head,global);
-                }
-            } else {
-                /*tex Prepend. */
-                if (token_ref_count(target) == 0) {
-                    h = null;
-                    p = null;
-                    while (s != null) {
-                        fast_store_new_token(token_info(s));
-                        if (h == null) {
-                            h = p;
-                        }
-                        s = token_link(s);
-                    }
-                    set_token_link(p,t);
-                    set_token_link(target,h);
-                } else {
-                    token_ref_count(target)--;
-                    append_copied_toks_list(s,t);
-                    set_toks_register(nt,temp_token_head,global);
-                }
-            }
-        }
-    }
-}
-
-/*tex
-
-    This routine, used in the next one, prints the job name, possibly modified by
-    the |process_jobname| callback.
-
-*/
-
-static void print_job_name(void)
-{
-    if (job_name) {
-        /*tex C strings for jobname before and after processing. */
-          char *s, *ss;
-          int callback_id, lua_retval;
-          s = (char*)str_string(job_name);
-          callback_id = callback_defined(process_jobname_callback);
-          if (callback_id > 0) {
-            lua_retval = run_callback(callback_id, "S->S", s, &ss);
-            if ((lua_retval == true) && (ss != NULL))
-                s = ss;
-        }
-        tprint(s);
-    } else {
-        print(job_name);
-    }
-}
-
-/*tex
-
-    Here is a routine that print the result of a convert command, using the
-    argument |i|. It returns |false | if it does not know to print the code |c|.
-    The function exists because lua code and tex code can both call it to convert
-    something.
-
-    Parse optional \LUA\ state integer, or an instance name to be stored in |sn|
-    and get the next non-blank non-relax non-call token.
-
-*/
-
-int scan_lua_state(void)
-{
-    int sn = 0;
-    do {
-        get_x_token();
-    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
-    back_input();
-    if (cur_cmd != left_brace_cmd) {
-        if (scan_keyword("name")) {
-            (void) scan_toks(false, true);
-            sn = def_ref;
-        } else {
-            scan_register_num();
-            if (get_lua_name(cur_val))
-                sn = (cur_val - 65536);
-        }
-    }
-    return sn;
-}
-
-/*tex
-
-    The procedure |conv_toks| uses |str_toks| to insert the token list for
-    |convert| functions into the scanner; `\.{\\outer}' control sequences are
-    allowed to follow `\.{\\string}' and `\.{\\meaning}'.
-
-    The extra temp string |u| is needed because |pdf_scan_ext_toks| incorporates
-    any pending string in its output. In order to save such a pending string, we
-    have to create a temporary string that is destroyed immediately after.
-
-*/
-
-#define push_selector { \
-    old_setting = selector; \
-    selector = new_string; \
-}
-
-#define pop_selector { \
-    selector = old_setting; \
-}
-
-static int do_variable_dvi(halfword c)
-{
-    return 0;
-}
-
-#define do_variable_backend_int(i) \
-    cur_cmd = assign_int_cmd; \
-    cur_val = backend_int_base + i; \
-    cur_tok = token_val(cur_cmd, cur_val); \
-    back_input();
-
-#define do_variable_backend_dimen(i) \
-    cur_cmd = assign_dimen_cmd; \
-    cur_val = backend_dimen_base + i; \
-    cur_tok = token_val(cur_cmd, cur_val); \
-    back_input();
-
-#define do_variable_backend_toks(i) \
-    cur_cmd = assign_toks_cmd; \
-    cur_val = backend_toks_base + i ; \
-    cur_tok = token_val(cur_cmd, cur_val); \
-    back_input();
-
-static int do_variable_pdf(halfword c)
-{
-         if (scan_keyword("compresslevel"))        { do_variable_backend_int(c_pdf_compress_level); }
-    else if (scan_keyword("decimaldigits"))        { do_variable_backend_int(c_pdf_decimal_digits); }
-    else if (scan_keyword("imageresolution"))      { do_variable_backend_int(c_pdf_image_resolution); }
-    else if (scan_keyword("pkresolution"))         { do_variable_backend_int(c_pdf_pk_resolution); }
-    else if (scan_keyword("uniqueresname"))        { do_variable_backend_int(c_pdf_unique_resname); }
-    else if (scan_keyword("majorversion"))         { do_variable_backend_int(c_pdf_major_version); }
-    else if (scan_keyword("minorversion"))         { do_variable_backend_int(c_pdf_minor_version); }
-    else if (scan_keyword("pagebox"))              { do_variable_backend_int(c_pdf_pagebox); }
-    else if (scan_keyword("inclusionerrorlevel"))  { do_variable_backend_int(c_pdf_inclusion_errorlevel); }
-    else if (scan_keyword("ignoreunknownimages"))  { do_variable_backend_int(c_pdf_ignore_unknown_images); }
-    else if (scan_keyword("gamma"))                { do_variable_backend_int(c_pdf_gamma); }
-    else if (scan_keyword("imageapplygamma"))      { do_variable_backend_int(c_pdf_image_apply_gamma); }
-    else if (scan_keyword("imagegamma"))           { do_variable_backend_int(c_pdf_image_gamma); }
-    else if (scan_keyword("imagehicolor"))         { do_variable_backend_int(c_pdf_image_hicolor); }
-    else if (scan_keyword("imageaddfilename"))     { do_variable_backend_int(c_pdf_image_addfilename); }
-    else if (scan_keyword("objcompresslevel"))     { do_variable_backend_int(c_pdf_obj_compress_level); }
-    else if (scan_keyword("inclusioncopyfonts"))   { do_variable_backend_int(c_pdf_inclusion_copy_font); }
-    else if (scan_keyword("gentounicode"))         { do_variable_backend_int(c_pdf_gen_tounicode); }
-    else if (scan_keyword("pkfixeddpi"))           { do_variable_backend_int(c_pdf_pk_fixed_dpi); }
-    else if (scan_keyword("suppressoptionalinfo")) { do_variable_backend_int(c_pdf_suppress_optional_info); }
-    else if (scan_keyword("omitcidset"))           { do_variable_backend_int(c_pdf_omit_cidset); }
-    else if (scan_keyword("omitcharset"))          { do_variable_backend_int(c_pdf_omit_charset); }
-    else if (scan_keyword("recompress"))           { do_variable_backend_int(c_pdf_recompress); }
-
-    else if (scan_keyword("horigin"))              { do_variable_backend_dimen(d_pdf_h_origin); }
-    else if (scan_keyword("vorigin"))              { do_variable_backend_dimen(d_pdf_v_origin); }
-    else if (scan_keyword("threadmargin"))         { do_variable_backend_dimen(d_pdf_thread_margin); }
-    else if (scan_keyword("destmargin"))           { do_variable_backend_dimen(d_pdf_dest_margin); }
-    else if (scan_keyword("linkmargin"))           { do_variable_backend_dimen(d_pdf_link_margin); }
-    else if (scan_keyword("xformmargin"))          { do_variable_backend_dimen(d_pdf_xform_margin); }
-
-    else if (scan_keyword("pageattr"))             { do_variable_backend_toks(t_pdf_page_attr); }
-    else if (scan_keyword("pageresources"))        { do_variable_backend_toks(t_pdf_page_resources); }
-    else if (scan_keyword("pagesattr"))            { do_variable_backend_toks(t_pdf_pages_attr); }
-    else if (scan_keyword("xformattr"))            { do_variable_backend_toks(t_pdf_xform_attr); }
-    else if (scan_keyword("xformresources"))       { do_variable_backend_toks(t_pdf_xform_resources); }
-    else if (scan_keyword("pkmode"))               { do_variable_backend_toks(t_pdf_pk_mode); }
-    else if (scan_keyword("trailerid"))            { do_variable_backend_toks(t_pdf_trailer_id); }
-
-    else
-        return 0;
-    return 1;
-}
-
-static int do_feedback_dvi(halfword c)
-{
-    return 0;
-}
-
-/*tex Codes not really needed but cleaner when testing */
-
-#define pdftex_version  140 /* these values will not change any more */
-#define pdftex_revision "0" /* these values will not change any more */
-
-static int do_feedback_pdf(halfword c)
-{
-    /*tex holds |selector| setting */
-    int old_setting;
-    /*tex |scanner_status| upon entry */
-    int save_scanner_status;
-    /*tex |def_ref| upon entry, important if inside `\.{\\message}' */
-    halfword save_def_ref;
-    halfword save_warning_index;
-    /*tex temp boolean */
-    boolean bool;
-    /*tex first temp string */
-    str_number s;
-    /*tex for use with |set_ff| */
-    int ff;
-    /*tex third temp string, will become non-nil if a string is already being built */
-    str_number u = 0;
-    /*tex color stack init str */
-    char *str;
-    if (scan_keyword("lastlink")) {
-        push_selector;
-        print_int(pdf_last_link);
-        pop_selector;
-    } else if (scan_keyword("retval")) {
-        push_selector;
-        print_int(pdf_retval);
-        pop_selector;
-    } else if (scan_keyword("lastobj")) {
-        push_selector;
-        print_int(pdf_last_obj);
-        pop_selector;
-    } else if (scan_keyword("lastannot")) {
-        push_selector;
-        print_int(pdf_last_annot);
-        pop_selector;
-    } else if (scan_keyword("xformname")) {
-        scan_int();
-        check_obj_type(static_pdf, obj_type_xform, cur_val);
-        push_selector;
-        print_int(obj_info(static_pdf, cur_val));
-        pop_selector;
-    } else if (scan_keyword("creationdate")) {
-        ins_list(string_to_toks(getcreationdate(static_pdf)));
-        /*tex No further action. */
-        return 2;
-    } else if (scan_keyword("fontname")) {
-        scan_font_ident();
-        if (cur_val == null_font)
-            normal_error("pdf backend", "invalid font identifier when asking 'fontname'");
-        pdf_check_vf(cur_val);
-        if (!font_used(cur_val))
-            pdf_init_font(static_pdf, cur_val);
-        push_selector;
-        set_ff(cur_val);
-        print_int(obj_info(static_pdf, pdf_font_num(ff)));
-        pop_selector;
-    } else if (scan_keyword("fontobjnum")) {
-        scan_font_ident();
-        if (cur_val == null_font)
-            normal_error("pdf backend", "invalid font identifier when asking 'objnum'");
-        pdf_check_vf(cur_val);
-        if (!font_used(cur_val))
-            pdf_init_font(static_pdf, cur_val);
-        push_selector;
-        set_ff(cur_val);
-        print_int(pdf_font_num(ff));
-        pop_selector;
-    } else if (scan_keyword("fontsize")) {
-        scan_font_ident();
-        if (cur_val == null_font)
-            normal_error("pdf backend", "invalid font identifier when asking 'fontsize'");
-        push_selector;
-        print_scaled(font_size(cur_val));
-        tprint("pt");
-        pop_selector;
-    } else if (scan_keyword("pageref")) {
-        scan_int();
-        if (cur_val <= 0)
-            normal_error("pdf backend", "invalid page number when asking 'pageref'");
-        push_selector;
-        print_int(pdf_get_obj(static_pdf, obj_type_page, cur_val, false));
-        pop_selector;
-    } else if (scan_keyword("colorstackinit")) {
-        bool = scan_keyword("page");
-        if (scan_keyword("direct"))
-            cur_val = direct_always;
-        else if (scan_keyword("page"))
-            cur_val = direct_page;
-        else if (scan_keyword("text"))
-            cur_val = direct_text;
-        else if (scan_keyword("raw"))
-            cur_val = direct_raw;
-        else if (scan_keyword("origin"))
-            cur_val = set_origin;
-        else
-            cur_val = set_origin;
-        save_scanner_status = scanner_status;
-        save_warning_index = warning_index;
-        save_def_ref = def_ref;
-        u = save_cur_string();
-        scan_toks(false, true);
-        s = tokens_to_string(def_ref);
-        delete_token_ref(def_ref);
-        def_ref = save_def_ref;
-        warning_index = save_warning_index;
-        scanner_status = save_scanner_status;
-        str = makecstring(s);
-        cur_val = newcolorstack(str, cur_val, bool);
-        free(str);
-        flush_str(s);
-        cur_val_level = int_val_level;
-        if (cur_val < 0) {
-            print_err("Too many color stacks");
-            help2("The number of color stacks is limited to 32768.",
-                  "I'll use the default color stack 0 here.");
-            error();
-            cur_val = 0;
-            restore_cur_string(u);
-        }
-        push_selector;
-        print_int(cur_val);
-        pop_selector;
-    } else if (scan_keyword("version")) {
-        push_selector;
-        print_int(pdftex_version);
-        pop_selector;
-    } else if (scan_keyword("revision")) {
-        ins_list(string_to_toks(pdftex_revision));
-        return 2;
-    } else {
-        return 0;
-    }
-    return 1;
-}
-
-void conv_toks(void)
-{
-    /*tex holds |selector| setting */
-    int old_setting;
-    halfword p, q;
-    /*tex |scanner_status| upon entry */
-    int save_scanner_status;
-    /*tex |def_ref| upon entry, important if inside `\.{\\message}' */
-    halfword save_def_ref;
-    halfword save_warning_index;
-    /*tex temp boolean */
-    boolean bool;
-    /*tex first temp string */
-    str_number s;
-    /*tex lua chunk name */
-    int sn;
-    /*tex third temp string, will become non-nil if a string is already being built */
-    str_number u = 0;
-    /*tex desired type of conversion */
-    int c = cur_chr;
-    str_number str;
-    int i = 0;
-    /*tex Scan the argument for command |c|. */
-    switch (c) {
-        case number_code:
-            scan_int();
-            push_selector;
-            print_int(cur_val);
-            pop_selector;
-            break;
-        case lua_function_code:
-            scan_int();
-            if (cur_val <= 0) {
-                normal_error("luafunction", "invalid number");
-            } else {
-                u = save_cur_string();
-                luacstrings = 0;
-                luafunctioncall(cur_val);
-                restore_cur_string(u);
-                if (luacstrings > 0)
-                    lua_string_start();
-            }
-            return;
-            break;
-        case lua_bytecode_code:
-            scan_int();
-            if (cur_val < 0 || cur_val > 65535) {
-                normal_error("luabytecode", "invalid number");
-            } else {
-                u = save_cur_string();
-                luacstrings = 0;
-                luabytecodecall(cur_val);
-                restore_cur_string(u);
-                if (luacstrings > 0)
-                    lua_string_start();
-            }
-            return;
-            break;
-        case lua_code:
-            u = save_cur_string();
-            save_scanner_status = scanner_status;
-            save_def_ref = def_ref;
-            save_warning_index = warning_index;
-            sn = scan_lua_state();
-            scan_toks(false, true);
-            s = def_ref;
-            warning_index = save_warning_index;
-            def_ref = save_def_ref;
-            scanner_status = save_scanner_status;
-            luacstrings = 0;
-            luatokencall(s, sn);
-            delete_token_ref(s);
-            restore_cur_string(u);
-            if (luacstrings > 0)
-                lua_string_start();
-            /*tex No further action. */
-            return;
-            break;
-        case expanded_code:
-            save_scanner_status = scanner_status;
-            save_warning_index = warning_index;
-            save_def_ref = def_ref;
-            u = save_cur_string();
-            scan_toks(false, true);
-            warning_index = save_warning_index;
-            scanner_status = save_scanner_status;
-            ins_list(token_link(def_ref));
-            def_ref = save_def_ref;
-            restore_cur_string(u);
-            /*tex No further action. */
-            return;
-            break;
-        case immediate_assignment_code:
-        case immediate_assigned_code:
-            /*tex
-
-                This is on-the-road-to-bachotex brain-wave but it needs a bit
-                more testing. A first variant did more in sequence till a relax
-                of spacer was seen (like do_assignments). It permits for instance
-                setting counters in full expansion.
-
-            */
-            save_scanner_status = scanner_status;
-            save_warning_index = warning_index;
-            save_def_ref = def_ref;
-            u = save_cur_string();
-            do {
-                get_x_token();
-            } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
-            if (c == immediate_assignment_code) {
-                /*tex one-step do_assignment */
-                if (cur_cmd > max_non_prefixed_command) {
-                    set_box_allowed = false;
-                    prefixed_command();
-                    set_box_allowed = true;
-                }
-                /*tex done */
-            } else {
-                /*tex pseudo token list do_assignment */
-                if (cur_cmd == left_brace_cmd) {
-                    while (1) {
-                        do {
-                            get_x_token();
-                        } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
-                        if (cur_cmd == right_brace_cmd) {
-                            break;
-                        } else {
-                            set_box_allowed = false;
-                            prefixed_command();
-                            set_box_allowed = true;
-                        }
-                    }
-                }
-                /*tex done */
-            }
-            warning_index = save_warning_index;
-            scanner_status = save_scanner_status;
-            def_ref = save_def_ref;
-            restore_cur_string(u);
-            return;
-            break;
-        case math_style_code:
-            push_selector;
-            print_math_style();
-            pop_selector;
-            break;
-        case string_code:
-            save_scanner_status = scanner_status;
-            scanner_status = normal;
-            get_token();
-            scanner_status = save_scanner_status;
-            push_selector;
-            if (cur_cs != 0)
-                sprint_cs(cur_cs);
-            else
-                print(cur_chr);
-            pop_selector;
-            break;
-        case cs_string_code:
-            save_scanner_status = scanner_status;
-            scanner_status = normal;
-            get_token();
-            scanner_status = save_scanner_status;
-            push_selector;
-            if (cur_cs != 0)
-                sprint_cs_name(cur_cs);
-            else
-                print(cur_chr);
-            pop_selector;
-            break;
-        case roman_numeral_code:
-            scan_int();
-            push_selector;
-            print_roman_int(cur_val);
-            pop_selector;
-            break;
-        case meaning_code:
-            save_scanner_status = scanner_status;
-            scanner_status = normal;
-            get_token();
-            scanner_status = save_scanner_status;
-            push_selector;
-            print_meaning();
-            pop_selector;
-            break;
-        case uchar_code:
-            scan_char_num();
-            push_selector;
-            print(cur_val);
-            pop_selector;
-            break;
-        case lua_escape_string_code:
-            {
-                lstring escstr;
-                int l = 0;
-                save_scanner_status = scanner_status;
-                save_def_ref = def_ref;
-                save_warning_index = warning_index;
-                scan_toks(false, true);
-                bool = in_lua_escape;
-                in_lua_escape = true;
-                escstr.s = (unsigned char *) tokenlist_to_cstring(def_ref, false, &l);
-                escstr.l = (unsigned) l;
-                in_lua_escape = bool;
-                delete_token_ref(def_ref);
-                def_ref = save_def_ref;
-                warning_index = save_warning_index;
-                scanner_status = save_scanner_status;
-                (void) lua_str_toks(escstr);
-                ins_list(token_link(temp_token_head));
-                free(escstr.s);
-                return;
-            }
-            /*tex no further action */
-            break;
-        case font_id_code:
-            scan_font_ident();
-            push_selector;
-            print_int(cur_val);
-            pop_selector;
-            break;
-        case font_name_code:
-            scan_font_ident();
-            push_selector;
-            append_string((unsigned char *) font_name(cur_val),(unsigned) strlen(font_name(cur_val)));
-            if (font_size(cur_val) != font_dsize(cur_val)) {
-                tprint(" at ");
-                print_scaled(font_size(cur_val));
-                tprint("pt");
-            }
-            pop_selector;
-            break;
-        case left_margin_kern_code:
-            scan_int();
-            if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node))
-                normal_error("marginkern", "a non-empty hbox expected");
-            push_selector;
-            p = list_ptr(box(cur_val));
-            while ((p != null) && (type(p) == glue_node)) {
-                p = vlink(p);
-            }
-            if ((p != null) && (type(p) == margin_kern_node) && (subtype(p) == left_side))
-                print_scaled(width(p));
-            else
-                print_char('0');
-            tprint("pt");
-            pop_selector;
-            break;
-        case right_margin_kern_code:
-            scan_int();
-            if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node))
-                normal_error("marginkern", "a non-empty hbox expected");
-            push_selector;
-            p = list_ptr(box(cur_val));
-            if (p != null) {
-                p = tail_of_list(p);
-                /*tex
-
-                    There can be a leftskip, rightskip, penalty and yes, also a
-                    disc node with a nesting node that points to glue spec ...
-                    and we don't want to analyze that messy lot.
-
-                */
-                while ((p != null) && (type(p) == glue_node)) {
-                    p = alink(p);
-                }
-                if ((p != null) && ! ((type(p) == margin_kern_node) && (subtype(p) == right_side))) {
-                    if (type(p) == disc_node) {
-                        q = alink(p);
-                        if ((q != null) && ((type(q) == margin_kern_node) && (subtype(q) == right_side))) {
-                            p = q;
-                        } else {
-                            /*tex
-
-                                Officially we should look in the replace but
-                                currently protrusion doesn't work anyway with
-                                "foo\discretionary{}{}{bar-} " (no following
-                                char) so we don't need it now.
-                            */
-                        }
-                    }
-                }
-            }
-            if ((p != null) && (type(p) == margin_kern_node) && (subtype(p) == right_side))
-                print_scaled(width(p));
-            else
-                print_char('0');
-            tprint("pt");
-            pop_selector;
-            break;
-        case uniform_deviate_code:
-            scan_int();
-            push_selector;
-            print_int(unif_rand(cur_val));
-            pop_selector;
-            break;
-        case normal_deviate_code:
-            push_selector;
-            print_int(norm_rand());
-            pop_selector;
-            break;
-        case math_char_class_code:
-            {
-                mathcodeval mval;
-                scan_int();
-                mval = get_math_code(cur_val);
-                push_selector;
-                print_int(mval.class_value);
-                pop_selector;
-            }
-            break;
-        case math_char_fam_code:
-            {
-                mathcodeval mval;
-                scan_int();
-                mval = get_math_code(cur_val);
-                push_selector;
-                print_int(mval.family_value);
-                pop_selector;
-            }
-            break;
-        case math_char_slot_code:
-            {
-                mathcodeval mval;
-                scan_int();
-                mval = get_math_code(cur_val);
-                push_selector;
-                print_int(mval.character_value);
-                pop_selector;
-            }
-            break;
-        case insert_ht_code:
-            scan_register_num();
-            push_selector;
-            i = cur_val;
-            p = page_ins_head;
-            while (i >= subtype(vlink(p)))
-                p = vlink(p);
-            if (subtype(p) == i)
-                print_scaled(height(p));
-            else
-                print_char('0');
-            tprint("pt");
-            pop_selector;
-            break;
-        case job_name_code:
-            if (job_name == 0)
-                open_log_file();
-            push_selector;
-            print_job_name();
-            pop_selector;
-            break;
-        case format_name_code:
-            if (job_name == 0)
-                open_log_file();
-            push_selector;
-            print(format_name);
-            pop_selector;
-            break;
-        case luatex_banner_code:
-            push_selector;
-            tprint(luatex_banner);
-            pop_selector;
-            break;
-        case luatex_revision_code:
-            push_selector;
-            print(get_luatexrevision());
-            pop_selector;
-            break;
-        case etex_code:
-            push_selector;
-            tprint(eTeX_version_string);
-            pop_selector;
-            break;
-        case eTeX_revision_code:
-            push_selector;
-            tprint(eTeX_revision);
-            pop_selector;
-            break;
-        case font_identifier_code:
-            confusion("convert");
-            break;
-        default:
-            confusion("convert");
-            break;
-    }
-    str = make_string();
-    (void) str_toks(str_lstring(str));
-    flush_str(str);
-    ins_list(token_link(temp_token_head));
-}
-
-void do_feedback(void)
-{
-    int c = cur_chr;
-    str_number str;
-    int done = 1;
-    switch (c) {
-        case dvi_feedback_code:
-            if (get_o_mode() == OMODE_DVI) {
-                done = do_feedback_dvi(c);
-            } else {
-                tex_error("unexpected use of \\dvifeedback",null);
-                return ;
-            }
-            if (done==0) {
-                /* we recover */
-                normal_warning("dvi backend","unexpected use of \\dvifeedback");
-                return;
-            } else if (done==2) {
-                return;
-            }
-            break;
-        case pdf_feedback_code:
-            if (get_o_mode() == OMODE_PDF) {
-                done = do_feedback_pdf(c);
-            } else {
-                tex_error("unexpected use of \\pdffeedback",null);
-                return ;
-            }
-            if (done==0) {
-                /*tex We recover. */
-                normal_warning("pdf backend","unexpected use of \\pdffeedback");
-                return;
-            } else if (done==2) {
-                return;
-            }
-            break;
-        default:
-            confusion("feedback");
-            break;
-    }
-    str = make_string();
-    (void) str_toks(str_lstring(str));
-    flush_str(str);
-    ins_list(token_link(temp_token_head));
-}
-
-void do_variable(void)
-{
-    int c = cur_chr;
-    int done = 1;
-    switch (c) {
-        case dvi_variable_code:
-            done = do_variable_dvi(c);
-            if (done==0) {
-                /*tex We recover. */
-                normal_warning("dvi backend","unexpected use of \\dvivariable");
-            }
-            return;
-            break;
-        case pdf_variable_code:
-            done = do_variable_pdf(c);
-            if (done==0) {
-                /*tex We recover. */
-                normal_warning("pdf backend","unexpected use of \\pdfvariable");
-            }
-            return;
-            break;
-        default:
-            confusion("variable");
-            break;
-    }
-}
-
-/*tex This boolean is keeping track of the lua string escape state */
-
-boolean in_lua_escape;
-
-static int the_convert_string_dvi(halfword c, int i)
-{
-    return 0 ;
-}
-
-static int the_convert_string_pdf(halfword c, int i)
-{
-    int ff;
-    if (get_o_mode() != OMODE_PDF) {
-        return 0;
-    } else if (scan_keyword("lastlink")) {
-        print_int(pdf_last_link);
-    } else if (scan_keyword("retval")) {
-        print_int(pdf_retval);
-    } else if (scan_keyword("lastobj")) {
-        print_int(pdf_last_obj);
-    } else if (scan_keyword("lastannot")) {
-        print_int(pdf_last_annot);
-    } else if (scan_keyword("xformname")) {
-        print_int(obj_info(static_pdf, i));
-    } else if (scan_keyword("creationdate")) {
-        return 0;
-    } else if (scan_keyword("fontname")) {
-        set_ff(i);
-        print_int(obj_info(static_pdf, pdf_font_num(ff)));
-    } else if (scan_keyword("fontobjnum")) {
-        set_ff(i);
-        print_int(pdf_font_num(ff));
-    } else if (scan_keyword("fontsize")) {
-        print_scaled(font_size(i));
-        tprint("pt");
-    } else if (scan_keyword("pageref")) {
-        print_int(pdf_get_obj(static_pdf, obj_type_page, i, false));
-    } else if (scan_keyword("colorstackinit")) {
-        return 0;
-    } else {
-        return 0;
-    }
-    return 1;
-}
-
-str_number the_convert_string(halfword c, int i)
-{
-    int old_setting;
-    str_number ret = 0;
-    boolean done = true ;
-    old_setting = selector;
-    selector = new_string;
-    switch (c) {
-        case number_code:
-            print_int(i);
-            break;
-     /* case lua_function_code: */
-     /* case lua_code: */
-     /* case expanded_code: */
-        case math_style_code:
-            print_math_style();
-            break;
-     /* case string_code: */
-     /* case cs_string_code: */
-        case roman_numeral_code:
-            print_roman_int(i);
-            break;
-     /* case meaning_code: */
-        case uchar_code:
-            print(i);
-            break;
-     /* lua_escape_string_code: */
-        case font_id_code:
-            print_int(i);
-            break;
-        case font_name_code:
-            append_string((unsigned char *) font_name(i),(unsigned) strlen(font_name(i)));
-            if (font_size(i) != font_dsize(i)) {
-                tprint(" at ");
-                print_scaled(font_size(i));
-                tprint("pt");
-            }
-            break;
-     /* left_margin_kern_code: */
-     /* right_margin_kern_code: */
-        case uniform_deviate_code:
-            print_int(unif_rand(i));
-            break;
-        case normal_deviate_code:
-            print_int(norm_rand());
-            break;
-     /* math_char_class_code: */
-     /* math_char_fam_code: */
-     /* math_char_slot_code: */
-     /* insert_ht_code: */
-        case job_name_code:
-            print_job_name();
-            break;
-        case format_name_code:
-            print(format_name);
-            break;
-        case luatex_banner_code:
-            tprint(luatex_banner);
-            break;
-        case luatex_revision_code:
-            print(get_luatexrevision());
-            break;
-        case etex_code:
-            tprint(eTeX_version_string);
-            break;
-        case eTeX_revision_code:
-            tprint(eTeX_revision);
-            break;
-        case font_identifier_code:
-            print_font_identifier(i);
-            break;
-        /*tex Backend: this might become obsolete! */
-        case dvi_feedback_code:
-            done = the_convert_string_dvi(c,i);
-            break;
-        case pdf_feedback_code:
-            done = the_convert_string_pdf(c,i);
-            break;
-        /*tex done */
-        default:
-            done = false;
-            break;
-    }
-    if (done) {
-        ret = make_string();
-    }
-    selector = old_setting;
-    return ret;
-}
-
-/*tex
-
-    Another way to create a token list is via the \.{\\read} command. The sixteen
-    files potentially usable for reading appear in the following global
-    variables. The value of |read_open[n]| will be |closed| if stream number |n|
-    has not been opened or if it has been fully read; |just_open| if an
-    \.{\\openin} but not a \.{\\read} has been done; and |normal| if it is open
-    and ready to read the next line.
-
-*/
-
-/*tex used for \.{\\read} */
-
-FILE *read_file[16];
-
-/*tex state of |read_file[n]| */
-
-int read_open[17];
-
-void initialize_read(void)
-{
-    int k;
-    for (k = 0; k <= 16; k++)
-        read_open[k] = closed;
-}
-
-/*tex
-
-    The |read_toks| procedure constructs a token list like that for any macro
-    definition, and makes |cur_val| point to it. Parameter |r| points to the
-    control sequence that will receive this token list.
-
-*/
-
-void read_toks(int n, halfword r, halfword j)
-{
-    /*tex tail of the token list */
-    halfword p;
-    /*tex new node being added to the token list via |store_new_token| */
-    halfword q;
-    /*tex saved value of |align_state| */
-    int s;
-    /*tex stream number */
-    int m;
-    scanner_status = defining;
-    warning_index = r;
-    p = get_avail();
-    def_ref = p;
-    set_token_ref_count(def_ref, 0);
-    /*tex the reference count */
-    p = def_ref;
-    store_new_token(end_match_token);
-    if ((n < 0) || (n > 15))
-        m = 16;
-    else
-        m = n;
-    s = align_state;
-    /*tex disable tab marks, etc. */
-    align_state = 1000000;
-    do {
-        /*tex Input and store tokens from the next line of the file. */
-        begin_file_reading();
-        iname = m + 1;
-        if (read_open[m] == closed) {
-            /*tex
-
-                Input for \.{\\read} from the terminal. We input on-line into the
-                |buffer| array, prompting the user explicitly if |n>=0|. The
-                value of |n| is set negative so that additional prompts will not
-                be given in the case of multi-line input.
-
-            */
-            if (interaction > nonstop_mode) {
-                if (n < 0) {
-                    prompt_input("");
-                } else {
-                    wake_up_terminal();
-                    print_ln();
-                    sprint_cs(r);
-                    prompt_input(" =");
-                    n = -1;
-                }
-            } else {
-                fatal_error
-                    ("*** (cannot \\read from terminal in nonstop modes)");
-            }
-
-        } else if (read_open[m] == just_open) {
-            /*tex
-
-                Input the first line of |read_file[m]|. The first line of a file
-                must be treated specially, since |lua_input_ln| must be told not
-                to start with |get|.
-
-            */
-            if (lua_input_ln(read_file[m], (m + 1), false)) {
-                read_open[m] = normal;
-            } else {
-                lua_a_close_in(read_file[m], (m + 1));
-                read_open[m] = closed;
-            }
-
-        } else {
-            /*tex
-
-                Input the next line of |read_file[m]|. An empty line is appended
-                at the end of a |read_file|.
-
-            */
-            if (!lua_input_ln(read_file[m], (m + 1), true)) {
-                lua_a_close_in(read_file[m], (m + 1));
-                read_open[m] = closed;
-                if (align_state != 1000000) {
-                    runaway();
-                    print_err("File ended within \\read");
-                    help1("This \\read has unbalanced braces.");
-                    align_state = 1000000;
-                    error();
-                }
-            }
-
-        }
-        ilimit = last;
-        if (end_line_char_inactive)
-            decr(ilimit);
-        else
-            buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
-        first = ilimit + 1;
-        iloc = istart;
-        istate = new_line;
-        /*tex Handle \.{\\readline} and |goto done|. */
-        if (j == 1) {
-            while (iloc <= ilimit) {
-                /*tex Current line not yet finished. */
-                do_buffer_to_unichar(cur_chr, iloc);
-                if (cur_chr == ' ')
-                    cur_tok = space_token;
-                else
-                    cur_tok = cur_chr + other_token;
-                store_new_token(cur_tok);
-            }
-        } else {
-            while (1) {
-                get_token();
-                if (cur_tok == 0) {
-                    /*tex |cur_cmd=cur_chr=0| will occur at the end of the line. */
-                    break;
-                }
-                if (align_state < 1000000) {
-                    /*tex Unmatched right brace aborts the line. */
-                    do {
-                        get_token();
-                    } while (cur_tok != 0);
-                    align_state = 1000000;
-                    break;
-                }
-                store_new_token(cur_tok);
-            }
-        }
-        end_file_reading();
-
-    } while (align_state != 1000000);
-    cur_val = def_ref;
-    scanner_status = normal;
-    align_state = s;
-}
-
-/*tex Return a string from tokens list: */
-
-str_number tokens_to_string(halfword p)
-{
-    int old_setting;
-    if (selector == new_string)
-        normal_error("tokens","tokens_to_string() called while selector = new_string");
-    old_setting = selector;
-    selector = new_string;
-    show_token_list(token_link(p), null, -1);
-    selector = old_setting;
-    return make_string();
-}
-
-/*tex
-
-    Values like 512 and 128 also work ok. There is not much to gain in
-    optimization here.
-
-*/
-
-#define alloci_default 1024
-#define alloci_step     256
-
-#define make_room(a)                     \
-    if ((unsigned)i+a+1>alloci) {        \
-        ret = xrealloc(ret,(alloci+alloci_step)); \
-        alloci = alloci + alloci_step; \
-    }
-
-#define append_i_byte(a) ret[i++] = (char)(a)
-
-#define Print_char(a) make_room(1); append_i_byte(a)
-
-#define Print_uchar(s) {                                       \
-    make_room(4);                                              \
-    if (s<=0x7F) {                                             \
-      append_i_byte(s);                                        \
-    } else if (s<=0x7FF) {                                     \
-      append_i_byte(0xC0 + (s / 0x40));                        \
-      append_i_byte(0x80 + (s % 0x40));                        \
-    } else if (s<=0xFFFF) {                                    \
-      append_i_byte(0xE0 + (s / 0x1000));                      \
-      append_i_byte(0x80 + ((s % 0x1000) / 0x40));             \
-      append_i_byte(0x80 + ((s % 0x1000) % 0x40));             \
-    } else if (s>=0x110000) {                                  \
-      append_i_byte(s-0x11000);                                \
-    } else {                                                   \
-      append_i_byte(0xF0 + (s / 0x40000));                     \
-      append_i_byte(0x80 + ((s % 0x40000) / 0x1000));          \
-      append_i_byte(0x80 + (((s % 0x40000) % 0x1000) / 0x40)); \
-      append_i_byte(0x80 + (((s % 0x40000) % 0x1000) % 0x40)); \
-    } }
-
-#define Print_esc(b) {                     \
-    const char *v = b;                     \
-    if (e>0 && e<STRING_OFFSET) {          \
-        Print_uchar (e);                   \
-    }                                      \
-    make_room(strlen(v));                  \
-    while (*v) { append_i_byte(*v); v++; } \
-  }
-
-#define Print_str(b) {                     \
-    const char *v = b;                     \
-    make_room(strlen(v));                  \
-    while (*v) { append_i_byte(*v); v++; } \
-  }
-
-#define is_cat_letter(a) \
-    (get_char_cat_code(pool_to_unichar(str_string((a)))) == 11)
-
-/*tex
-
-    The actual token conversion in this function is now functionally equivalent
-    to |show_token_list|, except that it always prints the whole token list.
-    TODO: check whether this causes problems in the lua library.
-
-*/
-
-char *tokenlist_to_cstring(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 = alloci_default;
-    int i = 0;
-    p = pp;
-    if (p == null) {
-        if (siz != NULL)
-            *siz = 0;
-        return NULL;
-    }
-    ret = xmalloc(alloci);
-    /*tex Skip refcount. */
-    p = token_link(p);
-    if (p != null) {
-        e = escape_char_par;
-    }
-    while (p != null) {
-        if (p < (int) fix_mem_min || p > (int) fix_mem_end) {
-            Print_esc("CLOBBERED.");
-            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) {
-                    if (q == null_cs) {
-                        Print_esc("csname");
-                        Print_esc("endcsname");
-                    } else {
-                        Print_esc("IMPOSSIBLE.");
-                    }
-                } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) {
-                    Print_esc("IMPOSSIBLE.");
-                } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) {
-                    Print_esc("NONEXISTENT.");
-                } else {
-                    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) {
-                Print_esc("BAD");
-            } 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:
-                        Print_uchar(c);
-                        break;
-                    case mac_param_cmd:
-                        if (!in_lua_escape && (is_in_csname==0))
-                            Print_uchar(c);
-                        Print_uchar(c);
-                        break;
-                    case out_param_cmd:
-                        Print_uchar(match_chr);
-                        if (c <= 9) {
-                            Print_char(c + '0');
-                        } else {
-                            Print_char('!');
-                            goto EXIT;
-                        }
-                        break;
-                    case match_cmd:
-                        match_chr = c;
-                        Print_uchar(c);
-                        n++;
-                        Print_char(n);
-                        if (n > '9')
-                            goto EXIT;
-                        break;
-                    case end_match_cmd:
-                        if (c == 0) {
-                            Print_char('-');
-                            Print_char('>');
-                        }
-                        break;
-                    default:
-                        not_so_bad(Print_esc);
-                        break;
-                }
-            }
-        }
-        p = token_link(p);
-    }
-  EXIT:
-    ret[i] = '\0';
-    if (siz != NULL)
-        *siz = i;
-    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 = alloci_default;
-    int i = 0;
-    int skipping = 1;
-    p = pp;
-    if (p == null) {
-        if (siz != NULL)
-            *siz = 0;
-        return NULL;
-    }
-    ret = xmalloc(alloci);
-    /*tex Skip refcount. */
-    p = token_link(p);
-    if (p != null) {
-        e = escape_char_par;
-    }
-    while (p != null) {
-        if (p < (int) fix_mem_min || p > (int) fix_mem_end) {
-            /*tex Nothing done. */
-            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) {
-                    /*tex Nothing done. */
-                } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) {
-                    /*tex Nothing done. */
-                } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) {
-                    /*tex Nothing done. */
-                } 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) {
-                /*tex Nothing done. */
-            } 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 {
-                            /*tex Nothing done. */
-                            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:
-                        /*tex Nothing done. */
-                        break;
-                }
-            }
-        }
-        p = token_link(p);
-    }
-  EXIT:
-    ret[i] = '\0';
-    if (siz != NULL)
-        *siz = i;
-    return ret;
-}
-
-lstring *tokenlist_to_lstring(int pp, int inhibit_par)
-{
-    int siz;
-    lstring *ret = xmalloc(sizeof(lstring));
-    ret->s = (unsigned char *) tokenlist_to_cstring(pp, inhibit_par, &siz);
-    ret->l = (size_t) siz;
-    return ret;
-}
-
-void free_lstring(lstring * ls)
-{
-    if (ls == NULL)
-        return;
-    if (ls->s != NULL)
-        free(ls->s);
-    free(ls);
-}
--- texlive-bin.orig/texk/web2c/luatexdir/utils/avlstuff.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-Copyright 2006-2009 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include "utils/avl.h"
-
-/*tex
-
-    Some memory management functions for AVL.
-
-*/
-
-static void *avl_xmalloc(struct libavl_allocator *allocator, size_t size)
-{
-    assert(allocator != NULL && size > 0);
-    return xmalloc((unsigned) size);
-}
-
-static void avl_xfree(struct libavl_allocator *allocator, void *block)
-{
-    assert(allocator != NULL && block != NULL);
-    xfree(block);
-}
-
-struct libavl_allocator avl_xallocator = {
-    avl_xmalloc,
-    avl_xfree
-};
-
-/*tex
-
-    The general AVL comparison functions.
-
-*/
-int comp_int_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    cmp_return(*(const int *) pa, *(const int *) pb);
-    return 0;
-}
-
-int comp_string_entry(const void *pa, const void *pb, void *p)
-{
-    (void) p;
-    return strcmp((const char *) pa, (const char *) pb);
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/utils/avlstuff.w
@@ -0,0 +1,61 @@
+% avlstuff.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% Copyright 2006-2009 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/>.
+
+@* AVL helper functions.
+
+@ @c
+
+
+#include "ptexlib.h"
+#include "utils/avl.h"
+
+@ memory management functions for AVL
+@c
+static void *avl_xmalloc(struct libavl_allocator *allocator, size_t size)
+{
+    assert(allocator != NULL && size > 0);
+    return xmalloc((unsigned) size);
+}
+
+static void avl_xfree(struct libavl_allocator *allocator, void *block)
+{
+    assert(allocator != NULL && block != NULL);
+    xfree(block);
+}
+
+struct libavl_allocator avl_xallocator = {
+    avl_xmalloc,
+    avl_xfree
+};
+
+@ general AVL comparison functions
+@c
+int comp_int_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    cmp_return(*(const int *) pa, *(const int *) pb);
+    return 0;
+}
+
+int comp_string_entry(const void *pa, const void *pb, void *p)
+{
+    (void) p;
+    return strcmp((const char *) pa, (const char *) pb);
+}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/utils/managed-sa.w
@@ -0,0 +1,306 @@
+% managed-sa.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/>.
+
+@* Sparse arrays with an embedded save stack.
+
+These functions are called very often but a few days of experimenting proved that
+there is not much to gain (if at all) from using macros or optimizations like
+preallocating and fast access to the first 128 entries. In practice the overhead
+is mostly in accessing memory and not in (probably inlined) calls. So, we should
+accept fate and wait for faster memory. It's the price we pay for being unicode
+on the one hand and sparse on the other.
+
+@ @c
+
+#include "ptexlib.h"
+
+@ @c
+static void store_sa_stack(sa_tree a, int n, sa_tree_item v, int gl)
+{
+    sa_stack_item st;
+    st.code = n;
+    st.value = v;
+    st.level = gl;
+    if (a->stack == NULL) {
+        a->stack = Mxmalloc_array(sa_stack_item, a->stack_size);
+    } else if (((a->stack_ptr) + 1) >= a->stack_size) {
+        a->stack_size += a->stack_step;
+        a->stack = Mxrealloc_array(a->stack, sa_stack_item, a->stack_size);
+    }
+    (a->stack_ptr)++;
+    a->stack[a->stack_ptr] = st;
+}
+
+@ @c
+static void skip_in_stack(sa_tree a, int n)
+{
+    int p = a->stack_ptr;
+    if (a->stack == NULL)
+        return;
+    while (p > 0) {
+        if (a->stack[p].code == n && a->stack[p].level > 0) {
+            a->stack[p].level = -(a->stack[p].level);
+        }
+        p--;
+    }
+}
+
+@ @c
+sa_tree_item get_sa_item(const sa_tree head, const int n)
+{
+    if (head->tree != NULL) {
+        register int h = HIGHPART_PART(n);
+        if (head->tree[h] != NULL) {
+            register int m = MIDPART_PART(n);
+            if (head->tree[h][m] != NULL) {
+                return head->tree[h][m][LOWPART_PART(n)];
+            }
+        }
+    }
+    return head->dflt;
+}
+
+@ @c
+void set_sa_item(sa_tree head, int n, sa_tree_item v, int gl)
+{
+    int h = HIGHPART_PART(n);
+    int m = MIDPART_PART(n);
+    int l = LOWPART_PART(n);
+    if (head->tree == NULL) {
+        head->tree = (sa_tree_item ***) Mxcalloc_array(sa_tree_item **, HIGHPART);
+    }
+    if (head->tree[h] == NULL) {
+        head->tree[h] = (sa_tree_item **) Mxcalloc_array(sa_tree_item *, MIDPART);
+    }
+    if (head->tree[h][m] == NULL) {
+        int i;
+        head->tree[h][m] = (sa_tree_item *) Mxmalloc_array(sa_tree_item, LOWPART);
+        for (i = 0; i < LOWPART; i++) {
+            head->tree[h][m][i] = head->dflt;
+        }
+    }
+    if (gl <= 1) {
+        skip_in_stack(head, n);
+    } else {
+        store_sa_stack(head, n, head->tree[h][m][l], gl);
+    }
+    head->tree[h][m][l] = v;
+}
+
+@ @c
+void rawset_sa_item(sa_tree head, int n, sa_tree_item v)
+{
+    head->tree[HIGHPART_PART(n)][MIDPART_PART(n)][LOWPART_PART(n)] = v;
+}
+
+@ @c
+void clear_sa_stack(sa_tree a)
+{
+    xfree(a->stack);
+    a->stack_ptr = 0;
+    a->stack_size = a->stack_step;
+}
+
+@ @c
+void destroy_sa_tree(sa_tree a)
+{
+    if (a == NULL)
+        return;
+    if (a->tree != NULL) {
+        int h, m;
+        for (h = 0; h < HIGHPART; h++) {
+            if (a->tree[h] != NULL) {
+                for (m = 0; m < MIDPART; m++) {
+                    xfree(a->tree[h][m]);
+                }
+                xfree(a->tree[h]);
+            }
+        }
+        xfree(a->tree);
+    }
+    xfree(a->stack);
+    xfree(a);
+}
+
+@ @c
+sa_tree copy_sa_tree(sa_tree b)
+{
+    sa_tree a = (sa_tree) Mxmalloc_array(sa_tree_head, 1);
+    a->stack_step = b->stack_step;
+    a->stack_size = b->stack_size;
+    a->stack_type = b->stack_type;
+    a->dflt = b->dflt;
+    a->stack = NULL;
+    a->stack_ptr = 0;
+    a->tree = NULL;
+    if (b->tree != NULL) {
+        int h, m;
+        a->tree = (sa_tree_item ***) Mxcalloc_array(void *, HIGHPART);
+        for (h = 0; h < HIGHPART; h++) {
+            if (b->tree[h] != NULL) {
+                a->tree[h] = (sa_tree_item **) Mxcalloc_array(void *, MIDPART);
+                for (m = 0; m < MIDPART; m++) {
+                    if (b->tree[h][m] != NULL) {
+                        a->tree[h][m] = Mxmalloc_array(sa_tree_item, LOWPART);
+                        memcpy(a->tree[h][m], b->tree[h][m],
+                               sizeof(sa_tree_item) * LOWPART);
+                    }
+                }
+            }
+        }
+    }
+    return a;
+}
+
+@ The main reason to fill in the lowest entry branches here immediately
+is that most of the sparse arrays have a bias toward ASCII values.
+
+Allocating those here immediately improves the chance of the structure
+|a->tree[0][0][x]| being close together in actual memory locations
+
+@c
+
+/* we could save less for type 0 stacks */
+
+sa_tree new_sa_tree(int size, int type, sa_tree_item dflt)
+{
+    sa_tree_head *a;
+    a = (sa_tree_head *) xmalloc(sizeof(sa_tree_head));
+    a->dflt = dflt;
+    a->stack = NULL;
+    a->tree = (sa_tree_item ***) Mxcalloc_array(sa_tree_item **, HIGHPART);
+    a->tree[0] = (sa_tree_item **) Mxcalloc_array(sa_tree_item *, MIDPART);
+    a->stack_size = size;
+    a->stack_step = size;
+    a->stack_type = type;
+    a->stack_ptr = 0;
+ /* printf("creating sa tree of type %d\n",type); */
+    return (sa_tree) a;
+}
+
+@ @c
+void restore_sa_stack(sa_tree head, int gl)
+{
+    sa_stack_item st;
+    if (head->stack == NULL)
+        return;
+    while (head->stack_ptr > 0 && abs(head->stack[head->stack_ptr].level) >= gl) {
+        st = head->stack[head->stack_ptr];
+        if (st.level > 0) {
+            rawset_sa_item(head, st.code, st.value);
+        }
+        (head->stack_ptr)--;
+    }
+}
+
+@ @c
+void dump_sa_tree(sa_tree a, const char * name)
+{
+    boolean f;
+    int x, n;
+    int h, m, l;
+    dump_int(a->stack_step);
+    x = a->dflt.int_value;
+    dump_int(x);
+    if (a->tree != NULL) {
+        dump_int(1); /* marker */
+        n = a->stack_type;
+        dump_int(n);
+     /* printf("dumping sa tree %s with type %d\n",name,n); */
+        for (h = 0; h < HIGHPART; h++) {
+            if (a->tree[h] != NULL) {
+                f = 1;
+                dump_qqqq(f);
+                for (m = 0; m < MIDPART; m++) {
+                    if (a->tree[h][m] != NULL) {
+                        f = 1;
+                        dump_qqqq(f);
+                        for (l = 0; l < LOWPART; l++) {
+                            if (n == 2) {
+                                x = a->tree[h][m][l].dump_uint.value_1;
+                                dump_int(x);
+                                x = a->tree[h][m][l].dump_uint.value_2;
+                                dump_int(x);
+                            } else {
+                                x = a->tree[h][m][l].uint_value;
+                                dump_int(x);
+                            }
+                        }
+                    } else {
+                        f = 0;
+                        dump_qqqq(f);
+                    }
+                }
+            } else {
+                f = 0;
+                dump_qqqq(f);
+            }
+        }
+    } else {
+        dump_int(0); /* marker */
+    }
+}
+
+@ @c
+sa_tree undump_sa_tree(const char * name)
+{
+    int x, n;
+    int h, m, l;
+    boolean f;
+    sa_tree a = (sa_tree) Mxmalloc_array(sa_tree_head, 1);
+    undump_int(x);
+    a->stack_step = x;
+    a->stack_size = x;
+    undump_int(x);
+    a->dflt.int_value = x;
+    a->stack = Mxmalloc_array(sa_stack_item, a->stack_size);
+    a->stack_ptr = 0;
+    a->tree = NULL;
+    undump_int(x); /* marker */
+    if (x == 0)
+        return a;
+    a->tree = (sa_tree_item ***) Mxcalloc_array(void *, HIGHPART);
+    undump_int(n);
+    a->stack_type = n;
+ /* printf("undumping sa tree %s with type %d\n",name,n); */
+    for (h = 0; h < HIGHPART; h++) {
+        undump_qqqq(f);
+        if (f > 0) {
+            a->tree[h] = (sa_tree_item **) Mxcalloc_array(void *, MIDPART);
+            for (m = 0; m < MIDPART; m++) {
+                undump_qqqq(f);
+                if (f > 0) {
+                    a->tree[h][m] = Mxmalloc_array(sa_tree_item, LOWPART);
+                    for (l = 0; l < LOWPART; l++) {
+                        if (n == 2) {
+                            undump_int(x);
+                            a->tree[h][m][l].dump_uint.value_1 = x;
+                            undump_int(x);
+                            a->tree[h][m][l].dump_uint.value_2 = x;
+                        } else {
+                            undump_int(x);
+                            a->tree[h][m][l].uint_value = x;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return a;
+}
--- texlive-bin.orig/texk/web2c/luatexdir/utils/managed-sa.c
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
-
-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/>.
-
-*/
-
-/*tex
-
-    Here we implement sparse arrays with an embedded save stack. These functions
-    are called very often but a few days of experimenting proved that there is
-    not much to gain (if at all) from using macros or optimizations like
-    preallocating and fast access to the first 128 entries. In practice the
-    overhead is mostly in accessing memory and not in (probably inlined) calls.
-    So, we should accept fate and wait for faster memory. It's the price we pay
-    for being unicode on the one hand and sparse on the other.
-
-*/
-
-#include "ptexlib.h"
-
-static void store_sa_stack(sa_tree a, int n, sa_tree_item v, int gl)
-{
-    sa_stack_item st;
-    st.code = n;
-    st.value = v;
-    st.level = gl;
-    if (a->stack == NULL) {
-        a->stack = Mxmalloc_array(sa_stack_item, a->stack_size);
-    } else if (((a->stack_ptr) + 1) >= a->stack_size) {
-        a->stack_size += a->stack_step;
-        a->stack = Mxrealloc_array(a->stack, sa_stack_item, a->stack_size);
-    }
-    (a->stack_ptr)++;
-    a->stack[a->stack_ptr] = st;
-}
-
-static void skip_in_stack(sa_tree a, int n)
-{
-    int p = a->stack_ptr;
-    if (a->stack == NULL)
-        return;
-    while (p > 0) {
-        if (a->stack[p].code == n && a->stack[p].level > 0) {
-            a->stack[p].level = -(a->stack[p].level);
-        }
-        p--;
-    }
-}
-
-sa_tree_item get_sa_item(const sa_tree head, const int n)
-{
-    if (head->tree != NULL) {
-        register int h = HIGHPART_PART(n);
-        if (head->tree[h] != NULL) {
-            register int m = MIDPART_PART(n);
-            if (head->tree[h][m] != NULL) {
-                return head->tree[h][m][LOWPART_PART(n)];
-            }
-        }
-    }
-    return head->dflt;
-}
-
-void set_sa_item(sa_tree head, int n, sa_tree_item v, int gl)
-{
-    int h = HIGHPART_PART(n);
-    int m = MIDPART_PART(n);
-    int l = LOWPART_PART(n);
-    if (head->tree == NULL) {
-        head->tree = (sa_tree_item ***) Mxcalloc_array(sa_tree_item **, HIGHPART);
-    }
-    if (head->tree[h] == NULL) {
-        head->tree[h] = (sa_tree_item **) Mxcalloc_array(sa_tree_item *, MIDPART);
-    }
-    if (head->tree[h][m] == NULL) {
-        int i;
-        head->tree[h][m] = (sa_tree_item *) Mxmalloc_array(sa_tree_item, LOWPART);
-        for (i = 0; i < LOWPART; i++) {
-            head->tree[h][m][i] = head->dflt;
-        }
-    }
-    if (gl <= 1) {
-        skip_in_stack(head, n);
-    } else {
-        store_sa_stack(head, n, head->tree[h][m][l], gl);
-    }
-    head->tree[h][m][l] = v;
-}
-
-void rawset_sa_item(sa_tree head, int n, sa_tree_item v)
-{
-    head->tree[HIGHPART_PART(n)][MIDPART_PART(n)][LOWPART_PART(n)] = v;
-}
-
-void clear_sa_stack(sa_tree a)
-{
-    xfree(a->stack);
-    a->stack_ptr = 0;
-    a->stack_size = a->stack_step;
-}
-
-void destroy_sa_tree(sa_tree a)
-{
-    if (a == NULL)
-        return;
-    if (a->tree != NULL) {
-        int h, m;
-        for (h = 0; h < HIGHPART; h++) {
-            if (a->tree[h] != NULL) {
-                for (m = 0; m < MIDPART; m++) {
-                    xfree(a->tree[h][m]);
-                }
-                xfree(a->tree[h]);
-            }
-        }
-        xfree(a->tree);
-    }
-    xfree(a->stack);
-    xfree(a);
-}
-
-sa_tree copy_sa_tree(sa_tree b)
-{
-    sa_tree a = (sa_tree) Mxmalloc_array(sa_tree_head, 1);
-    a->stack_step = b->stack_step;
-    a->stack_size = b->stack_size;
-    a->stack_type = b->stack_type;
-    a->dflt = b->dflt;
-    a->stack = NULL;
-    a->stack_ptr = 0;
-    a->tree = NULL;
-    if (b->tree != NULL) {
-        int h, m;
-        a->tree = (sa_tree_item ***) Mxcalloc_array(void *, HIGHPART);
-        for (h = 0; h < HIGHPART; h++) {
-            if (b->tree[h] != NULL) {
-                a->tree[h] = (sa_tree_item **) Mxcalloc_array(void *, MIDPART);
-                for (m = 0; m < MIDPART; m++) {
-                    if (b->tree[h][m] != NULL) {
-                        a->tree[h][m] = Mxmalloc_array(sa_tree_item, LOWPART);
-                        memcpy(a->tree[h][m], b->tree[h][m],
-                               sizeof(sa_tree_item) * LOWPART);
-                    }
-                }
-            }
-        }
-    }
-    return a;
-}
-
-/*tes
-
-    The main reason to fill in the lowest entry branches here immediately is that
-    most of the sparse arrays have a bias toward \ASCII\ values.
-
-    Allocating those here immediately improves the chance of the structure
-    |a->tree[0][0][x]| being close together in actual memory locations
-
-    We could save less for type 0 stacks.
-
-*/
-
-sa_tree new_sa_tree(int size, int type, sa_tree_item dflt)
-{
-    sa_tree_head *a;
-    a = (sa_tree_head *) xmalloc(sizeof(sa_tree_head));
-    a->dflt = dflt;
-    a->stack = NULL;
-    a->tree = (sa_tree_item ***) Mxcalloc_array(sa_tree_item **, HIGHPART);
-    a->tree[0] = (sa_tree_item **) Mxcalloc_array(sa_tree_item *, MIDPART);
-    a->stack_size = size;
-    a->stack_step = size;
-    a->stack_type = type;
-    a->stack_ptr = 0;
-    return (sa_tree) a;
-}
-
-void restore_sa_stack(sa_tree head, int gl)
-{
-    sa_stack_item st;
-    if (head->stack == NULL)
-        return;
-    while (head->stack_ptr > 0 && abs(head->stack[head->stack_ptr].level) >= gl) {
-        st = head->stack[head->stack_ptr];
-        if (st.level > 0) {
-            rawset_sa_item(head, st.code, st.value);
-        }
-        (head->stack_ptr)--;
-    }
-}
-
-void dump_sa_tree(sa_tree a, const char * name)
-{
-    boolean f;
-    int x, n;
-    int h, m, l;
-    dump_int(a->stack_step);
-    x = a->dflt.int_value;
-    dump_int(x);
-    if (a->tree != NULL) {
-        /*tex A marker: */
-        dump_int(1);
-        n = a->stack_type;
-        dump_int(n);
-        for (h = 0; h < HIGHPART; h++) {
-            if (a->tree[h] != NULL) {
-                f = 1;
-                dump_qqqq(f);
-                for (m = 0; m < MIDPART; m++) {
-                    if (a->tree[h][m] != NULL) {
-                        f = 1;
-                        dump_qqqq(f);
-                        for (l = 0; l < LOWPART; l++) {
-                            if (n == 2) {
-                                x = a->tree[h][m][l].dump_uint.value_1;
-                                dump_int(x);
-                                x = a->tree[h][m][l].dump_uint.value_2;
-                                dump_int(x);
-                            } else {
-                                x = a->tree[h][m][l].uint_value;
-                                dump_int(x);
-                            }
-                        }
-                    } else {
-                        f = 0;
-                        dump_qqqq(f);
-                    }
-                }
-            } else {
-                f = 0;
-                dump_qqqq(f);
-            }
-        }
-    } else {
-        /*tex A marker: */
-        dump_int(0);
-    }
-}
-
-sa_tree undump_sa_tree(const char * name)
-{
-    int x, n;
-    int h, m, l;
-    boolean f;
-    sa_tree a = (sa_tree) Mxmalloc_array(sa_tree_head, 1);
-    undump_int(x);
-    a->stack_step = x;
-    a->stack_size = x;
-    undump_int(x);
-    a->dflt.int_value = x;
-    a->stack = Mxmalloc_array(sa_stack_item, a->stack_size);
-    a->stack_ptr = 0;
-    a->tree = NULL;
-    /*tex The marker: */
-    undump_int(x);
-    if (x == 0)
-        return a;
-    a->tree = (sa_tree_item ***) Mxcalloc_array(void *, HIGHPART);
-    undump_int(n);
-    a->stack_type = n;
-    for (h = 0; h < HIGHPART; h++) {
-        undump_qqqq(f);
-        if (f > 0) {
-            a->tree[h] = (sa_tree_item **) Mxcalloc_array(void *, MIDPART);
-            for (m = 0; m < MIDPART; m++) {
-                undump_qqqq(f);
-                if (f > 0) {
-                    a->tree[h][m] = Mxmalloc_array(sa_tree_item, LOWPART);
-                    for (l = 0; l < LOWPART; l++) {
-                        if (n == 2) {
-                            undump_int(x);
-                            a->tree[h][m][l].dump_uint.value_1 = x;
-                            undump_int(x);
-                            a->tree[h][m][l].dump_uint.value_2 = x;
-                        } else {
-                            undump_int(x);
-                            a->tree[h][m][l].uint_value = x;
-                        }
-                    }
-                }
-            }
-        }
-    }
-    return a;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/utils/unistring.w
@@ -0,0 +1,210 @@
+% unistring.w
+%
+% Copyright 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
+
+
+@ @c
+#include "ptexlib.h"
+#include <string.h>
+
+@ @c
+static void utf_error(void)
+{
+    const char *hlp[] =
+        { "A funny symbol that I can't read has just been (re)read.",
+        "Just continue, I'll change it to 0xFFFD.",
+        NULL
+    };
+    deletions_allowed = false;
+    tex_error("String contains an invalid utf-8 sequence", hlp);
+    deletions_allowed = true;
+}
+
+@ @c
+unsigned str2uni(const unsigned char *k)
+{
+    register int ch;
+    int val = 0xFFFD;
+    const unsigned char *text = k;
+    if ((ch = *text++) < 0x80) {
+        val = (unsigned) ch;
+    } else if (ch <= 0xbf) {    /* error */
+    } else if (ch <= 0xdf) {
+        if (*text >= 0x80 && *text < 0xc0)
+            val = (unsigned) (((ch & 0x1f) << 6) | (*text++ & 0x3f));
+    } else if (ch <= 0xef) {
+        if (*text >= 0x80 && *text < 0xc0 && text[1] >= 0x80 && text[1] < 0xc0) {
+            val = (unsigned)
+                (((ch & 0xf) << 12) | ((text[0] & 0x3f) << 6) |
+                 (text[1] & 0x3f));
+        }
+    } else if (ch <= 0xf7) {
+        int w = (((ch & 0x7) << 2) | ((text[0] & 0x30) >> 4)) - 1, w2;
+        w = (w << 6) | ((text[0] & 0xf) << 2) | ((text[1] & 0x30) >> 4);
+        w2 = ((text[1] & 0xf) << 6) | (text[2] & 0x3f);
+        val = (unsigned) (w * 0x400 + w2 + 0x10000);
+        if (*text < 0x80 || text[1] < 0x80 || text[2] < 0x80 ||
+            *text >= 0xc0 || text[1] >= 0xc0 || text[2] >= 0xc0)
+            val = 0xFFFD;
+    } else {
+        /* the 5- and 6-byte UTF-8 sequences generate integers
+           that are outside of the valid UCS range, and therefore
+           unsupported
+         */
+    }
+    if (val == 0xFFFD)
+        utf_error();
+    return (val);
+}
+
+@ This is a very basic helper
+@c
+unsigned char *uni2str(unsigned unic)
+{
+    unsigned char *buf = xmalloc(5);
+    unsigned char *pt = buf;
+    if (unic < 0x80)
+        *pt++ = (unsigned char) unic;
+    else if (unic < 0x800) {
+        *pt++ = (unsigned char) (0xc0 | (unic >> 6));
+        *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
+    } else if (unic >= 0x110000) {
+        *pt++ = (unsigned char) (unic - 0x110000);
+    } else if (unic < 0x10000) {
+        *pt++ = (unsigned char) (0xe0 | (unic >> 12));
+        *pt++ = (unsigned char) (0x80 | ((unic >> 6) & 0x3f));
+        *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
+    } else {
+        unsigned val = unic - 0x10000;
+        int u = (int) (((val & 0xf0000) >> 16) + 1);
+        int z = (int)  ((val & 0x0f000) >> 12);
+        int y = (int)  ((val & 0x00fc0) >> 6);
+        int x = (int)   (val & 0x0003f);
+        *pt++ = (unsigned char) (0xf0 | (u >> 2));
+        *pt++ = (unsigned char) (0x80 | ((u & 3) << 4) | z);
+        *pt++ = (unsigned char) (0x80 | y);
+        *pt++ = (unsigned char) (0x80 | x);
+    }
+    *pt = '\0';
+    return buf;
+}
+
+@ |buffer_to_unichar| converts a sequence of bytes in the |buffer|
+into a unicode character value. It does not check for overflow
+of the |buffer|, but it is careful to check the validity of the
+UTF-8 encoding.
+
+@c
+int buffer_to_unichar(int k)
+{
+   return str2uni((const unsigned char *)(buffer+k));
+}
+
+
+@ These came from texlang.w
+@c
+char *uni2string(char *utf8_text, unsigned ch)
+{
+    /* Increment and deposit character */
+    if (ch >= 17 * 65536)
+        return (utf8_text);
+
+    if (ch <= 127)
+        *utf8_text++ = (char) ch;
+    else if (ch <= 0x7ff) {
+        *utf8_text++ = (char) (0xc0 | (ch >> 6));
+        *utf8_text++ = (char) (0x80 | (ch & 0x3f));
+    } else if (ch <= 0xffff) {
+        *utf8_text++ = (char) (0xe0 | (ch >> 12));
+        *utf8_text++ = (char) (0x80 | ((ch >> 6) & 0x3f));
+        *utf8_text++ = (char) (0x80 | (ch & 0x3f));
+    } else {
+        unsigned val = ch - 0x10000;
+        unsigned u = ((val & 0xf0000) >> 16) + 1, z = (val & 0x0f000) >> 12, y =
+            (val & 0x00fc0) >> 6, x = val & 0x0003f;
+        *utf8_text++ = (char) (0xf0 | (u >> 2));
+        *utf8_text++ = (char) (0x80 | ((u & 3) << 4) | z);
+        *utf8_text++ = (char) (0x80 | y);
+        *utf8_text++ = (char) (0x80 | x);
+    }
+    return (utf8_text);
+}
+
+unsigned u_length(register unsigned int *str)
+{
+    register unsigned len = 0;
+    while (*str++ != '\0')
+        ++len;
+    return (len);
+}
+
+
+void utf2uni_strcpy(unsigned int *ubuf, const char *utf8buf)
+{
+    int len = (int) strlen(utf8buf) + 1;
+    unsigned int *upt = ubuf, *uend = ubuf + len - 1;
+    const unsigned char *pt = (const unsigned char *) utf8buf, *end =
+        pt + strlen(utf8buf);
+    int w, w2;
+
+    while (pt < end && *pt != '\0' && upt < uend) {
+        if (*pt <= 127)
+            *upt = *pt++;
+        else if (*pt <= 0xdf) {
+            *upt = (unsigned int) (((*pt & 0x1f) << 6) | (pt[1] & 0x3f));
+            pt += 2;
+        } else if (*pt <= 0xef) {
+            *upt =
+                (unsigned int) (((*pt & 0xf) << 12) | ((pt[1] & 0x3f) << 6) |
+                                (pt[2] & 0x3f));
+            pt += 3;
+        } else {
+            w = (((*pt & 0x7) << 2) | ((pt[1] & 0x30) >> 4)) - 1;
+            w = (w << 6) | ((pt[1] & 0xf) << 2) | ((pt[2] & 0x30) >> 4);
+            w2 = ((pt[2] & 0xf) << 6) | (pt[3] & 0x3f);
+            *upt = (unsigned int) (w * 0x400 + w2 + 0x10000);
+            pt += 4;
+        }
+        ++upt;
+    }
+    *upt = '\0';
+}
+
+@ @c
+char *utf16be_str(long code)
+{
+    static char buf[SMALL_BUF_SIZE];
+    long v;
+    unsigned vh, vl;
+
+    assert(code >= 0);
+
+    if (code <= 0xFFFF)
+        sprintf(buf, "%04lX", code);
+    else {
+        v = code - 0x10000;
+        vh = (unsigned) (v / 0x400 + 0xD800);
+        vl = (unsigned) (v % 0x400 + 0xDC00);
+        sprintf(buf, "%04X%04X", vh, vl);
+    }
+    return buf;
+}
+
+
--- texlive-bin.orig/texk/web2c/luatexdir/utils/unistring.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
-
-Copyright 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/>.
-
-*/
-
-#include "ptexlib.h"
-#include <string.h>
-
-static void utf_error(void)
-{
-    const char *hlp[] = {
-        "A funny symbol that I can't read has just been (re)read.",
-        "Just continue, I'll change it to 0xFFFD.",
-        NULL
-    };
-    deletions_allowed = false;
-    tex_error("String contains an invalid utf-8 sequence", hlp);
-    deletions_allowed = true;
-}
-
-unsigned str2uni(const unsigned char *k)
-{
-    register int ch;
-    int val = 0xFFFD;
-    const unsigned char *text = k;
-    if ((ch = *text++) < 0x80) {
-        val = (unsigned) ch;
-    } else if (ch <= 0xbf) {
-        /*tex An error that we skip. */
-    } else if (ch <= 0xdf) {
-        if (*text >= 0x80 && *text < 0xc0)
-            val = (unsigned) (((ch & 0x1f) << 6) | (*text++ & 0x3f));
-    } else if (ch <= 0xef) {
-        if (*text >= 0x80 && *text < 0xc0 && text[1] >= 0x80 && text[1] < 0xc0) {
-            val = (unsigned)
-                (((ch & 0xf) << 12) | ((text[0] & 0x3f) << 6) |
-                 (text[1] & 0x3f));
-        }
-    } else if (ch <= 0xf7) {
-        int w = (((ch & 0x7) << 2) | ((text[0] & 0x30) >> 4)) - 1, w2;
-        w = (w << 6) | ((text[0] & 0xf) << 2) | ((text[1] & 0x30) >> 4);
-        w2 = ((text[1] & 0xf) << 6) | (text[2] & 0x3f);
-        val = (unsigned) (w * 0x400 + w2 + 0x10000);
-        if (*text < 0x80 || text[1] < 0x80 || text[2] < 0x80 ||
-            *text >= 0xc0 || text[1] >= 0xc0 || text[2] >= 0xc0)
-            val = 0xFFFD;
-    } else {
-        /*tex
-
-            The 5- and 6-byte UTF-8 sequences generate integers that are outside
-            of the valid UCS range, and therefore unsupported.
-
-         */
-    }
-    if (val == 0xFFFD)
-        utf_error();
-    return (val);
-}
-
-/*tex
-
-    A real basic helper.
-*/
-
-unsigned char *uni2str(unsigned unic)
-{
-    unsigned char *buf = xmalloc(5);
-    unsigned char *pt = buf;
-    if (unic < 0x80)
-        *pt++ = (unsigned char) unic;
-    else if (unic < 0x800) {
-        *pt++ = (unsigned char) (0xc0 | (unic >> 6));
-        *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
-    } else if (unic >= 0x110000) {
-        *pt++ = (unsigned char) (unic - 0x110000);
-    } else if (unic < 0x10000) {
-        *pt++ = (unsigned char) (0xe0 | (unic >> 12));
-        *pt++ = (unsigned char) (0x80 | ((unic >> 6) & 0x3f));
-        *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
-    } else {
-        unsigned val = unic - 0x10000;
-        int u = (int) (((val & 0xf0000) >> 16) + 1);
-        int z = (int)  ((val & 0x0f000) >> 12);
-        int y = (int)  ((val & 0x00fc0) >> 6);
-        int x = (int)   (val & 0x0003f);
-        *pt++ = (unsigned char) (0xf0 | (u >> 2));
-        *pt++ = (unsigned char) (0x80 | ((u & 3) << 4) | z);
-        *pt++ = (unsigned char) (0x80 | y);
-        *pt++ = (unsigned char) (0x80 | x);
-    }
-    *pt = '\0';
-    return buf;
-}
-
-/*tex
-
-    Function |buffer_to_unichar| converts a sequence of bytes in the |buffer|
-    into a unicode character value. It does not check for overflow of the
-    |buffer|, but it is careful to check the validity of the \UTF-8 encoding.
-
-*/
-
-int buffer_to_unichar(int k)
-{
-   return str2uni((const unsigned char *)(buffer+k));
-}
-
-/*tex
-
-    These came from texlang.c:
-
-*/
-
-char *uni2string(char *utf8_text, unsigned ch)
-{
-    /*tex Increment and deposit character: */
-    if (ch >= 17 * 65536)
-        return (utf8_text);
-    if (ch <= 127)
-        *utf8_text++ = (char) ch;
-    else if (ch <= 0x7ff) {
-        *utf8_text++ = (char) (0xc0 | (ch >> 6));
-        *utf8_text++ = (char) (0x80 | (ch & 0x3f));
-    } else if (ch <= 0xffff) {
-        *utf8_text++ = (char) (0xe0 | (ch >> 12));
-        *utf8_text++ = (char) (0x80 | ((ch >> 6) & 0x3f));
-        *utf8_text++ = (char) (0x80 | (ch & 0x3f));
-    } else {
-        unsigned val = ch - 0x10000;
-        unsigned u = ((val & 0xf0000) >> 16) + 1, z = (val & 0x0f000) >> 12, y =
-            (val & 0x00fc0) >> 6, x = val & 0x0003f;
-        *utf8_text++ = (char) (0xf0 | (u >> 2));
-        *utf8_text++ = (char) (0x80 | ((u & 3) << 4) | z);
-        *utf8_text++ = (char) (0x80 | y);
-        *utf8_text++ = (char) (0x80 | x);
-    }
-    return (utf8_text);
-}
-
-unsigned u_length(register unsigned int *str)
-{
-    register unsigned len = 0;
-    while (*str++ != '\0')
-        ++len;
-    return (len);
-}
-
-void utf2uni_strcpy(unsigned int *ubuf, const char *utf8buf)
-{
-    int len = (int) strlen(utf8buf) + 1;
-    unsigned int *upt = ubuf, *uend = ubuf + len - 1;
-    const unsigned char *pt = (const unsigned char *) utf8buf, *end = pt + strlen(utf8buf);
-    int w, w2;
-    while (pt < end && *pt != '\0' && upt < uend) {
-        if (*pt <= 127)
-            *upt = *pt++;
-        else if (*pt <= 0xdf) {
-            *upt = (unsigned int) (((*pt & 0x1f) << 6) | (pt[1] & 0x3f));
-            pt += 2;
-        } else if (*pt <= 0xef) {
-            *upt = (unsigned int) (((*pt & 0xf) << 12) | ((pt[1] & 0x3f) << 6) | (pt[2] & 0x3f));
-            pt += 3;
-        } else {
-            w = (((*pt & 0x7) << 2) | ((pt[1] & 0x30) >> 4)) - 1;
-            w = (w << 6) | ((pt[1] & 0xf) << 2) | ((pt[2] & 0x30) >> 4);
-            w2 = ((pt[2] & 0xf) << 6) | (pt[3] & 0x3f);
-            *upt = (unsigned int) (w * 0x400 + w2 + 0x10000);
-            pt += 4;
-        }
-        ++upt;
-    }
-    *upt = '\0';
-}
-
-char *utf16be_str(long code)
-{
-    static char buf[SMALL_BUF_SIZE];
-    long v;
-    unsigned vh, vl;
-    if (code <= 0xFFFF)
-        sprintf(buf, "%04lX", code);
-    else {
-        v = code - 0x10000;
-        vh = (unsigned) (v / 0x400 + 0xD800);
-        vl = (unsigned) (v % 0x400 + 0xDC00);
-        sprintf(buf, "%04X%04X", vh, vl);
-    }
-    return buf;
-}
--- /dev/null
+++ texlive-bin/texk/web2c/luatexdir/utils/utils.w
@@ -0,0 +1,499 @@
+% utils.w
+%
+% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
+% Copyright 2006-2012 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
+
+
+@ @c
+#include "ptexlib.h"
+
+#include <kpathsea/config.h> /* this is a trick to load mingw32's io.h early,
+				using a macro redefinition of |eof()|. */
+#include "sys/types.h"
+#include <kpathsea/c-stat.h>
+#include <kpathsea/c-fopen.h>
+#include <string.h>
+#include <time.h>
+#include <float.h>              /* for |DBL_EPSILON| */
+#include "zlib.h"
+#include "md5.h"
+
+#include "lua/luatex-api.h"     /* for luatex_banner */
+#include "luatex_svnversion.h"
+
+#include "png.h"
+#include "mplib.h"
+
+/* POPPLER_VERSION is defined in poppler-config.h for poppler from
+ * the TeX Live tree, or in the Makefile for an installed version.  */
+#include "poppler-config.h"
+
+@ @c
+#define check_nprintf(size_get, size_want) \
+    if ((unsigned)(size_get) >= (unsigned)(size_want)) \
+        formatted_error("internal","snprintf failed: file %s, line %d", __FILE__, __LINE__);
+
+char *cur_file_name = NULL;
+static char print_buf[PRINTF_BUF_SIZE];
+int epochseconds;
+int microseconds;
+
+/* define |char_ptr|, |char_array|, and |char_limit| */
+typedef char char_entry;
+define_array(char);
+
+@ @c
+#define SUBSET_TAG_LENGTH 6
+void make_subset_tag(fd_entry * fd)
+{
+    int i, j = 0, a[SUBSET_TAG_LENGTH];
+    md5_state_t pms;
+    char *glyph;
+    glw_entry *glw_glyph;
+    struct avl_traverser t;
+    md5_byte_t digest[16];
+    void **aa;
+    static struct avl_table *st_tree = NULL;
+    if (st_tree == NULL)
+        st_tree = avl_create(comp_string_entry, NULL, &avl_xallocator);
+    assert(fd != NULL);
+    assert(fd->gl_tree != NULL);
+    assert(fd->fontname != NULL);
+    assert(fd->subset_tag == NULL);
+    fd->subset_tag = xtalloc(SUBSET_TAG_LENGTH + 1, char);
+    do {
+        md5_init(&pms);
+        avl_t_init(&t, fd->gl_tree);
+        if (is_cidkeyed(fd->fm)) {      /* |glw_entry| items */
+            for (glw_glyph = (glw_entry *) avl_t_first(&t, fd->gl_tree);
+                 glw_glyph != NULL; glw_glyph = (glw_entry *) avl_t_next(&t)) {
+                glyph = malloc(24);
+                sprintf(glyph, "%05u%05u ", glw_glyph->id, glw_glyph->wd);
+                md5_append(&pms, (md5_byte_t *) glyph, (int) strlen(glyph));
+                free(glyph);
+            }
+        } else {
+            for (glyph = (char *) avl_t_first(&t, fd->gl_tree); glyph != NULL;
+                 glyph = (char *) avl_t_next(&t)) {
+                md5_append(&pms, (md5_byte_t *) glyph, (int) strlen(glyph));
+                md5_append(&pms, (const md5_byte_t *) " ", 1);
+            }
+        }
+        md5_append(&pms, (md5_byte_t *) fd->fontname,
+                   (int) strlen(fd->fontname));
+        md5_append(&pms, (md5_byte_t *) & j, sizeof(int));      /* to resolve collision */
+        md5_finish(&pms, digest);
+        for (a[0] = 0, i = 0; i < 13; i++)
+            a[0] += digest[i];
+        for (i = 1; i < SUBSET_TAG_LENGTH; i++)
+            a[i] = a[i - 1] - digest[i - 1] + digest[(i + 12) % 16];
+        for (i = 0; i < SUBSET_TAG_LENGTH; i++)
+            fd->subset_tag[i] = (char) (a[i] % 26 + 'A');
+        fd->subset_tag[SUBSET_TAG_LENGTH] = '\0';
+        j++;
+        assert(j < 100);
+    }
+    while ((char *) avl_find(st_tree, fd->subset_tag) != NULL);
+    aa = avl_probe(st_tree, fd->subset_tag);
+    assert(aa != NULL);
+    if (j > 2)
+        formatted_warning("subsets","subset-tag collision, resolved in round %d",j);
+}
+
+@ @c
+__attribute__ ((format(printf, 1, 2)))
+void tex_printf(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
+    tprint(print_buf);
+    xfflush(stdout);
+    va_end(args);
+}
+
+@ @c
+size_t xfwrite(void *ptr, size_t size, size_t nmemb, FILE * stream)
+{
+    if (fwrite(ptr, size, nmemb, stream) != nmemb)
+        formatted_error("file io","fwrite() failed");
+    return nmemb;
+}
+
+@ @c
+int xfflush(FILE * stream)
+{
+    if (fflush(stream) != 0)
+        formatted_error("file io","fflush() failed (%s)", strerror(errno));
+    return 0;
+}
+
+@ @c
+int xgetc(FILE * stream)
+{
+    int c = getc(stream);
+    if (c < 0 && c != EOF)
+        formatted_error("file io","getc() failed (%s)", strerror(errno));
+    return c;
+}
+
+@ @c
+int xputc(int c, FILE * stream)
+{
+    int i = putc(c, stream);
+    if (i < 0)
+        formatted_error("file io","putc() failed (%s)", strerror(errno));
+    return i;
+}
+
+@ @c
+scaled ext_xn_over_d(scaled x, scaled n, scaled d)
+{
+    double r = (((double) x) * ((double) n)) / ((double) d);
+    if (r > DBL_EPSILON)
+        r += 0.5;
+    else
+        r -= 0.5;
+    if (r >= (double) max_integer || r <= -(double) max_integer)
+        normal_warning("internal","arithmetic number too big");
+    return (scaled) r;
+}
+
+@ function strips trailing zeros in string with numbers;
+leading zeros are not stripped (as in real life)
+@c
+#if 0
+char *stripzeros(char *a)
+{
+    enum { NONUM, DOTNONUM, INT, DOT, LEADDOT, FRAC } s = NONUM, t = NONUM;
+    char *p, *q, *r;
+    for (p = q = r = a; *p != '\0';) {
+        switch (s) {
+        case NONUM:
+            if (*p >= '0' && *p <= '9')
+                s = INT;
+            else if (*p == '.')
+                s = LEADDOT;
+            break;
+        case DOTNONUM:
+            if (*p != '.' && (*p < '0' || *p > '9'))
+                s = NONUM;
+            break;
+        case INT:
+            if (*p == '.')
+                s = DOT;
+            else if (*p < '0' || *p > '9')
+                s = NONUM;
+            break;
+        case DOT:
+        case LEADDOT:
+            if (*p >= '0' && *p <= '9')
+                s = FRAC;
+            else if (*p == '.')
+                s = DOTNONUM;
+            else
+                s = NONUM;
+            break;
+        case FRAC:
+            if (*p == '.')
+                s = DOTNONUM;
+            else if (*p < '0' || *p > '9')
+                s = NONUM;
+            break;
+        default:;
+        }
+        switch (s) {
+        case DOT:
+            r = q;
+            break;
+        case LEADDOT:
+            r = q + 1;
+            break;
+        case FRAC:
+            if (*p > '0')
+                r = q + 1;
+            break;
+        case NONUM:
+            if ((t == FRAC || t == DOT) && r != a) {
+                q = r--;
+                if (*r == '.')  /* was a LEADDOT */
+                    *r = '0';
+                r = a;
+            }
+            break;
+        default:;
+        }
+        *q++ = *p++;
+        t = s;
+    }
+    *q = '\0';
+    return a;
+}
+#endif
+
+@ @c
+void initversionstring(char **versions)
+{
+#ifdef LuajitTeX
+#define LUA_VER_STRING  LUAJIT_VERSION
+#else
+#define LUA_VER_STRING  "lua version " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "." LUA_VERSION_RELEASE
+#endif
+#define STR(tok) STR2(tok)
+#define STR2(tok) #tok
+    const_string fmt =
+                    "Compiled with libpng %s; using %s\n"
+                    "Compiled with %s\n" /* Lua or LuaJIT */
+                    "Compiled with mplib version %s\n"
+                    "Compiled with poppler version %s\n"
+                    "Compiled with zlib %s; using %s\n"
+                    "\nDevelopment id: %s\n";
+    size_t len = strlen(fmt)
+                    + strlen(PNG_LIBPNG_VER_STRING) + strlen(png_libpng_ver)
+                    + strlen(LUA_VER_STRING) 
+                    + strlen(mp_metapost_version())
+                    + strlen(POPPLER_VERSION)
+                    + strlen(ZLIB_VERSION) + strlen(zlib_version)
+                    + strlen(STR(luatex_svn_revision))
+                    + 1;
+
+    /* len will be more than enough, because of the placeholder chars in fmt
+       that get replaced by the arguments.  */
+    *versions = xmalloc(len);
+    sprintf(*versions, fmt,
+                    PNG_LIBPNG_VER_STRING, png_libpng_ver, LUA_VER_STRING,
+                    mp_metapost_version(),POPPLER_VERSION,
+                    ZLIB_VERSION, zlib_version,STR(luatex_svn_revision));
+#undef STR2
+#undef STR
+#undef LUA_VER_STRING
+}
+
+@ @c
+void check_buffer_overflow(int wsize)
+{
+    if (wsize > buf_size) {
+        int nsize = buf_size + buf_size / 5 + 5;
+        if (nsize < wsize) {
+            nsize = wsize + 5;
+        }
+        buffer =
+            (unsigned char *) xreallocarray(buffer, char, (unsigned) nsize);
+        buf_size = nsize;
+    }
+}
+
+@  the return value is a decimal number with the point |dd| places from the back,
+   |scaled_out| is the number of scaled points corresponding to that.
+
+@c
+#define max_integer 0x7FFFFFFF
+
+scaled divide_scaled(scaled s, scaled m, int dd)
+{
+    register scaled q;
+    register scaled r;
+    int i;
+    int sign = 1;
+    if (s < 0) {
+        sign = -sign;
+        s = -s;
+    }
+    if (m < 0) {
+        sign = -sign;
+        m = -m;
+    }
+    if (m == 0) {
+        normal_error("arithmetic", "divided by zero");
+    } else if (m >= (max_integer / 10)) {
+        normal_error("arithmetic", "number too big");
+    }
+    q = s / m;
+    r = s % m;
+    for (i = 1; i <= (int) dd; i++) {
+        q = 10 * q + (10 * r) / m;
+        r = (10 * r) % m;
+    }
+    /* rounding */
+    if (2 * r >= m) {
+        q++;
+    }
+    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)
+{
+    double dd, di = 0.0;
+    dd = sd / md * n;
+    if (dd > 0.0)
+        di = floor(dd + 0.5);
+    else if (dd < 0.0)
+        di = -floor((-dd) + 0.5);
+    return (scaled) di;
+}
+
+@ @c
+int do_zround(double r)
+{
+    int i;
+
+    if (r > 2147483647.0)
+        i = 2147483647;
+    else if (r < -2147483647.0)
+        i = -2147483647;
+    else if (r >= 0.0)
+        i = (int) (r + 0.5);
+    else
+        i = (int) (r - 0.5);
+
+    return i;
+}
+
+
+@ Old MSVC doesn't have |rint|.
+@c
+#if defined(_MSC_VER) && _MSC_VER <= 1600
+
+#  include <math.h>
+double rint(double x)
+{
+    return floor(x+0.5);
+}
+
+#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
--- texlive-bin.orig/texk/web2c/luatexdir/utils/utils.c
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
-
-Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
-Copyright 2006-2012 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/>.
-
-*/
-
-#include "ptexlib.h"
-
-/*tex
-
-    This is a trick to load mingw32's io.h early, using a macro redefinition of
-    |eof()|.
-
-*/
-
-#include <kpathsea/config.h>
-#include "sys/types.h"
-#include <kpathsea/c-stat.h>
-#include <kpathsea/c-fopen.h>
-#include <string.h>
-#include <time.h>
-
-/*tex For |DBL_EPSILON|: */
-
-#include <float.h>
-
-#include "zlib.h"
-#include "md5.h"
-
-#include "lua/luatex-api.h"
-#include "luatex_svnversion.h"
-
-#include "png.h"
-#include "mplib.h"
-
-#define check_nprintf(size_get, size_want) \
-    if ((unsigned)(size_get) >= (unsigned)(size_want)) \
-        formatted_error("internal","snprintf failed: file %s, line %d", __FILE__, __LINE__);
-
-char *cur_file_name = NULL;
-static char print_buf[PRINTF_BUF_SIZE];
-int epochseconds;
-int microseconds;
-
-typedef char char_entry;
-define_array(char);
-
-#define SUBSET_TAG_LENGTH 6
-
-void make_subset_tag(fd_entry * fd)
-{
-    int i, j = 0, a[SUBSET_TAG_LENGTH];
-    md5_state_t pms;
-    char *glyph;
-    glw_entry *glw_glyph;
-    struct avl_traverser t;
-    md5_byte_t digest[16];
-    void **aa;
-    static struct avl_table *st_tree = NULL;
-    if (st_tree == NULL)
-        st_tree = avl_create(comp_string_entry, NULL, &avl_xallocator);
-    assert(fd != NULL);
-    assert(fd->gl_tree != NULL);
-    assert(fd->fontname != NULL);
-    assert(fd->subset_tag == NULL);
-    fd->subset_tag = xtalloc(SUBSET_TAG_LENGTH + 1, char);
-    do {
-        md5_init(&pms);
-        avl_t_init(&t, fd->gl_tree);
-        if (is_cidkeyed(fd->fm)) {      /* |glw_entry| items */
-            for (glw_glyph = (glw_entry *) avl_t_first(&t, fd->gl_tree);
-                 glw_glyph != NULL; glw_glyph = (glw_entry *) avl_t_next(&t)) {
-                glyph = malloc(24);
-                sprintf(glyph, "%05u%05u ", glw_glyph->id, glw_glyph->wd);
-                md5_append(&pms, (md5_byte_t *) glyph, (int) strlen(glyph));
-                free(glyph);
-            }
-        } else {
-            for (glyph = (char *) avl_t_first(&t, fd->gl_tree); glyph != NULL;
-                 glyph = (char *) avl_t_next(&t)) {
-                md5_append(&pms, (md5_byte_t *) glyph, (int) strlen(glyph));
-                md5_append(&pms, (const md5_byte_t *) " ", 1);
-            }
-        }
-        md5_append(&pms, (md5_byte_t *) fd->fontname,
-                   (int) strlen(fd->fontname));
-        md5_append(&pms, (md5_byte_t *) & j, sizeof(int));      /* to resolve collision */
-        md5_finish(&pms, digest);
-        for (a[0] = 0, i = 0; i < 13; i++)
-            a[0] += digest[i];
-        for (i = 1; i < SUBSET_TAG_LENGTH; i++)
-            a[i] = a[i - 1] - digest[i - 1] + digest[(i + 12) % 16];
-        for (i = 0; i < SUBSET_TAG_LENGTH; i++)
-            fd->subset_tag[i] = (char) (a[i] % 26 + 'A');
-        fd->subset_tag[SUBSET_TAG_LENGTH] = '\0';
-        j++;
-        assert(j < 100);
-    }
-    while ((char *) avl_find(st_tree, fd->subset_tag) != NULL);
-    aa = avl_probe(st_tree, fd->subset_tag);
-    assert(aa != NULL);
-    if (j > 2)
-        formatted_warning("subsets","subset-tag collision, resolved in round %d",j);
-}
-
-__attribute__ ((format(printf, 1, 2)))
-void tex_printf(const char *fmt, ...)
-{
-    va_list args;
-    va_start(args, fmt);
-    vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
-    tprint(print_buf);
-    xfflush(stdout);
-    va_end(args);
-}
-
-size_t xfwrite(void *ptr, size_t size, size_t nmemb, FILE * stream)
-{
-    if (fwrite(ptr, size, nmemb, stream) != nmemb)
-        formatted_error("file io","fwrite() failed");
-    return nmemb;
-}
-
-int xfflush(FILE * stream)
-{
-    if (fflush(stream) != 0)
-        formatted_error("file io","fflush() failed (%s)", strerror(errno));
-    return 0;
-}
-
-int xgetc(FILE * stream)
-{
-    int c = getc(stream);
-    if (c < 0 && c != EOF)
-        formatted_error("file io","getc() failed (%s)", strerror(errno));
-    return c;
-}
-
-int xputc(int c, FILE * stream)
-{
-    int i = putc(c, stream);
-    if (i < 0)
-        formatted_error("file io","putc() failed (%s)", strerror(errno));
-    return i;
-}
-
-scaled ext_xn_over_d(scaled x, scaled n, scaled d)
-{
-    double r = (((double) x) * ((double) n)) / ((double) d);
-    if (r > DBL_EPSILON)
-        r += 0.5;
-    else
-        r -= 0.5;
-    if (r >= (double) max_integer || r <= -(double) max_integer)
-        normal_warning("internal","arithmetic number too big");
-    return (scaled) r;
-}
-
-/*tex
-
-    This function strips trailing zeros in string with numbers; leading zeros are
-    not stripped (as in real life), It's not used.
-
-*/
-
-#if 0
-char *stripzeros(char *a)
-{
-    enum { NONUM, DOTNONUM, INT, DOT, LEADDOT, FRAC } s = NONUM, t = NONUM;
-    char *p, *q, *r;
-    for (p = q = r = a; *p != '\0';) {
-        switch (s) {
-        case NONUM:
-            if (*p >= '0' && *p <= '9')
-                s = INT;
-            else if (*p == '.')
-                s = LEADDOT;
-            break;
-        case DOTNONUM:
-            if (*p != '.' && (*p < '0' || *p > '9'))
-                s = NONUM;
-            break;
-        case INT:
-            if (*p == '.')
-                s = DOT;
-            else if (*p < '0' || *p > '9')
-                s = NONUM;
-            break;
-        case DOT:
-        case LEADDOT:
-            if (*p >= '0' && *p <= '9')
-                s = FRAC;
-            else if (*p == '.')
-                s = DOTNONUM;
-            else
-                s = NONUM;
-            break;
-        case FRAC:
-            if (*p == '.')
-                s = DOTNONUM;
-            else if (*p < '0' || *p > '9')
-                s = NONUM;
-            break;
-        default:;
-        }
-        switch (s) {
-        case DOT:
-            r = q;
-            break;
-        case LEADDOT:
-            r = q + 1;
-            break;
-        case FRAC:
-            if (*p > '0')
-                r = q + 1;
-            break;
-        case NONUM:
-            if ((t == FRAC || t == DOT) && r != a) {
-                q = r--;
-                if (*r == '.')  /* was a LEADDOT */
-                    *r = '0';
-                r = a;
-            }
-            break;
-        default:;
-        }
-        *q++ = *p++;
-        t = s;
-    }
-    *q = '\0';
-    return a;
-}
-#endif
-
-void initversionstring(char **versions)
-{
-
-#ifdef LuajitTeX
-#define LUA_VER_STRING  LUAJIT_VERSION
-#else
-#define LUA_VER_STRING  "lua version " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "." LUA_VERSION_RELEASE
-#endif
-#define STR(tok) STR2(tok)
-#define STR2(tok) #tok
-
-    const_string fmt =
-        "Compiled with libpng %s; using %s\n"
-        "Compiled with %s\n" /* Lua or LuaJIT */
-        "Compiled with mplib version %s\n"
-        "Compiled with zlib %s; using %s\n"
-        "\nDevelopment id: %s\n";
-    size_t len = strlen(fmt)
-               + strlen(PNG_LIBPNG_VER_STRING) + strlen(png_libpng_ver)
-               + strlen(LUA_VER_STRING)
-               + strlen(mp_metapost_version())
-               + strlen(ZLIB_VERSION) + strlen(zlib_version)
-               + strlen(STR(luatex_svn_revision))
-               + 1;
-
-    /*tex
-        The size of |len| will be more than enough, because of the placeholder
-        chars in fmt that get replaced by the arguments.
-    */
-    *versions = xmalloc(len);
-    sprintf(*versions, fmt,
-                    PNG_LIBPNG_VER_STRING, png_libpng_ver, LUA_VER_STRING,
-                    mp_metapost_version(),
-                    ZLIB_VERSION, zlib_version,STR(luatex_svn_revision));
-
-#undef STR2
-#undef STR
-#undef LUA_VER_STRING
-
-}
-
-void check_buffer_overflow(int wsize)
-{
-    if (wsize > buf_size) {
-        int nsize = buf_size + buf_size / 5 + 5;
-        if (nsize < wsize) {
-            nsize = wsize + 5;
-        }
-        buffer = (unsigned char *) xreallocarray(buffer, char, (unsigned) nsize);
-        buf_size = nsize;
-    }
-}
-
-/*tex
-
-    The return value is a decimal number with the point |dd| places from the
-    back, |scaled_out| is the number of scaled points corresponding to that.
-
-*/
-
-#define max_integer 0x7FFFFFFF
-
-scaled divide_scaled(scaled s, scaled m, int dd)
-{
-    register scaled q;
-    register scaled r;
-    int i;
-    int sign = 1;
-    if (s < 0) {
-        sign = -sign;
-        s = -s;
-    }
-    if (m < 0) {
-        sign = -sign;
-        m = -m;
-    }
-    if (m == 0) {
-        normal_error("arithmetic", "divided by zero");
-    } else if (m >= (max_integer / 10)) {
-        normal_error("arithmetic", "number too big");
-    }
-    q = s / m;
-    r = s % m;
-    for (i = 1; i <= (int) dd; i++) {
-        q = 10 * q + (10 * r) / m;
-        r = (10 * r) % m;
-    }
-    /*tex Rounding: */
-    if (2 * r >= m) {
-        q++;
-    }
-    return sign * q;
-}
-
-#ifdef _WIN32
-#undef floor
-#define floor win32_floor
-#endif
-
-/*tex
-
-    The same function, but using doubles instead of integers (faster).
-
-*/
-
-scaled divide_scaled_n(double sd, double md, double n)
-{
-    double dd, di = 0.0;
-    dd = sd / md * n;
-    if (dd > 0.0)
-        di = floor(dd + 0.5);
-    else if (dd < 0.0)
-        di = -floor((-dd) + 0.5);
-    return (scaled) di;
-}
-
-int do_zround(double r)
-{
-    int i;
-    if (r > 2147483647.0)
-        i = 2147483647;
-    else if (r < -2147483647.0)
-        i = -2147483647;
-    else if (r >= 0.0)
-        i = (int) (r + 0.5);
-    else
-        i = (int) (r - 0.5);
-    return i;
-}
-
-
-/*tex
-
-    Old MSVC doesn't have |rint|.
-
-*/
-
-#if defined(_MSC_VER) && _MSC_VER <= 1600
-
-#  include <math.h>
-
-double rint(double x)
-{
-    return floor(x+0.5);
-}
-
-#endif
-
-/*tex
-
-    We replace |tmpfile| on \MSWINDOWS:
-
-*/
-
-#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
-
-/*tex
-
-    We require \MSWINDOWS\ 2000 features such as |ETO_PDY|. We probably can now
-    assume that all \MSWINDOWS\ versions are recent.
-
-*/
-
-#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>
-
-/*tex
-
-    On \MSWINDOWS\ |tmpfile| creates the file in the root directory. This may
-    fail due to unsufficient privileges. However, this isn't a problem on
-    \MSWINDOWS\ CE so we don't use it there. Who is actually using CE anyway?
-
-*/
-
-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
