From a4d0778c4234bb95ae5a420e09961f453924c4ef Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 18 2021 07:08:14 +0000 Subject: import texlive-20180414-20.el8 --- diff --git a/.texlive.metadata b/.texlive.metadata index 1cb35f7..de3fc9f 100644 --- a/.texlive.metadata +++ b/.texlive.metadata @@ -356,10 +356,10 @@ c8dc5278bc3236d67392cc48d57dbd418ad7b65e SOURCES/lualatex-math.doc.tar.xz 66db9d59cee313f1d9a5a12ed49e0ff7352524ef SOURCES/lualatex-math.tar.xz 3eb41f1bdcd85b4f9e343a3f3f768f9cfc2e990d SOURCES/lualibs.doc.tar.xz aa647be6d40cb4f8a205e6a0d4c72e59f7d3dec7 SOURCES/lualibs.source.tar.xz -6dee23b12f227a53ddb27510891d6ea7a759bc64 SOURCES/lualibs.tar.xz +26edaa475405e2b0d8bb40784f5f61bfb11e7d00 SOURCES/lualibs.tar.xz 40de22da707ad55182a2c7b31857fa22a9089366 SOURCES/luaotfload.doc.tar.xz db45b9d60c1dea91d9504643aa3756d0f335391b SOURCES/luaotfload.source.tar.xz -86e081e21c3820e180eb868ee4de71c5ac6e3c84 SOURCES/luaotfload.tar.xz +bc32d9e53b120f39a2e33f80a0b0eef677d47180 SOURCES/luaotfload.tar.xz 9e0a8234fa33130033471c70ffde9bc9bc43ac97 SOURCES/luatex.doc.tar.xz 25e59d11497fb0edefa29160004212f4d12bd832 SOURCES/luatex.tar.xz 766ea89503ff3dbc6acc845b98cc40b56435d3da SOURCES/luatex85.doc.tar.xz diff --git a/SOURCES/texlive-20180414-poppler-0.63.patch b/SOURCES/texlive-20180414-poppler-0.63.patch deleted file mode 100644 index 04aca34..0000000 --- a/SOURCES/texlive-20180414-poppler-0.63.patch +++ /dev/null @@ -1,225 +0,0 @@ -From b14146667f4cd6cbc5d5821c88e47096df3c78b5 Mon Sep 17 00:00:00 2001 -From: Akira Kakuto -Date: Sat, 28 Apr 2018 07:36:22 +0000 -Subject: support poppler-0.64.0 - -git-svn-id: svn://tug.org/texlive/trunk@47470 c570f23f-e606-0410-a88d-b1316a301751 ---- - Build/source/texk/web2c/luatexdir/image/pdftoepdf.w | 4 ++-- - Build/source/texk/web2c/luatexdir/lua/lepdflib.cc | 4 ++-- - Build/source/texk/web2c/pdftexdir/ChangeLog | 5 +++++ - Build/source/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc | 14 +++++++------- - Build/source/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc | 2 +- - 5 files changed, 17 insertions(+), 12 deletions(-) - -diff --git a/Build/source/texk/web2c/luatexdir/image/pdftoepdf.w b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.w -index 7ba2973..d697959 100644 ---- Build/source/texk/web2c/luatexdir/image/pdftoepdf.w -+++ Build/source/texk/web2c/luatexdir/image/pdftoepdf.w -@@ -472,10 +472,10 @@ static void copyObject(PDF pdf, PdfDocument * pdf_doc, Object * obj) - break; - */ - case objString: -- copyString(pdf, obj->getString()); -+ copyString(pdf, (GooString *)obj->getString()); - break; - case objName: -- copyName(pdf, obj->getName()); -+ copyName(pdf, (char *)obj->getName()); - break; - case objNull: - pdf_add_null(pdf); -diff --git a/Build/source/texk/web2c/luatexdir/lua/lepdflib.cc b/Build/source/texk/web2c/luatexdir/lua/lepdflib.cc -index a16bf3b..32bcdab 100644 ---- Build/source/texk/web2c/luatexdir/lua/lepdflib.cc -+++ Build/source/texk/web2c/luatexdir/lua/lepdflib.cc -@@ -674,7 +674,7 @@ static int m_##in##_##function(lua_State * L) \ - uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ - if (uin->pd != NULL && uin->pd->pc != uin->pc) \ - pdfdoc_changed_error(L); \ -- gs = ((in *) uin->d)->function(); \ -+ gs = (GooString *)((in *) uin->d)->function(); \ - if (gs != NULL) \ - lua_pushlstring(L, gs->getCString(), gs->getLength()); \ - else \ -@@ -1813,7 +1813,7 @@ static int m_Object_getString(lua_State * L) - if (uin->pd != NULL && uin->pd->pc != uin->pc) - pdfdoc_changed_error(L); - if (((Object *) uin->d)->isString()) { -- gs = ((Object *) uin->d)->getString(); -+ gs = (GooString *)((Object *) uin->d)->getString(); - lua_pushlstring(L, gs->getCString(), gs->getLength()); - } else - lua_pushnil(L); -diff --git a/Build/source/texk/web2c/pdftexdir/ChangeLog b/Build/source/texk/web2c/pdftexdir/ChangeLog -index c022bc2..f4af035 100644 ---- Build/source/texk/web2c/pdftexdir/ChangeLog -+++ Build/source/texk/web2c/pdftexdir/ChangeLog -@@ -1,3 +1,8 @@ -+2018-04-28 Akira Kakuto -+ -+ * pdftoepdf-newpoppler.cc, pdftosrc-newpoppler.cc: -+ Support poppler 0.64.0. -+ - 2018-04-14 Karl Berry - - * TeX Live 2018 release, pdftex 1.40.19. -diff --git a/Build/source/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc b/Build/source/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc -index 10fea29..750579d 100644 ---- Build/source/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc -+++ Build/source/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc -@@ -290,7 +290,7 @@ static void copyName(char *s) - static void copyDictEntry(Object * obj, int i) - { - Object obj1; -- copyName(obj->dictGetKey(i)); -+ copyName((char *)obj->dictGetKey(i)); - pdf_puts(" "); - obj1 = obj->dictGetValNF(i); - copyObject(&obj1); -@@ -355,7 +355,7 @@ static void copyProcSet(Object * obj) - if (!procset.isName()) - pdftex_fail("PDF inclusion: invalid ProcSet entry type <%s>", - procset.getTypeName()); -- copyName(procset.getName()); -+ copyName((char *)procset.getName()); - pdf_puts(" "); - } - pdf_puts("]\n"); -@@ -418,7 +418,7 @@ static void copyFont(char *tag, Object * fontRef) - && fontdescRef.isRef() - && fontdesc.isDict() - && embeddableFont(&fontdesc) -- && (fontmap = lookup_fontmap(basefont.getName())) != NULL) { -+ && (fontmap = lookup_fontmap((char *)basefont.getName())) != NULL) { - // round /StemV value, since the PDF input is a float - // (see Font Descriptors in PDF reference), but we only store an - // integer, since we don't want to change the struct. -@@ -427,7 +427,7 @@ static void copyFont(char *tag, Object * fontRef) - charset = fontdesc.dictLookup("CharSet"); - if (!charset.isNull() && - charset.isString() && is_subsetable(fontmap)) -- epdf_mark_glyphs(fd, charset.getString()->getCString()); -+ epdf_mark_glyphs(fd, (char *)charset.getString()->getCString()); - else - embed_whole_font(fd); - addFontDesc(fontdescRef.getRef(), fd); -@@ -456,7 +456,7 @@ static void copyFontResources(Object * obj) - if (fontRef.isRef()) - copyFont(obj->dictGetKey(i), &fontRef); - else if (fontRef.isDict()) { // some programs generate pdf with embedded font object -- copyName(obj->dictGetKey(i)); -+ copyName((char *)obj->dictGetKey(i)); - pdf_puts(" "); - copyObject(&fontRef); - } -@@ -565,7 +565,7 @@ static void copyObject(Object * obj) - } else if (obj->isNum()) { - pdf_printf("%s", convertNumToPDF(obj->getNum())); - } else if (obj->isString()) { -- s = obj->getString(); -+ s = (GooString *)obj->getString(); - p = s->getCString(); - l = s->getLength(); - if (strlen(p) == (unsigned int) l) { -@@ -589,7 +589,7 @@ static void copyObject(Object * obj) - pdf_puts(">"); - } - } else if (obj->isName()) { -- copyName(obj->getName()); -+ copyName((char *)obj->getName()); - } else if (obj->isNull()) { - pdf_puts("null"); - } else if (obj->isArray()) { -diff --git a/Build/source/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc b/Build/source/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc -index 4e2bcad..0db154b 100644 ---- Build/source/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc -+++ Build/source/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc -@@ -109,7 +109,7 @@ int main(int argc, char *argv[]) - fprintf(stderr, "No SourceName found\n"); - exit(1); - } -- outname = srcName.getString()->getCString(); -+ outname = (char *)srcName.getString()->getCString(); - // We cannot free srcName, as objname shares its string. - // srcName.free(); - } else if (objnum > 0) { ---- Build/source/texk/web2c/pdftexdir/pdftosrc.cc -+++ Build/source/texk/web2c/pdftexdir/pdftosrc.cc -@@ -109,7 +109,7 @@ int main(int argc, char *argv[]) - fprintf(stderr, "No SourceName found\n"); - exit(1); - } -- outname = srcName.getString()->getCString(); -+ outname = (char *)srcName.getString()->getCString(); - // We cannot free srcName, as objname shares its string. - // srcName.free(); - } else if (objnum > 0) { ---- Build/source/texk/web2c/pdftexdir/pdftoepdf.cc -+++ Build/source/texk/web2c/pdftexdir/pdftoepdf.cc -@@ -290,7 +290,7 @@ static void copyName(char *s) - static void copyDictEntry(Object * obj, int i) - { - Object obj1; -- copyName(obj->dictGetKey(i)); -+ copyName((char *)obj->dictGetKey(i)); - pdf_puts(" "); - obj1 = obj->dictGetValNF(i); - copyObject(&obj1); -@@ -355,7 +355,7 @@ static void copyProcSet(Object * obj) - if (!procset.isName()) - pdftex_fail("PDF inclusion: invalid ProcSet entry type <%s>", - procset.getTypeName()); -- copyName(procset.getName()); -+ copyName((char *)procset.getName()); - pdf_puts(" "); - } - pdf_puts("]\n"); -@@ -418,7 +418,7 @@ static void copyFont(char *tag, Object * fontRef) - && fontdescRef.isRef() - && fontdesc.isDict() - && embeddableFont(&fontdesc) -- && (fontmap = lookup_fontmap(basefont.getName())) != NULL) { -+ && (fontmap = lookup_fontmap((char *)basefont.getName())) != NULL) { - // round /StemV value, since the PDF input is a float - // (see Font Descriptors in PDF reference), but we only store an - // integer, since we don't want to change the struct. -@@ -427,7 +427,7 @@ static void copyFont(char *tag, Object * fontRef) - charset = fontdesc.dictLookup("CharSet"); - if (!charset.isNull() && - charset.isString() && is_subsetable(fontmap)) -- epdf_mark_glyphs(fd, charset.getString()->getCString()); -+ epdf_mark_glyphs(fd, (char *)charset.getString()->getCString()); - else - embed_whole_font(fd); - addFontDesc(fontdescRef.getRef(), fd); -@@ -456,7 +456,7 @@ static void copyFontResources(Object * obj) - if (fontRef.isRef()) - copyFont(obj->dictGetKey(i), &fontRef); - else if (fontRef.isDict()) { // some programs generate pdf with embedded font object -- copyName(obj->dictGetKey(i)); -+ copyName((char *)obj->dictGetKey(i)); - pdf_puts(" "); - copyObject(&fontRef); - } -@@ -565,7 +565,7 @@ static void copyObject(Object * obj) - } else if (obj->isNum()) { - pdf_printf("%s", convertNumToPDF(obj->getNum())); - } else if (obj->isString()) { -- s = obj->getString(); -+ s = (GooString *)obj->getString(); - p = s->getCString(); - l = s->getLength(); - if (strlen(p) == (unsigned int) l) { -@@ -589,7 +589,7 @@ static void copyObject(Object * obj) - pdf_puts(">"); - } - } else if (obj->isName()) { -- copyName(obj->getName()); -+ copyName((char *)obj->getName()); - } else if (obj->isNull()) { - pdf_puts("null"); - } else if (obj->isArray()) { --- -cgit v1.1 - diff --git a/SOURCES/texlive-20180414-poppler-20.11.0-luatex.patch b/SOURCES/texlive-20180414-poppler-20.11.0-luatex.patch new file mode 100644 index 0000000..cc6e76c --- /dev/null +++ b/SOURCES/texlive-20180414-poppler-20.11.0-luatex.patch @@ -0,0 +1,155471 @@ +commit 69c061f2071d5826fee7940ce7f83ae4a1c8fc2e +Author: Akira Kakuto +Date: Sat Apr 28 07:36:22 2018 +0000 + + support poppler-0.64.0 + + git-svn-id: svn://tug.org/texlive/trunk/Build/source@47470 c570f23f-e606-0410-a88d-b1316a301751 + +diff --git a/texk/web2c/luatexdir/image/pdftoepdf.w b/texk/web2c/luatexdir/image/pdftoepdf.w +index 7ba29731c..d69795926 100644 +--- a/texk/web2c/luatexdir/image/pdftoepdf.w ++++ b/texk/web2c/luatexdir/image/pdftoepdf.w +@@ -472,10 +472,10 @@ static void copyObject(PDF pdf, PdfDocument * pdf_doc, Object * obj) + break; + */ + case objString: +- copyString(pdf, obj->getString()); ++ copyString(pdf, (GooString *)obj->getString()); + break; + case objName: +- copyName(pdf, obj->getName()); ++ copyName(pdf, (char *)obj->getName()); + break; + case objNull: + pdf_add_null(pdf); +diff --git a/texk/web2c/luatexdir/lua/lepdflib.cc b/texk/web2c/luatexdir/lua/lepdflib.cc +index a16bf3bd4..32bcdab01 100644 +--- a/texk/web2c/luatexdir/lua/lepdflib.cc ++++ b/texk/web2c/luatexdir/lua/lepdflib.cc +@@ -674,7 +674,7 @@ static int m_##in##_##function(lua_State * L) \ + uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ + if (uin->pd != NULL && uin->pd->pc != uin->pc) \ + pdfdoc_changed_error(L); \ +- gs = ((in *) uin->d)->function(); \ ++ gs = (GooString *)((in *) uin->d)->function(); \ + if (gs != NULL) \ + lua_pushlstring(L, gs->getCString(), gs->getLength()); \ + else \ +@@ -1813,7 +1813,7 @@ static int m_Object_getString(lua_State * L) + if (uin->pd != NULL && uin->pd->pc != uin->pc) + pdfdoc_changed_error(L); + if (((Object *) uin->d)->isString()) { +- gs = ((Object *) uin->d)->getString(); ++ gs = (GooString *)((Object *) uin->d)->getString(); + lua_pushlstring(L, gs->getCString(), gs->getLength()); + } else + lua_pushnil(L); +diff --git a/texk/web2c/pdftexdir/ChangeLog b/texk/web2c/pdftexdir/ChangeLog +index c022bc252..f4af0358e 100644 +--- a/texk/web2c/pdftexdir/ChangeLog ++++ b/texk/web2c/pdftexdir/ChangeLog +@@ -1,3 +1,8 @@ ++2018-04-28 Akira Kakuto ++ ++ * pdftoepdf-newpoppler.cc, pdftosrc-newpoppler.cc: ++ Support poppler 0.64.0. ++ + 2018-04-14 Karl Berry + + * TeX Live 2018 release, pdftex 1.40.19. +diff --git a/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc b/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc +index 10fea2999..750579d61 100644 +--- a/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc ++++ b/texk/web2c/pdftexdir/pdftoepdf-newpoppler.cc +@@ -290,7 +290,7 @@ static void copyName(char *s) + static void copyDictEntry(Object * obj, int i) + { + Object obj1; +- copyName(obj->dictGetKey(i)); ++ copyName((char *)obj->dictGetKey(i)); + pdf_puts(" "); + obj1 = obj->dictGetValNF(i); + copyObject(&obj1); +@@ -355,7 +355,7 @@ static void copyProcSet(Object * obj) + if (!procset.isName()) + pdftex_fail("PDF inclusion: invalid ProcSet entry type <%s>", + procset.getTypeName()); +- copyName(procset.getName()); ++ copyName((char *)procset.getName()); + pdf_puts(" "); + } + pdf_puts("]\n"); +@@ -418,7 +418,7 @@ static void copyFont(char *tag, Object * fontRef) + && fontdescRef.isRef() + && fontdesc.isDict() + && embeddableFont(&fontdesc) +- && (fontmap = lookup_fontmap(basefont.getName())) != NULL) { ++ && (fontmap = lookup_fontmap((char *)basefont.getName())) != NULL) { + // round /StemV value, since the PDF input is a float + // (see Font Descriptors in PDF reference), but we only store an + // integer, since we don't want to change the struct. +@@ -427,7 +427,7 @@ static void copyFont(char *tag, Object * fontRef) + charset = fontdesc.dictLookup("CharSet"); + if (!charset.isNull() && + charset.isString() && is_subsetable(fontmap)) +- epdf_mark_glyphs(fd, charset.getString()->getCString()); ++ epdf_mark_glyphs(fd, (char *)charset.getString()->getCString()); + else + embed_whole_font(fd); + addFontDesc(fontdescRef.getRef(), fd); +@@ -456,7 +456,7 @@ static void copyFontResources(Object * obj) + if (fontRef.isRef()) + copyFont(obj->dictGetKey(i), &fontRef); + else if (fontRef.isDict()) { // some programs generate pdf with embedded font object +- copyName(obj->dictGetKey(i)); ++ copyName((char *)obj->dictGetKey(i)); + pdf_puts(" "); + copyObject(&fontRef); + } +@@ -565,7 +565,7 @@ static void copyObject(Object * obj) + } else if (obj->isNum()) { + pdf_printf("%s", convertNumToPDF(obj->getNum())); + } else if (obj->isString()) { +- s = obj->getString(); ++ s = (GooString *)obj->getString(); + p = s->getCString(); + l = s->getLength(); + if (strlen(p) == (unsigned int) l) { +@@ -589,7 +589,7 @@ static void copyObject(Object * obj) + pdf_puts(">"); + } + } else if (obj->isName()) { +- copyName(obj->getName()); ++ copyName((char *)obj->getName()); + } else if (obj->isNull()) { + pdf_puts("null"); + } else if (obj->isArray()) { +diff --git a/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc b/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc +index 4e2bcadbd..0db154b4f 100644 +--- a/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc ++++ b/texk/web2c/pdftexdir/pdftosrc-newpoppler.cc +@@ -109,7 +109,7 @@ int main(int argc, char *argv[]) + fprintf(stderr, "No SourceName found\n"); + exit(1); + } +- outname = srcName.getString()->getCString(); ++ outname = (char *)srcName.getString()->getCString(); + // We cannot free srcName, as objname shares its string. + // srcName.free(); + } else if (objnum > 0) { +commit 5f7832c4bf868184f41ac0a5f80af10475ae1ff4 +Author: Karl Berry +Date: Tue Jul 31 20:52:36 2018 +0000 + + doc,sync + + git-svn-id: svn://tug.org/texlive/trunk/Build/source@48318 c570f23f-e606-0410-a88d-b1316a301751 + +diff --git a/doc/tlbuild.info b/doc/tlbuild.info +index 62391d8af..fb7848e3a 100644 +--- a/doc/tlbuild.info ++++ b/doc/tlbuild.info +@@ -356,7 +356,8 @@ finish for working on 'dvipdfm-x'. + make check + + Then you modify source files in 'mydir/texk/dvipdfm-x' and rerun +-'make' in 'mydir/Work/texk/dvipdfm-x' to rebuild. ++'make' in 'mydir/Work/texk/dvipdfm-x' to rebuild (that build directory ++is where the binaries end up). + + The second line of the 'configure' invocation shows examples of extra + things you likely want to specify if you intend to hack the sources (and +@@ -375,8 +376,8 @@ do so. If you cut down the source tree, you must also give additional + 'configure' flags to individually disable using system versions of + libraries, or the intricacies of the dependencies (such as 'teckit' + requiring 'zlib') will have undesired side effects. For an example, see +-the 'build-pdftex.sh' script in the 'pdftex' development source +-(), which is indeed a cut-down TL source tree. ++the 'build-pdftex.sh' script in the 'pdftex' development source (details ++at ), which is indeed a cut-down TL source tree. + + Even with '--disable-all-pkgs', dependencies will be checked. For + instance, if a non-MacOSX system does not have 'fontconfig', XeTeX +@@ -517,7 +518,7 @@ This section discusses the results of 'make install' in the source tree. + + The main consideration is that 'make install' is not enough to make a + usable TeX installation. Beyond the compiled binaries, (thousands of) +-support files are needed; just as a first example 'plain.tex' is not in ++support files are needed; just as a first example, 'plain.tex' is not in + the source tree. + + These support files are maintained completely independently and are +@@ -5045,7 +5046,7 @@ Index + * --enable-maintainer-mode: Build system tools. (line 28) + * --enable-maintainer-mode <1>: --enable-maintainer-mode. + (line 6) +-* --enable-missing to ignore dependencies: Build one package. (line 67) ++* --enable-missing to ignore dependencies: Build one package. (line 68) + * --enable-mktextfm-default: kpathsea library. (line 20) + * --enable-multiplatform: --enable-multiplatform. + (line 6) +@@ -5161,7 +5162,7 @@ Index + * callexe.c: Macros for Windows. (line 32) + * CC: Variables for configure. + (line 10) +-* CC=C-COMPILER: Build one package. (line 73) ++* CC=C-COMPILER: Build one package. (line 74) + * CC_BUILD: Cross problems. (line 13) + * chktex: Declarations and definitions. + (line 18) +@@ -5220,7 +5221,7 @@ Index + * ctangle: Cross problems. (line 26) + * CXX: Variables for configure. + (line 11) +-* CXX=C++-COMPILER: Build one package. (line 73) ++* CXX=C++-COMPILER: Build one package. (line 74) + * Debian installation of build prerequisites: Prerequisites. (line 60) + * declarations and definitions, in source code: Declarations and definitions. + (line 6) +@@ -5408,7 +5409,7 @@ Index + * motif: Configure options for texk/xdvik. + (line 9) + * native cross compilation: Cross compilation. (line 10) +-* OBJCXX=OBJC-COMPILER: Build one package. (line 73) ++* OBJCXX=OBJC-COMPILER: Build one package. (line 74) + * one package, building: Build one package. (line 6) + * OpenGL, required for Asymptote: asymptote. (line 6) + * operating system distribution, building for: Distro builds. (line 6) +@@ -5451,7 +5452,7 @@ Index + * setup macros, general: General setup macros. (line 6) + * shared libraries, using vs. avoiding: Distro builds. (line 11) + * size of PDF and PS files: --disable-largefile. (line 10) +-* size of source tree: Build one package. (line 57) ++* size of source tree: Build one package. (line 58) + * source code declarations: Declarations and definitions. + (line 6) + * source directory building, not supported: Building. (line 17) +@@ -5547,360 +5548,360 @@ Node: Build problems10656 + Node: Build in parallel11059 + Node: Build distribution11651 + Node: Build one package12222 +-Node: Cross compilation15600 +-Node: Cross configuring16881 +-Node: Cross problems18558 +-Node: Installing20209 +-Node: Installation directories21224 +-Node: Linked scripts23040 +-Node: Distro builds24521 +-Node: Layout and infrastructure26911 +-Node: Build system tools27739 +-Node: Top-level directories29750 +-Node: Autoconf macros32164 +-Node: General setup macros32865 +-Node: Macros for programs33732 +-Node: Macros for compilers34544 +-Node: Macros for libraries35978 +-Node: Macros for library and header flags36404 +-Node: Macros for Windows38284 +-Node: Library modules39861 +-Node: png library40350 +-Node: zlib library42624 +-Node: freetype library43139 +-Node: kpathsea library43667 +-Node: Program modules45066 +-Node: t1utils package45494 +-Node: xindy package46045 +-Node: xdvik package47195 +-Node: asymptote48268 +-Node: Extending TeX Live48719 +-Node: Adding a new program module49496 +-Node: Adding a new generic library module52791 +-Node: Adding a new TeX-specific library module55004 +-Node: Configure options55691 +-Node: Global configure options57074 +-Node: --disable-native-texlive-build57616 +-Node: --prefix --bindir ...58606 +-Node: --disable-largefile59146 +-Node: --disable-missing59831 +-Node: --enable-compiler-warnings=LEVEL60232 +-Node: --enable-cxx-runtime-hack60971 +-Node: --enable-maintainer-mode61398 +-Node: --enable-multiplatform61927 +-Node: --enable-shared62465 +-Node: --enable-silent-rules62836 +-Node: --without-ln-s63292 +-Node: --without-x63643 +-Node: Program-specific configure options63831 +-Node: --enable-PROG --disable-PROG64474 +-Node: --disable-all-pkgs64751 +-Node: Configure options for texk/web2c65737 +-Node: Configure options for texk/bibtex-x68255 +-Node: Configure options for texk/dvipdfm-x68798 +-Node: Configure options for texk/dvisvgm69571 +-Node: Configure options for texk/texlive70457 +-Node: Configure options for texk/xdvik70878 +-Node: Configure options for utils/xindy71482 +-Node: Library-specific configure options72383 +-Node: Configure options for kpathsea73394 +-Node: Configure options for system poppler74103 +-Node: Variables for configure74894 +-Node: Coding conventions76322 +-Node: Declarations and definitions77061 +-Node: Const79243 +-Node: Continuous integration81106 +-Node: Transfer from Subversion to Github81760 +-Node: Automatic update of the Git mirror83942 +-Node: CI testing on Travis-CI84530 +-Node: install-tl85210 +-Node: install-tl NAME85579 +-Node: install-tl SYNOPSIS85737 +-Node: install-tl DESCRIPTION85995 +-Node: install-tl REFERENCES87062 +-Node: install-tl OPTIONS87588 +-Ref: install-tl *-gui* [[=]_module_]87929 +-Ref: install-tl text88139 +-Ref: install-tl wizard88262 +-Ref: install-tl perltk88416 +-Ref: install-tl *-no-gui*88847 +-Ref: install-tl *-lang* _llcode_88928 +-Ref: install-tl *-repository* _url|path_89615 +-Ref: install-tl *-select-repository*91495 +-Ref: install-tl *-all-options*91931 +-Ref: install-tl *-custom-bin* _path_92186 +-Ref: install-tl *-debug-translation*93017 +-Ref: install-tl *-force-platform* _platform_93236 +-Ref: install-tl *-help*, *--help*, *-?*93480 +-Ref: install-tl *-in-place*93887 +-Ref: install-tl *-init-from-profile* _profile_file_94432 +-Ref: install-tl *-logfile* _file_94652 +-Ref: install-tl *-no-cls*95003 +-Ref: install-tl *-non-admin*95137 +-Ref: install-tl *-persistent-downloads*95242 +-Ref: install-tl *-no-persistent-downloads*95270 +-Ref: install-tl *-no-verify-downloads*95888 +-Ref: install-tl *-portable*96249 +-Ref: install-tl *-print-platform*96388 +-Ref: install-tl *-profile* _profile_file_96586 +-Ref: install-tl *-q*96766 +-Ref: install-tl *-scheme* _scheme_96828 +-Ref: install-tl *-v*97302 +-Ref: install-tl *-version*, *--version*97457 +-Node: install-tl PROFILES97588 +-Ref: install-tl instopt_adjustpath (default 0 on Unix, 1 on Windows)100238 +-Ref: install-tl instopt_adjustrepo (default 1)100314 +-Ref: install-tl instopt_letter (default 0)100451 +-Ref: install-tl instopt_portable (default 0)100542 +-Ref: install-tl instopt_write18_restricted (default 1)100638 +-Node: install-tl ENVIRONMENT VARIABLES101957 +-Ref: install-tl TEXLIVE_INSTALL_ENV_NOCHECK102348 +-Ref: install-tl TEXLIVE_INSTALL_NO_CONTEXT_CACHE102550 +-Ref: install-tl TEXLIVE_INSTALL_NO_WELCOME102660 +-Ref: install-tl TEXLIVE_INSTALL_PREFIX102781 +-Ref: install-tl TEXLIVE_INSTALL_TEXDIR102807 +-Ref: install-tl TEXLIVE_INSTALL_TEXMFCONFIG102838 +-Ref: install-tl TEXLIVE_INSTALL_TEXMFVAR102866 +-Ref: install-tl TEXLIVE_INSTALL_TEXMFHOME102895 +-Ref: install-tl TEXLIVE_INSTALL_TEXMFLOCAL102925 +-Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSCONFIG102959 +-Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSVAR102990 +-Ref: install-tl NOPERLDOC103361 +-Node: install-tl AUTHORS AND COPYRIGHT103425 +-Node: tlmgr103841 +-Node: tlmgr NAME104294 +-Node: tlmgr SYNOPSIS104426 +-Node: tlmgr DESCRIPTION104616 +-Node: tlmgr EXAMPLES105712 +-Ref: tlmgr tlmgr option repository ctan105963 +-Ref: tlmgr tlmgr option repository http://mirror.ctan.org/systems/texlive/tlnet106035 +-Ref: tlmgr tlmgr update --list106487 +-Ref: tlmgr tlmgr update --all106580 +-Ref: tlmgr tlmgr info _what_106737 +-Node: tlmgr OPTIONS106999 +-Ref: tlmgr *--repository* _url|path_107519 +-Ref: tlmgr *--gui* [_action_]108244 +-Ref: tlmgr *--gui-lang* _llcode_108651 +-Ref: tlmgr *--debug-translation*109334 +-Ref: tlmgr *--machine-readable*109537 +-Ref: tlmgr *--no-execute-actions*109805 +-Ref: tlmgr *--package-logfile* _file_109998 +-Ref: tlmgr *--pause*110252 +-Ref: tlmgr *--persistent-downloads*110407 +-Ref: tlmgr *--no-persistent-downloads*110435 +-Ref: tlmgr *--pin-file*110929 +-Ref: tlmgr *--usermode*111147 +-Ref: tlmgr *--usertree* _dir_111267 +-Ref: tlmgr *--verify-repo=[none|main|all]*111393 +-Node: tlmgr ACTIONS112292 +-Node: tlmgr help113144 +-Node: tlmgr version113620 +-Node: tlmgr backup113883 +-Ref: tlmgr *backup [_option_...] --all*114054 +-Ref: tlmgr *backup [_option_...] _pkg_...*114087 +-Ref: tlmgr *--backupdir* _directory_114942 +-Ref: tlmgr *--all*115159 +-Ref: tlmgr *--clean*[=_N_]115411 +-Ref: tlmgr *--dry-run*115738 +-Node: tlmgr candidates _pkg_115868 +-Node: tlmgr check [_option_...] [files|depends|executes|runfiles|all]116214 +-Ref: tlmgr *files*116587 +-Ref: tlmgr *depends*116722 +-Ref: tlmgr *executes*117064 +-Ref: tlmgr *runfiles*117182 +-Ref: tlmgr *--use-svn*117303 +-Node: tlmgr conf117420 +-Ref: tlmgr *conf [texmf|tlmgr|updmap [--conffile _file_] [--delete] [_key_ [_value_]]]*117699 +-Ref: tlmgr *conf auxtrees [--conffile _file_] [show|add|delete] [_value_]*117763 +-Node: tlmgr dump-tlpdb [_option_...] [--json]120108 +-Ref: tlmgr *--local*120541 +-Ref: tlmgr *--remote*120580 +-Ref: tlmgr *--json*120618 +-Node: tlmgr generate121189 +-Ref: tlmgr *generate [_option_...] language*121385 +-Ref: tlmgr *generate [_option_...] language.dat*121424 +-Ref: tlmgr *generate [_option_...] language.def*121463 +-Ref: tlmgr *generate [_option_...] language.dat.lua*121506 +-Ref: tlmgr *--dest* _output_file_123832 +-Ref: tlmgr *--localcfg* _local_conf_file_124408 +-Ref: tlmgr *--rebuild-sys*124531 +-Node: tlmgr gui125346 +-Node: tlmgr info125524 +-Ref: tlmgr *info [_option_...] _pkg_...*125686 +-Ref: tlmgr *info [_option_...] collections*125720 +-Ref: tlmgr *info [_option_...] schemes*125750 +-Ref: tlmgr *--list*127280 +-Ref: tlmgr *--only-installed*127594 +-Ref: tlmgr *--data item1,item2,...*127793 +-Ref: tlmgr *--json* 1128374 +-Node: tlmgr init-usertree128757 +-Node: tlmgr install [_option_...] _pkg_...129138 +-Ref: tlmgr *--dry-run* 1129648 +-Ref: tlmgr *--file*129765 +-Ref: tlmgr *--force*129987 +-Ref: tlmgr *--no-depends*130207 +-Ref: tlmgr *--no-depends-at-all*130366 +-Ref: tlmgr *--reinstall*130766 +-Ref: tlmgr *--with-doc*131144 +-Ref: tlmgr *--with-src*131157 +-Node: tlmgr key131679 +-Ref: tlmgr *key list*131837 +-Ref: tlmgr *key add _file_*131855 +-Ref: tlmgr *key remove _keyid_*131877 +-Node: tlmgr list132472 +-Node: tlmgr option132634 +-Ref: tlmgr *option [--json] [show]*132789 +-Ref: tlmgr *option [--json] showall*132815 +-Ref: tlmgr *option _key_ [_value_]*132841 +-Node: tlmgr paper137238 +-Ref: tlmgr *paper [a4|letter]*137387 +-Ref: tlmgr *[xdvi|pdftex|dvips|dvipdfmx|context|psutils] paper [_papersize_|--list]*137461 +-Ref: tlmgr *paper --json*137476 +-Node: tlmgr path138691 +-Ref: tlmgr *path [--w32mode=user|admin] add*138852 +-Ref: tlmgr *path [--w32mode=user|admin] remove*138889 +-Node: tlmgr pinning140229 +-Ref: tlmgr pinning show140470 +-Ref: tlmgr pinning add _repo_ _pkgglob_...140543 +-Ref: tlmgr pinning remove _repo_ _pkgglob_...140662 +-Ref: tlmgr pinning remove _repo_ --all140815 +-Node: tlmgr platform140869 +-Ref: tlmgr *platform list|add|remove _platform_...*141055 +-Ref: tlmgr *platform set _platform_*141082 +-Ref: tlmgr *platform set auto*141103 +-Ref: tlmgr *--dry-run* 2141989 +-Node: tlmgr postaction142108 +-Ref: tlmgr *postaction [_option_...] install [shortcut|fileassoc|script] [_pkg_...]*142338 +-Ref: tlmgr *postaction [_option_...] remove [shortcut|fileassoc|script] [_pkg_...]*142412 +-Ref: tlmgr *--w32mode=[user|admin]*142727 +-Ref: tlmgr *--fileassocmode=[1|2]*143143 +-Ref: tlmgr *--all* 1143428 +-Node: tlmgr print-platform143483 +-Node: tlmgr print-platform-info143814 +-Node: tlmgr remove [_option_...] _pkg_...144114 +-Ref: tlmgr *--all* 2144598 +-Ref: tlmgr *--backup*144708 +-Ref: tlmgr *--backupdir* _directory_ 1144734 +-Ref: tlmgr *--no-depends* 1145139 +-Ref: tlmgr *--no-depends-at-all* 1145201 +-Ref: tlmgr *--force* 1145304 +-Ref: tlmgr *--dry-run* 3145777 +-Node: tlmgr repository145884 +-Ref: tlmgr *repository list*146072 +-Ref: tlmgr *repository list _path|tag_*146102 +-Ref: tlmgr *repository add _path_ [_tag_]*146135 +-Ref: tlmgr *repository remove _path|tag_*146167 +-Ref: tlmgr *repository set _path_[#_tag_] [_path_[#_tag_] ...]*146221 +-Node: tlmgr restore147274 +-Ref: tlmgr *restore [_option_...] _pkg_ [_rev_]*147453 +-Ref: tlmgr *restore [_option_...] --all*147483 +-Ref: tlmgr *--all* 3148183 +-Ref: tlmgr *--backupdir* _directory_ 2148397 +-Ref: tlmgr *--dry-run* 4148578 +-Ref: tlmgr *--force* 2148710 +-Ref: tlmgr *--json* 2148756 +-Node: tlmgr search149083 +-Ref: tlmgr *search [_option_...] _what_*149247 +-Ref: tlmgr *search [_option_...] --file _what_*149284 +-Ref: tlmgr *search [_option_...] --all _what_*149320 +-Ref: tlmgr *--file* 1149540 +-Ref: tlmgr *--all* 4149602 +-Ref: tlmgr *--global*149691 +-Ref: tlmgr *--word*149818 +-Node: tlmgr shell150133 +-Ref: tlmgr protocol150868 +-Ref: tlmgr help 1150932 +-Ref: tlmgr version 1150985 +-Ref: tlmgr quit, end, bye, byebye, EOF151053 +-Ref: tlmgr restart151074 +-Ref: tlmgr load [local|remote]151197 +-Ref: tlmgr save151267 +-Ref: tlmgr get [_var_] =item set [_var_ [_val_]]151390 +-Node: tlmgr show151991 +-Node: tlmgr uninstall152158 +-Node: tlmgr update [_option_...] [_pkg_...]152388 +-Ref: tlmgr *--all* 5152759 +-Ref: tlmgr *--self*154500 +-Ref: tlmgr *--dry-run* 5155264 +-Ref: tlmgr *--list* [_pkg_]155441 +-Ref: tlmgr *--exclude* _pkg_156130 +-Ref: tlmgr *--no-auto-remove* [_pkg_...]156930 +-Ref: tlmgr *--no-auto-install* [_pkg_...]157381 +-Ref: tlmgr *--reinstall-forcibly-removed*158037 +-Ref: tlmgr *--backup* 1158572 +-Ref: tlmgr *--backupdir* _directory_ 3158598 +-Ref: tlmgr *--no-depends* 2159764 +-Ref: tlmgr *--no-depends-at-all* 2159967 +-Ref: tlmgr *--force* 3160070 +-Node: tlmgr CONFIGURATION FILE FOR TLMGR160885 +-Ref: tlmgr auto-remove, value 0 or 1 (default 1), same as command-line option.161898 +-Ref: tlmgr gui-expertmode, value 0 or 1 (default 1). This switches between the full GUI and a simplified GUI with only the most common settings.162035 +-Ref: tlmgr gui-lang _llcode_, with a language code value as with the command-line option.162117 +-Ref: tlmgr no-checksums, value 0 or 1 (default 0, see below).162171 +-Ref: tlmgr persistent-downloads, value 0 or 1 (default 1), same as command-line option.162251 +-Ref: tlmgr require-verification, value 0 or 1 (default 0), same as command-line option.162331 +-Ref: tlmgr update-exclude, value: comma-separated list of packages (no space allowed). Same as the command line option --exclude for the action update.162479 +-Ref: tlmgr verify-downloads, value 0 or 1 (default 1), same as command-line option.162555 +-Ref: tlmgr allowed-actions _action1_ [,_action_,...] The value is a comma-separated list of tlmgr actions which are allowed to be executed when tlmgr is invoked in system mode (that is, without --usermode).162824 +-Node: tlmgr CRYPTOGRAPHIC VERIFICATION163910 +-Node: tlmgr Configuration of GnuPG invocation166059 +-Node: tlmgr USER MODE166697 +-Node: tlmgr User mode install169543 +-Node: tlmgr User mode backup, restore, remove, update170687 +-Node: tlmgr User mode generate, option, paper171129 +-Node: tlmgr MULTIPLE REPOSITORIES171505 +-Node: tlmgr Pinning173234 +-Node: tlmgr GUI FOR TLMGR175209 +-Node: tlmgr Main display176549 +-Node: tlmgr Display configuration area176801 +-Ref: tlmgr Status177162 +-Ref: tlmgr Category177326 +-Ref: tlmgr Match177512 +-Ref: tlmgr Selection177693 +-Ref: tlmgr Display configuration buttons177897 +-Node: tlmgr Package list area178080 +-Ref: tlmgr a checkbox178664 +-Ref: tlmgr package name178800 +-Ref: tlmgr local revision (and version)178899 +-Ref: tlmgr remote revision (and version)179274 +-Ref: tlmgr short description179571 +-Node: tlmgr Main display action buttons179616 +-Ref: tlmgr Update all installed179882 +-Ref: tlmgr Update180254 +-Ref: tlmgr Install180304 +-Ref: tlmgr Remove180490 +-Ref: tlmgr Backup180668 +-Node: tlmgr Menu bar180825 +-Ref: tlmgr tlmgr menu181048 +-Ref: tlmgr Options menu181356 +-Ref: tlmgr Actions menu182439 +-Ref: tlmgr Help menu182867 +-Node: tlmgr GUI options183000 +-Ref: tlmgr -background _color_183246 +-Ref: tlmgr -font " _fontname_ _fontsize_ "183311 +-Ref: tlmgr -foreground _color_183469 +-Ref: tlmgr -geometry _geomspec_183521 +-Ref: tlmgr -xrm _xresource_183713 +-Node: tlmgr MACHINE-READABLE OUTPUT183981 +-Node: tlmgr Machine-readable update and install output184791 +-Ref: tlmgr location-url _location_186067 +-Ref: tlmgr total-bytes _count_186283 +-Ref: tlmgr _pkgname_186693 +-Ref: tlmgr _status_186903 +-Ref: tlmgr d186981 +-Ref: tlmgr f187041 +-Ref: tlmgr u187220 +-Ref: tlmgr r187266 +-Ref: tlmgr a187389 +-Ref: tlmgr i187567 +-Ref: tlmgr I187686 +-Ref: tlmgr _localrev_187788 +-Ref: tlmgr _serverrev_187895 +-Ref: tlmgr _size_188007 +-Ref: tlmgr _runtime_188176 +-Ref: tlmgr _esttot_188246 +-Node: tlmgr Machine-readable option output188279 +-Node: tlmgr AUTHORS AND COPYRIGHT188791 +-Node: Index189190 ++Node: Cross compilation15663 ++Node: Cross configuring16944 ++Node: Cross problems18621 ++Node: Installing20272 ++Node: Installation directories21288 ++Node: Linked scripts23104 ++Node: Distro builds24585 ++Node: Layout and infrastructure26975 ++Node: Build system tools27803 ++Node: Top-level directories29814 ++Node: Autoconf macros32228 ++Node: General setup macros32929 ++Node: Macros for programs33796 ++Node: Macros for compilers34608 ++Node: Macros for libraries36042 ++Node: Macros for library and header flags36468 ++Node: Macros for Windows38348 ++Node: Library modules39925 ++Node: png library40414 ++Node: zlib library42688 ++Node: freetype library43203 ++Node: kpathsea library43731 ++Node: Program modules45130 ++Node: t1utils package45558 ++Node: xindy package46109 ++Node: xdvik package47259 ++Node: asymptote48332 ++Node: Extending TeX Live48783 ++Node: Adding a new program module49560 ++Node: Adding a new generic library module52855 ++Node: Adding a new TeX-specific library module55068 ++Node: Configure options55755 ++Node: Global configure options57138 ++Node: --disable-native-texlive-build57680 ++Node: --prefix --bindir ...58670 ++Node: --disable-largefile59210 ++Node: --disable-missing59895 ++Node: --enable-compiler-warnings=LEVEL60296 ++Node: --enable-cxx-runtime-hack61035 ++Node: --enable-maintainer-mode61462 ++Node: --enable-multiplatform61991 ++Node: --enable-shared62529 ++Node: --enable-silent-rules62900 ++Node: --without-ln-s63356 ++Node: --without-x63707 ++Node: Program-specific configure options63895 ++Node: --enable-PROG --disable-PROG64538 ++Node: --disable-all-pkgs64815 ++Node: Configure options for texk/web2c65801 ++Node: Configure options for texk/bibtex-x68319 ++Node: Configure options for texk/dvipdfm-x68862 ++Node: Configure options for texk/dvisvgm69635 ++Node: Configure options for texk/texlive70521 ++Node: Configure options for texk/xdvik70942 ++Node: Configure options for utils/xindy71546 ++Node: Library-specific configure options72447 ++Node: Configure options for kpathsea73458 ++Node: Configure options for system poppler74167 ++Node: Variables for configure74958 ++Node: Coding conventions76386 ++Node: Declarations and definitions77125 ++Node: Const79307 ++Node: Continuous integration81170 ++Node: Transfer from Subversion to Github81824 ++Node: Automatic update of the Git mirror84006 ++Node: CI testing on Travis-CI84594 ++Node: install-tl85274 ++Node: install-tl NAME85643 ++Node: install-tl SYNOPSIS85801 ++Node: install-tl DESCRIPTION86059 ++Node: install-tl REFERENCES87126 ++Node: install-tl OPTIONS87652 ++Ref: install-tl *-gui* [[=]_module_]87993 ++Ref: install-tl text88203 ++Ref: install-tl wizard88326 ++Ref: install-tl perltk88480 ++Ref: install-tl *-no-gui*88911 ++Ref: install-tl *-lang* _llcode_88992 ++Ref: install-tl *-repository* _url|path_89679 ++Ref: install-tl *-select-repository*91559 ++Ref: install-tl *-all-options*91995 ++Ref: install-tl *-custom-bin* _path_92250 ++Ref: install-tl *-debug-translation*93081 ++Ref: install-tl *-force-platform* _platform_93300 ++Ref: install-tl *-help*, *--help*, *-?*93544 ++Ref: install-tl *-in-place*93951 ++Ref: install-tl *-init-from-profile* _profile_file_94496 ++Ref: install-tl *-logfile* _file_94716 ++Ref: install-tl *-no-cls*95067 ++Ref: install-tl *-non-admin*95201 ++Ref: install-tl *-persistent-downloads*95306 ++Ref: install-tl *-no-persistent-downloads*95334 ++Ref: install-tl *-no-verify-downloads*95952 ++Ref: install-tl *-portable*96313 ++Ref: install-tl *-print-platform*96452 ++Ref: install-tl *-profile* _profile_file_96650 ++Ref: install-tl *-q*96830 ++Ref: install-tl *-scheme* _scheme_96892 ++Ref: install-tl *-v*97366 ++Ref: install-tl *-version*, *--version*97521 ++Node: install-tl PROFILES97652 ++Ref: install-tl instopt_adjustpath (default 0 on Unix, 1 on Windows)100302 ++Ref: install-tl instopt_adjustrepo (default 1)100378 ++Ref: install-tl instopt_letter (default 0)100515 ++Ref: install-tl instopt_portable (default 0)100606 ++Ref: install-tl instopt_write18_restricted (default 1)100702 ++Node: install-tl ENVIRONMENT VARIABLES102021 ++Ref: install-tl TEXLIVE_INSTALL_ENV_NOCHECK102412 ++Ref: install-tl TEXLIVE_INSTALL_NO_CONTEXT_CACHE102614 ++Ref: install-tl TEXLIVE_INSTALL_NO_WELCOME102724 ++Ref: install-tl TEXLIVE_INSTALL_PREFIX102845 ++Ref: install-tl TEXLIVE_INSTALL_TEXDIR102871 ++Ref: install-tl TEXLIVE_INSTALL_TEXMFCONFIG102902 ++Ref: install-tl TEXLIVE_INSTALL_TEXMFVAR102930 ++Ref: install-tl TEXLIVE_INSTALL_TEXMFHOME102959 ++Ref: install-tl TEXLIVE_INSTALL_TEXMFLOCAL102989 ++Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSCONFIG103023 ++Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSVAR103054 ++Ref: install-tl NOPERLDOC103425 ++Node: install-tl AUTHORS AND COPYRIGHT103489 ++Node: tlmgr103905 ++Node: tlmgr NAME104358 ++Node: tlmgr SYNOPSIS104490 ++Node: tlmgr DESCRIPTION104680 ++Node: tlmgr EXAMPLES105776 ++Ref: tlmgr tlmgr option repository ctan106027 ++Ref: tlmgr tlmgr option repository http://mirror.ctan.org/systems/texlive/tlnet106099 ++Ref: tlmgr tlmgr update --list106551 ++Ref: tlmgr tlmgr update --all106644 ++Ref: tlmgr tlmgr info _what_106801 ++Node: tlmgr OPTIONS107063 ++Ref: tlmgr *--repository* _url|path_107583 ++Ref: tlmgr *--gui* [_action_]108308 ++Ref: tlmgr *--gui-lang* _llcode_108715 ++Ref: tlmgr *--debug-translation*109398 ++Ref: tlmgr *--machine-readable*109601 ++Ref: tlmgr *--no-execute-actions*109869 ++Ref: tlmgr *--package-logfile* _file_110062 ++Ref: tlmgr *--pause*110316 ++Ref: tlmgr *--persistent-downloads*110471 ++Ref: tlmgr *--no-persistent-downloads*110499 ++Ref: tlmgr *--pin-file*110993 ++Ref: tlmgr *--usermode*111211 ++Ref: tlmgr *--usertree* _dir_111331 ++Ref: tlmgr *--verify-repo=[none|main|all]*111457 ++Node: tlmgr ACTIONS112356 ++Node: tlmgr help113208 ++Node: tlmgr version113684 ++Node: tlmgr backup113947 ++Ref: tlmgr *backup [_option_...] --all*114118 ++Ref: tlmgr *backup [_option_...] _pkg_...*114151 ++Ref: tlmgr *--backupdir* _directory_115006 ++Ref: tlmgr *--all*115223 ++Ref: tlmgr *--clean*[=_N_]115475 ++Ref: tlmgr *--dry-run*115802 ++Node: tlmgr candidates _pkg_115932 ++Node: tlmgr check [_option_...] [files|depends|executes|runfiles|all]116278 ++Ref: tlmgr *files*116651 ++Ref: tlmgr *depends*116786 ++Ref: tlmgr *executes*117128 ++Ref: tlmgr *runfiles*117246 ++Ref: tlmgr *--use-svn*117367 ++Node: tlmgr conf117484 ++Ref: tlmgr *conf [texmf|tlmgr|updmap [--conffile _file_] [--delete] [_key_ [_value_]]]*117763 ++Ref: tlmgr *conf auxtrees [--conffile _file_] [show|add|delete] [_value_]*117827 ++Node: tlmgr dump-tlpdb [_option_...] [--json]120172 ++Ref: tlmgr *--local*120605 ++Ref: tlmgr *--remote*120644 ++Ref: tlmgr *--json*120682 ++Node: tlmgr generate121253 ++Ref: tlmgr *generate [_option_...] language*121449 ++Ref: tlmgr *generate [_option_...] language.dat*121488 ++Ref: tlmgr *generate [_option_...] language.def*121527 ++Ref: tlmgr *generate [_option_...] language.dat.lua*121570 ++Ref: tlmgr *--dest* _output_file_123896 ++Ref: tlmgr *--localcfg* _local_conf_file_124472 ++Ref: tlmgr *--rebuild-sys*124595 ++Node: tlmgr gui125410 ++Node: tlmgr info125588 ++Ref: tlmgr *info [_option_...] _pkg_...*125750 ++Ref: tlmgr *info [_option_...] collections*125784 ++Ref: tlmgr *info [_option_...] schemes*125814 ++Ref: tlmgr *--list*127344 ++Ref: tlmgr *--only-installed*127658 ++Ref: tlmgr *--data item1,item2,...*127857 ++Ref: tlmgr *--json* 1128438 ++Node: tlmgr init-usertree128821 ++Node: tlmgr install [_option_...] _pkg_...129202 ++Ref: tlmgr *--dry-run* 1129712 ++Ref: tlmgr *--file*129829 ++Ref: tlmgr *--force*130051 ++Ref: tlmgr *--no-depends*130271 ++Ref: tlmgr *--no-depends-at-all*130430 ++Ref: tlmgr *--reinstall*130830 ++Ref: tlmgr *--with-doc*131208 ++Ref: tlmgr *--with-src*131221 ++Node: tlmgr key131743 ++Ref: tlmgr *key list*131901 ++Ref: tlmgr *key add _file_*131919 ++Ref: tlmgr *key remove _keyid_*131941 ++Node: tlmgr list132536 ++Node: tlmgr option132698 ++Ref: tlmgr *option [--json] [show]*132853 ++Ref: tlmgr *option [--json] showall*132879 ++Ref: tlmgr *option _key_ [_value_]*132905 ++Node: tlmgr paper137302 ++Ref: tlmgr *paper [a4|letter]*137451 ++Ref: tlmgr *[xdvi|pdftex|dvips|dvipdfmx|context|psutils] paper [_papersize_|--list]*137525 ++Ref: tlmgr *paper --json*137540 ++Node: tlmgr path138755 ++Ref: tlmgr *path [--w32mode=user|admin] add*138916 ++Ref: tlmgr *path [--w32mode=user|admin] remove*138953 ++Node: tlmgr pinning140293 ++Ref: tlmgr pinning show140534 ++Ref: tlmgr pinning add _repo_ _pkgglob_...140607 ++Ref: tlmgr pinning remove _repo_ _pkgglob_...140726 ++Ref: tlmgr pinning remove _repo_ --all140879 ++Node: tlmgr platform140933 ++Ref: tlmgr *platform list|add|remove _platform_...*141119 ++Ref: tlmgr *platform set _platform_*141146 ++Ref: tlmgr *platform set auto*141167 ++Ref: tlmgr *--dry-run* 2142053 ++Node: tlmgr postaction142172 ++Ref: tlmgr *postaction [_option_...] install [shortcut|fileassoc|script] [_pkg_...]*142402 ++Ref: tlmgr *postaction [_option_...] remove [shortcut|fileassoc|script] [_pkg_...]*142476 ++Ref: tlmgr *--w32mode=[user|admin]*142791 ++Ref: tlmgr *--fileassocmode=[1|2]*143207 ++Ref: tlmgr *--all* 1143492 ++Node: tlmgr print-platform143547 ++Node: tlmgr print-platform-info143878 ++Node: tlmgr remove [_option_...] _pkg_...144178 ++Ref: tlmgr *--all* 2144662 ++Ref: tlmgr *--backup*144772 ++Ref: tlmgr *--backupdir* _directory_ 1144798 ++Ref: tlmgr *--no-depends* 1145203 ++Ref: tlmgr *--no-depends-at-all* 1145265 ++Ref: tlmgr *--force* 1145368 ++Ref: tlmgr *--dry-run* 3145841 ++Node: tlmgr repository145948 ++Ref: tlmgr *repository list*146136 ++Ref: tlmgr *repository list _path|tag_*146166 ++Ref: tlmgr *repository add _path_ [_tag_]*146199 ++Ref: tlmgr *repository remove _path|tag_*146231 ++Ref: tlmgr *repository set _path_[#_tag_] [_path_[#_tag_] ...]*146285 ++Node: tlmgr restore147338 ++Ref: tlmgr *restore [_option_...] _pkg_ [_rev_]*147517 ++Ref: tlmgr *restore [_option_...] --all*147547 ++Ref: tlmgr *--all* 3148247 ++Ref: tlmgr *--backupdir* _directory_ 2148461 ++Ref: tlmgr *--dry-run* 4148642 ++Ref: tlmgr *--force* 2148774 ++Ref: tlmgr *--json* 2148820 ++Node: tlmgr search149147 ++Ref: tlmgr *search [_option_...] _what_*149311 ++Ref: tlmgr *search [_option_...] --file _what_*149348 ++Ref: tlmgr *search [_option_...] --all _what_*149384 ++Ref: tlmgr *--file* 1149604 ++Ref: tlmgr *--all* 4149666 ++Ref: tlmgr *--global*149755 ++Ref: tlmgr *--word*149882 ++Node: tlmgr shell150197 ++Ref: tlmgr protocol150932 ++Ref: tlmgr help 1150996 ++Ref: tlmgr version 1151049 ++Ref: tlmgr quit, end, bye, byebye, EOF151117 ++Ref: tlmgr restart151138 ++Ref: tlmgr load [local|remote]151261 ++Ref: tlmgr save151331 ++Ref: tlmgr get [_var_] =item set [_var_ [_val_]]151454 ++Node: tlmgr show152055 ++Node: tlmgr uninstall152222 ++Node: tlmgr update [_option_...] [_pkg_...]152452 ++Ref: tlmgr *--all* 5152823 ++Ref: tlmgr *--self*154564 ++Ref: tlmgr *--dry-run* 5155328 ++Ref: tlmgr *--list* [_pkg_]155505 ++Ref: tlmgr *--exclude* _pkg_156194 ++Ref: tlmgr *--no-auto-remove* [_pkg_...]156994 ++Ref: tlmgr *--no-auto-install* [_pkg_...]157445 ++Ref: tlmgr *--reinstall-forcibly-removed*158101 ++Ref: tlmgr *--backup* 1158636 ++Ref: tlmgr *--backupdir* _directory_ 3158662 ++Ref: tlmgr *--no-depends* 2159828 ++Ref: tlmgr *--no-depends-at-all* 2160031 ++Ref: tlmgr *--force* 3160134 ++Node: tlmgr CONFIGURATION FILE FOR TLMGR160949 ++Ref: tlmgr auto-remove, value 0 or 1 (default 1), same as command-line option.161962 ++Ref: tlmgr gui-expertmode, value 0 or 1 (default 1). This switches between the full GUI and a simplified GUI with only the most common settings.162099 ++Ref: tlmgr gui-lang _llcode_, with a language code value as with the command-line option.162181 ++Ref: tlmgr no-checksums, value 0 or 1 (default 0, see below).162235 ++Ref: tlmgr persistent-downloads, value 0 or 1 (default 1), same as command-line option.162315 ++Ref: tlmgr require-verification, value 0 or 1 (default 0), same as command-line option.162395 ++Ref: tlmgr update-exclude, value: comma-separated list of packages (no space allowed). Same as the command line option --exclude for the action update.162543 ++Ref: tlmgr verify-downloads, value 0 or 1 (default 1), same as command-line option.162619 ++Ref: tlmgr allowed-actions _action1_ [,_action_,...] The value is a comma-separated list of tlmgr actions which are allowed to be executed when tlmgr is invoked in system mode (that is, without --usermode).162888 ++Node: tlmgr CRYPTOGRAPHIC VERIFICATION163974 ++Node: tlmgr Configuration of GnuPG invocation166123 ++Node: tlmgr USER MODE166761 ++Node: tlmgr User mode install169607 ++Node: tlmgr User mode backup, restore, remove, update170751 ++Node: tlmgr User mode generate, option, paper171193 ++Node: tlmgr MULTIPLE REPOSITORIES171569 ++Node: tlmgr Pinning173298 ++Node: tlmgr GUI FOR TLMGR175273 ++Node: tlmgr Main display176613 ++Node: tlmgr Display configuration area176865 ++Ref: tlmgr Status177226 ++Ref: tlmgr Category177390 ++Ref: tlmgr Match177576 ++Ref: tlmgr Selection177757 ++Ref: tlmgr Display configuration buttons177961 ++Node: tlmgr Package list area178144 ++Ref: tlmgr a checkbox178728 ++Ref: tlmgr package name178864 ++Ref: tlmgr local revision (and version)178963 ++Ref: tlmgr remote revision (and version)179338 ++Ref: tlmgr short description179635 ++Node: tlmgr Main display action buttons179680 ++Ref: tlmgr Update all installed179946 ++Ref: tlmgr Update180318 ++Ref: tlmgr Install180368 ++Ref: tlmgr Remove180554 ++Ref: tlmgr Backup180732 ++Node: tlmgr Menu bar180889 ++Ref: tlmgr tlmgr menu181112 ++Ref: tlmgr Options menu181420 ++Ref: tlmgr Actions menu182503 ++Ref: tlmgr Help menu182931 ++Node: tlmgr GUI options183064 ++Ref: tlmgr -background _color_183310 ++Ref: tlmgr -font " _fontname_ _fontsize_ "183375 ++Ref: tlmgr -foreground _color_183533 ++Ref: tlmgr -geometry _geomspec_183585 ++Ref: tlmgr -xrm _xresource_183777 ++Node: tlmgr MACHINE-READABLE OUTPUT184045 ++Node: tlmgr Machine-readable update and install output184855 ++Ref: tlmgr location-url _location_186131 ++Ref: tlmgr total-bytes _count_186347 ++Ref: tlmgr _pkgname_186757 ++Ref: tlmgr _status_186967 ++Ref: tlmgr d187045 ++Ref: tlmgr f187105 ++Ref: tlmgr u187284 ++Ref: tlmgr r187330 ++Ref: tlmgr a187453 ++Ref: tlmgr i187631 ++Ref: tlmgr I187750 ++Ref: tlmgr _localrev_187852 ++Ref: tlmgr _serverrev_187959 ++Ref: tlmgr _size_188071 ++Ref: tlmgr _runtime_188240 ++Ref: tlmgr _esttot_188310 ++Node: tlmgr Machine-readable option output188343 ++Node: tlmgr AUTHORS AND COPYRIGHT188855 ++Node: Index189254 +  + End Tag Table +diff --git a/texk/texlive/linked_scripts/texlive/tlmgr.pl b/texk/texlive/linked_scripts/texlive/tlmgr.pl +index f0c125b5a..d2cfaaebb 100755 +--- a/texk/texlive/linked_scripts/texlive/tlmgr.pl ++++ b/texk/texlive/linked_scripts/texlive/tlmgr.pl +@@ -8178,7 +8178,7 @@ With the C argument, C lists all keys. + + The C argument requires another argument, either a filename or + C<-> for stdin, from which the key is added. The key is added to the +-local keyring C, which is normally) ++local keyring C, which is normally + C. + + The C argument requires a key id and removes the requested id +diff --git a/texk/web2c/configure b/texk/web2c/configure +index 65955e941..681a176fc 100755 +--- a/texk/web2c/configure ++++ b/texk/web2c/configure +@@ -18592,6 +18592,7 @@ fi + + + ++# Include additional code for web2c. + + ## texk/web2c/ac/web2c.ac: configure.ac fragment for the TeX Live subdirectory texk/web2c/ + ## configure options for TeX and MF +@@ -19003,12 +19004,14 @@ fi + + + ++# LuaTeX and XeTeX now require C++11 because poppler does :(. ++# XeTeX also requires C+11 because of ICU. + if test "x$enable_xetex" = xyes \ + || test "x$enable_luatex" = xyes \ + || test "x$enable_luajittex" = xyes \ + || test "x$enable_luatex53" = xyes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: LuaTeX enabled, requiring C++11 support" >&5 +-$as_echo "$as_me: LuaTeX enabled, requiring C++11 support" >&6;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++11, since LuaTeX and/or XeTeX enabled" >&5 ++$as_echo "$as_me: checking for C++11, since LuaTeX and/or XeTeX enabled" >&6;} + ax_cxx_compile_alternatives="11 0x" ax_cxx_compile_cxx11_required=true + ac_ext=cpp + ac_cpp='$CXXCPP $CPPFLAGS' +diff --git a/texk/web2c/configure.ac b/texk/web2c/configure.ac +index 902902ca5..6c0839297 100644 +--- a/texk/web2c/configure.ac ++++ b/texk/web2c/configure.ac +@@ -42,16 +42,17 @@ AC_PROG_OBJCXX + KPSE_CXX_HACK + KPSE_LT_HACK + +-dnl Include additional code for web2c. ++# Include additional code for web2c. + KPSE_WEB2C_PREPARE + m4_include([ac/web2c.ac]) + +-dnl LuaTeX requires C++11 because poppler does :(. ++# LuaTeX and XeTeX now require C++11 because poppler does :(. ++# XeTeX also requires C+11 because of ICU. + if test "x$enable_xetex" = xyes \ + || test "x$enable_luatex" = xyes \ + || test "x$enable_luajittex" = xyes \ + || test "x$enable_luatex53" = xyes; then +- AC_MSG_NOTICE([LuaTeX enabled, requiring C++11 support]) ++ AC_MSG_NOTICE([checking for C++11, since LuaTeX and/or XeTeX enabled]) + AX_CXX_COMPILE_STDCXX([11]) + fi + +commit aa5363bd0dc180752c7d8eb9d847c2581e453b1a +Author: Luigi Scarso +Date: Wed Sep 5 21:30:41 2018 +0000 + + sync with luatex revision 6924. + + git-svn-id: svn://tug.org/texlive/trunk/Build/source@48591 c570f23f-e606-0410-a88d-b1316a301751 + +diff --git a/libs/lua53/ChangeLog b/libs/lua53/ChangeLog +index 8ef10ff28..d5c4b88bc 100644 +--- a/libs/lua53/ChangeLog ++++ b/libs/lua53/ChangeLog +@@ -1,3 +1,14 @@ ++2018-07-21 Luigi Scarso ++ ++ * Adapted for Lua 5.3.5 ++ ++ ++2018-06-18 Luigi Scarso ++ ++ * dropped poppler, new pplib from ++ http://eurydyka.kaliope.org.pl/~pawel/libpp/html/ppapi.html ++ ++ + 2017-10-24 Luigi Scarso + + * Adapted for Lua 5.3.4 +diff --git a/libs/lua53/Makefile.am b/libs/lua53/Makefile.am +index a7994ea5a..f736ce9a3 100644 +--- a/libs/lua53/Makefile.am ++++ b/libs/lua53/Makefile.am +@@ -65,6 +65,7 @@ nodist_libtexlua53_la_SOURCES = \ + @LUA53_TREE@/src/lvm.c \ + @LUA53_TREE@/src/lzio.c + ++ + lua53includedir = ${includedir}/texlua53 + + lua53include_HEADERS = \ +diff --git a/libs/lua53/TLpatches/ChangeLog b/libs/lua53/TLpatches/ChangeLog +index 882ddcdde..dd2bcb2f0 100644 +--- a/libs/lua53/TLpatches/ChangeLog ++++ b/libs/lua53/TLpatches/ChangeLog +@@ -1,3 +1,7 @@ ++2018-07-21 Luigi Scarso ++ Adapted for lua 5.3.5 ++ ++ + 2017-10-24 Luigi Scarso + Adapted for lua 5.3.4 + +diff --git a/libs/lua53/TLpatches/patch-01-utf-8 b/libs/lua53/TLpatches/patch-01-utf-8 +index f2ac40134..0f7cb8981 100644 +--- a/libs/lua53/TLpatches/patch-01-utf-8 ++++ b/libs/lua53/TLpatches/patch-01-utf-8 +@@ -1,7 +1,16 @@ +-diff -ur lua-5.3.4.orig/src/lctype.h lua-5.3.4/src/lctype.h +---- lctype.h.orig 2017-10-24 15:14:50.724139638 +0200 +-+++ lctype.h 2017-10-24 15:15:51.704137138 +0200 +-@@ -53,9 +53,11 @@ ++diff -u lctype.h.orig lctype.h ++--- lctype.h.orig 2018-07-21 09:57:36.061692228 +0200 +++++ lctype.h 2018-07-21 10:03:29.625677730 +0200 ++@@ -7,6 +7,8 @@ ++ #ifndef lctype_h ++ #define lctype_h ++ +++#include +++ ++ #include "lua.h" ++ ++ ++@@ -53,9 +55,11 @@ + + /* + ** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' +diff --git a/libs/lua53/TLpatches/patch-02-FreeBSD b/libs/lua53/TLpatches/patch-02-FreeBSD +index fc59f4842..ad85a5988 100644 +--- a/libs/lua53/TLpatches/patch-02-FreeBSD ++++ b/libs/lua53/TLpatches/patch-02-FreeBSD +@@ -1,6 +1,6 @@ +-diff -ur liolib.c.orig liolib.c +---- liolib.c.orig 2017-10-24 15:16:06.036136550 +0200 +-+++ liolib.c 2017-10-24 15:16:49.968134748 +0200 ++diff -u liolib.c.orig liolib.c ++--- liolib.c.orig 2017-04-19 19:29:57.000000000 +0200 +++++ liolib.c 2018-07-21 10:04:50.965674394 +0200 + @@ -16,6 +16,9 @@ + #include + #include +diff --git a/libs/lua53/TLpatches/patch-03-export b/libs/lua53/TLpatches/patch-03-export +index dda2b87b2..9bc2a4627 100644 +--- a/libs/lua53/TLpatches/patch-03-export ++++ b/libs/lua53/TLpatches/patch-03-export +@@ -1,6 +1,6 @@ +-diff -ur lopcodes.h.orig lopcodes.h +---- lopcodes.h.orig 2017-10-24 15:22:51.012119943 +0200 +-+++ lopcodes.h 2017-10-24 15:18:37.924130321 +0200 ++diff -u lopcodes.h.orig lopcodes.h ++--- lopcodes.h.orig 2018-07-21 09:59:37.349687255 +0200 +++++ lopcodes.h 2018-07-21 10:07:04.413668921 +0200 + @@ -278,7 +278,7 @@ + OpArgK /* argument is a constant or register/constant */ + }; +@@ -20,9 +20,10 @@ diff -ur lopcodes.h.orig lopcodes.h + + /* number of list items to accumulate before a SETLIST instruction */ + +-diff -ur lundump.h.orig lundump.h +---- lundump.h.orig 2017-10-24 15:19:03.860129258 +0200 +-+++ lundump.h 2017-10-24 15:19:47.088127485 +0200 ++ ++diff -u lundump.h.orig lundump.h ++--- lundump.h.orig 2018-07-21 10:00:01.545686262 +0200 +++++ lundump.h 2018-07-21 10:08:12.341666136 +0200 + @@ -26,7 +26,7 @@ + LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); + +@@ -32,4 +33,3 @@ diff -ur lundump.h.orig lundump.h + void* data, int strip); + + #endif +- +diff --git a/libs/lua53/lua53-src/README b/libs/lua53/lua53-src/README +index 0b31908a0..ed424defe 100644 +--- a/libs/lua53/lua53-src/README ++++ b/libs/lua53/lua53-src/README +@@ -1,5 +1,5 @@ + +-This is Lua 5.3.4, released on 12 Jan 2017. ++This is Lua 5.3.5, released on 26 Jun 2018. + + For installation instructions, license details, and + further information about Lua, see doc/readme.html. +diff --git a/libs/lua53/lua53-src/doc/contents.html b/libs/lua53/lua53-src/doc/contents.html +index 445556f96..c4eb26779 100644 +--- a/libs/lua53/lua53-src/doc/contents.html ++++ b/libs/lua53/lua53-src/doc/contents.html +@@ -32,7 +32,7 @@ For a complete introduction to Lua programming, see the book + +

+ +-Copyright © 2015–2017 Lua.org, PUC-Rio. ++Copyright © 2015–2018 Lua.org, PUC-Rio. + Freely available under the terms of the + Lua license. + +@@ -609,10 +609,10 @@ Freely available under the terms of the + +

+ + + +diff --git a/libs/lua53/lua53-src/doc/lua.css b/libs/lua53/lua53-src/doc/lua.css +index 5bedf7eb8..cbd0799d1 100644 +--- a/libs/lua53/lua53-src/doc/lua.css ++++ b/libs/lua53/lua53-src/doc/lua.css +@@ -10,7 +10,7 @@ body { + line-height: 1.25 ; + margin: 16px auto ; + padding: 32px ; +- border: solid #a0a0a0 1px ; ++ border: solid #ccc 1px ; + border-radius: 20px ; + max-width: 70em ; + width: 90% ; +@@ -111,36 +111,29 @@ pre.session { + border-radius: 8px ; + } + +-td.gutter { +- width: 4% ; +-} +- +-table.columns { ++table { + border: none ; + border-spacing: 0 ; + border-collapse: collapse ; + } + +-table.columns td { +- vertical-align: top ; ++td { + padding: 0 ; +- padding-bottom: 1em ; +- text-align: justify ; +- line-height: 1.25 ; ++ margin: 0 ; + } + +-p.logos a:link:hover, p.logos a:visited:hover { +- background-color: inherit ; ++td.gutter { ++ width: 4% ; + } + +-table.book { +- border: none ; +- border-spacing: 0 ; +- border-collapse: collapse ; ++table.columns td { ++ vertical-align: top ; ++ padding-bottom: 1em ; ++ text-align: justify ; ++ line-height: 1.25 ; + } + + table.book td { +- padding: 0 ; + vertical-align: top ; + } + +@@ -159,6 +152,10 @@ table.book span { + margin-top: 0.25em ; + } + ++p.logos a:link:hover, p.logos a:visited:hover { ++ background-color: inherit ; ++} ++ + img { + background-color: white ; + } +diff --git a/libs/lua53/lua53-src/doc/manual.html b/libs/lua53/lua53-src/doc/manual.html +index 3126b5d6a..89a642a45 100644 +--- a/libs/lua53/lua53-src/doc/manual.html ++++ b/libs/lua53/lua53-src/doc/manual.html +@@ -19,7 +19,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes + +

+ +-Copyright © 2015–2017 Lua.org, PUC-Rio. ++Copyright © 2015–2018 Lua.org, PUC-Rio. + Freely available under the terms of the + Lua license. + +@@ -35,7 +35,7 @@ Freely available under the terms of the + +

+ +- ++ + + + +@@ -203,8 +203,8 @@ even those that do not support threads natively. + +

+ The type table implements associative arrays, +-that is, arrays that can be indexed not only with numbers, +-but with any Lua value except nil and NaN. ++that is, arrays that can have as indices not only numbers, ++but any Lua value except nil and NaN. + (Not a Number is a special value used to represent + undefined or unrepresentable numerical results, such as 0/0.) + Tables can be heterogeneous; +@@ -400,6 +400,8 @@ with the event name prefixed by two underscores; + the corresponding values are called metamethods. + In the previous example, the key is "__add" + and the metamethod is the function that performs the addition. ++Unless stated otherwise, ++metamethods should be function values. + + +

+@@ -597,7 +599,7 @@ it is also slower than a real __le metamethod.) + + +

  • __index: +-The indexing access table[key]. ++The indexing access operation table[key]. + This event happens when table is not a table or + when key is not present in table. + The metamethod is looked up in table. +@@ -1276,13 +1278,8 @@ Square brackets are used to index a table: +
    + 	var ::= prefixexp ‘[’ exp ‘]’
    + 

    +-The meaning of accesses to table fields can be changed via metatables. +-An access to an indexed variable t[i] is equivalent to +-a call gettable_event(t,i). +-(See §2.4 for a complete description of the +-gettable_event function. +-This function is not defined or callable in Lua. +-We use it here only for explanatory purposes.) ++The meaning of accesses to table fields can be changed via metatables ++(see §2.4). + + +

    +@@ -1476,23 +1473,18 @@ and + cyclically permutes the values of x, y, and z. + + +-

    +-The meaning of assignments to global variables +-and table fields can be changed via metatables. +-An assignment to an indexed variable t[i] = val is equivalent to +-settable_event(t,i,val). +-(See §2.4 for a complete description of the +-settable_event function. +-This function is not defined or callable in Lua. +-We use it here only for explanatory purposes.) +- +- +

    + An assignment to a global name x = val + is equivalent to the assignment + _ENV.x = val (see §2.2). + + ++

    ++The meaning of assignments to table fields and ++global variables (which are actually table fields, too) ++can be changed via metatables (see §2.4). ++ ++ + + + +@@ -1831,17 +1823,17 @@ Here are some examples: + g(f(), x) -- f() is adjusted to 1 result + g(x, f()) -- g gets x plus all results from f() + a,b,c = f(), x -- f() is adjusted to 1 result (c gets nil) +- a,b = ... -- a gets the first vararg parameter, b gets ++ a,b = ... -- a gets the first vararg argument, b gets + -- the second (both a and b can get nil if there +- -- is no corresponding vararg parameter) ++ -- is no corresponding vararg argument) + + a,b,c = x, f() -- f() is adjusted to 2 results + a,b,c = f() -- f() is adjusted to 3 results + return f() -- returns all results from f() +- return ... -- returns all received vararg parameters ++ return ... -- returns all received vararg arguments + return x,y,f() -- returns x, y, and all results from f() + {f()} -- creates a list with all results from f() +- {...} -- creates a list with all vararg parameters ++ {...} -- creates a list with all vararg arguments + {f(), nil} -- f() is adjusted to 1 result + + +@@ -2039,9 +2031,12 @@ two objects are considered equal only if they are the same object. + Every time you create a new object + (a table, userdata, or thread), + this new object is different from any previously existing object. +-Closures with the same reference are always equal. ++A closure is always equal to itself. + Closures with any detectable difference + (different behavior, different definition) are always different. ++Closures created at different times but with no detectable differences ++may be classified as equal or not ++(depending on internal caching details). + + +

    +@@ -2303,7 +2298,7 @@ If the value of prefixexp has type function, + then this function is called + with the given arguments. + Otherwise, the prefixexp "call" metamethod is called, +-having as first parameter the value of prefixexp, ++having as first argument the value of prefixexp, + followed by the original call arguments + (see §2.4). + +@@ -2881,7 +2876,7 @@ it can do whatever it wants on that Lua state, + as it should be already protected. + However, + when C code operates on other Lua states +-(e.g., a Lua parameter to the function, ++(e.g., a Lua argument to the function, + a Lua state stored in the registry, or + the result of lua_newthread), + it should use them only in API calls that cannot raise errors. +@@ -3370,7 +3365,7 @@ it is left unchanged. + Destroys all objects in the given Lua state + (calling the corresponding garbage-collection metamethods, if any) + and frees all dynamic memory used by this state. +-On several platforms, you may not need to call this function, ++In several platforms, you may not need to call this function, + because all resources are naturally released when the host program ends. + On the other hand, long-running programs that create multiple states, + such as daemons or web servers, +@@ -5584,7 +5579,7 @@ given as argument to a hook (see lua_Hook). + + +

    +-To get information about a function you push it onto the stack ++To get information about a function, you push it onto the stack + and start the what string with the character '>'. + (In that case, + lua_getinfo pops the function from the top of the stack.) +@@ -6462,7 +6457,7 @@ file-related functions in the standard library + +

    + Pushes onto the stack the field e from the metatable +-of the object at index obj and returns the type of pushed value. ++of the object at index obj and returns the type of the pushed value. + If the object does not have a metatable, + or if the metatable does not have this field, + pushes nothing and returns LUA_TNIL. +@@ -6749,7 +6744,7 @@ In words, if the argument arg is nil or absent, + the macro results in the default dflt. + Otherwise, it results in the result of calling func + with the state L and the argument index arg as +-parameters. ++arguments. + Note that it evaluates the expression dflt only if needed. + + +@@ -8680,7 +8675,7 @@ the lowercase letters plus the '-' character. +

    + You can put a closing square bracket in a set + by positioning it as the first character in the set. +-You can put an hyphen in a set ++You can put a hyphen in a set + by positioning it as the first or the last character in the set. + (You can also use an escape for both cases.) + +@@ -9082,8 +9077,8 @@ Returns the destination table a2. + + +

    +-Returns a new table with all parameters stored into keys 1, 2, etc. +-and with a field "n" with the total number of parameters. ++Returns a new table with all arguments stored into keys 1, 2, etc. ++and with a field "n" with the total number of arguments. + Note that the resulting table may not be a sequence. + + +@@ -9215,7 +9210,7 @@ Returns the arc sine of x (in radians). +

    + + Returns the arc tangent of y/x (in radians), +-but uses the signs of both parameters to find the ++but uses the signs of both arguments to find the + quadrant of the result. + (It also handles correctly the case of x being zero.) + +@@ -9516,7 +9511,7 @@ all I/O functions return nil on failure + (plus an error message as a second result and + a system-dependent error code as a third result) + and some value different from nil on success. +-On non-POSIX systems, ++In non-POSIX systems, + the computation of the error message and error code + in case of errors + may be not thread safe, +@@ -9553,7 +9548,7 @@ When called with a file name, it opens the named file (in text mode), + and sets its handle as the default input file. + When called with a file handle, + it simply sets this file handle as the default input file. +-When called without parameters, ++When called without arguments, + it returns the current default input file. + + +@@ -9580,7 +9575,7 @@ it returns no values (to finish the loop) and automatically closes the file. + The call io.lines() (with no file name) is equivalent + to io.input():lines("*l"); + that is, it iterates over the lines of the default input file. +-In this case it does not close the file when the loop ends. ++In this case, the iterator does not close the file when the loop ends. + + +

    +@@ -9963,7 +9958,7 @@ the host system and on the current locale. + + +

    +-On non-POSIX systems, ++In non-POSIX systems, + this function may be not thread safe + because of its reliance on C function gmtime and C function localtime. + +@@ -10163,7 +10158,7 @@ and explicitly removed when no longer needed. + + +

    +-On POSIX systems, ++In POSIX systems, + this function also creates a file with that name, + to avoid security risks. + (Someone else might create the file with wrong permissions +@@ -10301,8 +10296,8 @@ The first parameter or local variable has index 1, and so on, + following the order that they are declared in the code, + counting only the variables that are active + in the current scope of the function. +-Negative indices refer to vararg parameters; +--1 is the first vararg parameter. ++Negative indices refer to vararg arguments; ++-1 is the first vararg argument. + The function returns nil if there is no variable with the given index, + and raises an error when called with a level out of range. + (You can call debug.getinfo to check whether the level is valid.) +@@ -10400,7 +10395,7 @@ When called without arguments, + + +

    +-When the hook is called, its first parameter is a string ++When the hook is called, its first argument is a string + describing the event that has triggered its call: + "call" (or "tail call"), + "return", +@@ -10551,7 +10546,8 @@ The options are: + +

      +
    • -e stat: executes string stat;
    • +-
    • -l mod: "requires" mod;
    • ++
    • -l mod: "requires" mod and assigns the ++ result to global @mod;
    • +
    • -i: enters interactive mode after running script;
    • +
    • -v: prints version information;
    • +
    • -E: ignores environment variables;
    • +@@ -10629,7 +10625,7 @@ For instance, the call +

      + will print "-e". + If there is a script, +-the script is called with parameters ++the script is called with arguments + arg[1], ···, arg[#arg]. + (Like all chunks in Lua, + the script is compiled as a vararg function.) +@@ -10815,7 +10811,7 @@ The following functions were deprecated in the mathematical library: + frexp, and ldexp. + You can replace math.pow(x,y) with x^y; + you can replace math.atan2 with math.atan, +-which now accepts one or two parameters; ++which now accepts one or two arguments; + you can replace math.ldexp(x,exp) with x * 2.0^exp. + For the other operations, + you can either use an external library or +@@ -10850,7 +10846,7 @@ of the first result.) +

        + +
      • +-Continuation functions now receive as parameters what they needed ++Continuation functions now receive as arguments what they needed + to get through lua_getctx, + so lua_getctx has been removed. + Adapt your code accordingly. +@@ -10973,12 +10969,13 @@ and LiteralString, see §3.1.) + + + ++ + + + + +diff --git a/libs/lua53/lua53-src/doc/readme.html b/libs/lua53/lua53-src/doc/readme.html +index 96a9386e2..b118f7b02 100644 +--- a/libs/lua53/lua53-src/doc/readme.html ++++ b/libs/lua53/lua53-src/doc/readme.html +@@ -107,7 +107,7 @@ Here are the details. +
          +
        1. + Open a terminal window and move to +-the top-level directory, which is named lua-5.3.x. ++the top-level directory, which is named lua-5.3.5. + The Makefile there controls both the build process and the installation process. +

          +

        2. +@@ -355,10 +355,10 @@ THE SOFTWARE. + + + + + +diff --git a/libs/lua53/lua53-src/src/Makefile b/libs/lua53/lua53-src/src/Makefile +index d71c75c87..64c78f775 100644 +--- a/libs/lua53/lua53-src/src/Makefile ++++ b/libs/lua53/lua53-src/src/Makefile +@@ -102,7 +102,7 @@ c89: + + + freebsd: +- $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -lreadline" ++ $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc" + + generic: $(ALL) + +@@ -110,7 +110,7 @@ linux: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl -lreadline" + + macosx: +- $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX" SYSLIBS="-lreadline" CC=cc ++ $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX" SYSLIBS="-lreadline" + + mingw: + $(MAKE) "LUA_A=lua53.dll" "LUA_T=lua.exe" \ +diff --git a/libs/lua53/lua53-src/src/lapi.c b/libs/lua53/lua53-src/src/lapi.c +index c9455a5d8..02b7fab7e 100644 +--- a/libs/lua53/lua53-src/src/lapi.c ++++ b/libs/lua53/lua53-src/src/lapi.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lapi.c,v 2.259 2016/02/29 14:27:14 roberto Exp $ ++** $Id: lapi.c,v 2.259.1.2 2017/12/06 18:35:12 roberto Exp $ + ** Lua API + ** See Copyright Notice in lua.h + */ +@@ -533,6 +533,7 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + lua_lock(L); + if (n == 0) { + setfvalue(L->top, fn); ++ api_incr_top(L); + } + else { + CClosure *cl; +@@ -546,9 +547,9 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + /* does not need barrier because closure is white */ + } + setclCvalue(L, L->top, cl); ++ api_incr_top(L); ++ luaC_checkGC(L); + } +- api_incr_top(L); +- luaC_checkGC(L); + lua_unlock(L); + } + +diff --git a/libs/lua53/lua53-src/src/lapi.h b/libs/lua53/lua53-src/src/lapi.h +index 6d36dee3f..8e16ad53d 100644 +--- a/libs/lua53/lua53-src/src/lapi.h ++++ b/libs/lua53/lua53-src/src/lapi.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lapi.h,v 2.9 2015/03/06 19:49:50 roberto Exp $ ++** $Id: lapi.h,v 2.9.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Auxiliary functions from Lua API + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lauxlib.c b/libs/lua53/lua53-src/src/lauxlib.c +index f7a383663..8bdada50a 100644 +--- a/libs/lua53/lua53-src/src/lauxlib.c ++++ b/libs/lua53/lua53-src/src/lauxlib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lauxlib.c,v 1.289 2016/12/20 18:37:00 roberto Exp $ ++** $Id: lauxlib.c,v 1.289.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Auxiliary functions for building Lua libraries + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lauxlib.h b/libs/lua53/lua53-src/src/lauxlib.h +index 9a2e66aa0..9857d3a83 100644 +--- a/libs/lua53/lua53-src/src/lauxlib.h ++++ b/libs/lua53/lua53-src/src/lauxlib.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lauxlib.h,v 1.131 2016/12/06 14:54:31 roberto Exp $ ++** $Id: lauxlib.h,v 1.131.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Auxiliary functions for building Lua libraries + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lbaselib.c b/libs/lua53/lua53-src/src/lbaselib.c +index 08523e6e7..6460e4f8d 100644 +--- a/libs/lua53/lua53-src/src/lbaselib.c ++++ b/libs/lua53/lua53-src/src/lbaselib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lbaselib.c,v 1.314 2016/09/05 19:06:34 roberto Exp $ ++** $Id: lbaselib.c,v 1.314.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Basic library + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lbitlib.c b/libs/lua53/lua53-src/src/lbitlib.c +index 1cb1d5b93..4786c0d48 100644 +--- a/libs/lua53/lua53-src/src/lbitlib.c ++++ b/libs/lua53/lua53-src/src/lbitlib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lbitlib.c,v 1.30 2015/11/11 19:08:09 roberto Exp $ ++** $Id: lbitlib.c,v 1.30.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Standard library for bitwise operations + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lcode.c b/libs/lua53/lua53-src/src/lcode.c +index 0bb414262..12619f54a 100644 +--- a/libs/lua53/lua53-src/src/lcode.c ++++ b/libs/lua53/lua53-src/src/lcode.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lcode.c,v 2.112 2016/12/22 13:08:50 roberto Exp $ ++** $Id: lcode.c,v 2.112.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Code generator for Lua + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lcode.h b/libs/lua53/lua53-src/src/lcode.h +index cd306d573..882dc9c15 100644 +--- a/libs/lua53/lua53-src/src/lcode.h ++++ b/libs/lua53/lua53-src/src/lcode.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lcode.h,v 1.64 2016/01/05 16:22:37 roberto Exp $ ++** $Id: lcode.h,v 1.64.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Code generator for Lua + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lcorolib.c b/libs/lua53/lua53-src/src/lcorolib.c +index 2303429e7..0b17af9e3 100644 +--- a/libs/lua53/lua53-src/src/lcorolib.c ++++ b/libs/lua53/lua53-src/src/lcorolib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lcorolib.c,v 1.10 2016/04/11 19:19:55 roberto Exp $ ++** $Id: lcorolib.c,v 1.10.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Coroutine Library + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lctype.c b/libs/lua53/lua53-src/src/lctype.c +index ae9367e69..f8ad7a2ed 100644 +--- a/libs/lua53/lua53-src/src/lctype.c ++++ b/libs/lua53/lua53-src/src/lctype.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lctype.c,v 1.12 2014/11/02 19:19:04 roberto Exp $ ++** $Id: lctype.c,v 1.12.1.1 2017/04/19 17:20:42 roberto Exp $ + ** 'ctype' functions for Lua + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lctype.h b/libs/lua53/lua53-src/src/lctype.h +index b961175bc..a963eb901 100644 +--- a/libs/lua53/lua53-src/src/lctype.h ++++ b/libs/lua53/lua53-src/src/lctype.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lctype.h,v 1.12 2011/07/15 12:50:29 roberto Exp $ ++** $Id: lctype.h,v 1.12.1.1 2013/04/12 18:48:47 roberto Exp $ + ** 'ctype' functions for Lua + ** See Copyright Notice in lua.h + */ +@@ -76,6 +76,7 @@ LUAI_DDEC const lu_byte luai_ctype_[UCHAR_MAX + 2]; + + + #else /* }{ */ ++ + /* + ** use standard C ctypes + */ +diff --git a/libs/lua53/lua53-src/src/lctype.h.orig b/libs/lua53/lua53-src/src/lctype.h.orig +index 99c7d1223..b09b21a33 100644 +--- a/libs/lua53/lua53-src/src/lctype.h.orig ++++ b/libs/lua53/lua53-src/src/lctype.h.orig +@@ -1,5 +1,5 @@ + /* +-** $Id: lctype.h,v 1.12 2011/07/15 12:50:29 roberto Exp $ ++** $Id: lctype.h,v 1.12.1.1 2013/04/12 18:48:47 roberto Exp $ + ** 'ctype' functions for Lua + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/ldblib.c b/libs/lua53/lua53-src/src/ldblib.c +index 786f6cd95..9d29afb0a 100644 +--- a/libs/lua53/lua53-src/src/ldblib.c ++++ b/libs/lua53/lua53-src/src/ldblib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: ldblib.c,v 1.151 2015/11/23 11:29:43 roberto Exp $ ++** $Id: ldblib.c,v 1.151.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Interface from Lua to its debug API + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/ldebug.c b/libs/lua53/lua53-src/src/ldebug.c +index 239affb76..e1389296e 100644 +--- a/libs/lua53/lua53-src/src/ldebug.c ++++ b/libs/lua53/lua53-src/src/ldebug.c +@@ -1,5 +1,5 @@ + /* +-** $Id: ldebug.c,v 2.121 2016/10/19 12:32:10 roberto Exp $ ++** $Id: ldebug.c,v 2.121.1.2 2017/07/10 17:21:50 roberto Exp $ + ** Debug Interface + ** See Copyright Notice in lua.h + */ +@@ -653,6 +653,7 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { + CallInfo *ci = L->ci; + const char *msg; + va_list argp; ++ luaC_checkGC(L); /* error message uses memory */ + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); /* format message */ + va_end(argp); +diff --git a/libs/lua53/lua53-src/src/ldebug.h b/libs/lua53/lua53-src/src/ldebug.h +index 0e31546b1..8cea0ee0a 100644 +--- a/libs/lua53/lua53-src/src/ldebug.h ++++ b/libs/lua53/lua53-src/src/ldebug.h +@@ -1,5 +1,5 @@ + /* +-** $Id: ldebug.h,v 2.14 2015/05/22 17:45:56 roberto Exp $ ++** $Id: ldebug.h,v 2.14.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Auxiliary functions from Debug Interface module + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/ldo.c b/libs/lua53/lua53-src/src/ldo.c +index 90b695fb0..316e45c8f 100644 +--- a/libs/lua53/lua53-src/src/ldo.c ++++ b/libs/lua53/lua53-src/src/ldo.c +@@ -1,5 +1,5 @@ + /* +-** $Id: ldo.c,v 2.157 2016/12/13 15:52:21 roberto Exp $ ++** $Id: ldo.c,v 2.157.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Stack and Call structure of Lua + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/ldo.h b/libs/lua53/lua53-src/src/ldo.h +index 4f5d51c3c..3b2983a38 100644 +--- a/libs/lua53/lua53-src/src/ldo.h ++++ b/libs/lua53/lua53-src/src/ldo.h +@@ -1,5 +1,5 @@ + /* +-** $Id: ldo.h,v 2.29 2015/12/21 13:02:14 roberto Exp $ ++** $Id: ldo.h,v 2.29.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Stack and Call structure of Lua + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/ldump.c b/libs/lua53/lua53-src/src/ldump.c +index 016e30082..f025acac3 100644 +--- a/libs/lua53/lua53-src/src/ldump.c ++++ b/libs/lua53/lua53-src/src/ldump.c +@@ -1,5 +1,5 @@ + /* +-** $Id: ldump.c,v 2.37 2015/10/08 15:53:49 roberto Exp $ ++** $Id: ldump.c,v 2.37.1.1 2017/04/19 17:20:42 roberto Exp $ + ** save precompiled Lua chunks + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lfunc.c b/libs/lua53/lua53-src/src/lfunc.c +index 67967dab3..ccafbb8ab 100644 +--- a/libs/lua53/lua53-src/src/lfunc.c ++++ b/libs/lua53/lua53-src/src/lfunc.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lfunc.c,v 2.45 2014/11/02 19:19:04 roberto Exp $ ++** $Id: lfunc.c,v 2.45.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Auxiliary functions to manipulate prototypes and closures + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lfunc.h b/libs/lua53/lua53-src/src/lfunc.h +index 2eeb0d5a4..c916e9878 100644 +--- a/libs/lua53/lua53-src/src/lfunc.h ++++ b/libs/lua53/lua53-src/src/lfunc.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lfunc.h,v 2.15 2015/01/13 15:49:11 roberto Exp $ ++** $Id: lfunc.h,v 2.15.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Auxiliary functions to manipulate prototypes and closures + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lgc.c b/libs/lua53/lua53-src/src/lgc.c +index ba2c19e14..db4df8292 100644 +--- a/libs/lua53/lua53-src/src/lgc.c ++++ b/libs/lua53/lua53-src/src/lgc.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lgc.c,v 2.215 2016/12/22 13:08:50 roberto Exp $ ++** $Id: lgc.c,v 2.215.1.2 2017/08/31 16:15:27 roberto Exp $ + ** Garbage Collector + ** See Copyright Notice in lua.h + */ +@@ -643,8 +643,9 @@ static void clearkeys (global_State *g, GCObject *l, GCObject *f) { + for (n = gnode(h, 0); n < limit; n++) { + if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) { + setnilvalue(gval(n)); /* remove value ... */ +- removeentry(n); /* and remove entry from table */ + } ++ if (ttisnil(gval(n))) /* is entry empty? */ ++ removeentry(n); /* remove entry from table */ + } + } + } +diff --git a/libs/lua53/lua53-src/src/lgc.h b/libs/lua53/lua53-src/src/lgc.h +index aed3e18a5..425cd7cef 100644 +--- a/libs/lua53/lua53-src/src/lgc.h ++++ b/libs/lua53/lua53-src/src/lgc.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lgc.h,v 2.91 2015/12/21 13:02:14 roberto Exp $ ++** $Id: lgc.h,v 2.91.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Garbage Collector + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/linit.c b/libs/lua53/lua53-src/src/linit.c +index afcaf98b2..480da52c7 100644 +--- a/libs/lua53/lua53-src/src/linit.c ++++ b/libs/lua53/lua53-src/src/linit.c +@@ -1,5 +1,5 @@ + /* +-** $Id: linit.c,v 1.39 2016/12/04 20:17:24 roberto Exp $ ++** $Id: linit.c,v 1.39.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Initialization of libraries for lua.c and other clients + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/liolib.c b/libs/lua53/lua53-src/src/liolib.c +index d47be5a27..76f372acb 100644 +--- a/libs/lua53/lua53-src/src/liolib.c ++++ b/libs/lua53/lua53-src/src/liolib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: liolib.c,v 2.151 2016/12/20 18:37:00 roberto Exp $ ++** $Id: liolib.c,v 2.151.1.1 2017/04/19 17:29:57 roberto Exp $ + ** Standard I/O (and system) library + ** See Copyright Notice in lua.h + */ +@@ -209,11 +209,16 @@ static int aux_close (lua_State *L) { + } + + ++static int f_close (lua_State *L) { ++ tofile(L); /* make sure argument is an open stream */ ++ return aux_close(L); ++} ++ ++ + static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) /* no argument? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */ +- tofile(L); /* make sure argument is an open stream */ +- return aux_close(L); ++ return f_close(L); + } + + +@@ -715,7 +720,7 @@ static const luaL_Reg iolib[] = { + ** methods for file handles + */ + static const luaL_Reg flib[] = { +- {"close", io_close}, ++ {"close", f_close}, + {"flush", f_flush}, + {"lines", f_lines}, + {"read", f_read}, +diff --git a/libs/lua53/lua53-src/src/liolib.c.orig b/libs/lua53/lua53-src/src/liolib.c.orig +index 156840358..8a9e75cd0 100644 +--- a/libs/lua53/lua53-src/src/liolib.c.orig ++++ b/libs/lua53/lua53-src/src/liolib.c.orig +@@ -1,5 +1,5 @@ + /* +-** $Id: liolib.c,v 2.151 2016/12/20 18:37:00 roberto Exp $ ++** $Id: liolib.c,v 2.151.1.1 2017/04/19 17:29:57 roberto Exp $ + ** Standard I/O (and system) library + ** See Copyright Notice in lua.h + */ +@@ -206,11 +206,16 @@ static int aux_close (lua_State *L) { + } + + ++static int f_close (lua_State *L) { ++ tofile(L); /* make sure argument is an open stream */ ++ return aux_close(L); ++} ++ ++ + static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) /* no argument? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */ +- tofile(L); /* make sure argument is an open stream */ +- return aux_close(L); ++ return f_close(L); + } + + +@@ -712,7 +717,7 @@ static const luaL_Reg iolib[] = { + ** methods for file handles + */ + static const luaL_Reg flib[] = { +- {"close", io_close}, ++ {"close", f_close}, + {"flush", f_flush}, + {"lines", f_lines}, + {"read", f_read}, +diff --git a/libs/lua53/lua53-src/src/llex.c b/libs/lua53/lua53-src/src/llex.c +index 70328273f..66fd411ba 100644 +--- a/libs/lua53/lua53-src/src/llex.c ++++ b/libs/lua53/lua53-src/src/llex.c +@@ -1,5 +1,5 @@ + /* +-** $Id: llex.c,v 2.96 2016/05/02 14:02:12 roberto Exp $ ++** $Id: llex.c,v 2.96.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Lexical Analyzer + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/llex.h b/libs/lua53/lua53-src/src/llex.h +index 2363d87e4..2ed0af66a 100644 +--- a/libs/lua53/lua53-src/src/llex.h ++++ b/libs/lua53/lua53-src/src/llex.h +@@ -1,5 +1,5 @@ + /* +-** $Id: llex.h,v 1.79 2016/05/02 14:02:12 roberto Exp $ ++** $Id: llex.h,v 1.79.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Lexical Analyzer + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/llimits.h b/libs/lua53/lua53-src/src/llimits.h +index f21377fef..d1036f6bc 100644 +--- a/libs/lua53/lua53-src/src/llimits.h ++++ b/libs/lua53/lua53-src/src/llimits.h +@@ -1,5 +1,5 @@ + /* +-** $Id: llimits.h,v 1.141 2015/11/19 19:16:22 roberto Exp $ ++** $Id: llimits.h,v 1.141.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Limits, basic types, and some other 'installation-dependent' definitions + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lmathlib.c b/libs/lua53/lua53-src/src/lmathlib.c +index b7f8baee0..7ef7e593f 100644 +--- a/libs/lua53/lua53-src/src/lmathlib.c ++++ b/libs/lua53/lua53-src/src/lmathlib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lmathlib.c,v 1.119 2016/12/22 13:08:50 roberto Exp $ ++** $Id: lmathlib.c,v 1.119.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Standard mathematical library + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lmem.c b/libs/lua53/lua53-src/src/lmem.c +index 0a0476cc7..0241cc3ba 100644 +--- a/libs/lua53/lua53-src/src/lmem.c ++++ b/libs/lua53/lua53-src/src/lmem.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lmem.c,v 1.91 2015/03/06 19:45:54 roberto Exp $ ++** $Id: lmem.c,v 1.91.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Interface to Memory Manager + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lmem.h b/libs/lua53/lua53-src/src/lmem.h +index 30f484895..357b1e43e 100644 +--- a/libs/lua53/lua53-src/src/lmem.h ++++ b/libs/lua53/lua53-src/src/lmem.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lmem.h,v 1.43 2014/12/19 17:26:14 roberto Exp $ ++** $Id: lmem.h,v 1.43.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Interface to Memory Manager + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/loadlib.c b/libs/lua53/lua53-src/src/loadlib.c +index 4791e748b..45f44d322 100644 +--- a/libs/lua53/lua53-src/src/loadlib.c ++++ b/libs/lua53/lua53-src/src/loadlib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: loadlib.c,v 1.130 2017/01/12 17:14:26 roberto Exp $ ++** $Id: loadlib.c,v 1.130.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Dynamic library loader for Lua + ** See Copyright Notice in lua.h + ** +diff --git a/libs/lua53/lua53-src/src/lobject.c b/libs/lua53/lua53-src/src/lobject.c +index 2da76899a..2218c8cdd 100644 +--- a/libs/lua53/lua53-src/src/lobject.c ++++ b/libs/lua53/lua53-src/src/lobject.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lobject.c,v 2.113 2016/12/22 13:08:50 roberto Exp $ ++** $Id: lobject.c,v 2.113.1.1 2017/04/19 17:29:57 roberto Exp $ + ** Some generic functions over Lua objects + ** See Copyright Notice in lua.h + */ +@@ -435,7 +435,8 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + } + case 'p': { /* a pointer */ + char buff[4*sizeof(void *) + 8]; /* should be enough space for a '%p' */ +- int l = l_sprintf(buff, sizeof(buff), "%p", va_arg(argp, void *)); ++ void *p = va_arg(argp, void *); ++ int l = lua_pointer2str(buff, sizeof(buff), p); + pushstr(L, buff, l); + break; + } +diff --git a/libs/lua53/lua53-src/src/lobject.h b/libs/lua53/lua53-src/src/lobject.h +index 3c0422894..240886140 100644 +--- a/libs/lua53/lua53-src/src/lobject.h ++++ b/libs/lua53/lua53-src/src/lobject.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lobject.h,v 2.117 2016/08/01 19:51:24 roberto Exp $ ++** $Id: lobject.h,v 2.117.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Type definitions for Lua objects + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lopcodes.c b/libs/lua53/lua53-src/src/lopcodes.c +index a1cbef857..5ca3eb261 100644 +--- a/libs/lua53/lua53-src/src/lopcodes.c ++++ b/libs/lua53/lua53-src/src/lopcodes.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lopcodes.c,v 1.55 2015/01/05 13:48:33 roberto Exp $ ++** $Id: lopcodes.c,v 1.55.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Opcodes for Lua virtual machine + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lopcodes.h b/libs/lua53/lua53-src/src/lopcodes.h +index f9b438e15..df6c2264b 100644 +--- a/libs/lua53/lua53-src/src/lopcodes.h ++++ b/libs/lua53/lua53-src/src/lopcodes.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lopcodes.h,v 1.149 2016/07/19 17:12:21 roberto Exp $ ++** $Id: lopcodes.h,v 1.149.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Opcodes for Lua virtual machine + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lopcodes.h.orig b/libs/lua53/lua53-src/src/lopcodes.h.orig +index bbc4b6196..6feaa1cd0 100644 +--- a/libs/lua53/lua53-src/src/lopcodes.h.orig ++++ b/libs/lua53/lua53-src/src/lopcodes.h.orig +@@ -1,5 +1,5 @@ + /* +-** $Id: lopcodes.h,v 1.149 2016/07/19 17:12:21 roberto Exp $ ++** $Id: lopcodes.h,v 1.149.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Opcodes for Lua virtual machine + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/loslib.c b/libs/lua53/lua53-src/src/loslib.c +index 5a94eb906..de590c6b7 100644 +--- a/libs/lua53/lua53-src/src/loslib.c ++++ b/libs/lua53/lua53-src/src/loslib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: loslib.c,v 1.65 2016/07/18 17:58:58 roberto Exp $ ++** $Id: loslib.c,v 1.65.1.1 2017/04/19 17:29:57 roberto Exp $ + ** Standard Operating System library + ** See Copyright Notice in lua.h + */ +@@ -293,7 +293,8 @@ static int os_date (lua_State *L) { + else + stm = l_localtime(&t, &tmr); + if (stm == NULL) /* invalid date? */ +- luaL_error(L, "time result cannot be represented in this installation"); ++ return luaL_error(L, ++ "time result cannot be represented in this installation"); + if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setallfields(L, stm); +@@ -340,7 +341,8 @@ static int os_time (lua_State *L) { + setallfields(L, &ts); /* update fields with normalized values */ + } + if (t != (time_t)(l_timet)t || t == (time_t)(-1)) +- luaL_error(L, "time result cannot be represented in this installation"); ++ return luaL_error(L, ++ "time result cannot be represented in this installation"); + l_pushtime(L, t); + return 1; + } +diff --git a/libs/lua53/lua53-src/src/lparser.c b/libs/lua53/lua53-src/src/lparser.c +index cd4512d4d..cc54de43c 100644 +--- a/libs/lua53/lua53-src/src/lparser.c ++++ b/libs/lua53/lua53-src/src/lparser.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lparser.c,v 2.155 2016/08/01 19:51:24 roberto Exp $ ++** $Id: lparser.c,v 2.155.1.2 2017/04/29 18:11:40 roberto Exp $ + ** Lua Parser + ** See Copyright Notice in lua.h + */ +@@ -1392,7 +1392,7 @@ static void test_then_block (LexState *ls, int *escapelist) { + luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ + enterblock(fs, &bl, 0); /* must enter block before 'goto' */ + gotostat(ls, v.t); /* handle goto/break */ +- skipnoopstat(ls); /* skip other no-op statements */ ++ while (testnext(ls, ';')) {} /* skip colons */ + if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ + leaveblock(fs); + return; /* and that is it */ +diff --git a/libs/lua53/lua53-src/src/lparser.h b/libs/lua53/lua53-src/src/lparser.h +index 02e9b03ae..f45b23cba 100644 +--- a/libs/lua53/lua53-src/src/lparser.h ++++ b/libs/lua53/lua53-src/src/lparser.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lparser.h,v 1.76 2015/12/30 18:16:13 roberto Exp $ ++** $Id: lparser.h,v 1.76.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Lua Parser + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lprefix.h b/libs/lua53/lua53-src/src/lprefix.h +index 02daa837f..9a749a3f3 100644 +--- a/libs/lua53/lua53-src/src/lprefix.h ++++ b/libs/lua53/lua53-src/src/lprefix.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lprefix.h,v 1.2 2014/12/29 16:54:13 roberto Exp $ ++** $Id: lprefix.h,v 1.2.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Definitions for Lua code that must come before any other header file + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lstate.c b/libs/lua53/lua53-src/src/lstate.c +index 9194ac341..c1a76643c 100644 +--- a/libs/lua53/lua53-src/src/lstate.c ++++ b/libs/lua53/lua53-src/src/lstate.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lstate.c,v 2.133 2015/11/13 12:16:51 roberto Exp $ ++** $Id: lstate.c,v 2.133.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Global State + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lstate.h b/libs/lua53/lua53-src/src/lstate.h +index a469466c4..56b374100 100644 +--- a/libs/lua53/lua53-src/src/lstate.h ++++ b/libs/lua53/lua53-src/src/lstate.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lstate.h,v 2.133 2016/12/22 13:08:50 roberto Exp $ ++** $Id: lstate.h,v 2.133.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Global State + ** See Copyright Notice in lua.h + */ +@@ -26,6 +26,24 @@ + ** 'tobefnz': all objects ready to be finalized; + ** 'fixedgc': all objects that are not to be collected (currently + ** only small strings, such as reserved words). ++** ++** Moreover, there is another set of lists that control gray objects. ++** These lists are linked by fields 'gclist'. (All objects that ++** can become gray have such a field. The field is not the same ++** in all objects, but it always has this name.) Any gray object ++** must belong to one of these lists, and all objects in these lists ++** must be gray: ++** ++** 'gray': regular gray objects, still waiting to be visited. ++** 'grayagain': objects that must be revisited at the atomic phase. ++** That includes ++** - black objects got in a write barrier; ++** - all kinds of weak tables during propagation phase; ++** - all threads. ++** 'weak': tables with weak values to be cleared; ++** 'ephemeron': ephemeron tables with white->white entries; ++** 'allweak': tables with weak keys and/or weak values to be cleared. ++** The last three lists are used only during the atomic phase. + + */ + +diff --git a/libs/lua53/lua53-src/src/lstring.c b/libs/lua53/lua53-src/src/lstring.c +index 9351766fd..6257f211d 100644 +--- a/libs/lua53/lua53-src/src/lstring.c ++++ b/libs/lua53/lua53-src/src/lstring.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lstring.c,v 2.56 2015/11/23 11:32:51 roberto Exp $ ++** $Id: lstring.c,v 2.56.1.1 2017/04/19 17:20:42 roberto Exp $ + ** String table (keeps all strings handled by Lua) + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lstring.h b/libs/lua53/lua53-src/src/lstring.h +index 27efd2077..d612abd33 100644 +--- a/libs/lua53/lua53-src/src/lstring.h ++++ b/libs/lua53/lua53-src/src/lstring.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lstring.h,v 1.61 2015/11/03 15:36:01 roberto Exp $ ++** $Id: lstring.h,v 1.61.1.1 2017/04/19 17:20:42 roberto Exp $ + ** String table (keep all strings handled by Lua) + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lstrlib.c b/libs/lua53/lua53-src/src/lstrlib.c +index c7aa755fa..b4bed7e93 100644 +--- a/libs/lua53/lua53-src/src/lstrlib.c ++++ b/libs/lua53/lua53-src/src/lstrlib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lstrlib.c,v 1.254 2016/12/22 13:08:50 roberto Exp $ ++** $Id: lstrlib.c,v 1.254.1.1 2017/04/19 17:29:57 roberto Exp $ + ** Standard library for string operations and pattern-matching + ** See Copyright Notice in lua.h + */ +@@ -879,7 +879,7 @@ static int lua_number2strx (lua_State *L, char *buff, int sz, + buff[i] = toupper(uchar(buff[i])); + } + else if (fmt[SIZELENMOD] != 'a') +- luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); ++ return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); + return n; + } + +@@ -1199,8 +1199,8 @@ static int getnum (const char **fmt, int df) { + static int getnumlimit (Header *h, const char **fmt, int df) { + int sz = getnum(fmt, df); + if (sz > MAXINTSIZE || sz <= 0) +- luaL_error(h->L, "integral size (%d) out of limits [1,%d]", +- sz, MAXINTSIZE); ++ return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", ++ sz, MAXINTSIZE); + return sz; + } + +diff --git a/libs/lua53/lua53-src/src/ltable.c b/libs/lua53/lua53-src/src/ltable.c +index d080189f2..ea4fe7fcb 100644 +--- a/libs/lua53/lua53-src/src/ltable.c ++++ b/libs/lua53/lua53-src/src/ltable.c +@@ -1,5 +1,5 @@ + /* +-** $Id: ltable.c,v 2.118 2016/11/07 12:38:35 roberto Exp $ ++** $Id: ltable.c,v 2.118.1.4 2018/06/08 16:22:51 roberto Exp $ + ** Lua tables (hash) + ** See Copyright Notice in lua.h + */ +@@ -223,7 +223,9 @@ static unsigned int computesizes (unsigned int nums[], unsigned int *pna) { + unsigned int na = 0; /* number of elements to go to array part */ + unsigned int optimal = 0; /* optimal size for array part */ + /* loop while keys can fill more than half of total size */ +- for (i = 0, twotoi = 1; *pna > twotoi / 2; i++, twotoi *= 2) { ++ for (i = 0, twotoi = 1; ++ twotoi > 0 && *pna > twotoi / 2; ++ i++, twotoi *= 2) { + if (nums[i] > 0) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ +@@ -330,17 +332,34 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { + } + + ++typedef struct { ++ Table *t; ++ unsigned int nhsize; ++} AuxsetnodeT; ++ ++ ++static void auxsetnode (lua_State *L, void *ud) { ++ AuxsetnodeT *asn = cast(AuxsetnodeT *, ud); ++ setnodevector(L, asn->t, asn->nhsize); ++} ++ ++ + void luaH_resize (lua_State *L, Table *t, unsigned int nasize, + unsigned int nhsize) { + unsigned int i; + int j; ++ AuxsetnodeT asn; + unsigned int oldasize = t->sizearray; + int oldhsize = allocsizenode(t); + Node *nold = t->node; /* save old hash ... */ + if (nasize > oldasize) /* array part must grow? */ + setarrayvector(L, t, nasize); + /* create new hash part with appropriate size */ +- setnodevector(L, t, nhsize); ++ asn.t = t; asn.nhsize = nhsize; ++ if (luaD_rawrunprotected(L, auxsetnode, &asn) != LUA_OK) { /* mem. error? */ ++ setarrayvector(L, t, oldasize); /* array back to its original size */ ++ luaD_throw(L, LUA_ERRMEM); /* rethrow memory error */ ++ } + if (nasize < oldasize) { /* array part must shrink? */ + t->sizearray = nasize; + /* re-insert elements from vanishing slice */ +@@ -610,13 +629,13 @@ void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { + } + + +-static int unbound_search (Table *t, unsigned int j) { +- unsigned int i = j; /* i is zero or a present index */ ++static lua_Unsigned unbound_search (Table *t, lua_Unsigned j) { ++ lua_Unsigned i = j; /* i is zero or a present index */ + j++; + /* find 'i' and 'j' such that i is present and j is not */ + while (!ttisnil(luaH_getint(t, j))) { + i = j; +- if (j > cast(unsigned int, MAX_INT)/2) { /* overflow? */ ++ if (j > l_castS2U(LUA_MAXINTEGER) / 2) { /* overflow? */ + /* table was built with bad purposes: resort to linear search */ + i = 1; + while (!ttisnil(luaH_getint(t, i))) i++; +@@ -626,7 +645,7 @@ static int unbound_search (Table *t, unsigned int j) { + } + /* now do a binary search between them */ + while (j - i > 1) { +- unsigned int m = (i+j)/2; ++ lua_Unsigned m = (i+j)/2; + if (ttisnil(luaH_getint(t, m))) j = m; + else i = m; + } +@@ -638,7 +657,7 @@ static int unbound_search (Table *t, unsigned int j) { + ** Try to find a boundary in table 't'. A 'boundary' is an integer index + ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). + */ +-int luaH_getn (Table *t) { ++lua_Unsigned luaH_getn (Table *t) { + unsigned int j = t->sizearray; + if (j > 0 && ttisnil(&t->array[j - 1])) { + /* there is a boundary in the array part: (binary) search for it */ +diff --git a/libs/lua53/lua53-src/src/ltable.h b/libs/lua53/lua53-src/src/ltable.h +index 6da9024fe..92db0ac7b 100644 +--- a/libs/lua53/lua53-src/src/ltable.h ++++ b/libs/lua53/lua53-src/src/ltable.h +@@ -1,5 +1,5 @@ + /* +-** $Id: ltable.h,v 2.23 2016/12/22 13:08:50 roberto Exp $ ++** $Id: ltable.h,v 2.23.1.2 2018/05/24 19:39:05 roberto Exp $ + ** Lua tables (hash) + ** See Copyright Notice in lua.h + */ +@@ -54,7 +54,7 @@ LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, + LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); + LUAI_FUNC void luaH_free (lua_State *L, Table *t); + LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +-LUAI_FUNC int luaH_getn (Table *t); ++LUAI_FUNC lua_Unsigned luaH_getn (Table *t); + + + #if defined(LUA_DEBUG) +diff --git a/libs/lua53/lua53-src/src/ltablib.c b/libs/lua53/lua53-src/src/ltablib.c +index 98b2f8713..c5349578e 100644 +--- a/libs/lua53/lua53-src/src/ltablib.c ++++ b/libs/lua53/lua53-src/src/ltablib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: ltablib.c,v 1.93 2016/02/25 19:41:54 roberto Exp $ ++** $Id: ltablib.c,v 1.93.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Library for Table Manipulation + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/ltm.c b/libs/lua53/lua53-src/src/ltm.c +index 14e525788..0e7c71321 100644 +--- a/libs/lua53/lua53-src/src/ltm.c ++++ b/libs/lua53/lua53-src/src/ltm.c +@@ -1,5 +1,5 @@ + /* +-** $Id: ltm.c,v 2.38 2016/12/22 13:08:50 roberto Exp $ ++** $Id: ltm.c,v 2.38.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Tag methods + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/ltm.h b/libs/lua53/lua53-src/src/ltm.h +index 63db7269b..8170688da 100644 +--- a/libs/lua53/lua53-src/src/ltm.h ++++ b/libs/lua53/lua53-src/src/ltm.h +@@ -1,5 +1,5 @@ + /* +-** $Id: ltm.h,v 2.22 2016/02/26 19:20:15 roberto Exp $ ++** $Id: ltm.h,v 2.22.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Tag methods + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lua.c b/libs/lua53/lua53-src/src/lua.c +index 3f082da6b..ca5b29852 100644 +--- a/libs/lua53/lua53-src/src/lua.c ++++ b/libs/lua53/lua53-src/src/lua.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lua.c,v 1.230 2017/01/12 17:14:26 roberto Exp $ ++** $Id: lua.c,v 1.230.1.1 2017/04/19 17:29:57 roberto Exp $ + ** Lua stand-alone interpreter + ** See Copyright Notice in lua.h + */ +@@ -138,7 +138,7 @@ static void print_usage (const char *badoption) { + "Available options are:\n" + " -e stat execute string 'stat'\n" + " -i enter interactive mode after executing 'script'\n" +- " -l name require library 'name'\n" ++ " -l name require library 'name' into global 'name'\n" + " -v show version information\n" + " -E ignore environment variables\n" + " -- stop handling options\n" +diff --git a/libs/lua53/lua53-src/src/lua.h b/libs/lua53/lua53-src/src/lua.h +index 26c0e2d69..c236e3609 100644 +--- a/libs/lua53/lua53-src/src/lua.h ++++ b/libs/lua53/lua53-src/src/lua.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lua.h,v 1.332 2016/12/22 15:51:20 roberto Exp $ ++** $Id: lua.h,v 1.332.1.2 2018/06/13 16:58:17 roberto Exp $ + ** Lua - A Scripting Language + ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) + ** See Copyright Notice at the end of this file +@@ -19,11 +19,11 @@ + #define LUA_VERSION_MAJOR "5" + #define LUA_VERSION_MINOR "3" + #define LUA_VERSION_NUM 503 +-#define LUA_VERSION_RELEASE "4" ++#define LUA_VERSION_RELEASE "5" + + #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR + #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE +-#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2017 Lua.org, PUC-Rio" ++#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2018 Lua.org, PUC-Rio" + #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" + + +@@ -460,7 +460,7 @@ struct lua_Debug { + + + /****************************************************************************** +-* Copyright (C) 1994-2017 Lua.org, PUC-Rio. ++* Copyright (C) 1994-2018 Lua.org, PUC-Rio. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the +diff --git a/libs/lua53/lua53-src/src/luac.c b/libs/lua53/lua53-src/src/luac.c +index c0c91d017..549ad3950 100644 +--- a/libs/lua53/lua53-src/src/luac.c ++++ b/libs/lua53/lua53-src/src/luac.c +@@ -1,5 +1,5 @@ + /* +-** $Id: luac.c,v 1.75 2015/03/12 01:58:27 lhf Exp $ ++** $Id: luac.c,v 1.76 2018/06/19 01:32:02 lhf Exp $ + ** Lua compiler (saves bytecodes to files; also lists bytecodes) + ** See Copyright Notice in lua.h + */ +@@ -206,7 +206,7 @@ int main(int argc, char* argv[]) + } + + /* +-** $Id: luac.c,v 1.75 2015/03/12 01:58:27 lhf Exp $ ++** $Id: luac.c,v 1.76 2018/06/19 01:32:02 lhf Exp $ + ** print bytecodes + ** See Copyright Notice in lua.h + */ +@@ -348,6 +348,7 @@ static void PrintCode(const Proto* f) + case OP_ADD: + case OP_SUB: + case OP_MUL: ++ case OP_MOD: + case OP_POW: + case OP_DIV: + case OP_IDIV: +diff --git a/libs/lua53/lua53-src/src/luaconf.h b/libs/lua53/lua53-src/src/luaconf.h +index dbd399e3c..9eeeea69e 100644 +--- a/libs/lua53/lua53-src/src/luaconf.h ++++ b/libs/lua53/lua53-src/src/luaconf.h +@@ -1,5 +1,5 @@ + /* +-** $Id: luaconf.h,v 1.259 2016/12/22 13:08:50 roberto Exp $ ++** $Id: luaconf.h,v 1.259.1.1 2017/04/19 17:29:57 roberto Exp $ + ** Configuration file for Lua + ** See Copyright Notice in lua.h + */ +@@ -604,8 +604,6 @@ + */ + #if !defined(LUA_USE_C89) + #define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +-/* Should we use this line ? */ +-/*#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i))*/ + #else + #define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) + #endif +@@ -622,6 +620,13 @@ + #endif + + ++/* ++@@ lua_pointer2str converts a pointer to a readable string in a ++** non-specified way. ++*/ ++#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) ++ ++ + /* + @@ lua_number2strx converts a float to an hexadecimal numeric string. + ** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. +diff --git a/libs/lua53/lua53-src/src/lualib.h b/libs/lua53/lua53-src/src/lualib.h +index 6c0bc4cb0..f5304aa0d 100644 +--- a/libs/lua53/lua53-src/src/lualib.h ++++ b/libs/lua53/lua53-src/src/lualib.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lualib.h,v 1.45 2017/01/12 17:14:26 roberto Exp $ ++** $Id: lualib.h,v 1.45.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Lua standard libraries + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lundump.c b/libs/lua53/lua53-src/src/lundump.c +index 4080af9c0..7a67d75aa 100644 +--- a/libs/lua53/lua53-src/src/lundump.c ++++ b/libs/lua53/lua53-src/src/lundump.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lundump.c,v 2.44 2015/11/02 16:09:30 roberto Exp $ ++** $Id: lundump.c,v 2.44.1.1 2017/04/19 17:20:42 roberto Exp $ + ** load precompiled Lua chunks + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lundump.h b/libs/lua53/lua53-src/src/lundump.h +index fe4a97463..f3e2e9061 100644 +--- a/libs/lua53/lua53-src/src/lundump.h ++++ b/libs/lua53/lua53-src/src/lundump.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lundump.h,v 1.45 2015/09/08 15:41:05 roberto Exp $ ++** $Id: lundump.h,v 1.45.1.1 2017/04/19 17:20:42 roberto Exp $ + ** load precompiled Lua chunks + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lundump.h.orig b/libs/lua53/lua53-src/src/lundump.h.orig +index aa5cc82f1..ce492d689 100644 +--- a/libs/lua53/lua53-src/src/lundump.h.orig ++++ b/libs/lua53/lua53-src/src/lundump.h.orig +@@ -1,5 +1,5 @@ + /* +-** $Id: lundump.h,v 1.45 2015/09/08 15:41:05 roberto Exp $ ++** $Id: lundump.h,v 1.45.1.1 2017/04/19 17:20:42 roberto Exp $ + ** load precompiled Lua chunks + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lutf8lib.c b/libs/lua53/lua53-src/src/lutf8lib.c +index de9e3dcdd..10bd238a7 100644 +--- a/libs/lua53/lua53-src/src/lutf8lib.c ++++ b/libs/lua53/lua53-src/src/lutf8lib.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lutf8lib.c,v 1.16 2016/12/22 13:08:50 roberto Exp $ ++** $Id: lutf8lib.c,v 1.16.1.1 2017/04/19 17:29:57 roberto Exp $ + ** Standard library for UTF-8 manipulation + ** See Copyright Notice in lua.h + */ +@@ -171,7 +171,7 @@ static int byteoffset (lua_State *L) { + } + else { + if (iscont(s + posi)) +- luaL_error(L, "initial position is a continuation byte"); ++ return luaL_error(L, "initial position is a continuation byte"); + if (n < 0) { + while (n < 0 && posi > 0) { /* move back */ + do { /* find beginning of previous character */ +diff --git a/libs/lua53/lua53-src/src/lvm.c b/libs/lua53/lua53-src/src/lvm.c +index 84ade6b2f..cc43d8714 100644 +--- a/libs/lua53/lua53-src/src/lvm.c ++++ b/libs/lua53/lua53-src/src/lvm.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lvm.c,v 2.268 2016/02/05 19:59:14 roberto Exp $ ++** $Id: lvm.c,v 2.268.1.1 2017/04/19 17:39:34 roberto Exp $ + ** Lua virtual machine + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lvm.h b/libs/lua53/lua53-src/src/lvm.h +index 422f87194..a8f954f04 100644 +--- a/libs/lua53/lua53-src/src/lvm.h ++++ b/libs/lua53/lua53-src/src/lvm.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lvm.h,v 2.41 2016/12/22 13:08:50 roberto Exp $ ++** $Id: lvm.h,v 2.41.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Lua virtual machine + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lzio.c b/libs/lua53/lua53-src/src/lzio.c +index c9e1f491f..6f7909441 100644 +--- a/libs/lua53/lua53-src/src/lzio.c ++++ b/libs/lua53/lua53-src/src/lzio.c +@@ -1,5 +1,5 @@ + /* +-** $Id: lzio.c,v 1.37 2015/09/08 15:41:05 roberto Exp $ ++** $Id: lzio.c,v 1.37.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Buffered streams + ** See Copyright Notice in lua.h + */ +diff --git a/libs/lua53/lua53-src/src/lzio.h b/libs/lua53/lua53-src/src/lzio.h +index e7b6f34b1..d89787081 100644 +--- a/libs/lua53/lua53-src/src/lzio.h ++++ b/libs/lua53/lua53-src/src/lzio.h +@@ -1,5 +1,5 @@ + /* +-** $Id: lzio.h,v 1.31 2015/09/08 15:41:05 roberto Exp $ ++** $Id: lzio.h,v 1.31.1.1 2017/04/19 17:20:42 roberto Exp $ + ** Buffered streams + ** See Copyright Notice in lua.h + */ +diff --git a/libs/luajit/native/Makefile.am b/libs/luajit/native/Makefile.am +index f89c2fa76..c9ed9d7a1 100644 +--- a/libs/luajit/native/Makefile.am ++++ b/libs/luajit/native/Makefile.am +@@ -28,6 +28,7 @@ buildvm_arch.h: minilua$(EXEEXT) $(LUAJIT_TREE)/dynasm/dynasm.lua + `cat ../dynasm_flags` \ + -o $@ $(srcdir)/$(LUAJIT_TREE)/src/vm_$(DASM_ARCH).dasc + ++minilua_CPPFLAGS = $(AM_CPPFLAGS) $(LUAJIT_DEFINES) `cat ../native_flags` + nodist_minilua_SOURCES = \ + @LUAJIT_TREE@/src/host/minilua.c + minilua_LDADD = $(MATH_LIB) +diff --git a/libs/zziplib/zziplib-src/zzip/lib.h b/libs/zziplib/zziplib-src/zzip/lib.h +index 4a02f5473..b09a13028 100644 +--- a/libs/zziplib/zziplib-src/zzip/lib.h ++++ b/libs/zziplib/zziplib-src/zzip/lib.h +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + #ifdef __cplusplus + extern "C" { +diff --git a/texk/web2c/Makefile.am b/texk/web2c/Makefile.am +index 38740776a..ec388fc2b 100644 +--- a/texk/web2c/Makefile.am ++++ b/texk/web2c/Makefile.am +@@ -237,6 +237,7 @@ include $(srcdir)/luatexdir/am/libunilib.am + include $(srcdir)/luatexdir/am/luafontforge.am + include $(srcdir)/luatexdir/am/libluatex.am + include $(srcdir)/luatexdir/am/luaffi.am ++include $(srcdir)/luatexdir/am/luapplib.am + include $(srcdir)/luatexdir/am/luatex.am + + ## XeTeX +diff --git a/texk/web2c/ac/web2c.ac b/texk/web2c/ac/web2c.ac +index 8cf40e03c..27bd6bb2c 100644 +--- a/texk/web2c/ac/web2c.ac ++++ b/texk/web2c/ac/web2c.ac +@@ -35,9 +35,9 @@ m4_define([kpse_tex_progs], [dnl + [[euptex], [yes], [yes], [e-upTeX], [ptexenc]], + [[aleph], [yes], [], [Aleph], []], + [[pdftex], [yes], [yes], [pdfTeX], [xpdf libpng]], +-[[luatex], [yes], [], [LuaTeX], [poppler mpfr libpng zziplib lua52]], +-[[luatex53], [yes], [], [LuaTeX53], [poppler mpfr libpng zziplib lua53]], +-[[luajittex], [yes], [], [LuaJITTeX], [poppler mpfr libpng zziplib luajit]], ++[[luatex], [yes], [], [LuaTeX], [libpng zziplib lua52]], ++[[luatex53], [yes], [], [LuaTeX53], [libpng zziplib lua53]], ++[[luajittex], [yes], [], [LuaJITTeX], [libpng zziplib luajit]], + [[mp], [yes], [], [MetaPost], [mpfr cairo libpng]], + [[pmp], [yes], [], [pMetaPost], [mpfr cairo libpng ptexenc]], + [[upmp], [yes], [], [upMetaPost], [mpfr cairo libpng ptexenc]], +diff --git a/texk/web2c/configure.ac b/texk/web2c/configure.ac +index 6c0839297..5448f6203 100644 +--- a/texk/web2c/configure.ac ++++ b/texk/web2c/configure.ac +@@ -46,13 +46,10 @@ KPSE_LT_HACK + KPSE_WEB2C_PREPARE + m4_include([ac/web2c.ac]) + +-# LuaTeX and XeTeX now require C++11 because poppler does :(. ++# XeTeX now requires C++11 because poppler does :(. + # XeTeX also requires C+11 because of ICU. +-if test "x$enable_xetex" = xyes \ +- || test "x$enable_luatex" = xyes \ +- || test "x$enable_luajittex" = xyes \ +- || test "x$enable_luatex53" = xyes; then +- AC_MSG_NOTICE([checking for C++11, since LuaTeX and/or XeTeX enabled]) ++if test "x$enable_xetex" = xyes; then ++ AC_MSG_NOTICE([checking for C++11, since XeTeX enabled]) + AX_CXX_COMPILE_STDCXX([11]) + fi + +diff --git a/texk/web2c/luatexdir/ChangeLog b/texk/web2c/luatexdir/ChangeLog +index 94cbfc3e6..62d4ab049 100644 +--- a/texk/web2c/luatexdir/ChangeLog ++++ b/texk/web2c/luatexdir/ChangeLog +@@ -1,3 +1,7 @@ ++2018-08-27 Luigi Scarso ++ * dropped dependency from gmp and mpfr ++ ++ + 2017-11-02 Luigi Scarso + LuaFilesystem 1.7.0 + +diff --git a/texk/web2c/luatexdir/NEWS b/texk/web2c/luatexdir/NEWS +index 91a4ec7fe..5c044e77e 100644 +--- a/texk/web2c/luatexdir/NEWS ++++ b/texk/web2c/luatexdir/NEWS +@@ -1,3 +1,38 @@ ++============================================================== ++LuaTeX 1.08 2018-08-28 ++============================================================== ++ ++ ++(1) This release is a prelude to 1.10, the next stable iteration of LuaTeX ++after version 1.00. ++ ++(2) Lua 5.3 is now considered to be default and we might use 5.4 in version ++1.10. There are no real functional changed expected. You still need to rename ++the binary for 5.3! ++ ++(3) Binary mode is no longer available in MPlib but it is still available in ++stand alone MetaPost. This simplifies compilation and reduces dependencies. ++ ++(4) The dependency on Poppler for pdf image inclusion has been removed. We ++now use a small dedicated library written by Pawel Jakowski. We no longer ++need c++ compilers. We're in the process of making it behave well on all ++platforms. It has been tested on intel platforms. ++ ++(5) We know that there can be some (alignment) issues with the arm platform ++but these are looked into. Therefore, later this year we will release 1.09. ++Version 1.10 is planned for TeXlive. We hope that ffi works ok on intel and ++arm platforms at that point. ++ ++(6) There have been some extensions to the Lua libraries and some callbacks ++have been added. Also, a few new primitives have been introduced. The ++documentation mentions the stable extensions. ++ ++(7) There are the usual bug fixes and cleanups but there have been no real ++fundamental changes in the API. ++ ++ ++ ++ + ============================================================== + LuaTeX 1.07 2018-01-17 + ============================================================== +diff --git a/texk/web2c/luatexdir/am/libluatex.am b/texk/web2c/luatexdir/am/libluatex.am +index 3e90186ac..e3678b64b 100644 +--- a/texk/web2c/luatexdir/am/libluatex.am ++++ b/texk/web2c/luatexdir/am/libluatex.am +@@ -26,9 +26,10 @@ liblua53tex_a_DEPENDENCIES = libff.a liblua53misc.a + libluajittex_a_DEPENDENCIES = libff.a libluajitmisc.a + + libluatex_a_preflags = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(LIBPNG_INCLUDES) +-libluatex_a_preflags += $(POPPLER_INCLUDES) -I$(srcdir)/libmd5 ++libluatex_a_preflags += -I$(srcdir)/libmd5 + libluatex_a_preflags += -DpdfTeX -I$(srcdir)/luatexdir + libluatex_a_preflags += -I$(srcdir)/luatexdir/unilib ++libluatex_a_preflags += -I$(srcdir)/luatexdir/luapplib/util + libluatex_a_preflags += -I$(srcdir)/luatexdir/luafontloader/fontforge/inc + libluatex_a_preflags += -DLUA_FF_LIB=1 -I$(srcdir)/luatexdir/luafontloader/fontforge/fontforge + libluatex_a_preflags += -DSYNCTEX_ENGINE_H='' -I$(srcdir)/synctexdir +@@ -43,29 +44,31 @@ liblua53tex_a_CXXFLAGS = $(WARNING_CXXFLAGS) + libluajittex_a_CXXFLAGS = $(WARNING_CXXFLAGS) + + dist_libluatex_a_SOURCES = \ +- luatexdir/lua/lstrlibext.c ++ luatexdir/lua/lstrlibext.c \ ++ luatexdir/lua/helpers.c \ ++ luatexdir/lua/texluac.c + nodist_libluatex_a_SOURCES = \ +- helpers.c luastuff.c texluac.c \ + $(dist_libluatex_sources) \ + $(nodist_libluatex_sources) + dist_liblua53tex_a_SOURCES = \ +- luatexdir/lua/lstrlibext.c ++ luatexdir/lua/lstrlibext.c \ ++ luatexdir/lua/helpers.c \ ++ luatexdir/lua/texluac.c + nodist_liblua53tex_a_SOURCES = \ +- helpers.c luastuff.c texluac.c \ + $(dist_libluatex_sources) \ + $(nodist_libluatex_sources) + dist_libluajittex_a_SOURCES = \ + luatexdir/lua/lauxlib_bridge.h \ +- luatexdir/lua/lstrlibext.c ++ luatexdir/lua/lstrlibext.c \ ++ luatexdir/lua/texluajitc.c + nodist_libluajittex_a_SOURCES = \ +- luastuff.c texluajitc.c \ + $(dist_libluatex_sources) \ + $(nodist_libluatex_sources) + + ## mplib "stub" backends are in mplibstuff.c +-$(libluatex_a_OBJECTS): libff.a libmplibcore.a libluamisc.a $(POPPLER_DEPEND) +-$(liblua53tex_a_OBJECTS): libff.a libmplibcore.a liblua53misc.a $(POPPLER_DEPEND) +-$(libluajittex_a_OBJECTS): libff.a libmplibcore.a libluajitmisc.a $(POPPLER_DEPEND) ++$(libluatex_a_OBJECTS): libff.a libmplibcore.a libluamisc.a ++$(liblua53tex_a_OBJECTS): libff.a libmplibcore.a liblua53misc.a ++$(libluajittex_a_OBJECTS): libff.a libmplibcore.a libluajitmisc.a + + + ## from luatexdir +@@ -91,76 +94,77 @@ dist_libluatex_sources += \ + ## + luatex_dvi_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/dvi $(ctangle) + +-dvigen.c: ctangle$(EXEEXT) luatexdir/dvi/dvigen.w +- $(luatex_dvi_ctangle) dvigen.w ++#dvigen.c: ctangle$(EXEEXT) luatexdir/dvi/dvigen.w ++# $(luatex_dvi_ctangle) dvigen.w + +-libluatex_web += luatexdir/dvi/dvigen.w ++#libluatex_web += luatexdir/dvi/dvigen.w + +-nodist_libluatex_sources += dvigen.c ++#nodist_libluatex_sources += dvigen.c + + dist_libluatex_sources += \ +- luatexdir/dvi/dvigen.h ++ luatexdir/dvi/dvigen.h \ ++ luatexdir/dvi/dvigen.c + + ## from luatexdir/font + ## + luatex_font_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/font $(ctangle) + +-dofont.c: ctangle$(EXEEXT) luatexdir/font/dofont.w +- $(luatex_font_ctangle) dofont.w +-luafont.c: ctangle$(EXEEXT) luatexdir/font/luafont.w +- $(luatex_font_ctangle) luafont.w +-mapfile.c: ctangle$(EXEEXT) luatexdir/font/mapfile.w +- $(luatex_font_ctangle) mapfile.w +-pkin.c: ctangle$(EXEEXT) luatexdir/font/pkin.w +- $(luatex_font_ctangle) pkin.w +-sfnt.c: ctangle$(EXEEXT) luatexdir/font/sfnt.w +- $(luatex_font_ctangle) sfnt.w +-texfont.c: ctangle$(EXEEXT) luatexdir/font/texfont.w +- $(luatex_font_ctangle) texfont.w +-tfmofm.c: ctangle$(EXEEXT) luatexdir/font/tfmofm.w +- $(luatex_font_ctangle) tfmofm.w +-tounicode.c: ctangle$(EXEEXT) luatexdir/font/tounicode.w +- $(luatex_font_ctangle) tounicode.w +-tt_glyf.c: ctangle$(EXEEXT) luatexdir/font/tt_glyf.w +- $(luatex_font_ctangle) tt_glyf.w +-tt_table.c: ctangle$(EXEEXT) luatexdir/font/tt_table.w +- $(luatex_font_ctangle) tt_table.w +-vfovf.c: ctangle$(EXEEXT) luatexdir/font/vfovf.w +- $(luatex_font_ctangle) vfovf.w +-vfpacket.c: ctangle$(EXEEXT) luatexdir/font/vfpacket.w +- $(luatex_font_ctangle) vfpacket.w +-writecff.c: ctangle$(EXEEXT) luatexdir/font/writecff.w +- $(luatex_font_ctangle) writecff.w +-writeenc.c: ctangle$(EXEEXT) luatexdir/font/writeenc.w +- $(luatex_font_ctangle) writeenc.w +-writefont.c: ctangle$(EXEEXT) luatexdir/font/writefont.w +- $(luatex_font_ctangle) writefont.w +-writet1.c: ctangle$(EXEEXT) luatexdir/font/writet1.w +- $(luatex_font_ctangle) writet1.w +-writet3.c: ctangle$(EXEEXT) luatexdir/font/writet3.w +- $(luatex_font_ctangle) writet3.w +-writettf.c: ctangle$(EXEEXT) luatexdir/font/writettf.w +- $(luatex_font_ctangle) writettf.w +-writetype0.c: ctangle$(EXEEXT) luatexdir/font/writetype0.w +- $(luatex_font_ctangle) writetype0.w +-writetype2.c: ctangle$(EXEEXT) luatexdir/font/writetype2.w +- $(luatex_font_ctangle) writetype2.w +- +-libluatex_web += luatexdir/font/dofont.w luatexdir/font/luafont.w luatexdir/font/mapfile.w +-libluatex_web += luatexdir/font/pkin.w luatexdir/font/sfnt.w +-libluatex_web += luatexdir/font/texfont.w luatexdir/font/tfmofm.w +-libluatex_web += luatexdir/font/tounicode.w luatexdir/font/tt_glyf.w +-libluatex_web += luatexdir/font/tt_table.w luatexdir/font/vfovf.w +-libluatex_web += luatexdir/font/vfpacket.w luatexdir/font/writecff.w +-libluatex_web += luatexdir/font/writeenc.w luatexdir/font/writefont.w +-libluatex_web += luatexdir/font/writet1.w luatexdir/font/writet3.w +-libluatex_web += luatexdir/font/writettf.w luatexdir/font/writetype0.w +-libluatex_web += luatexdir/font/writetype2.w +- +-nodist_libluatex_sources += dofont.c luafont.c mapfile.c pkin.c sfnt.c +-nodist_libluatex_sources += texfont.c tfmofm.c tounicode.c tt_glyf.c tt_table.c vfovf.c vfpacket.c +-nodist_libluatex_sources += writecff.c writeenc.c writefont.c writet1.c writet3.c writettf.c +-nodist_libluatex_sources += writetype0.c writetype2.c ++# dofont.c: ctangle$(EXEEXT) luatexdir/font/dofont.w ++# $(luatex_font_ctangle) dofont.w ++# luafont.c: ctangle$(EXEEXT) luatexdir/font/luafont.w ++# $(luatex_font_ctangle) luafont.w ++# mapfile.c: ctangle$(EXEEXT) luatexdir/font/mapfile.w ++# $(luatex_font_ctangle) mapfile.w ++# pkin.c: ctangle$(EXEEXT) luatexdir/font/pkin.w ++# $(luatex_font_ctangle) pkin.w ++# sfnt.c: ctangle$(EXEEXT) luatexdir/font/sfnt.w ++# $(luatex_font_ctangle) sfnt.w ++# texfont.c: ctangle$(EXEEXT) luatexdir/font/texfont.w ++# $(luatex_font_ctangle) texfont.w ++# tfmofm.c: ctangle$(EXEEXT) luatexdir/font/tfmofm.w ++# $(luatex_font_ctangle) tfmofm.w ++# tounicode.c: ctangle$(EXEEXT) luatexdir/font/tounicode.w ++# $(luatex_font_ctangle) tounicode.w ++# tt_glyf.c: ctangle$(EXEEXT) luatexdir/font/tt_glyf.w ++# $(luatex_font_ctangle) tt_glyf.w ++# tt_table.c: ctangle$(EXEEXT) luatexdir/font/tt_table.w ++# $(luatex_font_ctangle) tt_table.w ++# vfovf.c: ctangle$(EXEEXT) luatexdir/font/vfovf.w ++# $(luatex_font_ctangle) vfovf.w ++# vfpacket.c: ctangle$(EXEEXT) luatexdir/font/vfpacket.w ++# $(luatex_font_ctangle) vfpacket.w ++# writecff.c: ctangle$(EXEEXT) luatexdir/font/writecff.w ++# $(luatex_font_ctangle) writecff.w ++# writeenc.c: ctangle$(EXEEXT) luatexdir/font/writeenc.w ++# $(luatex_font_ctangle) writeenc.w ++# writefont.c: ctangle$(EXEEXT) luatexdir/font/writefont.w ++# $(luatex_font_ctangle) writefont.w ++# writet1.c: ctangle$(EXEEXT) luatexdir/font/writet1.w ++# $(luatex_font_ctangle) writet1.w ++# writet3.c: ctangle$(EXEEXT) luatexdir/font/writet3.w ++# $(luatex_font_ctangle) writet3.w ++# writettf.c: ctangle$(EXEEXT) luatexdir/font/writettf.w ++# $(luatex_font_ctangle) writettf.w ++# writetype0.c: ctangle$(EXEEXT) luatexdir/font/writetype0.w ++# $(luatex_font_ctangle) writetype0.w ++# writetype2.c: ctangle$(EXEEXT) luatexdir/font/writetype2.w ++# $(luatex_font_ctangle) writetype2.w ++ ++# libluatex_web += luatexdir/font/dofont.w luatexdir/font/luafont.w luatexdir/font/mapfile.w ++# libluatex_web += luatexdir/font/pkin.w luatexdir/font/sfnt.w ++# libluatex_web += luatexdir/font/texfont.w luatexdir/font/tfmofm.w ++# libluatex_web += luatexdir/font/tounicode.w luatexdir/font/tt_glyf.w ++# libluatex_web += luatexdir/font/tt_table.w luatexdir/font/vfovf.w ++# libluatex_web += luatexdir/font/vfpacket.w luatexdir/font/writecff.w ++# libluatex_web += luatexdir/font/writeenc.w luatexdir/font/writefont.w ++# libluatex_web += luatexdir/font/writet1.w luatexdir/font/writet3.w ++# libluatex_web += luatexdir/font/writettf.w luatexdir/font/writetype0.w ++# libluatex_web += luatexdir/font/writetype2.w ++ ++# nodist_libluatex_sources += dofont.c luafont.c mapfile.c pkin.c sfnt.c ++# nodist_libluatex_sources += texfont.c tfmofm.c tounicode.c tt_glyf.c tt_table.c vfovf.c vfpacket.c ++# nodist_libluatex_sources += writecff.c writeenc.c writefont.c writet1.c writet3.c writettf.c ++# nodist_libluatex_sources += writetype0.c writetype2.c + + dist_libluatex_sources += \ + luatexdir/font/luatexfont.h \ +@@ -170,33 +174,54 @@ dist_libluatex_sources += \ + luatexdir/font/tt_glyf.h \ + luatexdir/font/tt_table.h \ + luatexdir/font/writecff.h \ +- luatexdir/font/writettf.h ++ luatexdir/font/writettf.h \ ++ luatexdir/font/dofont.c \ ++ luatexdir/font/luafont.c \ ++ luatexdir/font/mapfile.c \ ++ luatexdir/font/pkin.c \ ++ luatexdir/font/sfnt.c \ ++ luatexdir/font/texfont.c \ ++ luatexdir/font/tfmofm.c \ ++ luatexdir/font/tounicode.c \ ++ luatexdir/font/tt_glyf.c \ ++ luatexdir/font/tt_table.c \ ++ luatexdir/font/vfovf.c \ ++ luatexdir/font/vfpacket.c \ ++ luatexdir/font/writecff.c \ ++ luatexdir/font/writeenc.c \ ++ luatexdir/font/writefont.c \ ++ luatexdir/font/writet1.c \ ++ luatexdir/font/writet3.c \ ++ luatexdir/font/writettf.c \ ++ luatexdir/font/writetype0.c \ ++ luatexdir/font/writetype2.c ++ + + ## from luatexdir/image + ## + luatex_image_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/image $(ctangle) + +-writeimg.c: ctangle$(EXEEXT) luatexdir/image/writeimg.w +- $(luatex_image_ctangle) writeimg.w +-writejbig2.c: ctangle$(EXEEXT) luatexdir/image/writejbig2.w +- $(luatex_image_ctangle) writejbig2.w +-writejpg.c: ctangle$(EXEEXT) luatexdir/image/writejpg.w +- $(luatex_image_ctangle) writejpg.w +-writejp2.c: ctangle$(EXEEXT) luatexdir/image/writejp2.w +- $(luatex_image_ctangle) writejp2.w +-writepng.c: ctangle$(EXEEXT) luatexdir/image/writepng.w +- $(luatex_image_ctangle) writepng.w +-pdftoepdf.cc: ctangle$(EXEEXT) luatexdir/image/pdftoepdf.w +- $(luatex_image_ctangle) pdftoepdf.w - $@ +- +-libluatex_web += luatexdir/image/writeimg.w +-libluatex_web += luatexdir/image/writejbig2.w +-libluatex_web += luatexdir/image/writejpg.w +-libluatex_web += luatexdir/image/writejp2.w +-libluatex_web += luatexdir/image/writepng.w +-libluatex_web += luatexdir/image/pdftoepdf.w +- +-nodist_libluatex_sources += writeimg.c writejbig2.c writejpg.c writejp2.c writepng.c pdftoepdf.cc ++#writeimg.c: ctangle$(EXEEXT) luatexdir/image/writeimg.w ++# $(luatex_image_ctangle) writeimg.w ++#writejbig2.c: ctangle$(EXEEXT) luatexdir/image/writejbig2.w ++# $(luatex_image_ctangle) writejbig2.w ++#writejpg.c: ctangle$(EXEEXT) luatexdir/image/writejpg.w ++# $(luatex_image_ctangle) writejpg.w ++#writejp2.c: ctangle$(EXEEXT) luatexdir/image/writejp2.w ++# $(luatex_image_ctangle) writejp2.w ++#writepng.c: ctangle$(EXEEXT) luatexdir/image/writepng.w ++# $(luatex_image_ctangle) writepng.w ++#pdftoepdf.cc: ctangle$(EXEEXT) luatexdir/image/pdftoepdf.w ++# $(luatex_image_ctangle) pdftoepdf.w - $@ ++ ++#libluatex_web += luatexdir/image/writeimg.w ++#libluatex_web += luatexdir/image/writejbig2.w ++#libluatex_web += luatexdir/image/writejpg.w ++#libluatex_web += luatexdir/image/writejp2.w ++#libluatex_web += luatexdir/image/writepng.w ++#libluatex_web += luatexdir/image/pdftoepdf.w ++ ++#nodist_libluatex_sources += writeimg.c writejbig2.c writejpg.c writejp2.c writepng.c + + dist_libluatex_sources += \ + luatexdir/image/epdf.h \ +@@ -206,72 +231,61 @@ dist_libluatex_sources += \ + luatexdir/image/writejbig2.h \ + luatexdir/image/writejpg.h \ + luatexdir/image/writejp2.h \ +- luatexdir/image/writepng.h ++ luatexdir/image/writepng.h \ ++ luatexdir/image/pdftoepdf.c \ ++ luatexdir/image/writeimg.c \ ++ luatexdir/image/writejbig2.c \ ++ luatexdir/image/writejp2.c \ ++ luatexdir/image/writejpg.c \ ++ luatexdir/image/writepng.c + + ## from luatexdir/lang + ## + luatex_lang_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/lang $(ctangle) + +-hnjalloc.c: ctangle$(EXEEXT) luatexdir/lang/hnjalloc.w +- $(luatex_lang_ctangle) hnjalloc.w +-hyphen.c: ctangle$(EXEEXT) luatexdir/lang/hyphen.w +- $(luatex_lang_ctangle) hyphen.w +-texlang.c: ctangle$(EXEEXT) luatexdir/lang/texlang.w +- $(luatex_lang_ctangle) texlang.w +- +-libluatex_web += luatexdir/lang/texlang.w luatexdir/lang/hyphen.w luatexdir/lang/hnjalloc.w +- +-nodist_libluatex_sources += texlang.c hyphen.c hnjalloc.c +- + dist_libluatex_sources += \ + luatexdir/lang/hnjalloc.h \ + luatexdir/lang/hyphen.h \ +- luatexdir/lang/texlang.h ++ luatexdir/lang/texlang.h \ ++ luatexdir/lang/hnjalloc.c \ ++ luatexdir/lang/hyphen.c \ ++ luatexdir/lang/texlang.c + + ## from luatexdir/lua + ## + luatex_lua_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/lua $(ctangle) + +-helpers.c: ctangle$(EXEEXT) luatexdir/lua/helpers.w +- $(luatex_lua_ctangle) helpers.w +-luainit.c: ctangle$(EXEEXT) luatexdir/lua/luainit.w +- $(luatex_lua_ctangle) luainit.w ++#helpers.c: ctangle$(EXEEXT) luatexdir/lua/helpers.w ++# $(luatex_lua_ctangle) helpers.w ++#luainit.c: ctangle$(EXEEXT) luatexdir/lua/luainit.w ++# $(luatex_lua_ctangle) luainit.w + #luajitstuff.c: ctangle$(EXEEXT) luatexdir/lua/luajitstuff.w + # $(luatex_lua_ctangle) luajitstuff.w +-luanode.c: ctangle$(EXEEXT) luatexdir/lua/luanode.w +- $(luatex_lua_ctangle) luanode.w +-luastuff.c: ctangle$(EXEEXT) luatexdir/lua/luastuff.w +- $(luatex_lua_ctangle) luastuff.w +-luatoken.c: ctangle$(EXEEXT) luatexdir/lua/luatoken.w +- $(luatex_lua_ctangle) luatoken.w +-mplibstuff.c: ctangle$(EXEEXT) luatexdir/lua/mplibstuff.w +- $(luatex_lua_ctangle) mplibstuff.w +-texluac.c: ctangle$(EXEEXT) luatexdir/lua/texluac.w +- $(luatex_lua_ctangle) texluac.w +-texluajitc.c: ctangle$(EXEEXT) luatexdir/lua/texluajitc.w +- $(luatex_lua_ctangle) texluajitc.w +- +-libluatex_web += luatexdir/lua/helpers.w +-#libluatex_web += luatexdir/lua/luainit.w luatexdir/lua/luajitstuff.w +-libluatex_web += luatexdir/lua/luainit.w +-libluatex_web += luatexdir/lua/luanode.w luatexdir/lua/luastuff.w luatexdir/lua/luatoken.w +-libluatex_web += luatexdir/lua/mplibstuff.w +-libluatex_web += luatexdir/lua/texluac.w luatexdir/lua/texluajitc.w ++#luanode.c: ctangle$(EXEEXT) luatexdir/lua/luanode.w ++# $(luatex_lua_ctangle) luanode.w ++#luastuff.c: ctangle$(EXEEXT) luatexdir/lua/luastuff.w ++# $(luatex_lua_ctangle) luastuff.w ++#luatoken.c: ctangle$(EXEEXT) luatexdir/lua/luatoken.w ++# $(luatex_lua_ctangle) luatoken.w ++#mplibstuff.c: ctangle$(EXEEXT) luatexdir/lua/mplibstuff.w ++# $(luatex_lua_ctangle) mplibstuff.w ++#texluac.c: ctangle$(EXEEXT) luatexdir/lua/texluac.w ++# $(luatex_lua_ctangle) texluac.w ++#texluajitc.c: ctangle$(EXEEXT) luatexdir/lua/texluajitc.w ++# $(luatex_lua_ctangle) texluajitc.w + +-nodist_libluatex_sources += luainit.c luanode.c luatoken.c +-nodist_libluatex_sources += mplibstuff.c ++#libluatex_web += luatexdir/lua/luainit.w luatexdir/lua/luajitstuff.w + + + dist_libluatex_sources += \ + luatexdir/lua/lcallbacklib.c \ + luatexdir/lua/lfontlib.c \ + luatexdir/lua/limglib.c \ +- luatexdir/lua/lpdfscannerlib.cc \ +- luatexdir/lua/lepdflib.cc \ ++ luatexdir/lua/lpdfelib.c \ ++ luatexdir/lua/lpdfscannerlib.c \ + luatexdir/lua/lkpselib.c \ + luatexdir/lua/llanglib.c \ + luatexdir/lua/llualib.c \ +- luatexdir/lua/llfslibext.c \ + luatexdir/lua/lnodelib.c \ + luatexdir/lua/liolibext.c \ + luatexdir/lua/loslibext.c \ +@@ -281,71 +295,79 @@ dist_libluatex_sources += \ + luatexdir/lua/ltexlib.c \ + luatexdir/lua/lnewtokenlib.c \ + luatexdir/lua/luatex-api.h \ +- luatexdir/lua/luatex-core.c ++ luatexdir/lua/luatex-core.c \ ++ luatexdir/lua/helpers.c \ ++ luatexdir/lua/luainit.c \ ++ luatexdir/lua/luanode.c \ ++ luatexdir/lua/luastuff.c \ ++ luatexdir/lua/luatoken.c \ ++ luatexdir/lua/mplibstuff.c ++ ++ + + ## from luatexdir/pdf + ## + luatex_pdf_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/pdf $(ctangle) + +-pdfpagetree.c: ctangle$(EXEEXT) luatexdir/pdf/pdfpagetree.w +- $(luatex_pdf_ctangle) pdfpagetree.w +-pdfaction.c: ctangle$(EXEEXT) luatexdir/pdf/pdfaction.w +- $(luatex_pdf_ctangle) pdfaction.w +-pdfannot.c: ctangle$(EXEEXT) luatexdir/pdf/pdfannot.w +- $(luatex_pdf_ctangle) pdfannot.w +-pdfcolorstack.c: ctangle$(EXEEXT) luatexdir/pdf/pdfcolorstack.w +- $(luatex_pdf_ctangle) pdfcolorstack.w +-pdfdest.c: ctangle$(EXEEXT) luatexdir/pdf/pdfdest.w +- $(luatex_pdf_ctangle) pdfdest.w +-pdffont.c: ctangle$(EXEEXT) luatexdir/pdf/pdffont.w +- $(luatex_pdf_ctangle) pdffont.w +-pdfgen.c: ctangle$(EXEEXT) luatexdir/pdf/pdfgen.w +- $(luatex_pdf_ctangle) pdfgen.w +-pdfglyph.c: ctangle$(EXEEXT) luatexdir/pdf/pdfglyph.w +- $(luatex_pdf_ctangle) pdfglyph.w +-pdfimage.c: ctangle$(EXEEXT) luatexdir/pdf/pdfimage.w +- $(luatex_pdf_ctangle) pdfimage.w +-pdflink.c: ctangle$(EXEEXT) luatexdir/pdf/pdflink.w +- $(luatex_pdf_ctangle) pdflink.w +-pdflistout.c: ctangle$(EXEEXT) luatexdir/pdf/pdflistout.w +- $(luatex_pdf_ctangle) pdflistout.w +-pdfliteral.c: ctangle$(EXEEXT) luatexdir/pdf/pdfliteral.w +- $(luatex_pdf_ctangle) pdfliteral.w +-pdfobj.c: ctangle$(EXEEXT) luatexdir/pdf/pdfobj.w +- $(luatex_pdf_ctangle) pdfobj.w +-pdfoutline.c: ctangle$(EXEEXT) luatexdir/pdf/pdfoutline.w +- $(luatex_pdf_ctangle) pdfoutline.w +-pdfpage.c: ctangle$(EXEEXT) luatexdir/pdf/pdfpage.w +- $(luatex_pdf_ctangle) pdfpage.w +-pdfrule.c: ctangle$(EXEEXT) luatexdir/pdf/pdfrule.w +- $(luatex_pdf_ctangle) pdfrule.w +-pdfsaverestore.c: ctangle$(EXEEXT) luatexdir/pdf/pdfsaverestore.w +- $(luatex_pdf_ctangle) pdfsaverestore.w +-pdfsetmatrix.c: ctangle$(EXEEXT) luatexdir/pdf/pdfsetmatrix.w +- $(luatex_pdf_ctangle) pdfsetmatrix.w +-pdfshipout.c: ctangle$(EXEEXT) luatexdir/pdf/pdfshipout.w +- $(luatex_pdf_ctangle) pdfshipout.w +-pdftables.c: ctangle$(EXEEXT) luatexdir/pdf/pdftables.w +- $(luatex_pdf_ctangle) pdftables.w +-pdfthread.c: ctangle$(EXEEXT) luatexdir/pdf/pdfthread.w +- $(luatex_pdf_ctangle) pdfthread.w +-pdfxform.c: ctangle$(EXEEXT) luatexdir/pdf/pdfxform.w +- $(luatex_pdf_ctangle) pdfxform.w +- +-libluatex_web += luatexdir/pdf/pdfpagetree.w luatexdir/pdf/pdfaction.w luatexdir/pdf/pdfannot.w +-libluatex_web += luatexdir/pdf/pdfcolorstack.w luatexdir/pdf/pdfdest.w +-libluatex_web += luatexdir/pdf/pdffont.w luatexdir/pdf/pdfgen.w luatexdir/pdf/pdfglyph.w +-libluatex_web += luatexdir/pdf/pdfimage.w luatexdir/pdf/pdflink.w luatexdir/pdf/pdflistout.w +-libluatex_web += luatexdir/pdf/pdfliteral.w luatexdir/pdf/pdfobj.w +-libluatex_web += luatexdir/pdf/pdfoutline.w luatexdir/pdf/pdfpage.w luatexdir/pdf/pdfrule.w +-libluatex_web += luatexdir/pdf/pdfsaverestore.w luatexdir/pdf/pdfsetmatrix.w +-libluatex_web += luatexdir/pdf/pdfshipout.w luatexdir/pdf/pdftables.w +-libluatex_web += luatexdir/pdf/pdfthread.w luatexdir/pdf/pdfxform.w +- +-nodist_libluatex_sources += pdfpagetree.c pdfaction.c pdfannot.c pdfcolorstack.c pdfdest.c pdffont.c +-nodist_libluatex_sources += pdfgen.c pdfglyph.c pdfimage.c pdflink.c pdflistout.c pdfliteral.c +-nodist_libluatex_sources += pdfobj.c pdfoutline.c pdfpage.c pdfrule.c pdfsaverestore.c +-nodist_libluatex_sources += pdfsetmatrix.c pdfshipout.c pdftables.c pdfthread.c pdfxform.c ++# pdfpagetree.c: ctangle$(EXEEXT) luatexdir/pdf/pdfpagetree.w ++# $(luatex_pdf_ctangle) pdfpagetree.w ++# pdfaction.c: ctangle$(EXEEXT) luatexdir/pdf/pdfaction.w ++# $(luatex_pdf_ctangle) pdfaction.w ++# pdfannot.c: ctangle$(EXEEXT) luatexdir/pdf/pdfannot.w ++# $(luatex_pdf_ctangle) pdfannot.w ++# pdfcolorstack.c: ctangle$(EXEEXT) luatexdir/pdf/pdfcolorstack.w ++# $(luatex_pdf_ctangle) pdfcolorstack.w ++# pdfdest.c: ctangle$(EXEEXT) luatexdir/pdf/pdfdest.w ++# $(luatex_pdf_ctangle) pdfdest.w ++# pdffont.c: ctangle$(EXEEXT) luatexdir/pdf/pdffont.w ++# $(luatex_pdf_ctangle) pdffont.w ++# pdfgen.c: ctangle$(EXEEXT) luatexdir/pdf/pdfgen.w ++# $(luatex_pdf_ctangle) pdfgen.w ++# pdfglyph.c: ctangle$(EXEEXT) luatexdir/pdf/pdfglyph.w ++# $(luatex_pdf_ctangle) pdfglyph.w ++# pdfimage.c: ctangle$(EXEEXT) luatexdir/pdf/pdfimage.w ++# $(luatex_pdf_ctangle) pdfimage.w ++# pdflink.c: ctangle$(EXEEXT) luatexdir/pdf/pdflink.w ++# $(luatex_pdf_ctangle) pdflink.w ++# pdflistout.c: ctangle$(EXEEXT) luatexdir/pdf/pdflistout.w ++# $(luatex_pdf_ctangle) pdflistout.w ++# pdfliteral.c: ctangle$(EXEEXT) luatexdir/pdf/pdfliteral.w ++# $(luatex_pdf_ctangle) pdfliteral.w ++# pdfobj.c: ctangle$(EXEEXT) luatexdir/pdf/pdfobj.w ++# $(luatex_pdf_ctangle) pdfobj.w ++# pdfoutline.c: ctangle$(EXEEXT) luatexdir/pdf/pdfoutline.w ++# $(luatex_pdf_ctangle) pdfoutline.w ++# pdfpage.c: ctangle$(EXEEXT) luatexdir/pdf/pdfpage.w ++# $(luatex_pdf_ctangle) pdfpage.w ++# pdfrule.c: ctangle$(EXEEXT) luatexdir/pdf/pdfrule.w ++# $(luatex_pdf_ctangle) pdfrule.w ++# pdfsaverestore.c: ctangle$(EXEEXT) luatexdir/pdf/pdfsaverestore.w ++# $(luatex_pdf_ctangle) pdfsaverestore.w ++# pdfsetmatrix.c: ctangle$(EXEEXT) luatexdir/pdf/pdfsetmatrix.w ++# $(luatex_pdf_ctangle) pdfsetmatrix.w ++# pdfshipout.c: ctangle$(EXEEXT) luatexdir/pdf/pdfshipout.w ++# $(luatex_pdf_ctangle) pdfshipout.w ++# pdftables.c: ctangle$(EXEEXT) luatexdir/pdf/pdftables.w ++# $(luatex_pdf_ctangle) pdftables.w ++# pdfthread.c: ctangle$(EXEEXT) luatexdir/pdf/pdfthread.w ++# $(luatex_pdf_ctangle) pdfthread.w ++# pdfxform.c: ctangle$(EXEEXT) luatexdir/pdf/pdfxform.w ++# $(luatex_pdf_ctangle) pdfxform.w ++ ++#libluatex_web += luatexdir/pdf/pdfpagetree.w luatexdir/pdf/pdfaction.w luatexdir/pdf/pdfannot.w ++#libluatex_web += luatexdir/pdf/pdfcolorstack.w luatexdir/pdf/pdfdest.w ++#libluatex_web += luatexdir/pdf/pdffont.w luatexdir/pdf/pdfgen.w luatexdir/pdf/pdfglyph.w ++#libluatex_web += luatexdir/pdf/pdfimage.w luatexdir/pdf/pdflink.w luatexdir/pdf/pdflistout.w ++#libluatex_web += luatexdir/pdf/pdfliteral.w luatexdir/pdf/pdfobj.w ++#libluatex_web += luatexdir/pdf/pdfoutline.w luatexdir/pdf/pdfpage.w luatexdir/pdf/pdfrule.w ++#libluatex_web += luatexdir/pdf/pdfsaverestore.w luatexdir/pdf/pdfsetmatrix.w ++#libluatex_web += luatexdir/pdf/pdfshipout.w luatexdir/pdf/pdftables.w ++#libluatex_web += luatexdir/pdf/pdfthread.w luatexdir/pdf/pdfxform.w ++ ++#nodist_libluatex_sources += pdfpagetree.c pdfaction.c pdfannot.c pdfcolorstack.c pdfdest.c pdffont.c ++#nodist_libluatex_sources += pdfgen.c pdfglyph.c pdfimage.c pdflink.c pdflistout.c pdfliteral.c ++#nodist_libluatex_sources += pdfobj.c pdfoutline.c pdfpage.c pdfrule.c pdfsaverestore.c ++#nodist_libluatex_sources += pdfsetmatrix.c pdfshipout.c pdftables.c pdfthread.c pdfxform.c + + dist_libluatex_sources += \ + luatexdir/pdf/pdfpagetree.h \ +@@ -370,7 +392,29 @@ dist_libluatex_sources += \ + luatexdir/pdf/pdftables.h \ + luatexdir/pdf/pdfthread.h \ + luatexdir/pdf/pdftypes.h \ +- luatexdir/pdf/pdfxform.h ++ luatexdir/pdf/pdfxform.h \ ++ luatexdir/pdf/pdfaction.c \ ++ luatexdir/pdf/pdfannot.c \ ++ luatexdir/pdf/pdfcolorstack.c \ ++ luatexdir/pdf/pdfdest.c \ ++ luatexdir/pdf/pdffont.c \ ++ luatexdir/pdf/pdfgen.c \ ++ luatexdir/pdf/pdfglyph.c \ ++ luatexdir/pdf/pdfimage.c \ ++ luatexdir/pdf/pdflink.c \ ++ luatexdir/pdf/pdflistout.c \ ++ luatexdir/pdf/pdfliteral.c \ ++ luatexdir/pdf/pdfobj.c \ ++ luatexdir/pdf/pdfoutline.c \ ++ luatexdir/pdf/pdfpage.c \ ++ luatexdir/pdf/pdfpagetree.c \ ++ luatexdir/pdf/pdfrule.c \ ++ luatexdir/pdf/pdfsaverestore.c \ ++ luatexdir/pdf/pdfsetmatrix.c \ ++ luatexdir/pdf/pdfshipout.c \ ++ luatexdir/pdf/pdftables.c \ ++ luatexdir/pdf/pdfthread.c \ ++ luatexdir/pdf/pdfxform.c + + ################################################################################ + ################################################################################ +@@ -415,93 +459,79 @@ dist_libluatex_sources += \ + ## + luatex_tex_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/tex $(ctangle) + +-align.c: ctangle$(EXEEXT) luatexdir/tex/align.w +- $(luatex_tex_ctangle) align.w +-arithmetic.c: ctangle$(EXEEXT) luatexdir/tex/arithmetic.w +- $(luatex_tex_ctangle) arithmetic.w +-buildpage.c: ctangle$(EXEEXT) luatexdir/tex/buildpage.w +- $(luatex_tex_ctangle) buildpage.w +-commands.c: ctangle$(EXEEXT) luatexdir/tex/commands.w +- $(luatex_tex_ctangle) commands.w +-conditional.c: ctangle$(EXEEXT) luatexdir/tex/conditional.w +- $(luatex_tex_ctangle) conditional.w +-directions.c: ctangle$(EXEEXT) luatexdir/tex/directions.w +- $(luatex_tex_ctangle) directions.w +-dumpdata.c: ctangle$(EXEEXT) luatexdir/tex/dumpdata.w +- $(luatex_tex_ctangle) dumpdata.w +-equivalents.c: ctangle$(EXEEXT) luatexdir/tex/equivalents.w +- $(luatex_tex_ctangle) equivalents.w +-errors.c: ctangle$(EXEEXT) luatexdir/tex/errors.w +- $(luatex_tex_ctangle) errors.w +-expand.c: ctangle$(EXEEXT) luatexdir/tex/expand.w +- $(luatex_tex_ctangle) expand.w +-extensions.c: ctangle$(EXEEXT) luatexdir/tex/extensions.w +- $(luatex_tex_ctangle) extensions.w +-filename.c: ctangle$(EXEEXT) luatexdir/tex/filename.w +- $(luatex_tex_ctangle) filename.w +-inputstack.c: ctangle$(EXEEXT) luatexdir/tex/inputstack.w +- $(luatex_tex_ctangle) inputstack.w +-linebreak.c: ctangle$(EXEEXT) luatexdir/tex/linebreak.w +- $(luatex_tex_ctangle) linebreak.w +-mainbody.c: ctangle$(EXEEXT) luatexdir/tex/mainbody.w +- $(luatex_tex_ctangle) mainbody.w +-maincontrol.c: ctangle$(EXEEXT) luatexdir/tex/maincontrol.w +- $(luatex_tex_ctangle) maincontrol.w +-mathcodes.c: ctangle$(EXEEXT) luatexdir/tex/mathcodes.w +- $(luatex_tex_ctangle) mathcodes.w +-memoryword.c: ctangle$(EXEEXT) luatexdir/tex/memoryword.w +- $(luatex_tex_ctangle) memoryword.w +-mlist.c: ctangle$(EXEEXT) luatexdir/tex/mlist.w +- $(luatex_tex_ctangle) mlist.w +-nesting.c: ctangle$(EXEEXT) luatexdir/tex/nesting.w +- $(luatex_tex_ctangle) nesting.w +-packaging.c: ctangle$(EXEEXT) luatexdir/tex/packaging.w +- $(luatex_tex_ctangle) packaging.w +-postlinebreak.c: ctangle$(EXEEXT) luatexdir/tex/postlinebreak.w +- $(luatex_tex_ctangle) postlinebreak.w +-primitive.c: ctangle$(EXEEXT) luatexdir/tex/primitive.w +- $(luatex_tex_ctangle) primitive.w +-printing.c: ctangle$(EXEEXT) luatexdir/tex/printing.w +- $(luatex_tex_ctangle) printing.w +-scanning.c: ctangle$(EXEEXT) luatexdir/tex/scanning.w +- $(luatex_tex_ctangle) scanning.w +-stringpool.c: ctangle$(EXEEXT) luatexdir/tex/stringpool.w +- $(luatex_tex_ctangle) stringpool.w +-texdeffont.c: ctangle$(EXEEXT) luatexdir/tex/texdeffont.w +- $(luatex_tex_ctangle) texdeffont.w +-texfileio.c: ctangle$(EXEEXT) luatexdir/tex/texfileio.w +- $(luatex_tex_ctangle) texfileio.w +-texmath.c: ctangle$(EXEEXT) luatexdir/tex/texmath.w +- $(luatex_tex_ctangle) texmath.w +-texnodes.c: ctangle$(EXEEXT) luatexdir/tex/texnodes.w +- $(luatex_tex_ctangle) texnodes.w +-textcodes.c: ctangle$(EXEEXT) luatexdir/tex/textcodes.w +- $(luatex_tex_ctangle) textcodes.w +-textoken.c: ctangle$(EXEEXT) luatexdir/tex/textoken.w +- $(luatex_tex_ctangle) textoken.w +- +-libluatex_web += luatexdir/tex/align.w luatexdir/tex/arithmetic.w luatexdir/tex/buildpage.w +-libluatex_web += luatexdir/tex/commands.w luatexdir/tex/conditional.w luatexdir/tex/directions.w +-libluatex_web += luatexdir/tex/dumpdata.w luatexdir/tex/equivalents.w luatexdir/tex/errors.w +-libluatex_web += luatexdir/tex/expand.w luatexdir/tex/extensions.w luatexdir/tex/filename.w +-libluatex_web += luatexdir/tex/inputstack.w luatexdir/tex/linebreak.w luatexdir/tex/mainbody.w +-libluatex_web += luatexdir/tex/maincontrol.w luatexdir/tex/mathcodes.w luatexdir/tex/memoryword.w +-libluatex_web += luatexdir/tex/mlist.w luatexdir/tex/nesting.w luatexdir/tex/packaging.w +-libluatex_web += luatexdir/tex/postlinebreak.w luatexdir/tex/primitive.w luatexdir/tex/printing.w +-libluatex_web += luatexdir/tex/scanning.w luatexdir/tex/stringpool.w luatexdir/tex/texdeffont.w +-libluatex_web += luatexdir/tex/texfileio.w luatexdir/tex/texmath.w luatexdir/tex/texnodes.w +-libluatex_web += luatexdir/tex/textcodes.w luatexdir/tex/textoken.w +- +-nodist_libluatex_sources += align.c arithmetic.c buildpage.c commands.c conditional.c directions.c +-nodist_libluatex_sources += dumpdata.c equivalents.c errors.c expand.c extensions.c filename.c +-nodist_libluatex_sources += inputstack.c linebreak.c mainbody.c maincontrol.c mathcodes.c +-nodist_libluatex_sources += memoryword.c mlist.c nesting.c packaging.c postlinebreak.c +-nodist_libluatex_sources += primitive.c printing.c scanning.c stringpool.c texdeffont.c +-nodist_libluatex_sources += texfileio.c texmath.c texnodes.c textcodes.c textoken.c ++# align.c: ctangle$(EXEEXT) luatexdir/tex/align.w ++# $(luatex_tex_ctangle) align.w ++#arithmetic.c: ctangle$(EXEEXT) luatexdir/tex/arithmetic.w ++# $(luatex_tex_ctangle) arithmetic.w ++# buildpage.c: ctangle$(EXEEXT) luatexdir/tex/buildpage.w ++# $(luatex_tex_ctangle) buildpage.w ++#commands.c: ctangle$(EXEEXT) luatexdir/tex/commands.w ++# $(luatex_tex_ctangle) commands.w ++#conditional.c: ctangle$(EXEEXT) luatexdir/tex/conditional.w ++# $(luatex_tex_ctangle) conditional.w ++#directions.c: ctangle$(EXEEXT) luatexdir/tex/directions.w ++# $(luatex_tex_ctangle) directions.w ++#dumpdata.c: ctangle$(EXEEXT) luatexdir/tex/dumpdata.w ++# $(luatex_tex_ctangle) dumpdata.w ++#equivalents.c: ctangle$(EXEEXT) luatexdir/tex/equivalents.w ++# $(luatex_tex_ctangle) equivalents.w ++#errors.c: ctangle$(EXEEXT) luatexdir/tex/errors.w ++# $(luatex_tex_ctangle) errors.w ++# expand.c: ctangle$(EXEEXT) luatexdir/tex/expand.w ++# $(luatex_tex_ctangle) expand.w ++# extensions.c: ctangle$(EXEEXT) luatexdir/tex/extensions.w ++# $(luatex_tex_ctangle) extensions.w ++#filename.c: ctangle$(EXEEXT) luatexdir/tex/filename.w ++# $(luatex_tex_ctangle) filename.w ++#inputstack.c: ctangle$(EXEEXT) luatexdir/tex/inputstack.w ++# $(luatex_tex_ctangle) inputstack.w ++# linebreak.c: ctangle$(EXEEXT) luatexdir/tex/linebreak.w ++# $(luatex_tex_ctangle) linebreak.w ++#mainbody.c: ctangle$(EXEEXT) luatexdir/tex/mainbody.w ++# $(luatex_tex_ctangle) mainbody.w ++#maincontrol.c: ctangle$(EXEEXT) luatexdir/tex/maincontrol.w ++# $(luatex_tex_ctangle) maincontrol.w ++#mathcodes.c: ctangle$(EXEEXT) luatexdir/tex/mathcodes.w ++# $(luatex_tex_ctangle) mathcodes.w ++#memoryword.c: ctangle$(EXEEXT) luatexdir/tex/memoryword.w ++# $(luatex_tex_ctangle) memoryword.w ++# mlist.c: ctangle$(EXEEXT) luatexdir/tex/mlist.w ++# $(luatex_tex_ctangle) mlist.w ++#nesting.c: ctangle$(EXEEXT) luatexdir/tex/nesting.w ++# $(luatex_tex_ctangle) nesting.w ++# packaging.c: ctangle$(EXEEXT) luatexdir/tex/packaging.w ++# $(luatex_tex_ctangle) packaging.w ++#postlinebreak.c: ctangle$(EXEEXT) luatexdir/tex/postlinebreak.w ++# $(luatex_tex_ctangle) postlinebreak.w ++#primitive.c: ctangle$(EXEEXT) luatexdir/tex/primitive.w ++# $(luatex_tex_ctangle) primitive.w ++#printing.c: ctangle$(EXEEXT) luatexdir/tex/printing.w ++# $(luatex_tex_ctangle) printing.w ++# scanning.c: ctangle$(EXEEXT) luatexdir/tex/scanning.w ++# $(luatex_tex_ctangle) scanning.w ++#stringpool.c: ctangle$(EXEEXT) luatexdir/tex/stringpool.w ++# $(luatex_tex_ctangle) stringpool.w ++#texdeffont.c: ctangle$(EXEEXT) luatexdir/tex/texdeffont.w ++# $(luatex_tex_ctangle) texdeffont.w ++# texfileio.c: ctangle$(EXEEXT) luatexdir/tex/texfileio.w ++# $(luatex_tex_ctangle) texfileio.w ++# texmath.c: ctangle$(EXEEXT) luatexdir/tex/texmath.w ++# $(luatex_tex_ctangle) texmath.w ++# texnodes.c: ctangle$(EXEEXT) luatexdir/tex/texnodes.w ++# $(luatex_tex_ctangle) texnodes.w ++#textcodes.c: ctangle$(EXEEXT) luatexdir/tex/textcodes.w ++# $(luatex_tex_ctangle) textcodes.w ++# textoken.c: ctangle$(EXEEXT) luatexdir/tex/textoken.w ++# $(luatex_tex_ctangle) textoken.w ++ ++ ++ + + dist_libluatex_sources += \ + luatexdir/tex/align.h \ + luatexdir/tex/arithmetic.h \ ++ luatexdir/tex/backend.h \ ++ luatexdir/tex/backend.c \ + luatexdir/tex/buildpage.h \ + luatexdir/tex/commands.h \ + luatexdir/tex/conditional.h \ +@@ -530,25 +560,58 @@ dist_libluatex_sources += \ + luatexdir/tex/texfileio.h \ + luatexdir/tex/texmath.h \ + luatexdir/tex/texnodes.h \ ++ luatexdir/tex/textcodes.h \ + luatexdir/tex/textoken.h \ +- luatexdir/tex/textcodes.h ++ luatexdir/tex/align.c \ ++ luatexdir/tex/arithmetic.c \ ++ luatexdir/tex/buildpage.c \ ++ luatexdir/tex/commands.c \ ++ luatexdir/tex/conditional.c \ ++ luatexdir/tex/directions.c \ ++ luatexdir/tex/dumpdata.c \ ++ luatexdir/tex/equivalents.c \ ++ luatexdir/tex/errors.c \ ++ luatexdir/tex/expand.c \ ++ luatexdir/tex/extensions.c \ ++ luatexdir/tex/filename.c \ ++ luatexdir/tex/inputstack.c \ ++ luatexdir/tex/linebreak.c \ ++ luatexdir/tex/mainbody.c \ ++ luatexdir/tex/maincontrol.c \ ++ luatexdir/tex/mathcodes.c \ ++ luatexdir/tex/memoryword.c \ ++ luatexdir/tex/mlist.c \ ++ luatexdir/tex/nesting.c \ ++ luatexdir/tex/packaging.c \ ++ luatexdir/tex/postlinebreak.c \ ++ luatexdir/tex/primitive.c \ ++ luatexdir/tex/printing.c \ ++ luatexdir/tex/scanning.c \ ++ luatexdir/tex/stringpool.c \ ++ luatexdir/tex/texdeffont.c \ ++ luatexdir/tex/texfileio.c \ ++ luatexdir/tex/texmath.c \ ++ luatexdir/tex/texnodes.c \ ++ luatexdir/tex/textcodes.c \ ++ luatexdir/tex/textoken.c ++ + + ## from luatexdir/utils + ## + luatex_utils_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/utils $(ctangle) + +-avlstuff.c: ctangle$(EXEEXT) luatexdir/utils/avlstuff.w +- $(luatex_utils_ctangle) avlstuff.w +-managed-sa.c: ctangle$(EXEEXT) luatexdir/utils/managed-sa.w +- $(luatex_utils_ctangle) managed-sa.w +-utils.c: ctangle$(EXEEXT) luatexdir/utils/utils.w +- $(luatex_utils_ctangle) utils.w +-unistring.c: ctangle$(EXEEXT) luatexdir/utils/unistring.w +- $(luatex_utils_ctangle) unistring.w ++#avlstuff.c: ctangle$(EXEEXT) luatexdir/utils/avlstuff.w ++# $(luatex_utils_ctangle) avlstuff.w ++#managed-sa.c: ctangle$(EXEEXT) luatexdir/utils/managed-sa.w ++# $(luatex_utils_ctangle) managed-sa.w ++#utils.c: ctangle$(EXEEXT) luatexdir/utils/utils.w ++# $(luatex_utils_ctangle) utils.w ++#unistring.c: ctangle$(EXEEXT) luatexdir/utils/unistring.w ++# $(luatex_utils_ctangle) unistring.w + +-libluatex_web += luatexdir/utils/avlstuff.w luatexdir/utils/managed-sa.w luatexdir/utils/utils.w luatexdir/utils/unistring.w ++#libluatex_web += luatexdir/utils/avlstuff.w luatexdir/utils/managed-sa.w luatexdir/utils/utils.w luatexdir/utils/unistring.w + +-nodist_libluatex_sources += avlstuff.c managed-sa.c utils.c unistring.c ++#nodist_libluatex_sources += avlstuff.c managed-sa.c utils.c unistring.c + + dist_libluatex_sources += \ + luatexdir/utils/avl.c \ +@@ -556,7 +619,11 @@ dist_libluatex_sources += \ + luatexdir/utils/avlstuff.h \ + luatexdir/utils/managed-sa.h \ + luatexdir/utils/utils.h \ +- luatexdir/utils/unistring.h ++ luatexdir/utils/unistring.h \ ++ luatexdir/utils/avlstuff.c \ ++ luatexdir/utils/managed-sa.c \ ++ luatexdir/utils/unistring.c \ ++ luatexdir/utils/utils.c + + ## from ../synctexdir + ## +diff --git a/texk/web2c/luatexdir/am/luamisc.am b/texk/web2c/luatexdir/am/luamisc.am +index 7e874ae09..014b6eb3f 100644 +--- a/texk/web2c/luatexdir/am/luamisc.am ++++ b/texk/web2c/luatexdir/am/luamisc.am +@@ -8,9 +8,9 @@ + ## and slnunicode) + EXTRA_LIBRARIES += libluamisc.a liblua53misc.a libluajitmisc.a + +-libluamisc_a_DEPENDENCIES = $(ZZIPLIB_DEPEND) libluasocket.a libluaffi.a +-liblua53misc_a_DEPENDENCIES = $(ZZIPLIB_DEPEND) liblua53socket.a liblua53ffi.a +-libluajitmisc_a_DEPENDENCIES = $(ZZIPLIB_DEPEND) libluajitsocket.a ++libluamisc_a_DEPENDENCIES = $(ZZIPLIB_DEPEND) libluasocket.a libluaffi.a libluapplib.a ++liblua53misc_a_DEPENDENCIES = $(ZZIPLIB_DEPEND) liblua53socket.a liblua53ffi.a liblua53pplib.a ++libluajitmisc_a_DEPENDENCIES = $(ZZIPLIB_DEPEND) libluajitsocket.a libluajitpplib.a + + $(libluamisc_a_OBJECTS): $(libluamisc_a_DEPENDENCIES) + $(liblua53misc_a_OBJECTS): $(liblua53misc_a_DEPENDENCIES) +diff --git a/texk/web2c/luatexdir/am/luatex.am b/texk/web2c/luatexdir/am/luatex.am +index 4482ffe60..2fb4d64f6 100644 +--- a/texk/web2c/luatexdir/am/luatex.am ++++ b/texk/web2c/luatexdir/am/luatex.am +@@ -42,11 +42,11 @@ endif LUAJITTEX + EXTRA_PROGRAMS += luatex luatex53 luajittex + + # Force Automake to use CXXLD for linking +-nodist_EXTRA_luatex_SOURCES = dummy.cxx +-nodist_EXTRA_luatex53_SOURCES = dummy.cxx +-nodist_EXTRA_luajittex_SOURCES = dummy.cxx ++#nodist_EXTRA_luatex_SOURCES = dummy.cxx ++#nodist_EXTRA_luatex53_SOURCES = dummy.cxx ++#nodist_EXTRA_luajittex_SOURCES = dummy.cxx + +-luatex_preflags = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(LIBPNG_INCLUDES) $(POPPLER_INCLUDES) ++luatex_preflags = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(LIBPNG_INCLUDES) + luatex_postflags = -I$(srcdir)/libmd5 -DpdfTeX -I$(srcdir)/luatexdir -I$(srcdir)/mplibdir + luatex_postflags += -Dextra_version_info=`date +-%Y%m%d%H` + luatex_postflags += -I$(srcdir)/synctexdir -DSYNCTEX_ENGINE_H='' +@@ -64,14 +64,15 @@ luatex_LDFLAGS = -export-dynamic + luatex53_LDFLAGS = -export-dynamic + luajittex_LDFLAGS = -export-dynamic $(LUAJIT_LDEXTRA) + +-luatex_postldadd = libmplibcore.a $(MPFR_LIBS) $(GMP_LIBS) +-luatex_postldadd += $(ZZIPLIB_LIBS) $(LIBPNG_LIBS) $(ZLIB_LIBS) $(POPPLER_LIBS) ++#luatex_postldadd = libmplibcore.a $(MPFR_LIBS) $(GMP_LIBS) ++luatex_postldadd = libmplibcore.a ++luatex_postldadd += $(ZZIPLIB_LIBS) $(LIBPNG_LIBS) $(ZLIB_LIBS) + luatex_postldadd += $(LDADD) libmputil.a libunilib.a libmd5.a $(lua_socketlibs) + + +-luatex_LDADD = libluatex.a libff.a libluamisc.a libluasocket.a libluaffi.a $(LUA_LIBS) $(luatex_postldadd) +-luatex53_LDADD = liblua53tex.a libff.a liblua53misc.a liblua53socket.a liblua53ffi.a $(LUA_LUA53_LIBS) $(luatex_postldadd) +-luajittex_LDADD = libluajittex.a libff.a libluajitmisc.a libluajitsocket.a $(LUAJIT_LIBS) $(luatex_postldadd) ++luatex_LDADD = libluatex.a libff.a libluamisc.a libluasocket.a libluaffi.a libluapplib.a $(LUA_LIBS) $(luatex_postldadd) ++luatex53_LDADD = liblua53tex.a libff.a liblua53misc.a liblua53socket.a liblua53ffi.a liblua53pplib.a $(LUA_LUA53_LIBS) $(luatex_postldadd) ++luajittex_LDADD = libluajittex.a libff.a libluajitmisc.a libluajitsocket.a libluajitpplib.a $(LUAJIT_LIBS) $(luatex_postldadd) + + luatex_depend = $(proglib) $(KPATHSEA_DEPEND) $(LIBPNG_DEPEND) libmputil.a libmd5.a + luatex_DEPENDENCIES = $(luatex_depend) libluatex.a +diff --git a/texk/web2c/luatexdir/dvi/dvigen.h b/texk/web2c/luatexdir/dvi/dvigen.h +index 49552f1d9..cfbbc6245 100644 +--- a/texk/web2c/luatexdir/dvi/dvigen.h ++++ b/texk/web2c/luatexdir/dvi/dvigen.h +@@ -21,166 +21,34 @@ + #ifndef DVIGEN_H + # define DVIGEN_H + +-extern int total_pages; +-extern scaled max_v; +-extern scaled max_h; +-extern int max_push; +-extern int last_bop; +-extern int dead_cycles; +-extern boolean doing_leaders; +-extern int oval, ocmd; +-extern int lq, lr; +-extern int cur_s; +- +-typedef int dvi_index; /* an index into the output buffer */ ++/* todo: move initialization from mainbody to ensure_open */ + + extern int dvi_buf_size; + extern eight_bits *dvi_buf; /* 0 is unused */ +-extern dvi_index half_buf; +-extern dvi_index dvi_limit; +-extern dvi_index dvi_ptr; +-extern int dvi_offset; +-extern int dvi_gone; +- +-/* +-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) +- +-extern void dvi_swap(void); +-extern void dvi_four(int x); +-extern void dvi_push(void); +-extern void dvi_pop(int l); +-extern void out_cmd(void); +-extern void dvi_font_def(internal_font_number f); +- +-# 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) +- +-# define location(A) varmem[(A)+1].cint +- +-extern halfword down_ptr, right_ptr; /* heads of the down and right stacks */ +- +-/* +-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| */ +- +-extern 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. +-*/ +- +-# 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) +- +-# 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) +- +-extern scaledpos dvi; + +-extern void dvi_special(PDF pdf, halfword p); ++/*tex Housekeeping. */ + +-extern void ensure_dvi_header_written(PDF pdf); +-extern void finish_dvi_file(PDF pdf, int version, int revision); ++extern void dvi_open_file(PDF pdf); ++extern void dvi_write_header(PDF pdf); ++extern void dvi_finish_file(PDF pdf, int fatal_error); ++extern void dvi_begin_page(PDF pdf); ++extern void dvi_end_page(PDF pdf); ++ ++/*tex Specific injections. */ + + extern void dvi_place_glyph(PDF pdf, internal_font_number f, int c, int ex); + extern void dvi_place_rule(PDF pdf, halfword q, scaledpos size); ++extern void dvi_special(PDF pdf, halfword p); + +-extern void dvi_begin_page(PDF pdf); +-extern void dvi_end_page(PDF pdf); ++/*tex List handling (and nesting). */ ++ ++extern void dvi_push_list(PDF pdf, scaledpos *saved_pos, int *saved_loc); ++extern void dvi_pop_list(PDF pdf, scaledpos *saved_pos, int *saved_loc); ++extern void dvi_set_reference_point(PDF pdf, posstructure *refpoint); ++ ++/*tex Status information used in |lstatslib|. Not that useful. */ ++ ++extern int dvi_get_status_ptr(PDF pdf); ++extern int dvi_get_status_gone(PDF pdf); + + #endif +diff --git a/texk/web2c/luatexdir/font/luatexfont.h b/texk/web2c/luatexdir/font/luatexfont.h +index 459d2092b..7016b8872 100644 +--- a/texk/web2c/luatexdir/font/luatexfont.h ++++ b/texk/web2c/luatexdir/font/luatexfont.h +@@ -1,4 +1,4 @@ +-/* luatexfont.h --- General font definitions ++/* + + Copyright 2008-2013 Taco Hoekwater + +@@ -15,45 +15,36 @@ + License for more details. + + You should have received a copy of the GNU General Public License along +- with LuaTeX; if not, see . */ ++ with LuaTeX; if not, see . + ++*/ + + #ifndef LUATEXFONT_H +-# define LUATEXFONT_H +- +-# include "ptexlib.h" +-# ifndef pdfTeX +-# define pdfTeX +-# include "sfnt.h" /* which wants that pdfTeX is defined */ +-# undef pdfTeX +-# else +-# include "sfnt.h" +-# endif +- +-/**********************************************************************/ +- +-# define ASCENT_CODE 0 +-# define CAPHEIGHT_CODE 1 +-# define DESCENT_CODE 2 +-# define ITALIC_ANGLE_CODE 3 +-# define STEMV_CODE 4 +-# define XHEIGHT_CODE 5 +-# define FONTBBOX1_CODE 6 +-# define FONTBBOX2_CODE 7 +-# define FONTBBOX3_CODE 8 +-# define FONTBBOX4_CODE 9 +-# define FONTNAME_CODE 10 +-# define GEN_KEY_NUM (XHEIGHT_CODE + 1) +-# define MAX_KEY_CODE (FONTBBOX1_CODE + 1) +-# define INT_KEYS_NUM (FONTBBOX4_CODE + 1) +-# define FONT_KEYS_NUM (FONTNAME_CODE + 1) +- +-# define FD_FLAGS_NOT_SET_IN_MAPLINE -1 +-# define FD_FLAGS_DEFAULT_EMBED 4 /* a symbol font */ +-# define FD_FLAGS_DEFAULT_NON_EMBED 0x22 +- /* a nonsymbolic serif font */ +- +-/**********************************************************************/ ++ ++#define LUATEXFONT_H ++ ++#include "ptexlib.h" ++#include "sfnt.h" ++ ++#define ASCENT_CODE 0 ++#define CAPHEIGHT_CODE 1 ++#define DESCENT_CODE 2 ++#define ITALIC_ANGLE_CODE 3 ++#define STEMV_CODE 4 ++#define XHEIGHT_CODE 5 ++#define FONTBBOX1_CODE 6 ++#define FONTBBOX2_CODE 7 ++#define FONTBBOX3_CODE 8 ++#define FONTBBOX4_CODE 9 ++#define FONTNAME_CODE 10 ++#define GEN_KEY_NUM (XHEIGHT_CODE + 1) ++#define MAX_KEY_CODE (FONTBBOX1_CODE + 1) ++#define INT_KEYS_NUM (FONTBBOX4_CODE + 1) ++#define FONT_KEYS_NUM (FONTNAME_CODE + 1) ++ ++#define FD_FLAGS_NOT_SET_IN_MAPLINE -1 ++#define FD_FLAGS_DEFAULT_EMBED 4 /* a symbol font */ ++#define FD_FLAGS_DEFAULT_NON_EMBED 0x22 /* a nonsymbolic serif font */ + + typedef struct { + const char *pdfname; +@@ -63,60 +54,59 @@ typedef struct { + + extern const key_entry font_key[FONT_KEYS_NUM]; + +-# include "mapfile.h" ++#include "mapfile.h" + + typedef struct { +- int val; /* value */ +- boolean set; /* true if parameter has been set */ ++ int val; /* value */ ++ boolean set; /* true if parameter has been set */ + } intparm; + + typedef struct { +- int fe_objnum; /* object number */ +- char *name; /* encoding file name */ +- char **glyph_names; /* array of glyph names */ +- struct avl_table *tx_tree; /* tree of encoding positions marked as used by TeX */ ++ int fe_objnum; /* object number */ ++ char *name; /* encoding file name */ ++ char **glyph_names; /* array of glyph names */ ++ struct avl_table *tx_tree; /* tree of encoding positions marked as used by TeX */ + } fe_entry; + + typedef struct fd_entry_ { +- int fd_objnum; /* object number of the font descriptor object */ +- char *fontname; /* /FontName (without subset tag) */ +- char *subset_tag; /* 6-character subset tag */ ++ int fd_objnum; /* object number of the font descriptor object */ ++ char *fontname; /* /FontName (without subset tag) */ ++ char *subset_tag; /* 6-character subset tag */ + boolean ff_found; +- int ff_objnum; /* object number of the font program stream */ +- boolean all_glyphs; /* embed all glyphs? */ ++ int ff_objnum; /* object number of the font program stream */ ++ boolean all_glyphs; /* embed all glyphs? */ + boolean write_ttf_glyph_names; + intparm font_dim[FONT_KEYS_NUM]; +- fe_entry *fe; /* pointer to encoding structure */ +- char **builtin_glyph_names; /* builtin encoding as read from the Type1 font file */ +- fm_entry *fm; /* pointer to font map structure */ +- struct avl_table *tx_tree; /* tree of non-reencoded TeX characters marked as used */ +- struct avl_table *gl_tree; /* tree of all marked glyphs */ +- internal_font_number tex_font; /* needed for variable */ ++ fe_entry *fe; /* pointer to encoding structure */ ++ char **builtin_glyph_names; /* builtin encoding as read from the Type1 font file */ ++ fm_entry *fm; /* pointer to font map structure */ ++ struct avl_table *tx_tree; /* tree of non-reencoded TeX characters marked as used */ ++ struct avl_table *gl_tree; /* tree of all marked glyphs */ ++ internal_font_number tex_font; /* needed for variable */ + } fd_entry; + + typedef struct fo_entry_ { +- int fo_objnum; /* object number of the font dictionary */ +- internal_font_number tex_font; /* needed only for \pdffontattr{} */ +- fm_entry *fm; /* pointer to font map structure for this font dictionary */ +- fd_entry *fd; /* pointer to /FontDescriptor object structure */ +- fe_entry *fe; /* pointer to encoding structure */ +- int cw_objnum; /* object number of the font program object */ +- int first_char; /* first character used in this font */ +- int last_char; /* last character used in this font */ +- struct avl_table *tx_tree; /* tree of non-reencoded TeX characters marked as used */ +- int tounicode_objnum; /* object number of ToUnicode */ ++ int fo_objnum; /* object number of the font dictionary */ ++ internal_font_number tex_font; /* needed only for \pdffontattr{} */ ++ fm_entry *fm; /* pointer to font map structure for this font dictionary */ ++ fd_entry *fd; /* pointer to /FontDescriptor object structure */ ++ fe_entry *fe; /* pointer to encoding structure */ ++ int cw_objnum; /* object number of the font program object */ ++ int first_char; /* first character used in this font */ ++ int last_char; /* last character used in this font */ ++ struct avl_table *tx_tree; /* tree of non-reencoded TeX characters marked as used */ ++ int tounicode_objnum; /* object number of ToUnicode */ + } fo_entry; + + typedef struct { +- char *name; /* glyph name */ +- long code; /* -1 = undefined; -2 = multiple codes, stored +- as string in unicode_seq; otherwise unicode value */ +- char *unicode_seq; /* multiple unicode sequence */ ++ char *name; /* glyph name */ ++ long code; /* -1 = undefined; -2 = multiple codes, stored as string in unicode_seq; otherwise unicode value */ ++ char *unicode_seq; /* multiple unicode sequence */ + } glyph_unicode_entry; + +-typedef struct glw_entry_ { /* subset glyphs for inclusion in CID-based fonts */ +- unsigned int id; /* glyph CID */ +- signed int wd; /* glyph width in 1/1000 em parts */ ++typedef struct glw_entry_ { /* subset glyphs for inclusion in CID-based fonts */ ++ unsigned int id; /* glyph CID */ ++ signed int wd; /* glyph width in 1/1000 em parts */ + } glw_entry; + + typedef struct { +@@ -124,19 +114,18 @@ typedef struct { + halfword *raster; + } chardesc; + +-/**********************************************************************/ +- +-# include "texfont.h" ++#include "texfont.h" + + /* tounicode.c */ ++ + int write_cid_tounicode(PDF, fo_entry *, internal_font_number); + void glyph_unicode_free(void); + void def_tounicode(str_number, str_number); + int write_tounicode(PDF, char **, char *); + + /* vfpacket.c */ +-void replace_packet_fonts(internal_font_number f, int *old_fontid, +- int *new_fontid, int count); ++ ++void replace_packet_fonts(internal_font_number f, int *old_fontid, int *new_fontid, int count); + int *packet_local_fonts(internal_font_number f, int *num); + + int packet_cur_s; /* current |do_vf_packet()| recursion level */ +@@ -144,12 +133,15 @@ int packet_stack_ptr; /* pointer into |packet_stack| */ + vf_struct *new_vfstruct(void); + + /* writecff.c */ ++ + void writetype1w(PDF pdf, fd_entry * fd); + + /* writetype0.c */ ++ + void writetype0(PDF pdf, fd_entry * fd); + + /* writefont.c */ ++ + void do_pdf_font(PDF, internal_font_number); + fd_entry *lookup_fd_entry(char *); + fd_entry *new_fd_entry(internal_font_number); +@@ -157,6 +149,7 @@ void write_fontstuff(PDF); + void register_fd_entry(fd_entry * fd); + + /* writet1.c */ ++ + boolean t1_subset(char *, char *, unsigned char *); + char **load_enc_file(char *); + void writet1(PDF, fd_entry *); +@@ -164,36 +157,40 @@ void t1_free(void); + extern int t1_length1, t1_length2, t1_length3; + + /* writetype2.c */ ++ + boolean writetype2(PDF, fd_entry *); + extern unsigned long cidtogid_obj; +-pdf_obj *pdf_new_stream(void); +-void pdf_add_stream(pdf_obj * stream, unsigned char *buf, long len); +-void pdf_release_obj(pdf_obj * stream); + unsigned long ttc_read_offset(sfnt * sfont, int ttc_idx, fd_entry *fd); + + /* writeenc.c */ ++ + fe_entry *get_fe_entry(char *); + void enc_free(void); + void write_fontencodings(PDF pdf); + + /* writettf.c */ ++ + void writettf(PDF, fd_entry *); + void writeotf(PDF, fd_entry *); + void ttf_free(void); + extern int ttf_length; + + /* pkin.c */ ++ + int readchar(boolean, chardesc *); + + /* macnames.c */ ++ + extern char notdef[]; + + /* vfovf.c */ ++ + internal_font_number letter_space_font(internal_font_number f, int e, boolean nolig); + void pdf_check_vf(internal_font_number f); + internal_font_number copy_font_info(internal_font_number f); + + /* writet3.c */ ++ + extern FILE *t3_file; + void writet3(PDF, internal_font_number); + +@@ -201,12 +198,11 @@ extern unsigned char *t3_buffer; + extern int t3_size; + extern int t3_curbyte; + +-# define t3_read_file() readbinfile(t3_file, &t3_buffer, &t3_size) +-# define t3_close() xfclose(t3_file, cur_file_name) +-# define t3_getchar() t3_buffer[t3_curbyte++] +-# define t3_eof() (t3_curbyte>t3_size) +- +-# define t3_prefix(s) (!strncmp(t3_line_array, s, strlen(s))) +-# define t3_putchar(c) pdfout(c) ++#define t3_read_file() readbinfile(t3_file, &t3_buffer, &t3_size) ++#define t3_close() xfclose(t3_file, cur_file_name) ++#define t3_getchar() t3_buffer[t3_curbyte++] ++#define t3_eof() (t3_curbyte>t3_size) ++#define t3_prefix(s) (!strncmp(t3_line_array, s, strlen(s))) ++#define t3_putchar(c) pdfout(c) + +-#endif /* LUATEXFONT_H */ ++#endif +diff --git a/texk/web2c/luatexdir/font/mapfile.h b/texk/web2c/luatexdir/font/mapfile.h +index ad752af74..e3e0e613a 100644 +--- a/texk/web2c/luatexdir/font/mapfile.h ++++ b/texk/web2c/luatexdir/font/mapfile.h +@@ -98,13 +98,6 @@ typedef struct { + + /**********************************************************************/ + +-# define FONT_SLANT_MIN -2000 +-# define FONT_SLANT_MAX 2000 +-# define FONT_EXTEND_MIN -5000 +-# define FONT_EXTEND_MAX 5000 +- +-/**********************************************************************/ +- + fm_entry *getfontmap(char *tfm_name); + void fm_free(void); + ff_entry *check_ff_exist(char *, boolean); +diff --git a/texk/web2c/luatexdir/font/sfnt.h b/texk/web2c/luatexdir/font/sfnt.h +index dce4248e9..602a0805f 100644 +--- a/texk/web2c/luatexdir/font/sfnt.h ++++ b/texk/web2c/luatexdir/font/sfnt.h +@@ -28,11 +28,12 @@ + # endif /* HAVE_CONFIG_H_ */ + + /* Data Types as described in Apple's TTRefMan */ +-typedef unsigned char BYTE; ++ ++/*typedef unsigned char BYTE;*/ /* defined in pdfgen.h */ + typedef signed char ICHAR; + typedef unsigned short USHORT; + typedef signed short SHORT; +-typedef unsigned long ULONG; ++/*typedef unsigned long ULONG;*//* defined in pdfgen.h */ + typedef signed long LONG; + typedef unsigned long Fixed; /* 16.16-bit signed fixed-point number */ + typedef short FWord; +@@ -70,40 +71,16 @@ struct sfnt_table_directory { + typedef struct { + int type; + struct sfnt_table_directory *directory; +-# ifdef XETEX +- FT_Face ft_face; +- long loc; +-# elif defined(pdfTeX) + BYTE *buffer; + long buflen; + long loc; +-# else +- FILE *stream; +-# endif + } sfnt; + + /* Convert sfnt "fixed" type to double */ ++ + # define fixed(a) ((double)((a)%0x10000L)/(double)(0x10000L) + \ + (a)/0x10000L - (((a)/0x10000L > 0x7fffL) ? 0x10000L : 0)) + +-# ifdef XETEX +-UNSIGNED_BYTE ft_unsigned_byte(sfnt * f); +-SIGNED_BYTE ft_signed_byte(sfnt * f); +-UNSIGNED_PAIR ft_unsigned_pair(sfnt * f); +-SIGNED_PAIR ft_signed_pair(sfnt * f); +-UNSIGNED_QUAD ft_unsigned_quad(sfnt * f); +-unsigned long ft_read(unsigned char *buf, unsigned long len, sfnt * f); +- +-# define sfnt_get_byte(s) ((BYTE) ft_unsigned_byte(s)) +-# define sfnt_get_char(s) ((ICHAR) ft_signed_byte (s)) +-# define sfnt_get_ushort(s) ((USHORT) ft_unsigned_pair(s)) +-# define sfnt_get_short(s) ((SHORT) ft_signed_pair (s)) +-# define sfnt_get_ulong(s) ((ULONG) ft_unsigned_quad(s)) +-# define sfnt_get_long(s) ((LONG) ft_signed_quad (s)) +- +-# define sfnt_seek_set(s,o) (s)->loc = (o) +-# define sfnt_read(b,l,s) ft_read((b), (l), (s)) +-# elif defined(pdfTeX) + BYTE get_unsigned_byte(sfnt * f); + ICHAR get_signed_byte(sfnt * f); + USHORT get_unsigned_pair(sfnt * f); +@@ -111,45 +88,28 @@ SHORT get_signed_pair(sfnt * f); + ULONG get_unsigned_quad(sfnt * f); + int do_sfnt_read(unsigned char *dest, int len, sfnt * f); + +-# define sfnt_get_byte(s) ((BYTE) get_unsigned_byte(s)) +-# define sfnt_get_char(s) ((ICHAR) get_signed_byte (s)) +-# define sfnt_get_ushort(s) ((USHORT) get_unsigned_pair(s)) +-# define sfnt_get_short(s) ((SHORT) get_signed_pair (s)) +-# define sfnt_get_ulong(s) ((ULONG) get_unsigned_quad(s)) +-# define sfnt_get_long(s) ((LONG) get_signed_quad (s)) +- +-# define sfnt_seek_set(s,o) (s)->loc = (o) +-# define sfnt_read(b,l,s) do_sfnt_read((b), (l), (s)) +-# else +-/* get_***_*** from numbers.h */ +-# define sfnt_get_byte(s) ((BYTE) get_unsigned_byte((s)->stream)) +-# define sfnt_get_char(s) ((ICHAR) get_signed_byte ((s)->stream)) +-# define sfnt_get_ushort(s) ((USHORT) get_unsigned_pair((s)->stream)) +-# define sfnt_get_short(s) ((SHORT) get_signed_pair ((s)->stream)) +-# define sfnt_get_ulong(s) ((ULONG) get_unsigned_quad((s)->stream)) +-# define sfnt_get_long(s) ((LONG) get_signed_quad ((s)->stream)) +- +-# define sfnt_seek_set(s,o) seek_absolute((s)->stream, (o)) +-# define sfnt_read(b,l,s) fread((b), 1, (l), (s)->stream) +-# endif ++#define sfnt_get_byte(s) ((BYTE) get_unsigned_byte(s)) ++#define sfnt_get_char(s) ((ICHAR) get_signed_byte (s)) ++#define sfnt_get_ushort(s) ((USHORT) get_unsigned_pair(s)) ++#define sfnt_get_short(s) ((SHORT) get_signed_pair (s)) ++#define sfnt_get_ulong(s) ((ULONG) get_unsigned_quad(s)) ++#define sfnt_get_long(s) ((LONG) get_signed_quad (s)) ++ ++#define sfnt_seek_set(s,o) (s)->loc = (o) ++#define sfnt_read(b,l,s) do_sfnt_read((b), (l), (s)) + + extern int put_big_endian(void *s, LONG q, int n); + +-# define sfnt_put_ushort(s,v) put_big_endian((s), v, 2); +-# define sfnt_put_short(s,v) put_big_endian((s), v, 2); +-# define sfnt_put_ulong(s,v) put_big_endian((s), v, 4); +-# define sfnt_put_long(s,v) put_big_endian((s), v, 4); ++#define sfnt_put_ushort(s,v) put_big_endian((s), v, 2); ++#define sfnt_put_short(s,v) put_big_endian((s), v, 2); ++#define sfnt_put_ulong(s,v) put_big_endian((s), v, 4); ++#define sfnt_put_long(s,v) put_big_endian((s), v, 4); + +-# ifdef XETEX +-extern sfnt *sfnt_open(FT_Face face, int accept_types); +-# elif defined(pdfTeX) + extern sfnt *sfnt_open(unsigned char *buffer, int buflen); +-# else +-extern sfnt *sfnt_open(FILE * fp); +-# endif + extern void sfnt_close(sfnt * sfont); + + /* table directory */ ++ + extern int sfnt_read_table_directory(sfnt * sfont, ULONG offset); + extern ULONG sfnt_find_table_len(sfnt * sfont, const char *tag); + extern ULONG sfnt_find_table_pos(sfnt * sfont, const char *tag); +@@ -159,18 +119,10 @@ extern void sfnt_set_table(sfnt * sfont, + const char *tag, void *data, ULONG length); + extern int sfnt_require_table(sfnt * sfont, const char *tag, int must_exist); + +-# ifdef pdfTeX +-typedef struct { +- ULONG length; +- BYTE *data; +-} pdf_obj; +- +-# define ASSERT(a) assert(a) +-# define RELEASE(a) free(a) +-# define NEW(a,b) xmalloc((unsigned)((unsigned)(a)*sizeof(b))) +-# define RENEW(a,b,c) xrealloc(a, (unsigned)((unsigned)(b)*sizeof(c))) +- +-# endif ++#define ASSERT(a) assert(a) ++#define RELEASE(a) free(a) ++#define NEW(a,b) xmalloc((unsigned)((unsigned)(a)*sizeof(b))) ++#define RENEW(a,b,c) xrealloc(a, (unsigned)((unsigned)(b)*sizeof(c))) + + extern pdf_obj *sfnt_create_FontFile_stream(sfnt * sfont); + +diff --git a/texk/web2c/luatexdir/font/texfont.h b/texk/web2c/luatexdir/font/texfont.h +index 75beafe66..2622b7a14 100644 +--- a/texk/web2c/luatexdir/font/texfont.h ++++ b/texk/web2c/luatexdir/font/texfont.h +@@ -32,6 +32,17 @@ + + # define pointer halfword + ++# define FONT_SLANT_MIN -2000 ++# define FONT_SLANT_MAX 2000 ++# define FONT_EXTEND_MIN -5000 ++# define FONT_EXTEND_MAX 5000 ++# define FONT_SQUEEZE_MIN -5000 ++# define FONT_SQUEEZE_MAX 5000 ++# define FONT_MODE_MIN 0 ++# define FONT_MODE_MAX 3 /* pdf values */ ++# define FONT_WIDTH_MIN 0 ++# define FONT_WIDTH_MAX 5000 ++ + /* these are dumped en block, so they need endianness tests */ + + typedef struct liginfo { +@@ -141,6 +152,9 @@ typedef struct texfont { + boolean _font_oldmath; /* default to false when MathConstants seen */ + int _font_slant; /* a slant in ppt */ + int _font_extend; /* an extension in ppt, or 1000 */ ++ int _font_squeeze; /* an extension in ppt, or 1000 */ ++ int _font_width; ++ int _font_mode; + int font_max_shrink; + int font_max_stretch; + int _font_step; /* amount of one step of expansion */ +@@ -329,6 +343,15 @@ boolean cmp_font_area(int, str_number); + # define font_extend(a) font_tables[a]->_font_extend + # define set_font_extend(a,b) font_extend(a) = b + ++# define font_squeeze(a) font_tables[a]->_font_squeeze ++# define set_font_squeeze(a,b) font_squeeze(a) = b ++ ++# define font_width(a) font_tables[a]->_font_width ++# define set_font_width(a,b) font_width(a) = b ++ ++# define font_mode(a) font_tables[a]->_font_mode ++# define set_font_mode(a,b) font_mode(a) = b ++ + # define font_shrink(a) font_tables[a]->_font_shrink + # define set_font_shrink(a,b) font_shrink(a) = b + +@@ -625,7 +648,7 @@ typedef enum { packet_char_code, + packet_scale_code, + packet_lua_code, + packet_pdf_code, +- packet_pdf_mode, ++ packet_pdf_mode + } packet_command_codes; + + extern scaled store_scaled_f(scaled sq, int fw); +diff --git a/texk/web2c/luatexdir/image/epdf.h b/texk/web2c/luatexdir/image/epdf.h +index 57bb2e39a..9c32c06b7 100644 +--- a/texk/web2c/luatexdir/image/epdf.h ++++ b/texk/web2c/luatexdir/image/epdf.h +@@ -18,15 +18,19 @@ + with LuaTeX; if not, see . */ + + +-// this is the common header file for C++ sources pdftoepdf.cc and lepdflib.cc ++/* this is the common header file for C++ sources pdftoepdf.c and lepdflib.c */ + + #ifndef EPDF_H + # define EPDF_H +-extern "C" { ++ ++/*extern "C" {*/ ++ + #ifdef HAVE_CONFIG_H + #include + #endif +-} ++ ++/*}*/ ++ + # include + # include + # include +@@ -35,41 +39,22 @@ extern "C" { + # include + # include + # include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +- +-extern "C" { ++ ++/*extern "C" { */ + + # include + +- extern char *xstrdup(const char *); ++extern char *xstrdup(const char *); + +- typedef enum { FE_FAIL, FE_RETURN_NULL } file_error_mode; ++typedef enum { FE_FAIL, FE_RETURN_NULL } file_error_mode; + + /* the following code is extremly ugly but needed for including web2c/config.h */ + +- typedef const char *const_string; /* including kpathsea/types.h doesn't work on some systems */ ++typedef const char *const_string; /* including kpathsea/types.h doesn't work on some systems */ + + # define KPATHSEA_CONFIG_H /* avoid including other kpathsea header files */ +- /* from web2c/config.h */ ++ ++/* from web2c/config.h */ + + # ifdef CONFIG_H /* CONFIG_H has been defined by some xpdf */ + # undef CONFIG_H /* header file */ +@@ -84,117 +69,105 @@ extern "C" { + # include "lua.h" + # include "lauxlib.h" + +- /* pdfgen.w */ +- extern int ten_pow[10]; +- __attribute__ ((format(printf, 2, 3))) +- extern void pdf_printf(PDF, const char *fmt, ...); +- extern void pdf_begin_obj(PDF, int, int); +- extern void pdf_end_obj(PDF); +- extern void pdf_begin_dict(PDF); +- extern void pdf_end_dict(PDF); +- extern void pdf_begin_array(PDF); +- extern void pdf_end_array(PDF); +- extern void pdf_add_null(PDF); +- extern void pdf_add_bool(PDF, int i); +- extern void pdf_add_int(PDF, int i); +- extern void pdf_add_ref(PDF, int num); +- extern void pdf_add_name(PDF, const char *name); +- extern void pdf_dict_add_streaminfo(PDF); +- extern void pdf_begin_stream(PDF); +- extern void pdf_end_stream(PDF); +- extern void pdf_room(PDF, int); +- extern void pdf_out_block(PDF pdf, const char *s, size_t n); +- +- extern void pdf_dict_add_int(PDF, const char *key, int i); +- extern void pdf_dict_add_ref(PDF, const char *key, int num); +- extern void pdf_dict_add_name(PDF, const char *key, const char *val); +- extern void pdf_dict_add_streaminfo(PDF); +- +-# define pdf_out(pdf, A) do { pdf_room(pdf, 1); *(pdf->buf->p++) = A; } while (0) +-# define pdf_quick_out(pdf,A) *(pdf->buf->p++)=(unsigned char)(A) ++# include "luapplib/pplib.h" ++ ++/* pdfgen.w */ ++ ++extern int ten_pow[10]; ++__attribute__ ((format(printf, 2, 3))) ++extern void pdf_printf(PDF, const char *fmt, ...); ++extern void pdf_begin_obj(PDF, int, int); ++extern void pdf_end_obj(PDF); ++extern void pdf_begin_dict(PDF); ++extern void pdf_end_dict(PDF); ++extern void pdf_begin_array(PDF); ++extern void pdf_end_array(PDF); ++extern void pdf_add_null(PDF); ++extern void pdf_add_bool(PDF, int i); ++extern void pdf_add_int(PDF, int i); ++extern void pdf_add_real(PDF, double d); ++extern void pdf_add_ref(PDF, int num); ++extern void pdf_add_name(PDF, const char *name); ++extern void pdf_dict_add_streaminfo(PDF); ++extern void pdf_begin_stream(PDF); ++extern void pdf_end_stream(PDF); ++extern void pdf_room(PDF, int); ++extern void pdf_out_block(PDF pdf, const char *s, size_t n); ++ ++extern void pdf_dict_add_int(PDF, const char *key, int i); ++extern void pdf_dict_add_ref(PDF, const char *key, int num); ++extern void pdf_dict_add_name(PDF, const char *key, const char *val); ++extern void pdf_dict_add_streaminfo(PDF); ++ ++/* Conflict with pdfgen.h */ ++/*# define pdf_out(pdf, A) do { pdf_room(pdf, 1); *(pdf->buf->p++) = A; } while (0)*/ ++/*# define pdf_quick_out(pdf,A) *(pdf->buf->p++)=(unsigned char)(A) */ ++ + # define pdf_puts(pdf, s) pdf_out_block((pdf), (s), strlen(s)) + +- /* pdfpage.w */ +- extern void print_pdffloat(PDF pdf, pdffloat f); ++/* pdfpage.w */ + +- /* pdftables.w */ +- extern int pdf_create_obj(PDF pdf, int t, int i); ++extern void print_pdffloat(PDF pdf, pdffloat f); + +- /* pdftoepdf.cc */ +- extern void read_pdf_info(image_dict *); +- extern void flush_pdf_info(image_dict *); +- extern void write_epdf(PDF, image_dict *, int suppress_optional_info); +- extern void unrefPdfDocument(char *); +- extern void unrefMemStreamPdfDocument(char *); +- extern void epdf_free(void); +- extern void copyReal(PDF pdf, double d); ++/* pdftables.w */ + +- /* writeimg.w */ +- extern void pdf_dict_add_img_filename(PDF pdf, image_dict * idict); ++extern int pdf_create_obj(PDF pdf, int t, int i); + +- /* utils.w */ +- extern char *convertStringToPDFString(char *in, int len); ++/* pdftoepdf.c */ + +- /* lepdflib.w */ +- int luaopen_epdf(lua_State * L); ++extern void read_pdf_info(image_dict *); ++extern void flush_pdf_info(image_dict *); + +-# include "luatex-common.h" ++extern void write_epdf(PDF, image_dict *, int suppress_optional_info); ++extern int write_epdf_object(PDF, image_dict *, int n); + +-}; ++extern void unrefPdfDocument(char *); ++extern void unrefMemStreamPdfDocument(char *); + +-/**********************************************************************/ +- +-// PdfObject encapsulates the xpdf Object type, +-// and properly frees its resources on destruction. +-// Use obj-> to access members of the Object, +-// and &obj to get a pointer to the object. +-// It is no longer necessary to call Object::free explicitely. +- +-# if 0 +-// PdfObject is replaced by xpdf's Object type, with manual obj.free() +- +-// *INDENT-OFF* +-class PdfObject { +- public: +- PdfObject() { // nothing +- } +- ~PdfObject() { +- iObject.free(); +- } +- Object *operator->() { +- return &iObject; +- } +- Object *operator&() { +- return &iObject; +- } +- private: // no copying or assigning +- PdfObject(const PdfObject &); +- void operator=(const PdfObject &); +- public: +- Object iObject; +-}; +-// *INDENT-ON* +-# endif ++extern void epdf_free(void); ++ ++/* writeimg.w */ + +-/**********************************************************************/ ++extern void pdf_dict_add_img_filename(PDF pdf, image_dict * idict); ++ ++/* utils.w */ ++ ++/*extern char *convertStringToPDFString(const char *in, int len);*/ ++ ++/* lepdflib.w */ ++ ++int luaopen_epdf(lua_State * L); ++ ++# include "luatex-common.h" ++ ++/*}*/ ++ ++typedef struct InObj InObj; + + struct InObj { +- Ref ref; // ref in original PDF +- int num; // new object number in output PDF +- InObj *next; // next entry in list of indirect objects +-}; ++ ppref *ref; /* ref in original PDF */ ++ int num; /* new object number in output PDF */ ++ InObj *next; /* next entry in list of indirect objects */ ++} ; ++ ++ ++typedef struct avl_table avl_table; + + struct PdfDocument { +- char *file_path; // full file name including path +- char *checksum; // for reopening +- PDFDoc *doc; +- InObj *inObjList; // temporary linked list +- avl_table *ObjMapTree; // permanent over luatex run +- unsigned int occurences; // number of references to the PdfDocument; it can be deleted when occurences == 0 +- unsigned int pc; // counter to track PDFDoc generation or deletion ++ char *file_path; /* full file name including path */ ++ char *checksum; /* for reopening */ ++ ppdoc *pdfe; ++ InObj *inObjList; /* temporary linked list */ ++ avl_table *ObjMapTree; /* permanent over luatex run */ ++ int is_mem; ++ char *memstream; ++ unsigned int occurences; /* number of references to the PdfDocument; it can be deleted when occurences == 0 */ ++ unsigned int pc; /* counter to track PDFDoc generation or deletion */ + }; + +-PdfDocument *refPdfDocument(const char *file_path, file_error_mode fe); ++typedef struct PdfDocument PdfDocument; ++ ++PdfDocument *refPdfDocument(const char *file_path, file_error_mode fe, const char *userpassword, const char *ownerpassword); + + PdfDocument *refMemStreamPdfDocument(char *docstream, unsigned long long streamsize, const char *file_id); + +diff --git a/texk/web2c/luatexdir/image/image.h b/texk/web2c/luatexdir/image/image.h +index 7c79723f1..985f53d06 100644 +--- a/texk/web2c/luatexdir/image/image.h ++++ b/texk/web2c/luatexdir/image/image.h +@@ -39,6 +39,7 @@ extern scaled one_hundred_bp; /* from pdfgen.w */ + + typedef struct { + char *stream; ++ size_t size; + } pdf_stream_struct; + + typedef struct { +@@ -116,6 +117,8 @@ typedef struct { + char *filepath; /* full file path after kpathsea */ + char *attr; /* additional image dict entries */ + FILE *file; ++ char *userpassword; ++ char *ownerpassword; + imgtype_e image_type; + int procset; /* /ProcSet flags */ + int color_depth; /* color depth */ +@@ -125,6 +128,7 @@ typedef struct { + int flags; + int luaref ; + boolean keepopen; ++ boolean nolength; + int errorlevel; + int pdfmajorversion; + int pdfminorversion; +@@ -159,6 +163,8 @@ typedef struct { + # define img_pagename(N) ((N)->pagename) + # define img_filename(N) ((N)->filename) + # define img_visiblefilename(N) ((N)->visiblefilename) ++# define img_userpassword(N) ((N)->userpassword) ++# define img_ownerpassword(N) ((N)->ownerpassword) + # define img_filepath(N) ((N)->filepath) + # define img_attr(N) ((N)->attr) + # define img_file(N) ((N)->file) +@@ -171,12 +177,14 @@ typedef struct { + # define img_flags(N) ((N)->flags) + # define img_luaref(N) ((N)->luaref) + # define img_keepopen(N) ((N)->keepopen) ++# define img_nolength(N) ((N)->nolength) + # define img_errorlevel(N) ((N)->errorlevel) + # define img_pdfmajorversion(N) ((N)->pdfmajorversion) + # define img_pdfminorversion(N) ((N)->pdfminorversion) + + # define img_pdfstream_ptr(N) ((N)->img_struct.pdfstream) + # define img_pdfstream_stream(N) ((N)->img_struct.pdfstream->stream) ++# define img_pdfstream_size(N) ((N)->img_struct.pdfstream->size) + + # define img_png_ptr(N) ((N)->img_struct.png) + # define img_png_png_ptr(N) ((N)->img_struct.png->png_ptr) +diff --git a/texk/web2c/luatexdir/image/pdftoepdf.h b/texk/web2c/luatexdir/image/pdftoepdf.h +index 0a8ef942c..cc8f0e150 100644 +--- a/texk/web2c/luatexdir/image/pdftoepdf.h ++++ b/texk/web2c/luatexdir/image/pdftoepdf.h +@@ -30,12 +30,7 @@ void flush_pdf_info(image_dict *); + void unrefPdfDocument(char *); + void unrefMemStreamPdfDocument(char *); + void write_epdf(PDF, image_dict *, int suppress_optional_info); +-void epdf_check_mem(void); +-void copyReal(PDF pdf, double d); +- +-int poppler_version_major(void); +-int poppler_version_minor(void); +-int poppler_version_micro(void); ++int write_epdf_object(PDF, image_dict *, int n); + + /* epdf.c --- this should go in an own header file */ + +diff --git a/texk/web2c/luatexdir/image/writeimg.h b/texk/web2c/luatexdir/image/writeimg.h +index 46c42d265..dc86648e2 100644 +--- a/texk/web2c/luatexdir/image/writeimg.h ++++ b/texk/web2c/luatexdir/image/writeimg.h +@@ -38,6 +38,7 @@ void scan_pdfrefximage(PDF pdf); + scaled_whd tex_scale(scaled_whd nat, scaled_whd tex); + scaled_whd scale_img(image_dict *, scaled_whd, int); + void write_img(PDF, image_dict *); ++int write_img_object(PDF, image_dict *, int n); + void pdf_write_image(PDF pdf, int n); + void check_pdfstream_dict(image_dict *); + void write_pdfstream(PDF, image_dict *); +diff --git a/texk/web2c/luatexdir/lua/lcallbacklib.c b/texk/web2c/luatexdir/lua/lcallbacklib.c +index f3ff58a75..32cf66302 100644 +--- a/texk/web2c/luatexdir/lua/lcallbacklib.c ++++ b/texk/web2c/luatexdir/lua/lcallbacklib.c +@@ -22,6 +22,9 @@ + + int callback_count = 0; + int saved_callback_count = 0; ++int direct_callback_count = 0; ++int late_callback_count = 0; ++int function_callback_count = 0; + + int callback_set[total_callbacks] = { 0 }; + +@@ -74,7 +77,12 @@ static const char *const callbacknames[] = { + "call_edit", + "build_page_insert", + "glyph_stream_provider", +- "finish_synctex_callback", ++ "font_descriptor_objnum_provider", ++ "finish_synctex", ++ "wrapup_run", ++ "new_graf", ++ "page_objnum_provider", ++ "make_extensible", + NULL + }; + +@@ -207,7 +215,6 @@ int run_saved_callback(int r, const char *name, const char *values, ...) + lua_rawget(Luas, -2); + if (lua_isfunction(Luas, -1)) { + saved_callback_count++; +- callback_count++; + ret = do_run_callback(2, values, args); + } + va_end(args); +diff --git a/texk/web2c/luatexdir/lua/lepdflib.cc b/texk/web2c/luatexdir/lua/lepdflib.cc +index 32bcdab01..ea8ed96b0 100644 +--- a/texk/web2c/luatexdir/lua/lepdflib.cc ++++ b/texk/web2c/luatexdir/lua/lepdflib.cc +@@ -17,3627 +17,3 @@ + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see . */ +- +- +-#include "image/epdf.h" +- +-// 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. +- +-// define DEBUG +- +-//********************************************************************** +-// TODO: add more poppler functions (many are still missing) +- +-//********************************************************************** +-// objects allocated by poppler may not be deleted in the lepdflib +- +-typedef enum { ALLOC_POPPLER, ALLOC_LEPDF } alloctype; +- +-typedef struct { +- void *d; +- alloctype atype; // was it allocated by poppler or the lepdflib.cc? +- PdfDocument *pd; // reference to PdfDocument, or NULL +- unsigned long pc; // counter to detect PDFDoc change +-} udstruct; +- +-static const char *ErrorCodeNames[] = { "None", "OpenFile", "BadCatalog", +- "Damaged", "Encrypted", "HighlightFile", "BadPrinter", "Printing", +- "Permission", "BadPageNum", "FileIO", NULL +-}; +- +-//********************************************************************** +- +-#define M_Annot "epdf.Annot" /* ls-hh: epdf.* gives better protection in registry */ +-#define M_Annots "epdf.Annots" +-#define M_Array "epdf.Array" +-#define M_Catalog "epdf.Catalog" +-#define M_Dict "epdf.Dict" +-#define M_EmbFile "epdf.EmbFile" +-#define M_FileSpec "epdf.FileSpec" +-#define M_GooString "epdf.GooString" +-#define M_LinkDest "epdf.LinkDest" +-#define M_Link "epdf.Link" +-#define M_Links "epdf.Links" +-#define M_Object "epdf.Object" +-#define M_Page "epdf.Page" +-#define M_PDFDoc "epdf.PDFDoc" +-#define M_PDFRectangle "epdf.PDFRectangle" +-#define M_Ref "epdf.Ref" +-#define M_Stream "epdf.Stream" +-#define M_StructElement "epdf.StructElement" +-#define M_Attribute "epdf.Attribute" +-#define M_TextSpan "epdf.TextSpan" +-#define M_StructTreeRoot "epdf.StructTreeRoot" +-#define M_XRefEntry "epdf.XRefEntry" +-#define M_XRef "epdf.XRef" +- +-//********************************************************************** +- +-#define new_poppler_userdata(type) \ +-static udstruct *new_##type##_userdata(lua_State * L) \ +-{ \ +- udstruct *a; \ +- a = (udstruct *) lua_newuserdata(L, sizeof(udstruct)); /* udstruct ... */ \ +- a->atype = ALLOC_POPPLER; \ +- luaL_getmetatable(L, M_##type); /* m udstruct ... */ \ +- lua_setmetatable(L, -2); /* udstruct ... */ \ +- return a; \ +-} +- +-new_poppler_userdata(PDFDoc); +- +-new_poppler_userdata(Annot); +-new_poppler_userdata(Array); +-new_poppler_userdata(Catalog); +-new_poppler_userdata(Dict); +-new_poppler_userdata(EmbFile); +-new_poppler_userdata(FileSpec); +-new_poppler_userdata(LinkDest); +-new_poppler_userdata(Links); +-new_poppler_userdata(Object); +-new_poppler_userdata(Page); +-new_poppler_userdata(PDFRectangle); +-new_poppler_userdata(Ref); +-new_poppler_userdata(Stream); +-new_poppler_userdata(StructElement); +-new_poppler_userdata(Attribute); +-new_poppler_userdata(TextSpan); +-new_poppler_userdata(StructTreeRoot); +-new_poppler_userdata(XRef); +- +-//********************************************************************** +- +-static void pdfdoc_changed_error(lua_State * L) +-{ +- luaL_error(L, "PDFDoc changed or gone"); +-} +- +-static void pdfdoc_differs_error(lua_State * L) +-{ +- luaL_error(L, "PDFDoc differs between arguments"); +-} +- +-//********************************************************************** +- +-static int l_open_PDFDoc(lua_State * L) +-{ +- const char *file_path; +- udstruct *uout; +- PdfDocument *d; +- file_path = luaL_checkstring(L, 1); // path +- d = refPdfDocument(file_path, FE_RETURN_NULL); +- if (d == NULL) +- lua_pushnil(L); +- else { +- if (!(globalParams)) // globalParams could be already created +- globalParams = new GlobalParams(); +- uout = new_PDFDoc_userdata(L); +- uout->d = d; +- uout->atype = ALLOC_LEPDF; +- uout->pc = d->pc; +- uout->pd = d; +- } +- return 1; // doc path +-} +- +-static int l_open_MemStreamPDFDoc(lua_State * L) +-{ +- const char *docstream = NULL; +- char *docstream_usr = NULL ; +- const char *file_id; +- unsigned long long stream_size; +- udstruct *uout; +- PdfDocument *d; +- switch (lua_type(L, 1)) { +- case LUA_TSTRING: +- docstream = luaL_checkstring(L, 1); // stream as Lua string +- break; +- case LUA_TLIGHTUSERDATA: +- docstream = (const char *) lua_touserdata(L, 1); // stream as sequence of bytes +- break; +- default: +- luaL_error(L, "bad argument: string or lightuserdata expected"); +- } +- if (docstream==NULL) +- luaL_error(L, "bad document"); +- stream_size = (unsigned long long) luaL_checkint(L, 2);// size of the stream +- file_id = luaL_checkstring(L, 3); // a symbolic name for this stream, mandatory +- if (file_id == NULL) +- luaL_error(L, "PDFDoc has an invalid id"); +- if (strlen(file_id) >STREAM_FILE_ID_LEN ) // a limit to the length of the string +- luaL_error(L, "PDFDoc has a too long id"); +- docstream_usr = (char *)gmalloc((unsigned) (stream_size + 1)); +- if (!docstream_usr) +- luaL_error(L, "no room for PDFDoc"); +- memcpy(docstream_usr, docstream, (stream_size + 1)); +- docstream_usr[stream_size]='\0'; +- d = refMemStreamPdfDocument(docstream_usr, stream_size, file_id); +- if (d == NULL) { +- lua_pushnil(L); +- lua_pushnil(L); +- lua_pushnil(L); +- } +- else if (d->file_path == NULL ) { +- lua_pushnil(L); +- lua_pushnil(L); +- lua_pushnil(L); +- } +- else { +- if (!(globalParams)) // globalParams could be already created +- globalParams = new GlobalParams(); +- uout = new_PDFDoc_userdata(L); +- uout->d = d; +- uout->atype = ALLOC_LEPDF; +- uout->pc = d->pc; +- uout->pd = d; +- lua_pushstring(L,d->file_path); +- lua_pushstring(L,STREAM_URI); +- } +- return 3; // stream, stream_id, stream_uri +-} +- +- +- +- +-static int l_new_Array(lua_State * L) +-{ +- udstruct *uxref, *uout; +- uxref = (udstruct *) luaL_checkudata(L, 1, M_XRef); +- if (uxref->pd != NULL && uxref->pd->pc != uxref->pc) +- pdfdoc_changed_error(L); +- uout = new_Array_userdata(L); +- uout->d = new Array((XRef *) uxref->d); // automatic init to length 0 +- uout->atype = ALLOC_LEPDF; +- uout->pc = uxref->pc; +- uout->pd = uxref->pd; +- return 1; +-} +- +-static int l_new_Attribute(lua_State * L) +-{ +- Attribute::Type t; +- const char *n; +- int nlen; +- udstruct *uobj, *uout; +- +- if (lua_type(L,1)==LUA_TNUMBER) { +- uobj = (udstruct *) luaL_checkudata(L, 2, M_Object); +- if (uobj->pd != NULL && uobj->pd->pc != uobj->pc) +- pdfdoc_changed_error(L); +- t = (Attribute::Type) luaL_checkint(L, 1); +- uout = new_Attribute_userdata(L); +- uout->d = new Attribute(t, (Object *)uobj->d); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uobj->pc; +- uout->pd = uobj->pd; +- +- } else if (lua_type(L,1)==LUA_TSTRING) { +- n = luaL_checkstring(L,1); +- nlen = luaL_checkint(L,2); +- uobj = (udstruct *) luaL_checkudata(L, 3, M_Object); +- if (uobj->pd != NULL && uobj->pd->pc != uobj->pc) +- pdfdoc_changed_error(L); +- uout = new_Attribute_userdata(L); +- uout->d = new Attribute(n, nlen, (Object *)uobj->d); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uobj->pc; +- uout->pd = uobj->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-#define ATTRIBUTE_TYPE_ENTRY(name) \ +- lua_pushstring(L, #name); \ +- lua_pushinteger(L, Attribute::name); \ +- lua_settable(L,-3) +- +- +-#define OBJECT_TYPE(name) \ +- lua_pushstring(L, #name); \ +- lua_pushinteger(L, (int)name); \ +- lua_settable(L,-3) +- +- +-#define STRUCTELEMENT_TYPE_ENTRY(name) \ +- lua_pushstring(L, #name); \ +- lua_pushinteger(L, StructElement::name); \ +- lua_settable(L,-3) +- +- +-static int l_Attribute_Type(lua_State * L) { +- lua_createtable (L, 0, 42); +- ATTRIBUTE_TYPE_ENTRY(BBox); +- ATTRIBUTE_TYPE_ENTRY(BackgroundColor); +- ATTRIBUTE_TYPE_ENTRY(BorderColor); +- ATTRIBUTE_TYPE_ENTRY(BorderThickness); +- ATTRIBUTE_TYPE_ENTRY(Color); +- ATTRIBUTE_TYPE_ENTRY(ColumnGap); +- ATTRIBUTE_TYPE_ENTRY(ColumnWidths); +- ATTRIBUTE_TYPE_ENTRY(Desc); +- ATTRIBUTE_TYPE_ENTRY(Role); +- ATTRIBUTE_TYPE_ENTRY(TextDecorationColor); +- ATTRIBUTE_TYPE_ENTRY(TextDecorationThickness); +- ATTRIBUTE_TYPE_ENTRY(BaselineShift); +- ATTRIBUTE_TYPE_ENTRY(BlockAlign); +- ATTRIBUTE_TYPE_ENTRY(BorderStyle); +- ATTRIBUTE_TYPE_ENTRY(ColSpan); +- ATTRIBUTE_TYPE_ENTRY(ColumnCount); +- ATTRIBUTE_TYPE_ENTRY(EndIndent); +- ATTRIBUTE_TYPE_ENTRY(GlyphOrientationVertical); +- ATTRIBUTE_TYPE_ENTRY(Headers); +- ATTRIBUTE_TYPE_ENTRY(Height); +- ATTRIBUTE_TYPE_ENTRY(InlineAlign); +- ATTRIBUTE_TYPE_ENTRY(LineHeight); +- ATTRIBUTE_TYPE_ENTRY(ListNumbering); +- ATTRIBUTE_TYPE_ENTRY(Padding); +- ATTRIBUTE_TYPE_ENTRY(Placement); +- ATTRIBUTE_TYPE_ENTRY(RowSpan); +- ATTRIBUTE_TYPE_ENTRY(RubyAlign); +- ATTRIBUTE_TYPE_ENTRY(RubyPosition); +- ATTRIBUTE_TYPE_ENTRY(Scope); +- ATTRIBUTE_TYPE_ENTRY(SpaceAfter); +- ATTRIBUTE_TYPE_ENTRY(SpaceBefore); +- ATTRIBUTE_TYPE_ENTRY(StartIndent); +- ATTRIBUTE_TYPE_ENTRY(Summary); +- ATTRIBUTE_TYPE_ENTRY(TBorderStyle); +- ATTRIBUTE_TYPE_ENTRY(TPadding); +- ATTRIBUTE_TYPE_ENTRY(TextAlign); +- ATTRIBUTE_TYPE_ENTRY(TextDecorationType); +- ATTRIBUTE_TYPE_ENTRY(TextIndent); +- ATTRIBUTE_TYPE_ENTRY(Width); +- ATTRIBUTE_TYPE_ENTRY(WritingMode); +- ATTRIBUTE_TYPE_ENTRY(Unknown); +- ATTRIBUTE_TYPE_ENTRY(checked); +- return 1; +-} +- +-static int l_Object_Type(lua_State * L) { +- lua_createtable(L,0,16);/*nr of ObjType values*/ ; +- OBJECT_TYPE(objBool); +- OBJECT_TYPE(objInt); +- OBJECT_TYPE(objReal); +- OBJECT_TYPE(objString); +- OBJECT_TYPE(objName); +- OBJECT_TYPE(objNull); +- OBJECT_TYPE(objArray); +- OBJECT_TYPE(objDict); +- OBJECT_TYPE(objStream); +- OBJECT_TYPE(objRef); +- OBJECT_TYPE(objCmd); +- OBJECT_TYPE(objError); +- OBJECT_TYPE(objEOF); +- OBJECT_TYPE(objNone); +- OBJECT_TYPE(objInt64); +- OBJECT_TYPE(objDead); +- return 1; +-} +- +- +-static int l_StructElement_Type(lua_State * L) { +- lua_createtable (L, 0, 50); +- STRUCTELEMENT_TYPE_ENTRY(Document); +- STRUCTELEMENT_TYPE_ENTRY(Part); +- STRUCTELEMENT_TYPE_ENTRY(Art); +- STRUCTELEMENT_TYPE_ENTRY(Sect); +- STRUCTELEMENT_TYPE_ENTRY(Div); +- STRUCTELEMENT_TYPE_ENTRY(BlockQuote); +- STRUCTELEMENT_TYPE_ENTRY(Caption); +- STRUCTELEMENT_TYPE_ENTRY(NonStruct); +- STRUCTELEMENT_TYPE_ENTRY(Index); +- STRUCTELEMENT_TYPE_ENTRY(Private); +- STRUCTELEMENT_TYPE_ENTRY(Span); +- STRUCTELEMENT_TYPE_ENTRY(Quote); +- STRUCTELEMENT_TYPE_ENTRY(Note); +- STRUCTELEMENT_TYPE_ENTRY(Reference); +- STRUCTELEMENT_TYPE_ENTRY(BibEntry); +- STRUCTELEMENT_TYPE_ENTRY(Code); +- STRUCTELEMENT_TYPE_ENTRY(Link); +- STRUCTELEMENT_TYPE_ENTRY(Annot); +- STRUCTELEMENT_TYPE_ENTRY(Ruby); +- STRUCTELEMENT_TYPE_ENTRY(RB); +- STRUCTELEMENT_TYPE_ENTRY(RT); +- STRUCTELEMENT_TYPE_ENTRY(RP); +- STRUCTELEMENT_TYPE_ENTRY(Warichu); +- STRUCTELEMENT_TYPE_ENTRY(WT); +- STRUCTELEMENT_TYPE_ENTRY(WP); +- STRUCTELEMENT_TYPE_ENTRY(P); +- STRUCTELEMENT_TYPE_ENTRY(H); +- STRUCTELEMENT_TYPE_ENTRY(H1); +- STRUCTELEMENT_TYPE_ENTRY(H2); +- STRUCTELEMENT_TYPE_ENTRY(H3); +- STRUCTELEMENT_TYPE_ENTRY(H4); +- STRUCTELEMENT_TYPE_ENTRY(H5); +- STRUCTELEMENT_TYPE_ENTRY(H6); +- STRUCTELEMENT_TYPE_ENTRY(L); +- STRUCTELEMENT_TYPE_ENTRY(LI); +- STRUCTELEMENT_TYPE_ENTRY(Lbl); +- STRUCTELEMENT_TYPE_ENTRY(LBody); +- STRUCTELEMENT_TYPE_ENTRY(Table); +- STRUCTELEMENT_TYPE_ENTRY(TR); +- STRUCTELEMENT_TYPE_ENTRY(TH); +- STRUCTELEMENT_TYPE_ENTRY(TD); +- STRUCTELEMENT_TYPE_ENTRY(THead); +- STRUCTELEMENT_TYPE_ENTRY(TFoot); +- STRUCTELEMENT_TYPE_ENTRY(TBody); +- STRUCTELEMENT_TYPE_ENTRY(Figure); +- STRUCTELEMENT_TYPE_ENTRY(Formula); +- STRUCTELEMENT_TYPE_ENTRY(Form); +- STRUCTELEMENT_TYPE_ENTRY(TOC); +- STRUCTELEMENT_TYPE_ENTRY(TOCI); +- lua_pushstring(L, "Unknown"); +- lua_pushinteger(L, 0); +- lua_settable(L,-3); +- return 1; +-} +- +-static int l_AttributeOwner_Type(lua_State * L) { +- lua_createtable (L, 0, 12); +- lua_pushstring(L, "XML-1.00"); lua_pushinteger(L, Attribute::XML_1_00); lua_settable(L,-3); +- lua_pushstring(L, "HTML-3.20"); lua_pushinteger(L, Attribute::HTML_3_20); lua_settable(L,-3); +- lua_pushstring(L, "HTML-4.01"); lua_pushinteger(L, Attribute::HTML_4_01); lua_settable(L,-3); +- lua_pushstring(L, "OEB-1.00"); lua_pushinteger(L, Attribute::OEB_1_00); lua_settable(L,-3); +- lua_pushstring(L, "RTF-1.05"); lua_pushinteger(L, Attribute::RTF_1_05); lua_settable(L,-3); +- lua_pushstring(L, "CSS-1.00"); lua_pushinteger(L, Attribute::CSS_1_00); lua_settable(L,-3); +- lua_pushstring(L, "CSS-2.00"); lua_pushinteger(L, Attribute::CSS_2_00); lua_settable(L,-3); +- lua_pushstring(L, "Layout"); lua_pushinteger(L, Attribute::Layout); lua_settable(L,-3); +- lua_pushstring(L, "PrintField"); lua_pushinteger(L, Attribute::PrintField); lua_settable(L,-3); +- lua_pushstring(L, "Table"); lua_pushinteger(L, Attribute::Table); lua_settable(L,-3); +- lua_pushstring(L, "List"); lua_pushinteger(L, Attribute::List); lua_settable(L,-3); +- lua_pushstring(L, "UserProperties"); lua_pushinteger(L, Attribute::UserProperties);lua_settable(L,-3); +- return 1; +-} +- +- +-static int l_new_Dict(lua_State * L) +-{ +- udstruct *uxref, *uout; +- uxref = (udstruct *) luaL_checkudata(L, 1, M_XRef); +- if (uxref->pd != NULL && uxref->pd->pc != uxref->pc) +- pdfdoc_changed_error(L); +- uout = new_Dict_userdata(L); +- uout->d = new Dict((XRef *) uxref->d); // automatic init to length 0 +- uout->atype = ALLOC_LEPDF; +- uout->pc = uxref->pc; +- uout->pd = uxref->pd; +- return 1; +-} +- +-static int l_new_Object(lua_State * L) +-{ +- udstruct *uout; +- int n = lua_gettop(L); // number of arguments +- uout = new_Object_userdata(L); +- switch(n) { +- case 0: +- uout->d = new Object(); // automatic init to type "none" +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; // not connected to any PDFDoc +- break; +- case 1: +- if (lua_isboolean (L,1)) { +- uout->d = new Object(lua_toboolean(L, 1)? gTrue : gFalse); +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; +- } else if (lua_isnumber (L,1)) { +- double d = lua_tonumber(L,1); +- // Missed :Object(long long int64gA) +- if (d==((int)d)) { +- uout->d = new Object((int)d); +- } else { +- uout->d = new Object(d); +- } +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; +- } else if (lua_isstring (L,1)){ +- GooString *gs; +- const char *s; +- size_t len; +- s = luaL_checklstring(L, 2, &len); +- gs = new GooString(s, len); +- uout->d = new Object(gs); +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; +- } else if (luaL_testudata(L,1,M_Array)){ +- udstruct *u; +- Array *a; +- u = (udstruct *) luaL_checkudata(L, 1, M_Array); +- a = (Array *)u->d; +- uout->d = new Object(a); +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; +- } else if (luaL_testudata(L,1,M_Dict)){ +- udstruct *u; +- Dict *d; +- u = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- d = (Dict *)u->d; +- uout->d = new Object(d); +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; +- } else if (luaL_testudata(L,1,M_Stream)){ +- udstruct *u; +- Stream *s; +- u = (udstruct *) luaL_checkudata(L, 1, M_Stream); +- s = (Stream *)u->d; +- *((Object *) uout->d) = Object(s); +- } else +- luaL_error(L, "Invalid/unsupported value for Object constructor"); +- break; +- case 2: +- if (lua_isnumber (L,1) && lua_isnumber (L,2)) { +- double numA = lua_tonumber(L,1); +- double genA = lua_tonumber(L,2); +- if ( ((numA)==(int)(numA)) && ((genA)==(int)(genA)) ){ +- uout->d = new Object((int)(numA), (int)(genA)); +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; +- } +- } else if (lua_isnumber (L,1) && (lua_isstring(L,2)|| lua_isnoneornil(L,2))) { +- double d_typeA = lua_tonumber(L,1); +- int typeA = (int)(d_typeA); +- if (d_typeA==typeA){ +- switch((int)(typeA)) { +- case objBool: +- case objInt: +- case objReal: +- case objString: +- case objName: +- case objNull: +- case objArray: +- case objDict: +- case objStream: +- case objRef: +- case objCmd: +- case objError: +- case objEOF: +- case objNone: +- case objInt64: +- case objDead: +- if (lua_isstring(L,2)) +- uout->d = new Object((ObjType)(typeA), luaL_checkstring(L, 2)); +- else +- uout->d = new Object((ObjType)(typeA)); +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; +- +- break; +- default: +- luaL_error(L, "Invalid values for Object constructor"); +- break; +- }//switch((int)(d)) +- } else // (d_typeA)!=(typeA) +- luaL_error(L, "Invalid/unsupported values for Object constructor"); +- } // if (lua_isnumber (L,1) && (lua_isstring(L,2)|| lua_isnoneornil(L,2))) +- break; +- default: +- luaL_error(L, "Invalid specification for Object constructor"); +- } +- lua_settop(L,1); +- return 1; +-} +- +-// static int l_new_Object(lua_State * L) +-// { +-// udstruct *uout; +-// uout = new_Object_userdata(L); +-// uout->d = new Object(); // automatic init to type "none" +-// uout->atype = ALLOC_LEPDF; +-// uout->pc = 0; +-// uout->pd = NULL; // not connected to any PDFDoc +-// return 1; +-// } +- +- +-// PDFRectangle see Page.h +- +-static int l_new_PDFRectangle(lua_State * L) +-{ +- udstruct *uout; +- uout = new_PDFRectangle_userdata(L); +- uout->d = new PDFRectangle(); // automatic init to [0, 0, 0, 0] +- uout->atype = ALLOC_LEPDF; +- uout->pc = 0; +- uout->pd = NULL; +- return 1; +-} +- +-static const struct luaL_Reg epdflib_f[] = { +- {"open", l_open_PDFDoc}, +- {"openMemStream", l_open_MemStreamPDFDoc}, +- {"Array", l_new_Array}, +- {"Attribute", l_new_Attribute}, +- {"StructElement_Type", l_StructElement_Type}, +- {"Attribute_Type", l_Attribute_Type}, +- {"AttributeOwner_Type",l_AttributeOwner_Type}, +- {"Dict", l_new_Dict}, +- {"Object", l_new_Object}, +- {"Object_Type", l_Object_Type}, +- {"PDFRectangle", l_new_PDFRectangle}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +- +-#define m_poppler_get_poppler(in, out, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- out *o; \ +- udstruct *uin, *uout; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- o = ((in *) uin->d)->function(); \ +- if (o != NULL) { \ +- uout = new_##out##_userdata(L); \ +- uout->d = o; \ +- uout->pc = uin->pc; \ +- uout->pd = uin->pd; \ +- } else \ +- lua_pushnil(L); \ +- return 1; \ +-} +- +-#define m_poppler_get_BOOL(in, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- if (((in *) uin->d)->function()) \ +- lua_pushboolean(L, 1); \ +- else \ +- lua_pushboolean(L, 0); \ +- return 1; \ +-} +- +-#define m_poppler_get_INT(in, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- int i; \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- i = (int) ((in *) uin->d)->function(); \ +- lua_pushinteger(L, i); \ +- return 1; \ +-} +- +- +-#define m_poppler_get_GUINT(in, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- unsigned int i; \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- i = (unsigned int) ((in *) uin->d)->function(); \ +- lua_pushinteger(L, i); \ +- return 1; \ +-} +- +-#define m_poppler_get_UINT(in, function) \ +-m_poppler_get_GUINT(in, function) +- +- +- +-#define m_poppler_get_DOUBLE(in, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- double d; \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- d = (double) ((in *) uin->d)->function(); \ +- lua_pushnumber(L, d); /* float */ \ +- return 1; \ +-} +- +-#define m_poppler_get_GOOSTRING(in, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- GooString *gs; \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- gs = (GooString *)((in *) uin->d)->function(); \ +- if (gs != NULL) \ +- lua_pushlstring(L, gs->getCString(), gs->getLength()); \ +- else \ +- lua_pushnil(L); \ +- return 1; \ +-} +- +-#define m_poppler_get_OBJECT(in, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- udstruct *uin, *uout; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- uout = new_Object_userdata(L); \ +- uout->d = new Object(); \ +- *((Object *)uout->d) = ((in *) uin->d)->function(); \ +- uout->atype = ALLOC_LEPDF; \ +- uout->pc = uin->pc; \ +- uout->pd = uin->pd; \ +- return 1; \ +-} +- +-#define m_poppler_do(in, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- ((in *) uin->d)->function(); \ +- return 0; \ +-} +- +-#define m_poppler__tostring(type) \ +-static int m_##type##__tostring(lua_State * L) \ +-{ \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##type); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- lua_pushfstring(L, "%s: %p", #type, (type *) uin->d); \ +- return 1; \ +-} +- +-#define m_poppler_check_string(in, function) \ +-static int m_##in##_##function(lua_State * L) \ +-{ \ +- const char *s; \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_##in); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- s = luaL_checkstring(L, 2); \ +- if (((in *) uin->d)->function(s)) \ +- lua_pushboolean(L, 1); \ +- else \ +- lua_pushboolean(L, 0); \ +- return 1; \ +-} +- +-//********************************************************************** +-// Annot +- +-m_poppler_get_BOOL(Annot, isOk); +- +-static int m_Annot_match(lua_State * L) +-{ +- udstruct *uin, *uref; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Annot); +- uref = (udstruct *) luaL_checkudata(L, 2, M_Ref); +- if (uin->pd != NULL && uref->pd != NULL && uin->pd != uref->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (uref->pd != NULL && uref->pd->pc != uref->pc)) +- pdfdoc_changed_error(L); +- lua_pushboolean(L, ((Annot *) uin->d)->match((Ref *) uref->d)); +- return 1; +-} +- +-m_poppler__tostring(Annot); +- +-static int m_Annot__gc(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Annot); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +-#ifdef DEBUG +- printf("\n===== Annot GC ===== uin=<%p>\n", uin); +-#endif +- if (uin->atype == ALLOC_LEPDF) +-#if 1 /* def HAVE_ANNOTDECREFCNT */ +- ((Annot *) uin->d)->decRefCnt(); +-#else +- delete(Annot *) uin->d; +-#endif +- return 0; +-} +- +-static const struct luaL_Reg Annot_m[] = { +- {"isOk", m_Annot_isOk}, +- {"match", m_Annot_match}, +- {"__tostring", m_Annot__tostring}, +- {"__gc", m_Annot__gc}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Annots +- +-m_poppler_get_INT(Annots, getNumAnnots); +- +-static int m_Annots_getAnnot(lua_State * L) +-{ +- int i, annots; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Annots); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- annots = ((Annots *) uin->d)->getNumAnnots(); +- if (i > 0 && i <= annots) { +- uout = new_Annot_userdata(L); +- uout->d = ((Annots *) uin->d)->getAnnot(i); +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-m_poppler__tostring(Annots); +- +-static const struct luaL_Reg Annots_m[] = { +- {"getNumAnnots", m_Annots_getNumAnnots}, +- {"getAnnot", m_Annots_getAnnot}, +- {"__tostring", m_Annots__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Array +-// Now private +-// static int m_Array_incRef(lua_State * L) +-// { +-// udstruct *uin; +-// uin = (udstruct *) luaL_checkudata(L, 1, M_Array); +-// if (uin->pd != NULL && uin->pd->pc != uin->pc) +-// pdfdoc_changed_error(L); +-// lua_pushinteger(L, 1); +-// return 1; +-// } +-// Now private +-// static int m_Array_decRef(lua_State * L) +-// { +-// int i; +-// udstruct *uin; +-// uin = (udstruct *) luaL_checkudata(L, 1, M_Array); +-// if (uin->pd != NULL && uin->pd->pc != uin->pc) +-// pdfdoc_changed_error(L); +-// lua_pushinteger(L, 1); +-// return 1; +-// } +- +-m_poppler_get_INT(Array, getLength); +- +-static int m_Array_add(lua_State * L) +-{ +- udstruct *uin, *uobj; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Array); +- uobj = (udstruct *) luaL_checkudata(L, 2, M_Object); +- if (uin->pd != NULL && uobj->pd != NULL && uin->pd != uobj->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (uobj->pd != NULL && uobj->pd->pc != uobj->pc)) +- pdfdoc_changed_error(L); +- ((Array *) uin->d)->add(std::move(*((Object *) uobj->d))); +- return 0; +-} +- +-static int m_Array_get(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Array); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- len = ((Array *) uin->d)->getLength(); +- if (i > 0 && i <= len) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Array *) uin->d)->get(i - 1); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Array_getNF(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Array); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- len = ((Array *) uin->d)->getLength(); +- if (i > 0 && i <= len) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Array *) uin->d)->getNF(i - 1); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Array_getString(lua_State * L) +-{ +- GooString *gs; +- int i, len; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Array); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- len = ((Array *) uin->d)->getLength(); +- if (i > 0 && i <= len) { +- gs = new GooString(); +- if (((Array *) uin->d)->getString(i - 1, gs)) +- lua_pushlstring(L, gs->getCString(), gs->getLength()); +- else +- lua_pushnil(L); +- delete gs; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-m_poppler__tostring(Array); +- +-static const struct luaL_Reg Array_m[] = { +- // {"incRef", m_Array_incRef},// Now private +- // {"decRef", m_Array_decRef},// Now private +- {"getLength", m_Array_getLength}, +- {"add", m_Array_add}, +- {"get", m_Array_get}, +- {"getNF", m_Array_getNF}, +- {"getString", m_Array_getString}, +- {"__tostring", m_Array__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Catalog +- +-m_poppler_get_BOOL(Catalog, isOk); +-m_poppler_get_INT(Catalog, getNumPages); +- +-static int m_Catalog_getPage(lua_State * L) +-{ +- int i, pages; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Catalog); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- pages = ((Catalog *) uin->d)->getNumPages(); +- if (i > 0 && i <= pages) { +- uout = new_Page_userdata(L); +- uout->d = ((Catalog *) uin->d)->getPage(i); +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Catalog_getPageRef(lua_State * L) +-{ +- int i, pages; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Catalog); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- pages = ((Catalog *) uin->d)->getNumPages(); +- if (i > 0 && i <= pages) { +- uout = new_Ref_userdata(L); +- uout->d = (Ref *) gmalloc(sizeof(Ref)); +- ((Ref *) uout->d)->num = ((Catalog *) uin->d)->getPageRef(i)->num; +- ((Ref *) uout->d)->gen = ((Catalog *) uin->d)->getPageRef(i)->gen; +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-m_poppler_get_GOOSTRING(Catalog, getBaseURI); +-m_poppler_get_GOOSTRING(Catalog, readMetadata); +-m_poppler_get_poppler(Catalog, StructTreeRoot, getStructTreeRoot); +- +-static int m_Catalog_findPage(lua_State * L) +-{ +- int num, gen, i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Catalog); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- num = luaL_checkint(L, 2); +- gen = luaL_checkint(L, 3); +- i = ((Catalog *) uin->d)->findPage(num, gen); +- if (i > 0) +- lua_pushinteger(L, i); +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Catalog_findDest(lua_State * L) +-{ +- GooString *name; +- LinkDest *dest; +- const char *s; +- size_t len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Catalog); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checklstring(L, 2, &len); +- name = new GooString(s, len); +- dest = ((Catalog *) uin->d)->findDest(name); +- if (dest != NULL) { +- uout = new_LinkDest_userdata(L); +- uout->d = dest; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- delete name; +- return 1; +-} +- +-m_poppler_get_poppler(Catalog, Object, getDests); +-m_poppler_get_INT(Catalog, numEmbeddedFiles); +- +-static int m_Catalog_embeddedFile(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Catalog); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- len = ((Catalog *) uin->d)->numEmbeddedFiles(); +- if (i > 0 && i <= len) { +- uout = new_FileSpec_userdata(L); +- uout->d = ((Catalog *) uin->d)->embeddedFile(i - 1); +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-m_poppler_get_INT(Catalog, numJS); +- +-static int m_Catalog_getJS(lua_State * L) +-{ +- GooString *gs; +- int i, len; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Catalog); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- len = ((Catalog *) uin->d)->numJS(); +- if (i > 0 && i <= len) { +- gs = ((Catalog *) uin->d)->getJS(i - 1); +- if (gs != NULL) +- lua_pushlstring(L, gs->getCString(), gs->getLength()); +- else +- lua_pushnil(L); +- delete gs; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-m_poppler_get_poppler(Catalog, Object, getOutline); +-m_poppler_get_poppler(Catalog, Object, getAcroForm); +- +-m_poppler__tostring(Catalog); +- +-static const struct luaL_Reg Catalog_m[] = { +- {"isOk", m_Catalog_isOk}, +- {"getNumPages", m_Catalog_getNumPages}, +- {"getPage", m_Catalog_getPage}, +- {"getPageRef", m_Catalog_getPageRef}, +- {"getBaseURI", m_Catalog_getBaseURI}, +- {"readMetadata", m_Catalog_readMetadata}, +- {"getStructTreeRoot", m_Catalog_getStructTreeRoot}, +- {"findPage", m_Catalog_findPage}, +- {"findDest", m_Catalog_findDest}, +- {"getDests", m_Catalog_getDests}, +- {"numEmbeddedFiles", m_Catalog_numEmbeddedFiles}, +- {"embeddedFile", m_Catalog_embeddedFile}, +- {"numJS", m_Catalog_numJS}, +- {"getJS", m_Catalog_getJS}, +- {"getOutline", m_Catalog_getOutline}, +- {"getAcroForm", m_Catalog_getAcroForm}, +- {"__tostring", m_Catalog__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Dict +-// Now private +-// static int m_Dict_incRef(lua_State * L) +-// { +-// udstruct *uin; +-// uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +-// if (uin->pd != NULL && uin->pd->pc != uin->pc) +-// pdfdoc_changed_error(L); +-// lua_pushinteger(L, 1); +-// return 1; +-// } +-// Now private +-// static int m_Dict_decRef(lua_State * L) +-// { +-// udstruct *uin; +-// uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +-// if (uin->pd != NULL && uin->pd->pc != uin->pc) +-// pdfdoc_changed_error(L); +-// lua_pushinteger(L, 1); +-// return 1; +-// } +- +-m_poppler_get_INT(Dict, getLength); +- +-static int m_Dict_add(lua_State * L) +-{ +- char *s; +- udstruct *uin, *uobj; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = copyString(luaL_checkstring(L, 2)); +- uobj = (udstruct *) luaL_checkudata(L, 3, M_Object); +- ((Dict *) uin->d)->add(s, std::move(*((Object *) uobj->d))); +- return 0; +-} +- +-static int m_Dict_set(lua_State * L) +-{ +- const char *s; +- udstruct *uin, *uobj; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- uobj = (udstruct *) luaL_checkudata(L, 3, M_Object); +- ((Dict *) uin->d)->set(s, std::move(*((Object *) uobj->d))); +- return 0; +-} +- +-static int m_Dict_remove(lua_State * L) +-{ +- const char *s; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- ((Dict *) uin->d)->remove(s); +- return 0; +-} +- +-m_poppler_check_string(Dict, is); +- +-static int m_Dict_lookup(lua_State * L) +-{ +- const char *s; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Dict *) uin->d)->lookup(s); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +-static int m_Dict_lookupNF(lua_State * L) +-{ +- const char *s; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Dict *) uin->d)->lookupNF(s); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +-static int m_Dict_lookupInt(lua_State * L) +-{ +- const char *s1, *s2; +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s1 = luaL_checkstring(L, 2); +- s2 = luaL_checkstring(L, 3); +- if (((Dict *) uin->d)->lookupInt(s1, s2, &i)) +- lua_pushinteger(L, i); +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Dict_getKey(lua_State * L) +-{ +- int i, len; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- len = ((Dict *) uin->d)->getLength(); +- if (i > 0 && i <= len) +- lua_pushstring(L, ((Dict *) uin->d)->getKey(i - 1)); +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Dict_getVal(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- len = ((Dict *) uin->d)->getLength(); +- if (i > 0 && i <= len) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Dict *) uin->d)->getVal(i - 1); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Dict_getValNF(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Dict); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- len = ((Dict *) uin->d)->getLength(); +- if (i > 0 && i <= len) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Dict *) uin->d)->getValNF(i - 1); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-m_poppler_check_string(Dict, hasKey); +- +-m_poppler__tostring(Dict); +- +-static const struct luaL_Reg Dict_m[] = { +- // {"incRef", m_Dict_incRef},// Now private +- // {"decRef", m_Dict_decRef},// Now private +- {"getLength", m_Dict_getLength}, +- {"add", m_Dict_add}, +- {"set", m_Dict_set}, +- {"remove", m_Dict_remove}, +- {"is", m_Dict_is}, +- {"lookup", m_Dict_lookup}, +- {"lookupNF", m_Dict_lookupNF}, +- {"lookupInt", m_Dict_lookupInt}, +- {"getKey", m_Dict_getKey}, +- {"getVal", m_Dict_getVal}, +- {"getValNF", m_Dict_getValNF}, +- {"hasKey", m_Dict_hasKey}, +- {"__tostring", m_Dict__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// EmbFile +- +-m_poppler_get_INT(EmbFile, size); +-m_poppler_get_GOOSTRING(EmbFile, modDate); +-m_poppler_get_GOOSTRING(EmbFile, createDate); +-m_poppler_get_GOOSTRING(EmbFile, checksum); +-m_poppler_get_GOOSTRING(EmbFile, mimeType); +- +-m_poppler_get_BOOL(EmbFile, isOk); +- +-static int m_EmbFile_save(lua_State * L) +-{ +- const char *s; +- size_t len; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_EmbFile); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checklstring(L, 2, &len); +- if (((EmbFile *) uin->d)->save(s)) +- lua_pushboolean(L, 1); +- else +- lua_pushboolean(L, 0); +- return 1; +-} +- +-m_poppler__tostring(EmbFile); +- +-static const struct luaL_Reg EmbFile_m[] = { +- {"size", m_EmbFile_size}, +- {"modDate", m_EmbFile_modDate}, +- {"createDate", m_EmbFile_createDate}, +- {"checksum", m_EmbFile_checksum}, +- {"mimeType", m_EmbFile_mimeType}, +- {"isOk", m_EmbFile_isOk}, +- {"save", m_EmbFile_save}, +- {"__tostring", m_EmbFile__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// FileSpec +- +-m_poppler_get_BOOL(FileSpec, isOk); +-m_poppler_get_GOOSTRING(FileSpec, getFileName); +-m_poppler_get_GOOSTRING(FileSpec, getFileNameForPlatform); +-m_poppler_get_GOOSTRING(FileSpec, getDescription); +- +-static int m_FileSpec_getEmbeddedFile(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_FileSpec); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- uout = new_EmbFile_userdata(L); +- uout->d = ((FileSpec *) uin->d)->getEmbeddedFile(); +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +-m_poppler__tostring(FileSpec); +- +-static const struct luaL_Reg FileSpec_m[] = { +- {"isOk", m_FileSpec_isOk}, +- {"getFileName", m_FileSpec_getFileName}, +- {"getFileNameForPlatform", m_FileSpec_getFileNameForPlatform}, +- {"getDescription", m_FileSpec_getDescription}, +- {"getEmbeddedFile", m_FileSpec_getEmbeddedFile}, +- {"__tostring", m_FileSpec__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// GooString +- +-static int m_GooString__tostring(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_GooString); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- lua_pushlstring(L, ((GooString *) uin->d)->getCString(), +- ((GooString *) uin->d)->getLength()); +- return 1; +-} +- +-static const struct luaL_Reg GooString_m[] = { +- {"__tostring", m_GooString__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// LinkDest +- +-static const char *LinkDestKindNames[] = +- { "XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV", NULL }; +- +-m_poppler_get_BOOL(LinkDest, isOk); +- +-static int m_LinkDest_getKind(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_LinkDest); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (int) ((LinkDest *) uin->d)->getKind(); +- lua_pushinteger(L, i); +- return 1; +-} +- +-static int m_LinkDest_getKindName(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_LinkDest); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (int) ((LinkDest *) uin->d)->getKind(); +- lua_pushstring(L, LinkDestKindNames[i]); +- return 1; +-} +- +-m_poppler_get_BOOL(LinkDest, isPageRef); +-m_poppler_get_INT(LinkDest, getPageNum); +- +-static int m_LinkDest_getPageRef(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_LinkDest); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- uout = new_Ref_userdata(L); +- uout->d = (Ref *) gmalloc(sizeof(Ref)); +- ((Ref *) uout->d)->num = ((LinkDest *) uin->d)->getPageRef().num; +- ((Ref *) uout->d)->gen = ((LinkDest *) uin->d)->getPageRef().gen; +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +-m_poppler_get_DOUBLE(LinkDest, getLeft); +-m_poppler_get_DOUBLE(LinkDest, getBottom); +-m_poppler_get_DOUBLE(LinkDest, getRight); +-m_poppler_get_DOUBLE(LinkDest, getTop); +-m_poppler_get_DOUBLE(LinkDest, getZoom); +-m_poppler_get_BOOL(LinkDest, getChangeLeft); +-m_poppler_get_BOOL(LinkDest, getChangeTop); +-m_poppler_get_BOOL(LinkDest, getChangeZoom); +- +-m_poppler__tostring(LinkDest); +- +-static const struct luaL_Reg LinkDest_m[] = { +- {"isOk", m_LinkDest_isOk}, +- {"getKind", m_LinkDest_getKind}, +- {"getKindName", m_LinkDest_getKindName}, // not poppler +- {"isPageRef", m_LinkDest_isPageRef}, +- {"getPageNum", m_LinkDest_getPageNum}, +- {"getPageRef", m_LinkDest_getPageRef}, +- {"getLeft", m_LinkDest_getLeft}, +- {"getBottom", m_LinkDest_getBottom}, +- {"getRight", m_LinkDest_getRight}, +- {"getTop", m_LinkDest_getTop}, +- {"getZoom", m_LinkDest_getZoom}, +- {"getChangeLeft", m_LinkDest_getChangeLeft}, +- {"getChangeTop", m_LinkDest_getChangeTop}, +- {"getChangeZoom", m_LinkDest_getChangeZoom}, +- {"__tostring", m_LinkDest__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Links +- +-m_poppler_get_INT(Links, getNumLinks); +- +-m_poppler__tostring(Links); +- +-static const struct luaL_Reg Links_m[] = { +- {"getNumLinks", m_Links_getNumLinks}, +- //{"getLink", m_Links_getLink}, +- {"__tostring", m_Links__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Object +- +-#ifdef HAVE_OBJECT_INITCMD_CONST_CHARP +-#define CHARP_CAST +-#else +-// must cast arg of Object::initCmd, Object::isStream, and Object::streamIs +-// from 'const char *' to 'char *', although they are not modified. +-#define CHARP_CAST (char *) +-#endif +- +-// Special type checking. +-#define m_Object_isType_(function, cast) \ +-static int m_Object_##function(lua_State * L) \ +-{ \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- if (lua_gettop(L) >= 2) { \ +- if (lua_isstring(L, 2) \ +- && ((Object *) uin->d)->function(cast lua_tostring(L, 2))) \ +- lua_pushboolean(L, 1); \ +- else \ +- lua_pushboolean(L, 0); \ +- } else { \ +- if (((Object *) uin->d)->function()) \ +- lua_pushboolean(L, 1); \ +- else \ +- lua_pushboolean(L, 0); \ +- } \ +- return 1; \ +-} +-#define m_Object_isType(function) m_Object_isType_(function, ) +-#define m_Object_isType_nonconst(function) m_Object_isType_(function, CHARP_CAST) +- +-static int m_Object_initBool(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- luaL_checktype(L, 2, LUA_TBOOLEAN); +- if (lua_toboolean(L, 2) != 0) +- *((Object *) uin->d) = Object(gTrue); +- else +- *((Object *) uin->d) = Object(gFalse); +- return 0; +-} +- +-static int m_Object_initInt(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- *((Object *) uin->d) = Object(i); +- return 0; +-} +- +-static int m_Object_initReal(lua_State * L) +-{ +- double d; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- d = luaL_checknumber(L, 2); +- *((Object *) uin->d) = Object(d); +- return 0; +-} +- +-static int m_Object_initString(lua_State * L) +-{ +- GooString *gs; +- const char *s; +- size_t len; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checklstring(L, 2, &len); +- gs = new GooString(s, len); +- *((Object *) uin->d) = Object(gs); +- return 0; +-} +- +-static int m_Object_initName(lua_State * L) +-{ +- const char *s; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- *((Object *) uin->d) = Object(objName, s); +- return 0; +-} +- +-static int m_Object_initNull(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- *((Object *) uin->d) = Object(objNull); +- return 0; +-} +- +-static int m_Object_initArray(lua_State * L) +-{ +- udstruct *uin, *uxref; +- Array *a; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- uxref = (udstruct *) luaL_checkudata(L, 2, M_XRef); +- if (uin->pd != NULL && uxref->pd != NULL && uin->pd != uxref->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (uxref->pd != NULL && uxref->pd->pc != uxref->pc)) +- pdfdoc_changed_error(L); +- a = new Array((XRef *) uxref->d); +- *((Object *) uin->d) = Object(a); +- return 0; +-} +- +-// TODO: decide betweeen +-// Object *initDict(XRef *xref); +-// Object *initDict(Dict *dictA); +- +-static int m_Object_initDict(lua_State * L) +-{ +- udstruct *uin, *uxref; +- Dict *d; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- uxref = (udstruct *) luaL_checkudata(L, 2, M_XRef); +- if (uin->pd != NULL && uxref->pd != NULL && uin->pd != uxref->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (uxref->pd != NULL && uxref->pd->pc != uxref->pc)) +- pdfdoc_changed_error(L); +- d = new Dict((XRef *) uxref->d); +- *((Object *) uin->d) = Object(d); +- return 0; +-} +- +-static int m_Object_initStream(lua_State * L) +-{ +- udstruct *uin, *ustream; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- ustream = (udstruct *) luaL_checkudata(L, 2, M_Stream); +- if (uin->pd != NULL && ustream->pd != NULL && uin->pd != ustream->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (ustream->pd != NULL && ustream->pd->pc != ustream->pc)) +- pdfdoc_changed_error(L); +- *((Object *) uin->d) = Object((Stream *) ustream->d); +- return 0; +-} +- +-static int m_Object_initRef(lua_State * L) +-{ +- int num, gen; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- num = luaL_checkint(L, 2); +- gen = luaL_checkint(L, 3); +- *((Object *) uin->d) = Object(num, gen); +- return 0; +-} +- +-static int m_Object_initCmd(lua_State * L) +-{ +- const char *s; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- *((Object *) uin->d) = Object(objCmd, s); +- return 0; +-} +- +-static int m_Object_initError(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- *((Object *) uin->d) = Object(objError); +- return 0; +-} +- +-static int m_Object_initEOF(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- *((Object *) uin->d) = Object(objEOF); +- return 0; +-} +- +-static int m_Object_fetch(lua_State * L) +-{ +- udstruct *uin, *uxref, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- uxref = (udstruct *) luaL_checkudata(L, 2, M_XRef); +- if (uin->pd != NULL && uxref->pd != NULL && uin->pd != uxref->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (uxref->pd != NULL && uxref->pd->pc != uxref->pc)) +- pdfdoc_changed_error(L); +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Object *) uin->d)->fetch((XRef *) uxref->d); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +-static int m_Object_getType(lua_State * L) +-{ +- ObjType t; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- t = ((Object *) uin->d)->getType(); +- lua_pushinteger(L, (int) t); +- return 1; +-} +- +-static int m_Object_getTypeName(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- lua_pushstring(L, ((Object *) uin->d)->getTypeName()); +- return 1; +-} +- +-m_poppler_get_BOOL(Object, isBool); +-m_poppler_get_BOOL(Object, isInt); +-m_poppler_get_BOOL(Object, isReal); +-m_poppler_get_BOOL(Object, isNum); +-m_poppler_get_BOOL(Object, isString); +-m_Object_isType(isName); +-m_poppler_get_BOOL(Object, isNull); +-m_poppler_get_BOOL(Object, isArray); +-m_Object_isType(isDict); +-m_Object_isType_nonconst(isStream); +-m_poppler_get_BOOL(Object, isRef); +-m_Object_isType(isCmd); +-m_poppler_get_BOOL(Object, isError); +-m_poppler_get_BOOL(Object, isEOF); +-m_poppler_get_BOOL(Object, isNone); +- +-static int m_Object_getBool(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isBool()) { +- if (((Object *) uin->d)->getBool()) +- lua_pushboolean(L, 1); +- else +- lua_pushboolean(L, 0); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getInt(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isInt()) +- lua_pushinteger(L, ((Object *) uin->d)->getInt()); +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getReal(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isReal()) +- lua_pushnumber(L, ((Object *) uin->d)->getReal()); /* float */ +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getNum(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isInt()) +- lua_pushinteger(L, ((Object *) uin->d)->getInt()); +- else if (((Object *) uin->d)->isReal()) +- lua_pushinteger(L, ((Object *) uin->d)->getReal()); +- else if (((Object *) uin->d)->isNum()) /* redundant */ +- lua_pushnumber(L, ((Object *) uin->d)->getNum()); /* integer or float */ +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getString(lua_State * L) +-{ +- GooString *gs; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isString()) { +- gs = (GooString *)((Object *) uin->d)->getString(); +- lua_pushlstring(L, gs->getCString(), gs->getLength()); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getName(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isName()) +- lua_pushstring(L, ((Object *) uin->d)->getName()); +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getArray(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isArray()) { +- uout = new_Array_userdata(L); +- uout->d = ((Object *) uin->d)->getArray(); +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getDict(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isDict()) { +- uout = new_Dict_userdata(L); +- uout->d = ((Object *) uin->d)->getDict(); +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getStream(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isStream()) { +- uout = new_Stream_userdata(L); +- uout->d = ((Object *) uin->d)->getStream(); +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getRef(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isRef()) { +- uout = new_Ref_userdata(L); +- uout->d = (Ref *) gmalloc(sizeof(Ref)); +- ((Ref *) uout->d)->num = ((Object *) uin->d)->getRef().num; +- ((Ref *) uout->d)->gen = ((Object *) uin->d)->getRef().gen; +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getRefNum(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isRef()) { +- i = ((Object *) uin->d)->getRef().num; +- lua_pushinteger(L, i); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getRefGen(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isRef()) { +- i = ((Object *) uin->d)->getRef().gen; +- lua_pushinteger(L, i); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_getCmd(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isCmd()) +- lua_pushstring(L, ((Object *) uin->d)->getCmd()); +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_arrayGetLength(lua_State * L) +-{ +- int len; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isArray()) { +- len = ((Object *) uin->d)->arrayGetLength(); +- lua_pushinteger(L, len); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_arrayAdd(lua_State * L) +-{ +- udstruct *uin, *uobj; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- uobj = (udstruct *) luaL_checkudata(L, 2, M_Object); +- if (uin->pd != NULL && uobj->pd != NULL && uin->pd != uobj->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (uobj->pd != NULL && uobj->pd->pc != uobj->pd->pc)) +- pdfdoc_changed_error(L); +- if (!((Object *) uin->d)->isArray()) +- luaL_error(L, "Object is not an Array"); +- ((Object *) uin->d)->arrayAdd(std::move(*((Object *) uobj->d))); +- return 0; +-} +- +-static int m_Object_arrayGet(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- if (((Object *) uin->d)->isArray()) { +- len = ((Object *) uin->d)->arrayGetLength(); +- if (i > 0 && i <= len) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Object *) uin->d)->arrayGet(i - 1); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_arrayGetNF(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- if (((Object *) uin->d)->isArray()) { +- len = ((Object *) uin->d)->arrayGetLength(); +- if (i > 0 && i <= len) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Object *) uin->d)->arrayGetNF(i - 1); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_dictGetLength(lua_State * L) +-{ +- int len; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isDict()) { +- len = ((Object *) uin->d)->dictGetLength(); +- lua_pushinteger(L, len); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_dictAdd(lua_State * L) +-{ +- const char *s; +- udstruct *uin, *uobj; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- s = luaL_checkstring(L, 2); +- uobj = (udstruct *) luaL_checkudata(L, 3, M_Object); +- if (uin->pd != NULL && uobj->pd != NULL && uin->pd != uobj->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (uobj->pd != NULL && uobj->pd->pc != uobj->pd->pc)) +- pdfdoc_changed_error(L); +- if (!((Object *) uin->d)->isDict()) +- luaL_error(L, "Object is not a Dict"); +- ((Object *) uin->d)->dictAdd(copyString(s), std::move(*((Object *) uobj->d))); +- return 0; +-} +- +-static int m_Object_dictSet(lua_State * L) +-{ +- const char *s; +- udstruct *uin, *uobj; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- s = luaL_checkstring(L, 2); +- uobj = (udstruct *) luaL_checkudata(L, 3, M_Object); +- if (uin->pd != NULL && uobj->pd != NULL && uin->pd != uobj->pd) +- pdfdoc_differs_error(L); +- if ((uin->pd != NULL && uin->pd->pc != uin->pc) +- || (uobj->pd != NULL && uobj->pd->pc != uobj->pd->pc)) +- pdfdoc_changed_error(L); +- if (!((Object *) uin->d)->isDict()) +- luaL_error(L, "Object is not a Dict"); +- ((Object *) uin->d)->dictSet(s, std::move(*((Object *) uobj->d))); +- return 0; +-} +- +-static int m_Object_dictLookup(lua_State * L) +-{ +- const char *s; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- if (((Object *) uin->d)->isDict()) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Object *) uin->d)->dictLookup(s); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_dictLookupNF(lua_State * L) +-{ +- const char *s; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- if (((Object *) uin->d)->isDict()) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Object *) uin->d)->dictLookupNF(s); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_dictGetKey(lua_State * L) +-{ +- int i, len; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- if (((Object *) uin->d)->isDict()) { +- len = ((Object *) uin->d)->dictGetLength(); +- if (i > 0 && i <= len) +- lua_pushstring(L, ((Object *) uin->d)->dictGetKey(i - 1)); +- else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_dictGetVal(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- if (((Object *) uin->d)->isDict()) { +- len = ((Object *) uin->d)->dictGetLength(); +- if (i > 0 && i <= len) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Object *) uin->d)->dictGetVal(i - 1); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_dictGetValNF(lua_State * L) +-{ +- int i, len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- if (((Object *) uin->d)->isDict()) { +- len = ((Object *) uin->d)->dictGetLength(); +- if (i > 0 && i <= len) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((Object *) uin->d)->dictGetValNF(i - 1); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_streamIs(lua_State * L) +-{ +- const char *s; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- if (((Object *) uin->d)->isStream()) { +- if (((Object *) uin->d)->streamIs(CHARP_CAST s)) +- lua_pushboolean(L, 1); +- else +- lua_pushboolean(L, 0); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_streamReset(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isStream()) +- ((Object *) uin->d)->streamReset(); +- return 0; +-} +- +-static int m_Object_streamGetChar(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isStream()) { +- i = ((Object *) uin->d)->streamGetChar(); +- lua_pushinteger(L, i); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_streamLookChar(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isStream()) { +- i = ((Object *) uin->d)->streamLookChar(); +- lua_pushinteger(L, i); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_streamGetPos(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isStream()) { +- i = (int) ((Object *) uin->d)->streamGetPos(); +- lua_pushinteger(L, i); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object_streamSetPos(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- if (((Object *) uin->d)->isStream()) +- ((Object *) uin->d)->streamSetPos(i); +- return 0; +-} +- +-static int m_Object_streamGetDict(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((Object *) uin->d)->isStream()) { +- uout = new_Dict_userdata(L); +- uout->d = ((Object *) uin->d)->streamGetDict(); +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_Object__gc(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +-#ifdef DEBUG +- printf("\n===== Object GC ===== uin=<%p>\n", uin); +-#endif +- if (uin->atype == ALLOC_LEPDF) { +- // free() seems to collide with the lua gc +- //((Object *) uin->d)->free(); +- delete(Object *) uin->d; +- } +- return 0; +-} +- +-m_poppler__tostring(Object); +- +-static const struct luaL_Reg Object_m[] = { +- {"initBool", m_Object_initBool}, +- {"initInt", m_Object_initInt}, +- {"initReal", m_Object_initReal}, +- {"initString", m_Object_initString}, +- {"initName", m_Object_initName}, +- {"initNull", m_Object_initNull}, +- {"initArray", m_Object_initArray}, +- {"initDict", m_Object_initDict}, +- {"initStream", m_Object_initStream}, +- {"initRef", m_Object_initRef}, +- {"initCmd", m_Object_initCmd}, +- {"initError", m_Object_initError}, +- {"initEOF", m_Object_initEOF}, +- // {"copy", m_Object_copy}, +- {"fetch", m_Object_fetch}, +- {"getType", m_Object_getType}, +- {"getTypeName", m_Object_getTypeName}, +- {"isBool", m_Object_isBool}, +- {"isInt", m_Object_isInt}, +- {"isReal", m_Object_isReal}, +- {"isNum", m_Object_isNum}, +- {"isString", m_Object_isString}, +- {"isName", m_Object_isName}, +- {"isNull", m_Object_isNull}, +- {"isArray", m_Object_isArray}, +- {"isDict", m_Object_isDict}, +- {"isStream", m_Object_isStream}, +- {"isRef", m_Object_isRef}, +- {"isCmd", m_Object_isCmd}, +- {"isError", m_Object_isError}, +- {"isEOF", m_Object_isEOF}, +- {"isNone", m_Object_isNone}, +- {"getBool", m_Object_getBool}, +- {"getInt", m_Object_getInt}, +- {"getReal", m_Object_getReal}, +- {"getNum", m_Object_getNum}, +- {"getString", m_Object_getString}, +- {"getName", m_Object_getName}, +- {"getArray", m_Object_getArray}, +- {"getDict", m_Object_getDict}, +- {"getStream", m_Object_getStream}, +- {"getRef", m_Object_getRef}, +- {"getRefNum", m_Object_getRefNum}, +- {"getRefGen", m_Object_getRefGen}, +- {"getCmd", m_Object_getCmd}, +- {"arrayGetLength", m_Object_arrayGetLength}, +- {"arrayAdd", m_Object_arrayAdd}, +- {"arrayGet", m_Object_arrayGet}, +- {"arrayGetNF", m_Object_arrayGetNF}, +- {"dictGetLength", m_Object_dictGetLength}, +- {"dictAdd", m_Object_dictAdd}, +- {"dictSet", m_Object_dictSet}, +- {"dictLookup", m_Object_dictLookup}, +- {"dictLookupNF", m_Object_dictLookupNF}, +- {"dictGetKey", m_Object_dictGetKey}, +- {"dictGetVal", m_Object_dictGetVal}, +- {"dictGetValNF", m_Object_dictGetValNF}, +- {"streamIs", m_Object_streamIs}, +- {"streamReset", m_Object_streamReset}, +- // {"streamClose", m_Object_streamClose}, +- {"streamGetChar", m_Object_streamGetChar}, +- {"streamLookChar", m_Object_streamLookChar}, +- // {"streamGetLine", m_Object_streamGetLine}, +- {"streamGetPos", m_Object_streamGetPos}, +- {"streamSetPos", m_Object_streamSetPos}, +- {"streamGetDict", m_Object_streamGetDict}, +- {"__tostring", m_Object__tostring}, +- {"__gc", m_Object__gc}, // finalizer +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Page +- +-m_poppler_get_BOOL(Page, isOk); +-m_poppler_get_INT(Page, getNum); +-m_poppler_get_poppler(Page, PDFRectangle, getMediaBox); +-m_poppler_get_poppler(Page, PDFRectangle, getCropBox); +-m_poppler_get_BOOL(Page, isCropped); +-m_poppler_get_DOUBLE(Page, getMediaWidth); +-m_poppler_get_DOUBLE(Page, getMediaHeight); +-m_poppler_get_DOUBLE(Page, getCropWidth); +-m_poppler_get_DOUBLE(Page, getCropHeight); +-m_poppler_get_poppler(Page, PDFRectangle, getBleedBox); +-m_poppler_get_poppler(Page, PDFRectangle, getTrimBox); +-m_poppler_get_poppler(Page, PDFRectangle, getArtBox); +-m_poppler_get_INT(Page, getRotate); +-m_poppler_get_GOOSTRING(Page, getLastModified); +-m_poppler_get_poppler(Page, Dict, getBoxColorInfo); +-m_poppler_get_poppler(Page, Dict, getGroup); +-m_poppler_get_poppler(Page, Stream, getMetadata); +-m_poppler_get_poppler(Page, Dict, getPieceInfo); +-m_poppler_get_poppler(Page, Dict, getSeparationInfo); +-m_poppler_get_poppler(Page, Dict, getResourceDict); +-m_poppler_get_OBJECT(Page, getAnnotsObject); +- +-m_poppler_get_OBJECT(Page, getContents); +- +-m_poppler__tostring(Page); +- +-static const struct luaL_Reg Page_m[] = { +- {"isOk", m_Page_isOk}, +- {"getNum", m_Page_getNum}, +- {"getMediaBox", m_Page_getMediaBox}, +- {"getCropBox", m_Page_getCropBox}, +- {"isCropped", m_Page_isCropped}, +- {"getMediaWidth", m_Page_getMediaWidth}, +- {"getMediaHeight", m_Page_getMediaHeight}, +- {"getCropWidth", m_Page_getCropWidth}, +- {"getCropHeight", m_Page_getCropHeight}, +- {"getBleedBox", m_Page_getBleedBox}, +- {"getTrimBox", m_Page_getTrimBox}, +- {"getArtBox", m_Page_getArtBox}, +- {"getRotate", m_Page_getRotate}, +- {"getLastModified", m_Page_getLastModified}, +- {"getBoxColorInfo", m_Page_getBoxColorInfo}, +- {"getGroup", m_Page_getGroup}, +- {"getMetadata", m_Page_getMetadata}, +- {"getPieceInfo", m_Page_getPieceInfo}, +- {"getSeparationInfo", m_Page_getSeparationInfo}, +- {"getResourceDict", m_Page_getResourceDict}, +- {"getAnnotsObject", m_Page_getAnnotsObject}, +- {"getContents", m_Page_getContents}, +- {"__tostring", m_Page__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// PDFDoc +- +-#define m_PDFDoc_BOOL(function) \ +-static int m_PDFDoc_##function(lua_State * L) \ +-{ \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- if (((PdfDocument *) uin->d)->doc->function()) \ +- lua_pushboolean(L, 1); \ +- else \ +- lua_pushboolean(L, 0); \ +- return 1; \ +-} +- +-#define m_PDFDoc_INT(function) \ +-static int m_PDFDoc_##function(lua_State * L) \ +-{ \ +- int i; \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- i = ((PdfDocument *) uin->d)->doc->function(); \ +- lua_pushinteger(L, i); \ +- return 1; \ +-} +- +-m_PDFDoc_BOOL(isOk); +-m_PDFDoc_INT(getErrorCode); +- +-static int m_PDFDoc_getFileName(lua_State * L) +-{ +- GooString *gs; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- gs = ((PdfDocument *) uin->d)->doc->getFileName(); +- if (gs != NULL) +- lua_pushlstring(L, gs->getCString(), gs->getLength()); +- else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_PDFDoc_getErrorCodeName(lua_State * L) +-{ +- int i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = ((PdfDocument *) uin->d)->doc->getErrorCode(); +- lua_pushstring(L, ErrorCodeNames[i]); +- return 1; +-} +- +-static int m_PDFDoc_getXRef(lua_State * L) +-{ +- XRef *xref; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- xref = ((PdfDocument *) uin->d)->doc->getXRef(); +- if (xref->isOk()) { +- uout = new_XRef_userdata(L); +- uout->d = xref; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_PDFDoc_getCatalog(lua_State * L) +-{ +- Catalog *cat; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- cat = ((PdfDocument *) uin->d)->doc->getCatalog(); +- if (cat->isOk()) { +- uout = new_Catalog_userdata(L); +- uout->d = cat; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-#define m_PDFDoc_PAGEDIMEN(function) \ +-static int m_PDFDoc_##function(lua_State * L) \ +-{ \ +- int i, pages; \ +- double d; \ +- udstruct *uin; \ +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); \ +- if (uin->pd != NULL && uin->pd->pc != uin->pc) \ +- pdfdoc_changed_error(L); \ +- i = luaL_checkint(L, 2); \ +- pages = ((PdfDocument *) uin->d)->doc->getNumPages(); \ +- if (i > 0 && i <= pages) { \ +- d = (double) ((PdfDocument *) uin->d)->doc->function(i); \ +- lua_pushnumber(L, d); /* float */ \ +- } else \ +- lua_pushnil(L); \ +- return 1; \ +-} +- +-m_PDFDoc_PAGEDIMEN(getPageMediaWidth); +-m_PDFDoc_PAGEDIMEN(getPageMediaHeight); +-m_PDFDoc_PAGEDIMEN(getPageCropWidth); +-m_PDFDoc_PAGEDIMEN(getPageCropHeight); +-m_PDFDoc_INT(getNumPages); +- +-static int m_PDFDoc_readMetadata(lua_State * L) +-{ +- GooString *gs; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((PdfDocument *) uin->d)->doc->getCatalog()->isOk()) { +- gs = ((PdfDocument *) uin->d)->doc->readMetadata(); +- if (gs != NULL) +- lua_pushlstring(L, gs->getCString(), gs->getLength()); +- else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_PDFDoc_getStructTreeRoot(lua_State * L) +-{ +- StructTreeRoot *obj; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((PdfDocument *) uin->d)->doc->getCatalog()->isOk()) { +- obj = ((PdfDocument *) uin->d)->doc->getStructTreeRoot(); +- uout = new_StructTreeRoot_userdata(L); +- uout->d = obj; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_PDFDoc_findPage(lua_State * L) +-{ +- int num, gen, i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- num = luaL_checkint(L, 2); +- gen = luaL_checkint(L, 3); +- if (((PdfDocument *) uin->d)->doc->getCatalog()->isOk()) { +- i = ((PdfDocument *) uin->d)->doc->findPage(num, gen); +- if (i > 0) +- lua_pushinteger(L, i); +- else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_PDFDoc_getLinks(lua_State * L) +-{ +- int i, pages; +- Links *links; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = luaL_checkint(L, 2); +- pages = ((PdfDocument *) uin->d)->doc->getNumPages(); +- if (i > 0 && i <= pages) { +- links = ((PdfDocument *) uin->d)->doc->getLinks(i); +- if (links != NULL) { +- uout = new_Links_userdata(L); +- uout->d = links; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_PDFDoc_findDest(lua_State * L) +-{ +- GooString *name; +- LinkDest *dest; +- const char *s; +- size_t len; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checklstring(L, 2, &len); +- name = new GooString(s, len); +- if (((PdfDocument *) uin->d)->doc->getCatalog()->isOk()) { +- dest = ((PdfDocument *) uin->d)->doc->findDest(name); +- if (dest != NULL) { +- uout = new_LinkDest_userdata(L); +- uout->d = dest; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- delete name; +- return 1; +-} +- +-m_PDFDoc_BOOL(isEncrypted); +-m_PDFDoc_BOOL(okToPrint); +-m_PDFDoc_BOOL(okToChange); +-m_PDFDoc_BOOL(okToCopy); +-m_PDFDoc_BOOL(okToAddNotes); +-m_PDFDoc_BOOL(isLinearized); +- +-static int m_PDFDoc_getDocInfo(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((PdfDocument *) uin->d)->doc->getXRef()->isOk()) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((PdfDocument *) uin->d)->doc->getDocInfo(); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_PDFDoc_getDocInfoNF(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- if (((PdfDocument *) uin->d)->doc->getXRef()->isOk()) { +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((PdfDocument *) uin->d)->doc->getDocInfoNF(); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-m_PDFDoc_INT(getPDFMajorVersion); +-m_PDFDoc_INT(getPDFMinorVersion); +- +-m_poppler__tostring(PDFDoc); +- +-static int m_PDFDoc__gc(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFDoc); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +-#ifdef DEBUG +- printf("\n===== PDFDoc GC ===== file_path=<%s>\n", +- ((PdfDocument *) uin->d)->file_path); +-#endif +- assert(uin->atype == ALLOC_LEPDF); +- unrefPdfDocument(((PdfDocument *) uin->d)->file_path); +- return 0; +-} +- +-static const struct luaL_Reg PDFDoc_m[] = { +- {"isOk", m_PDFDoc_isOk}, +- {"getErrorCode", m_PDFDoc_getErrorCode}, +- {"getErrorCodeName", m_PDFDoc_getErrorCodeName}, // not poppler +- {"getFileName", m_PDFDoc_getFileName}, +- {"getXRef", m_PDFDoc_getXRef}, +- {"getCatalog", m_PDFDoc_getCatalog}, +- // {"getBaseStream", m_PDFDoc_getBaseStream}, +- {"getPageMediaWidth", m_PDFDoc_getPageMediaWidth}, +- {"getPageMediaHeight", m_PDFDoc_getPageMediaHeight}, +- {"getPageCropWidth", m_PDFDoc_getPageCropWidth}, +- {"getPageCropHeight", m_PDFDoc_getPageCropHeight}, +- {"getNumPages", m_PDFDoc_getNumPages}, +- {"readMetadata", m_PDFDoc_readMetadata}, +- {"getStructTreeRoot", m_PDFDoc_getStructTreeRoot}, +- {"findPage", m_PDFDoc_findPage}, +- {"getLinks", m_PDFDoc_getLinks}, +- {"findDest", m_PDFDoc_findDest}, +- {"isEncrypted", m_PDFDoc_isEncrypted}, +- {"okToPrint", m_PDFDoc_okToPrint}, +- {"okToChange", m_PDFDoc_okToChange}, +- {"okToCopy", m_PDFDoc_okToCopy}, +- {"okToAddNotes", m_PDFDoc_okToAddNotes}, +- {"isLinearized", m_PDFDoc_isLinearized}, +- {"getDocInfo", m_PDFDoc_getDocInfo}, +- {"getDocInfoNF", m_PDFDoc_getDocInfoNF}, +- {"getPDFMajorVersion", m_PDFDoc_getPDFMajorVersion}, +- {"getPDFMinorVersion", m_PDFDoc_getPDFMinorVersion}, +- {"__tostring", m_PDFDoc__tostring}, +- {"__gc", m_PDFDoc__gc}, // finalizer +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// PDFRectangle +- +-m_poppler_get_BOOL(PDFRectangle, isValid); +- +-m_poppler__tostring(PDFRectangle); +- +-static int m_PDFRectangle__index(lua_State * L) +-{ +- const char *s; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFRectangle); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- if (strlen(s) == 2) { +- if (s[0] == 'x') { +- if (s[1] == '1') +- lua_pushnumber(L, ((PDFRectangle *) uin->d)->x1); /* float */ +- else if (s[1] == '2') +- lua_pushnumber(L, ((PDFRectangle *) uin->d)->x2); /* float */ +- else +- lua_pushnil(L); +- } else if (s[0] == 'y') { +- if (s[1] == '1') +- lua_pushnumber(L, ((PDFRectangle *) uin->d)->y1); /* float */ +- else if (s[1] == '2') +- lua_pushnumber(L, ((PDFRectangle *) uin->d)->y2); /* float */ +- else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_PDFRectangle__newindex(lua_State * L) +-{ +- double d; +- const char *s; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFRectangle); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- d = luaL_checknumber(L, 3); +- if (strlen(s) == 2) { +- if (s[0] == 'x') { +- if (s[1] == '1') +- ((PDFRectangle *) uin->d)->x1 = d; +- else if (s[1] == '2') +- ((PDFRectangle *) uin->d)->x2 = d; +- else +- luaL_error(L, "wrong PDFRectangle coordinate (%s)", s); +- } else if (s[0] == 'y') { +- if (s[1] == '1') +- ((PDFRectangle *) uin->d)->y1 = d; +- else if (s[1] == '2') +- ((PDFRectangle *) uin->d)->y2 = d; +- } else +- luaL_error(L, "wrong PDFRectangle coordinate (%s)", s); +- } else +- luaL_error(L, "wrong PDFRectangle coordinate (%s)", s); +- return 0; +-} +- +-static int m_PDFRectangle__gc(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_PDFRectangle); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +-#ifdef DEBUG +- printf("\n===== PDFRectangle GC ===== uin=<%p>\n", uin); +-#endif +- if (uin->atype == ALLOC_LEPDF) +- delete(PDFRectangle *) uin->d; +- return 0; +-} +- +-static const struct luaL_Reg PDFRectangle_m[] = { +- {"isValid", m_PDFRectangle_isValid}, +- {"__index", m_PDFRectangle__index}, +- {"__newindex", m_PDFRectangle__newindex}, +- {"__tostring", m_PDFRectangle__tostring}, +- {"__gc", m_PDFRectangle__gc}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Ref +- +-static int m_Ref__index(lua_State * L) +-{ +- const char *s; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Ref); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- s = luaL_checkstring(L, 2); +- if (strcmp(s, "num") == 0) +- lua_pushinteger(L, ((Ref *) uin->d)->num); +- else if (strcmp(s, "gen") == 0) +- lua_pushinteger(L, ((Ref *) uin->d)->gen); +- else +- lua_pushnil(L); +- return 1; +-} +- +-m_poppler__tostring(Ref); +- +-static int m_Ref__gc(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Ref); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +-#ifdef DEBUG +- printf("\n===== Ref GC ===== uin=<%p>\n", uin); +-#endif +- if (uin->atype == ALLOC_LEPDF && ((Ref *) uin->d) != NULL) +- gfree(((Ref *) uin->d)); +- return 0; +-} +- +-static const struct luaL_Reg Ref_m[] = { +- {"__index", m_Ref__index}, +- {"__tostring", m_Ref__tostring}, +- {"__gc", m_Ref__gc}, // finalizer +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// Stream +- +-static const char *StreamKindNames[] = +- { "File", "ASCIIHex", "ASCII85", "LZW", "RunLength", "CCITTFax", "DCT", +- "Flate", "JBIG2", "JPX", "Weird", NULL +-}; +- +-m_poppler_get_INT(Stream, getKind); +- +-static int m_Stream_getKindName(lua_State * L) +-{ +- StreamKind t; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Stream); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- t = ((Stream *) uin->d)->getKind(); +- lua_pushstring(L, StreamKindNames[t]); +- return 1; +-} +- +-m_poppler_do(Stream, reset); +-m_poppler_do(Stream, close); +-m_poppler_get_INT(Stream, getChar); +-m_poppler_get_INT(Stream, lookChar); +-m_poppler_get_INT(Stream, getRawChar); +-m_poppler_get_INT(Stream, getUnfilteredChar); +-m_poppler_do(Stream, unfilteredReset); +-m_poppler_get_INT(Stream, getPos); +-m_poppler_get_BOOL(Stream, isBinary); +-m_poppler_get_poppler(Stream, Stream, getUndecodedStream); +-m_poppler_get_poppler(Stream, Dict, getDict); +- +-m_poppler__tostring(Stream); +- +-static const struct luaL_Reg Stream_m[] = { +- {"getKind", m_Stream_getKind}, +- {"getKindName", m_Stream_getKindName}, // not poppler +- {"reset", m_Stream_reset}, +- {"close", m_Stream_close}, +- {"getUndecodedStream", m_Stream_getUndecodedStream}, +- {"getChar", m_Stream_getChar}, +- {"lookChar", m_Stream_lookChar}, +- {"getRawChar", m_Stream_getRawChar}, +- {"getUnfilteredChar", m_Stream_getUnfilteredChar}, +- {"unfilteredReset", m_Stream_unfilteredReset}, +- // {"getLine", m_Stream_getLine}, +- {"getPos", m_Stream_getPos}, +- {"isBinary", m_Stream_isBinary}, +- {"getUndecodedStream", m_Stream_getUndecodedStream}, +- {"getDict", m_Stream_getDict}, +- {"__tostring", m_Stream__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// TextSpan +- +-m_poppler_get_GOOSTRING(TextSpan, getText); +-m_poppler__tostring(TextSpan); +- +-static const struct luaL_Reg TextSpan_m[] = { +- {"getText", m_TextSpan_getText}, +- {"__tostring", m_TextSpan__tostring}, +- {NULL, NULL} // sentinel +-}; +- +- +- +- +-//********************************************************************** +-// Attribute +-m_poppler_get_BOOL(Attribute,isOk); +-m_poppler_get_INT(Attribute,getType); +-m_poppler_get_INT(Attribute,getOwner); +-m_poppler_get_GOOSTRING(Attribute,getName); +- +-static int m_Attribute_getTypeName(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- lua_pushstring(L, ((Attribute *) uin->d)->getTypeName()); +- return 1; +-} +- +-static int m_Attribute_getOwnerName(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- lua_pushstring(L, ((Attribute *) uin->d)->getOwnerName()); +- return 1; +-} +- +-static int m_Attribute_getValue(lua_State * L) +-{ +- udstruct *uin, *uout; +- Object *origin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- origin = (Object *) (((Attribute *) uin->d)->getValue()); +- *((Object *) uout->d) = origin->copy(); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +- +-static int m_Attribute_getDefaultValue(lua_State * L) +-{ +- Attribute::Type t; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- t = (Attribute::Type) luaL_checkint(L, 2); +- uout = new_Object_userdata(L); +- uout->d = ((Attribute *)uin->d)->getDefaultValue(t) ; +- //uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +- +-m_poppler_get_GUINT(Attribute,getRevision); +- +-static int m_Attribute_setRevision(lua_State * L) +-{ +- Guint i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (Guint) luaL_checkint(L, 2); +- ((Attribute *) uin->d)->setRevision(i); +- return 0; +-} +- +-m_poppler_get_BOOL(Attribute, isHidden); +- +-static int m_Attribute_setHidden(lua_State * L) +-{ +- GBool i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (GBool) lua_toboolean(L, 2); +- ((Attribute *) uin->d)->setHidden(i); +- return 0; +-} +- +-static int m_Attribute_getFormattedValue(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- lua_pushstring(L, ((Attribute *) uin->d)->getFormattedValue()); +- return 1; +-} +- +- +-static int m_Attribute_setFormattedValue(lua_State * L) +-{ +- const char *c; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- c = luaL_checkstring(L, 2); +- ((Attribute *) uin->d)->setFormattedValue(c); +- return 0; +-} +- +-static int m_Attribute__gc(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_Attribute); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +-#ifdef DEBUG +- printf("\n===== Attribute GC ===== uin=<%p>\n", uin); +-#endif +- if (uin->atype == ALLOC_LEPDF) { +- delete(Attribute *) uin->d; +- } +- return 0; +-} +- +- +-m_poppler__tostring(Attribute); +- +- +-static const struct luaL_Reg Attribute_m[] = { +- {"isOk",m_Attribute_isOk}, +- {"getType",m_Attribute_getType}, +- {"getOwner",m_Attribute_getOwner}, +- {"getTypeName",m_Attribute_getTypeName}, +- {"getOwnerName",m_Attribute_getOwnerName}, +- {"getValue",m_Attribute_getValue}, +- {"getDefaultValue",m_Attribute_getDefaultValue}, +- {"getName",m_Attribute_getName}, +- {"getRevision",m_Attribute_getRevision}, +- {"setRevision",m_Attribute_setRevision}, +- {"istHidden",m_Attribute_isHidden}, +- {"setHidden",m_Attribute_setHidden}, +- {"getFormattedValue",m_Attribute_getFormattedValue}, +- {"setFormattedValue",m_Attribute_setFormattedValue}, +- {"__gc", m_Attribute__gc}, +- {"__tostring", m_Attribute__tostring}, +- {NULL, NULL} // sentinel +-}; +- +- +- +- +-//********************************************************************** +-// StructElement +- +- +-m_poppler_get_INT(StructElement,getType); +-m_poppler_get_BOOL(StructElement,isOk); +-m_poppler_get_BOOL(StructElement,isBlock); +-m_poppler_get_BOOL(StructElement,isInline); +-m_poppler_get_BOOL(StructElement,isGrouping); +-m_poppler_get_BOOL(StructElement,isContent); +-m_poppler_get_BOOL(StructElement,isObjectRef); +-m_poppler_get_BOOL(StructElement,hasPageRef); +-m_poppler_get_INT(StructElement,getMCID); +-m_poppler_get_INT(StructElement, getNumChildren); +- +-m_poppler_get_GUINT(StructElement,getRevision); +-m_poppler_get_UINT(StructElement,getNumAttributes); +- +-m_poppler_get_GOOSTRING(StructElement, getID); +-m_poppler_get_GOOSTRING(StructElement, getLanguage); +-m_poppler_get_GOOSTRING(StructElement, getTitle); +-m_poppler_get_GOOSTRING(StructElement, getExpandedAbbr); +-m_poppler_get_GOOSTRING(StructElement, getAltText); +-m_poppler_get_GOOSTRING(StructElement, getActualText); +- +-m_poppler_get_poppler(StructElement, StructTreeRoot, getStructTreeRoot); +-m_poppler__tostring(StructElement); +- +- +-static int m_StructElement_getObjectRef(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- uout = new_Ref_userdata(L); +- uout->d = (Ref *) gmalloc(sizeof(Ref)); +- ((Ref *) uout->d)->num = ((StructElement *) uin->d)->getObjectRef().num; +- ((Ref *) uout->d)->gen = ((StructElement *) uin->d)->getObjectRef().gen; +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +- +-static int m_StructElement_getParentRef(lua_State * L) +-{ +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- uout = new_Ref_userdata(L); +- uout->d = (Ref *) gmalloc(sizeof(Ref)); +- ((Ref *) uout->d)->num = ((StructElement *) uin->d)->getParentRef().num; +- ((Ref *) uout->d)->gen = ((StructElement *) uin->d)->getParentRef().gen; +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +-// Not exactly as the header: +-// Ref = StructElement:getPageRef() +-// Ref is false if the C++ functione return false +-static int m_StructElement_getPageRef(lua_State * L) +-{ +- GBool b; +- Ref *r; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- r = (Ref *) gmalloc(sizeof(Ref)); +- b = ((StructElement *) uin->d)->getPageRef( *r ); +- if (b) { +- uout = new_Ref_userdata(L); +- uout->d = r ; +- //uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushboolean(L,0); +- return 1; +-} +- +- +- +-static int m_StructElement_getTypeName(lua_State * L) +-{ +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- lua_pushstring(L, ((StructElement *) uin->d)->getTypeName()); +- return 1; +-} +- +- +-static int m_StructElement_setRevision(lua_State * L) +-{ +- Guint i; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (Guint) luaL_checkint(L, 2); +- ((StructElement *) uin->d)->setRevision(i); +- return 0; +-} +- +-static int m_StructElement_getText(lua_State * L) +-{ +- GBool i; +- GooString *gs; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (GBool) lua_toboolean(L, 2); +- gs = ((StructElement *) uin->d)->getText(i); +- if (gs != NULL) +- lua_pushlstring(L, gs->getCString(), gs->getLength()); +- else +- lua_pushnil(L); +- return 1; +-} +- +- +-static int m_StructElement_getChild(lua_State * L) +-{ +- StructElement *c; +- int i; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (int) luaL_checkint(L, 2); +- c = ((StructElement *) uin->d)->getChild(i-1); +- if (c != NULL) { +- uout = new_StructElement_userdata(L); +- uout->d = c ; +- //uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } +- else +- lua_pushnil(L); +- return 1; +-} +- +- +-static int m_StructElement_appendChild(lua_State * L) +-{ +- udstruct *uin, *uin1; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- uin1 = (udstruct *) luaL_checkudata(L, 2, M_StructElement); +- if (uin1->pd != NULL && uin1->pd->pc != uin1->pc) +- pdfdoc_changed_error(L); +- ((StructElement *) uin->d)->appendChild( (StructElement *)uin1->d ); +- return 0; +-} +- +- +-static int m_StructElement_getAttribute(lua_State * L) +-{ +- Attribute *a; +- int i; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (int) luaL_checkint(L, 2); +- a = ((StructElement *) uin->d)->getAttribute(i-1); +- if (a != NULL) { +- uout = new_Attribute_userdata(L); +- uout->d = a ; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } +- else +- lua_pushnil(L); +- return 1; +-} +- +- +- +-static int m_StructElement_appendAttribute(lua_State * L) +-{ +- +- udstruct *uin, *uin1; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- uin1 = (udstruct *) luaL_checkudata(L, 2, M_Attribute); +- if (uin1->pd != NULL && uin1->pd->pc != uin1->pc) +- pdfdoc_changed_error(L); +- ((StructElement *) uin->d)->appendAttribute( (Attribute *)uin1->d ); +- return 0; +-} +- +- +-static int m_StructElement_findAttribute(lua_State * L) +-{ +- Attribute::Type t; +- Attribute::Owner o; +- GBool g; +- udstruct *uin, *uout; +- const Attribute *a; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- t = (Attribute::Type) luaL_checkint(L,1); +- o = (Attribute::Owner) luaL_checkint(L,2); +- g = (GBool) lua_toboolean(L, 3); +- a = ((StructElement *) uin->d)->findAttribute(t,g,o); +- +- if (a!=NULL){ +- uout = new_Attribute_userdata(L); +- uout->d = new Attribute(a->getType(),a->getValue()); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-// This returns a lua table +-static int m_StructElement_getTextSpans(lua_State * L) +-{ +- int i ; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructElement); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- +- if ((((StructElement *) uin->d)->getTextSpans()).size()>0) { +- lua_createtable (L, +- (int) (((StructElement *) uin->d)->getTextSpans()).size(), +- 0); +- for(i=0;i<(int) (((StructElement *) uin->d)->getTextSpans()).size(); i++){ +- uout = new_TextSpan_userdata(L); +- uout->d = new TextSpan( (((StructElement *) uin->d)->getTextSpans())[i] ); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- lua_rawseti(L,-2,i+1); +- } +- } else +- lua_pushnil(L); +- return 1; +-} +- +- +- +-static const struct luaL_Reg StructElement_m[] = { +- {"getTypeName", m_StructElement_getTypeName}, +- {"getType",m_StructElement_getType}, +- {"isOk",m_StructElement_isOk}, +- {"isBlock",m_StructElement_isBlock}, +- {"isInline",m_StructElement_isInline}, +- {"isGrouping",m_StructElement_isGrouping}, +- {"isContent",m_StructElement_isContent}, +- {"isObjectRef",m_StructElement_isObjectRef}, +- {"getMCID",m_StructElement_getMCID}, +- {"getObjectRef",m_StructElement_getObjectRef}, +- {"getParentRef",m_StructElement_getParentRef}, +- {"hasPageRef",m_StructElement_hasPageRef}, +- {"getPageRef",m_StructElement_getPageRef}, +- {"getStructTreeRoot",m_StructElement_getStructTreeRoot}, +- {"getID",m_StructElement_getID}, +- {"getLanguage",m_StructElement_getLanguage}, +- {"getRevision",m_StructElement_getRevision}, +- {"setRevision",m_StructElement_setRevision}, +- {"getTitle",m_StructElement_getTitle}, +- {"getExpandedAbbr",m_StructElement_getExpandedAbbr}, +- {"getNumChildren",m_StructElement_getNumChildren}, +- {"getChild",m_StructElement_getChild}, +- {"appendChild",m_StructElement_appendChild}, +- {"getNumAttributes",m_StructElement_getNumAttributes}, +- {"getAttribute",m_StructElement_getAttribute}, +- {"appendAttribute",m_StructElement_appendAttribute}, +- {"findAttribute",m_StructElement_findAttribute}, +- {"getAltText",m_StructElement_getAltText}, +- {"getActualText",m_StructElement_getActualText}, +- {"getText",m_StructElement_getText}, +- {"getTextSpans",m_StructElement_getTextSpans}, +- {"__tostring", m_StructElement__tostring}, +- {NULL, NULL} // sentinel +-}; +- +- +-//********************************************************************** +-// StructTreeRoot +- +-m_poppler_get_INT(StructTreeRoot, getNumChildren); +-m_poppler_get_poppler(StructTreeRoot, PDFDoc, getDoc); +-m_poppler_get_poppler(StructTreeRoot, Dict, getRoleMap); +-m_poppler_get_poppler(StructTreeRoot, Dict, getClassMap); +-m_poppler__tostring(StructTreeRoot); +- +-static int m_StructTreeRoot_getChild(lua_State * L) +-{ +- unsigned int i; +- udstruct *uin, *uout; +- StructElement *child ; +- StructTreeRoot *root ; +- +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructTreeRoot); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (unsigned) luaL_checkint(L, 2); +- root = (StructTreeRoot *) uin->d; +- if (i-1 < root->getNumChildren() ){ +- child = root->getChild(i-1); +- uout = new_StructElement_userdata(L); +- uout->d = child; +- //uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +-static int m_StructTreeRoot_appendChild(lua_State * L) +-{ +- udstruct *uin, *uin_child; +- StructElement *child ; +- StructTreeRoot *root ; +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructTreeRoot); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- uin_child = (udstruct *) luaL_checkudata(L, 2, M_StructElement); +- if (uin_child->pd != NULL && uin_child->pd->pc != uin_child->pc) +- pdfdoc_changed_error(L); +- root = (StructTreeRoot *) uin->d; +- child = (StructElement *) uin_child->d; +- root->appendChild(child); +- return 0; +-} +- +- +-static int m_StructTreeRoot_findParentElement(lua_State * L) +-{ +- unsigned int i; +- udstruct *uin, *uout; +- const StructElement *parent ; +- StructTreeRoot *root ; +- +- uin = (udstruct *) luaL_checkudata(L, 1, M_StructTreeRoot); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- i = (unsigned) luaL_checkint(L, 2); +- root = (StructTreeRoot *) uin->d; +- parent = root->findParentElement(i-1); +- if (parent != NULL) { +- uout = new_StructElement_userdata(L); +- // see https://isocpp.org/wiki/faq/const-correctness#aliasing-and-const +- uout->d = (StructElement *) parent; +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- } else +- lua_pushnil(L); +- return 1; +-} +- +- +-static const struct luaL_Reg StructTreeRoot_m[] = { +- {"getDoc",m_StructTreeRoot_getDoc}, +- {"getRoleMap",m_StructTreeRoot_getRoleMap}, +- {"getClassMap",m_StructTreeRoot_getClassMap}, +- {"getNumChildren",m_StructTreeRoot_getNumChildren}, +- {"getChild",m_StructTreeRoot_getChild}, +- {"appendChild",m_StructTreeRoot_appendChild}, +- {"findParentElement",m_StructTreeRoot_findParentElement}, +- {"__tostring", m_StructTreeRoot__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// XRef +- +-m_poppler_get_BOOL(XRef, isOk); +-m_poppler_get_INT(XRef, getErrorCode); +-m_poppler_get_BOOL(XRef, isEncrypted); +-m_poppler_get_BOOL(XRef, okToPrint); +-m_poppler_get_BOOL(XRef, okToPrintHighRes); +-m_poppler_get_BOOL(XRef, okToChange); +-m_poppler_get_BOOL(XRef, okToCopy); +-m_poppler_get_BOOL(XRef, okToAddNotes); +-m_poppler_get_BOOL(XRef, okToFillForm); +-m_poppler_get_BOOL(XRef, okToAccessibility); +-m_poppler_get_BOOL(XRef, okToAssemble); +-m_poppler_get_OBJECT(XRef, getCatalog); +- +-static int m_XRef_fetch(lua_State * L) +-{ +- int num, gen; +- udstruct *uin, *uout; +- uin = (udstruct *) luaL_checkudata(L, 1, M_XRef); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- num = luaL_checkint(L, 2); +- gen = luaL_checkint(L, 3); +- uout = new_Object_userdata(L); +- uout->d = new Object(); +- *((Object *) uout->d) = ((XRef *) uin->d)->fetch(num, gen); +- uout->atype = ALLOC_LEPDF; +- uout->pc = uin->pc; +- uout->pd = uin->pd; +- return 1; +-} +- +-m_poppler_get_OBJECT(XRef, getDocInfo); +-m_poppler_get_OBJECT(XRef, getDocInfoNF); +-m_poppler_get_INT(XRef, getNumObjects); +-m_poppler_get_INT(XRef, getRootNum); +-m_poppler_get_INT(XRef, getRootGen); +-// getStreamEnd +- +-static int m_XRef_getNumEntry(lua_State * L) +-{ +- int i, offset; +- udstruct *uin; +- uin = (udstruct *) luaL_checkudata(L, 1, M_XRef); +- if (uin->pd != NULL && uin->pd->pc != uin->pc) +- pdfdoc_changed_error(L); +- offset = luaL_checkint(L, 2); +- i = ((XRef *) uin->d)->getNumEntry(offset); +- if (i >= 0) +- lua_pushinteger(L, i); +- else +- lua_pushnil(L); +- return 1; +-} +- +-m_poppler_get_poppler(XRef, Object, getTrailerDict); +- +-m_poppler__tostring(XRef); +- +-static const struct luaL_Reg XRef_m[] = { +- {"isOk", m_XRef_isOk}, +- {"getErrorCode", m_XRef_getErrorCode}, +- {"isEncrypted", m_XRef_isEncrypted}, +- {"okToPrint", m_XRef_okToPrint}, +- {"okToPrintHighRes", m_XRef_okToPrintHighRes}, +- {"okToChange", m_XRef_okToChange}, +- {"okToCopy", m_XRef_okToCopy}, +- {"okToAddNotes", m_XRef_okToAddNotes}, +- {"okToFillForm", m_XRef_okToFillForm}, +- {"okToAccessibility", m_XRef_okToAccessibility}, +- {"okToAssemble", m_XRef_okToAssemble}, +- {"getCatalog", m_XRef_getCatalog}, +- {"fetch", m_XRef_fetch}, +- {"getDocInfo", m_XRef_getDocInfo}, +- {"getDocInfoNF", m_XRef_getDocInfoNF}, +- {"getNumObjects", m_XRef_getNumObjects}, +- {"getRootNum", m_XRef_getRootNum}, +- {"getRootGen", m_XRef_getRootGen}, +- // {"getStreamEnd", m_XRef_getStreamEnd}, +- {"getNumEntry", m_XRef_getNumEntry}, +- {"getTrailerDict", m_XRef_getTrailerDict}, +- {"__tostring", m_XRef__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +-// XRefEntry +- +-m_poppler__tostring(XRefEntry); +- +-static const struct luaL_Reg XRefEntry_m[] = { +- {"__tostring", m_XRefEntry__tostring}, +- {NULL, NULL} // sentinel +-}; +- +-//********************************************************************** +- +-#ifdef LuajitTeX +-#define setfuncs_meta(type) \ +- luaL_newmetatable(L, M_##type); \ +- lua_pushvalue(L, -1); \ +- lua_setfield(L, -2, "__index"); \ +- lua_pushstring(L, "no user access"); \ +- lua_setfield(L, -2, "__metatable"); \ +- luaL_openlib(L, NULL, type##_m, 0) +-#else +-#define setfuncs_meta(type) \ +- luaL_newmetatable(L, M_##type); \ +- lua_pushvalue(L, -1); \ +- lua_setfield(L, -2, "__index"); \ +- lua_pushstring(L, "no user access"); \ +- lua_setfield(L, -2, "__metatable"); \ +- luaL_setfuncs(L, type##_m, 0) +-#endif +- +-int luaopen_epdf(lua_State * L) +-{ +- setfuncs_meta(Annot); +- setfuncs_meta(Annots); +- setfuncs_meta(Array); +- setfuncs_meta(Catalog); +- setfuncs_meta(Dict); +- setfuncs_meta(EmbFile); +- setfuncs_meta(FileSpec); +- setfuncs_meta(GooString); +- setfuncs_meta(LinkDest); +- setfuncs_meta(Links); +- setfuncs_meta(Object); +- setfuncs_meta(Page); +- setfuncs_meta(PDFDoc); +- setfuncs_meta(PDFRectangle); +- setfuncs_meta(Ref); +- setfuncs_meta(Stream); +- setfuncs_meta(Attribute); +- setfuncs_meta(StructElement); +- setfuncs_meta(StructTreeRoot); +- setfuncs_meta(TextSpan); +- setfuncs_meta(XRef); +- setfuncs_meta(XRefEntry); +- luaL_openlib(L, "epdf", epdflib_f, 0); +- return 1; +-} +diff --git a/texk/web2c/luatexdir/lua/lfontlib.c b/texk/web2c/luatexdir/lua/lfontlib.c +index a709e42c4..2592ffba6 100644 +--- a/texk/web2c/luatexdir/lua/lfontlib.c ++++ b/texk/web2c/luatexdir/lua/lfontlib.c +@@ -324,7 +324,9 @@ static int l_vf_char(lua_State * L) + } + mat_p = &(vsp->packet_stack[vsp->packet_stack_level]); + w = char_width(lf, k); +- mat_p->pos.h += round_xn_over_d(w, 1000 + ex_glyph, 1000); ++ if (ex_glyph != 0) ++ w = round_xn_over_d(w, 1000 + ex_glyph, 1000); ++ mat_p->pos.h += w; + synch_pos_with_cur(static_pdf->posstruct, vsp->refpos, mat_p->pos); + return 0; + } +@@ -416,11 +418,14 @@ static int l_vf_right(lua_State * L) + { + scaled i; + vf_struct *vsp = static_pdf->vfstruct; ++ int ex_glyph = vsp->ex_glyph/1000; + packet_stack_record *mat_p; + if (!vsp->vflua) + normal_error("vf", "vf.right() outside virtual font"); + mat_p = &(vsp->packet_stack[vsp->packet_stack_level]); + i = (scaled) luaL_checkinteger(L, 1); ++ if (ex_glyph != 0 && i != 0) /* new, experiment */ ++ i = round_xn_over_d(i, 1000 + ex_glyph, 1000); + i = store_scaled_f(i, vsp->fs_f); + mat_p->pos.h += i; + synch_pos_with_cur(static_pdf->posstruct, vsp->refpos, mat_p->pos); +@@ -431,11 +436,14 @@ static int l_vf_rule(lua_State * L) + { + scaledpos size; + vf_struct *vsp = static_pdf->vfstruct; ++ int ex_glyph = vsp->ex_glyph/1000; + packet_stack_record *mat_p; + if (!vsp->vflua) + normal_error("vf", "vf.rule() outside virtual font"); + size.h = (scaled) luaL_checkinteger(L, 1); + size.v = (scaled) luaL_checkinteger(L, 2); ++ if (ex_glyph != 0 && size.h > 0) /* new, experiment */ ++ size.h = round_xn_over_d(size.h, 1000 + ex_glyph, 1000); + size.h = store_scaled_f(size.h, vsp->fs_f); + size.v = store_scaled_f(size.v, vsp->fs_f); + if (size.h > 0 && size.v > 0) +@@ -460,6 +468,16 @@ static int l_vf_special(lua_State * L) + return 0; + } + ++static int l_vf_pdf(lua_State * L) ++{ ++ vf_struct *vsp = static_pdf->vfstruct; ++ if (!vsp->vflua) ++ normal_error("vf", "vf.special() outside virtual font"); ++ luapdfprint(L); ++ pdf_out(static_pdf, '\n'); ++ return 0; ++} ++ + static const struct luaL_Reg vflib[] = { + {"char", l_vf_char}, + {"down", l_vf_down}, +@@ -476,6 +494,7 @@ static const struct luaL_Reg vflib[] = { + /* {"scale", l_vf_scale}, */ + /* {"slot", l_vf_slot}, */ + {"special", l_vf_special}, ++ {"pdf", l_vf_pdf}, + {NULL, NULL} /* sentinel */ + }; + +diff --git a/texk/web2c/luatexdir/lua/limglib.c b/texk/web2c/luatexdir/lua/limglib.c +index f9d863eef..ce2b4b7b5 100644 +--- a/texk/web2c/luatexdir/lua/limglib.c ++++ b/texk/web2c/luatexdir/lua/limglib.c +@@ -24,7 +24,7 @@ + #include "lua.h" + #include "lauxlib.h" + +-#define img_types_max 7 ++#define img_types_max 8 + + const char *img_types[] = { + "none", +@@ -243,6 +243,26 @@ static void write_image_or_node(lua_State * L, wrtype_e writetype) + img_state(ad) = DICT_REFERED; + } + ++static int write_image_object(lua_State * L, wrtype_e writetype) ++{ ++ image *a, **aa; ++ image_dict *ad; ++ int num; ++ if (lua_gettop(L) != 2) ++ luaL_error(L, "%s expects two argument", wrtype_s[writetype]); ++ aa = (image **) luaL_checkudata(L, 1, TYPE_IMG); ++ a = *aa; ++ ad = img_dict(a); ++ ++ read_img(ad); ++ ++ /* setup_image(static_pdf, a, writetype); */ ++ num = (int) lua_tointeger(L, 2); ++ num = write_img_object(static_pdf, ad, num); ++ lua_pushinteger(L,num); ++ return 1; ++} ++ + static int l_write_image(lua_State * L) + { + write_image_or_node(L, WR_WRITE); +@@ -260,6 +280,17 @@ static int l_immediatewrite_image(lua_State * L) + return 1; + } + ++static int l_immediatewrite_image_object(lua_State * L) ++{ ++ check_o_mode(static_pdf, "img.immediatewriteobject", 1 << OMODE_PDF, true); ++ if (global_shipping_mode != NOT_SHIPPING) { ++ luaL_error(L, "img.immediatewriteobject can not be used with \\latelua"); ++ } else { ++ write_image_object(L, WR_IMMEDIATEWRITE); ++ } ++ return 1; ++} ++ + static int l_image_node(lua_State * L) + { + write_image_or_node(L, WR_NODE); +@@ -273,7 +304,7 @@ static int l_image_keys(lua_State * L) + + static int l_image_types(lua_State * L) + { +- return lua_show_valid_list(L, img_types, img_types_max); ++ return lua_show_valid_list(L, img_types, 0, img_types_max); + } + + static int l_image_boxes(lua_State * L) +@@ -287,10 +318,13 @@ static const struct luaL_Reg imglib_f[] = { + { "scan", l_scan_image }, + { "write", l_write_image }, + { "immediatewrite", l_immediatewrite_image }, ++ { "immediatewriteobject", l_immediatewrite_image_object }, + { "node", l_image_node }, +- { "keys", l_image_keys }, ++ { "fields", l_image_keys }, + { "types", l_image_types }, + { "boxes", l_image_boxes }, ++ /* for a while: */ ++ { "keys", l_image_keys }, + { NULL, NULL } + }; + +@@ -348,14 +382,10 @@ static int m_img_get(lua_State * L) + } else { + lua_pushstring(L, img_filename(d)); + } +- } else if (lua_key_eq(s,visiblefilename)) { +- if (img_visiblefilename(d) == NULL || strlen(img_visiblefilename(d)) == 0) { +- lua_pushnil(L); +- } else { +- lua_pushstring(L, img_visiblefilename(d)); +- } + } else if (lua_key_eq(s,keepopen)) { + lua_pushboolean(L, img_keepopen(d)); ++ } else if (lua_key_eq(s,nolength)) { ++ lua_pushboolean(L, img_nolength(d)); + } else if (lua_key_eq(s,filepath)) { + if (img_filepath(d) == NULL || strlen(img_filepath(d)) == 0) { + lua_pushnil(L); +@@ -461,10 +491,28 @@ static int m_img_get(lua_State * L) + if (img_type(d) != IMG_TYPE_PDFSTREAM + || img_pdfstream_ptr(d) == NULL + || img_pdfstream_stream(d) == NULL +- || strlen(img_pdfstream_stream(d)) == 0) { ++ || img_pdfstream_size(d) == 0) { ++ lua_pushnil(L); ++ } else { ++ lua_pushlstring(L, img_pdfstream_stream(d), img_pdfstream_size(d)); ++ } ++ } else if (lua_key_eq(s,visiblefilename)) { ++ if (img_visiblefilename(d) == NULL || strlen(img_visiblefilename(d)) == 0) { ++ lua_pushnil(L); ++ } else { ++ lua_pushstring(L, img_visiblefilename(d)); ++ } ++ } else if (lua_key_eq(s,userpassword)) { ++ if (img_userpassword(d) == NULL || strlen(img_userpassword(d)) == 0) { ++ lua_pushnil(L); ++ } else { ++ lua_pushstring(L, img_userpassword(d)); ++ } ++ } else if (lua_key_eq(s,ownerpassword)) { ++ if (img_ownerpassword(d) == NULL || strlen(img_ownerpassword(d)) == 0) { + lua_pushnil(L); + } else { +- lua_pushstring(L, img_pdfstream_stream(d)); ++ lua_pushstring(L, img_ownerpassword(d)); + } + } else if (lua_key_eq(s,ref_count)) { + lua_pushinteger(L, img_luaref(d)); +@@ -520,7 +568,7 @@ static void lua_to_image(lua_State * L, image * a, image_dict * d) + if (img_state(d) >= DICT_FILESCANNED) { + luaL_error(L, "image.filename is now read-only"); + } else if (img_type(d) == IMG_TYPE_PDFSTREAM) { +- luaL_error(L, "image.filename can't be used with image.stream"); ++ /* just ignore */ + } else if (t == LUA_TSTRING) { + xfree(img_filename(d)); + img_filename(d) = xstrdup(lua_tostring(L, -1)); +@@ -531,13 +579,35 @@ static void lua_to_image(lua_State * L, image * a, image_dict * d) + if (img_state(d) >= DICT_FILESCANNED) { + luaL_error(L, "image.visiblefilename is now read-only"); + } else if (img_type(d) == IMG_TYPE_PDFSTREAM) { +- luaL_error(L, "image.visiblefilename can't be used with image.stream"); ++ img_visiblefilename(d) = NULL; + } else if (t == LUA_TSTRING) { + xfree(img_visiblefilename(d)); + img_visiblefilename(d) = xstrdup(lua_tostring(L, -1)); + } else { + luaL_error(L, "image.visiblefilename needs string value"); + } ++ } else if (lua_key_eq(s,userpassword)) { ++ if (img_state(d) >= DICT_FILESCANNED) { ++ luaL_error(L, "image.userpassword is now read-only"); ++ } else if (img_type(d) == IMG_TYPE_PDFSTREAM) { ++ img_userpassword(d) = NULL; ++ } else if (t == LUA_TSTRING) { ++ xfree(img_userpassword(d)); ++ img_userpassword(d) = xstrdup(lua_tostring(L, -1)); ++ } else { ++ luaL_error(L, "image.userpassword needs string value"); ++ } ++ } else if (lua_key_eq(s,ownerpassword)) { ++ if (img_state(d) >= DICT_FILESCANNED) { ++ luaL_error(L, "image.ownerpassword is now read-only"); ++ } else if (img_type(d) == IMG_TYPE_PDFSTREAM) { ++ img_ownerpassword(d) = NULL; ++ } else if (t == LUA_TSTRING) { ++ xfree(img_ownerpassword(d)); ++ img_ownerpassword(d) = xstrdup(lua_tostring(L, -1)); ++ } else { ++ luaL_error(L, "image.ownerpassword needs string value"); ++ } + } else if (lua_key_eq(s,attr)) { + if (img_state(d) >= DICT_FILESCANNED) { + luaL_error(L, "image.attr is now read-only"); +@@ -607,6 +677,8 @@ static void lua_to_image(lua_State * L, image * a, image_dict * d) + } else { + img_keepopen(d) = lua_toboolean(L, -1); + } ++ } else if (lua_key_eq(s,nolength)) { ++ img_nolength(d) = lua_toboolean(L, -1); + } else if (lua_key_eq(s,bbox)) { + if (img_state(d) >= DICT_FILESCANNED) { + luaL_error(L, "image.bbox is now read-only"); +@@ -636,11 +708,15 @@ static void lua_to_image(lua_State * L, image * a, image_dict * d) + } else if (img_state(d) >= DICT_FILESCANNED) { + luaL_error(L, "image.stream is now read-only"); + } else { ++ size_t size = 0; ++ const char *stream = lua_tolstring(L, -1, &size); + if (img_pdfstream_ptr(d) == NULL) { + new_img_pdfstream_struct(d); + } + xfree(img_pdfstream_stream(d)); +- img_pdfstream_stream(d) = xstrdup(lua_tostring(L, -1)); ++ img_pdfstream_size(d) = size; ++ img_pdfstream_stream(d) = xmalloc(size); ++ memcpy(img_pdfstream_stream(d),stream,size); + img_type(d) = IMG_TYPE_PDFSTREAM; + } + } else { +diff --git a/texk/web2c/luatexdir/lua/liolibext.c b/texk/web2c/luatexdir/lua/liolibext.c +index 29ad8447a..71b9b41d1 100644 +--- a/texk/web2c/luatexdir/lua/liolibext.c ++++ b/texk/web2c/luatexdir/lua/liolibext.c +@@ -125,7 +125,7 @@ static int readcardinal1_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p >= l) { ++ if (p >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p]); +@@ -150,7 +150,7 @@ static int readcardinal2_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+1 >= l) { ++ if (p+1 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -177,7 +177,7 @@ static int readcardinal3_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+2 >= l) { ++ if (p+2 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -206,7 +206,7 @@ static int readcardinal4_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+3 >= l) { ++ if (p+3 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -218,6 +218,139 @@ static int readcardinal4_s(lua_State *L) { + return 1; + } + ++static int readcardinaltable(lua_State *L) { ++ FILE *f = tofile(L); ++ int n = lua_tointeger(L,2); ++ int b = lua_tointeger(L,3); ++ int i; ++ lua_createtable(L,n,0); ++ switch (b) { ++ case 1: ++ for (i=1;i<=n;i++) { ++ int a = getc(f); ++ if (a == EOF) { ++ break; ++ } else { ++ lua_pushinteger(L, a); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 2: ++ for (i=1;i<=n;i++) { ++ int a = getc(f); ++ int b = getc(f); ++ if (b == EOF) { ++ break; ++ } else { ++ /* (a<<8) | b */ ++ lua_pushinteger(L, 0x100 * a + b); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 3: ++ for (i=1;i<=n;i++) { ++ int a = getc(f); ++ int b = getc(f); ++ int c = getc(f); ++ if (c == EOF) { ++ break; ++ } else { ++ /* (a<<16) | (b<<8) | c */ ++ lua_pushinteger(L, 0x10000 * a + 0x100 * b + c); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 4: ++ for (i=1;i<=n;i++) { ++ int a = getc(f); ++ int b = getc(f); ++ int c = getc(f); ++ int d = getc(f); ++ if (d == EOF) { ++ break; ++ } else { ++ /* (a<<24) | (b<<16) | (c<<8) | d */ ++ lua_pushinteger(L,0x1000000 * a + 0x10000 * b + 0x100 * c + d); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ return 1; ++} ++ ++static int readcardinaltable_s(lua_State *L) { ++ size_t l; ++ const char *s = luaL_checklstring(L, 1, &l); ++ size_t p = luaL_checkinteger(L, 2) - 1; ++ int n = lua_tointeger(L,3); ++ int b = lua_tointeger(L,4); ++ int i; ++ lua_createtable(L,n,0); ++ /*if (p >= 0) {*/ ++ switch (b) { ++ case 1: ++ for (i=1;i<=n;i++) { ++ if (p >= l) { ++ break; ++ } else { ++ int a = uchar(s[p++]); ++ lua_pushinteger(L, a); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 2: ++ for (i=1;i<=n;i++) { ++ if (p+1 >= l) { ++ break; ++ } else { ++ int a = uchar(s[p++]); ++ int b = uchar(s[p++]); ++ lua_pushinteger(L, 0x100 * a + b); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 3: ++ for (i=1;i<=n;i++) { ++ if (p+2 >= l) { ++ break; ++ } else { ++ int a = uchar(s[p++]); ++ int b = uchar(s[p++]); ++ int c = uchar(s[p++]); ++ lua_pushinteger(L, 0x10000 * a + 0x100 * b + c); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 4: ++ for (i=1;i<=n;i++) { ++ if (p+3 >= l) { ++ break; ++ } else { ++ int a = uchar(s[p++]); ++ int b = uchar(s[p++]); ++ int c = uchar(s[p++]); ++ int d = uchar(s[p++]); ++ lua_pushinteger(L,0x1000000 * a + 0x10000 * b + 0x100 * c + d); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ /*}*/ ++ return 1; ++} ++ + static int readinteger1(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); +@@ -234,7 +367,7 @@ static int readinteger1_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p >= l) { ++ if (p >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p]); +@@ -263,7 +396,7 @@ static int readinteger2_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+1 >= l) { ++ if (p+1 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -294,7 +427,7 @@ static int readinteger3_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+2 >= l) { ++ if (p+2 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -327,7 +460,7 @@ static int readinteger4_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+3 >= l) { ++ if (p+3 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -342,6 +475,160 @@ static int readinteger4_s(lua_State *L) { + return 1; + } + ++static int readintegertable(lua_State *L) { ++ FILE *f = tofile(L); ++ int n = lua_tointeger(L,2); ++ int b = lua_tointeger(L,3); ++ int i; ++ lua_createtable(L,n,0); ++ switch (b) { ++ case 1: ++ for (i=1;i<=n;i++) { ++ int a = getc(f); ++ if (a == EOF) { ++ break; ++ } else if (a >= 0x80) { ++ lua_pushinteger(L, a - 0x100); ++ } else { ++ lua_pushinteger(L, a); ++ } ++ lua_rawseti(L, -2, i); ++ } ++ break; ++ case 2: ++ for (i=1;i<=n;i++) { ++ int a = getc(f); ++ int b = getc(f); ++ if (b == EOF) { ++ break; ++ } else if (a >= 0x80) { ++ lua_pushinteger(L, 0x100 * a + b - 0x10000); ++ } else { ++ lua_pushinteger(L, 0x100 * a + b); ++ } ++ lua_rawseti(L, -2, i); ++ } ++ break; ++ case 3: ++ for (i=1;i<=n;i++) { ++ int a = getc(f); ++ int b = getc(f); ++ int c = getc(f); ++ if (c == EOF) { ++ break; ++ } else if (a >= 0x80) { ++ lua_pushinteger(L, 0x10000 * a + 0x100 * b + c - 0x1000000); ++ } else { ++ lua_pushinteger(L, 0x10000 * a + 0x100 * b + c); ++ } ++ lua_rawseti(L, -2, i); ++ } ++ break; ++ case 4: ++ for (i=1;i<=n;i++) { ++ int a = getc(f); ++ int b = getc(f); ++ int c = getc(f); ++ int d = getc(f); ++ if (d == EOF) { ++ break; ++ } else if (a >= 0x80) { ++ lua_pushinteger(L, 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000); ++ } else { ++ lua_pushinteger(L, 0x1000000 * a + 0x10000 * b + 0x100 * c + d); ++ } ++ lua_rawseti(L, -2, i); ++ } ++ break; ++ default: ++ break; ++ } ++ return 1; ++} ++ ++static int readintegertable_s(lua_State *L) { ++ size_t l; ++ const char *s = luaL_checklstring(L, 1, &l); ++ size_t p = luaL_checkinteger(L, 2) - 1; ++ int n = lua_tointeger(L,3); ++ int b = lua_tointeger(L,4); ++ int i; ++ lua_createtable(L,n,0); ++ /*if (p >= 0) {*/ ++ switch (b) { ++ case 1: ++ for (i=1;i<=n;i++) { ++ if (p >= l) { ++ break; ++ } else { ++ int a = uchar(s[p++]); ++ if (a >= 0x80) { ++ lua_pushinteger(L, a - 0x100); ++ } else { ++ lua_pushinteger(L, a); ++ } ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 2: ++ for (i=1;i<=n;i++) { ++ if (p+1 >= l) { ++ break; ++ } else { ++ int a = uchar(s[p++]); ++ int b = uchar(s[p++]); ++ if (a >= 0x80) { ++ lua_pushinteger(L, 0x100 * a + b - 0x10000); ++ } else { ++ lua_pushinteger(L, 0x100 * a + b); ++ } ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 3: ++ for (i=1;i<=n;i++) { ++ if (p+2 >= l) { ++ break; ++ } else { ++ int a = uchar(s[p++]); ++ int b = uchar(s[p++]); ++ int c = uchar(s[p++]); ++ if (a >= 0x80) { ++ lua_pushinteger(L, 0x10000 * a + 0x100 * b + c - 0x1000000); ++ } else { ++ lua_pushinteger(L, 0x10000 * a + 0x100 * b + c); ++ } ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ case 4: ++ for (i=1;i<=n;i++) { ++ if (p+3 >= l) { ++ break; ++ } else { ++ int a = uchar(s[p++]); ++ int b = uchar(s[p++]); ++ int c = uchar(s[p++]); ++ int d = uchar(s[p++]); ++ if (a >= 0x80) { ++ lua_pushinteger(L, 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000); ++ } else { ++ lua_pushinteger(L, 0x1000000 * a + 0x10000 * b + 0x100 * c + d); ++ } ++ lua_rawseti(L, -2, i); ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ /*}*/ ++ return 1; ++} ++ + static int readfixed2(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); +@@ -359,7 +646,7 @@ static int readfixed2_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+3 >= l) { ++ if (p+3 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -394,7 +681,7 @@ static int readfixed4_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+3 >= l) { ++ if (p+3 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -427,7 +714,7 @@ static int read2dot14_s(lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; +- if (p < 0 || p+1 >= l) { ++ if (p+1 >= l) { + lua_pushnil(L); + } else { + int a = uchar(s[p++]); +@@ -497,7 +784,7 @@ static int readbytetable_s(lua_State *L) { + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; + int n = lua_tointeger(L,3); +- if (p < 0 || p >= l) { ++ if (p >= l) { + lua_pushnil(L); + } else { + int i ; +@@ -534,7 +821,7 @@ static int readbytes_s(lua_State *L) { + const char *s = luaL_checklstring(L, 1, &l); + size_t p = luaL_checkinteger(L, 2) - 1; + int n = lua_tointeger(L,3); +- if (p < 0 || p >= l) { ++ if (p >= l) { + return 0; + } else { + int i ; +@@ -644,44 +931,48 @@ static int readline(lua_State *L) + + static const luaL_Reg fiolib[] = { + /* helpers */ +- { "readcardinal1", readcardinal1 }, +- { "readcardinal2", readcardinal2 }, +- { "readcardinal3", readcardinal3 }, +- { "readcardinal4", readcardinal4 }, +- { "readinteger1", readinteger1 }, +- { "readinteger2", readinteger2 }, +- { "readinteger3", readinteger3 }, +- { "readinteger4", readinteger4 }, +- { "readfixed2", readfixed2 }, +- { "readfixed4", readfixed4 }, +- { "read2dot14", read2dot14 }, +- { "setposition", setposition }, +- { "getposition", getposition }, +- { "skipposition", skipposition }, +- { "readbytes", readbytes }, +- { "readbytetable", readbytetable }, +- { "readline", readline }, ++ { "readcardinal1", readcardinal1 }, ++ { "readcardinal2", readcardinal2 }, ++ { "readcardinal3", readcardinal3 }, ++ { "readcardinal4", readcardinal4 }, ++ { "readcardinaltable", readcardinaltable }, ++ { "readinteger1", readinteger1 }, ++ { "readinteger2", readinteger2 }, ++ { "readinteger3", readinteger3 }, ++ { "readinteger4", readinteger4 }, ++ { "readintegertable", readintegertable }, ++ { "readfixed2", readfixed2 }, ++ { "readfixed4", readfixed4 }, ++ { "read2dot14", read2dot14 }, ++ { "setposition", setposition }, ++ { "getposition", getposition }, ++ { "skipposition", skipposition }, ++ { "readbytes", readbytes }, ++ { "readbytetable", readbytetable }, ++ { "readline", readline }, + /* extras */ +- { "recordfilename", recordfilename }, +- { "checkpermission", checkpermission }, ++ { "recordfilename", recordfilename }, ++ { "checkpermission", checkpermission }, + /* done */ + {NULL, NULL} + }; + + static const luaL_Reg siolib[] = { +- { "readcardinal1", readcardinal1_s }, +- { "readcardinal2", readcardinal2_s }, +- { "readcardinal3", readcardinal3_s }, +- { "readcardinal4", readcardinal4_s }, +- { "readinteger1", readinteger1_s }, +- { "readinteger2", readinteger2_s }, +- { "readinteger3", readinteger3_s }, +- { "readinteger4", readinteger4_s }, +- { "readfixed2", readfixed2_s }, +- { "readfixed4", readfixed4_s }, +- { "read2dot14", read2dot14_s }, +- { "readbytes", readbytes_s }, +- { "readbytetable", readbytetable_s }, ++ { "readcardinal1", readcardinal1_s }, ++ { "readcardinal2", readcardinal2_s }, ++ { "readcardinal3", readcardinal3_s }, ++ { "readcardinal4", readcardinal4_s }, ++ { "readcardinaltable", readcardinaltable_s }, ++ { "readinteger1", readinteger1_s }, ++ { "readinteger2", readinteger2_s }, ++ { "readinteger3", readinteger3_s }, ++ { "readinteger4", readinteger4_s }, ++ { "readintegertable", readintegertable_s }, ++ { "readfixed2", readfixed2_s }, ++ { "readfixed4", readfixed4_s }, ++ { "read2dot14", read2dot14_s }, ++ { "readbytes", readbytes_s }, ++ { "readbytetable", readbytetable_s }, + /* done */ + {NULL, NULL} + }; +diff --git a/texk/web2c/luatexdir/lua/llanglib.c b/texk/web2c/luatexdir/lua/llanglib.c +index 382d40944..5855f33fc 100644 +--- a/texk/web2c/luatexdir/lua/llanglib.c ++++ b/texk/web2c/luatexdir/lua/llanglib.c +@@ -20,7 +20,6 @@ + #include "ptexlib.h" + #include "lua/luatex-api.h" + +- + #define LANG_METATABLE "luatex.lang" + + #define check_islang(L,b) (struct tex_language **)luaL_checkudata(L,b,LANG_METATABLE) +@@ -84,7 +83,6 @@ static int lang_clear_patterns(lua_State * L) + return 0; + } + +- + static int lang_hyphenation(lua_State * L) + { + struct tex_language **lang_ptr; +@@ -137,7 +135,6 @@ static int lang_post_hyphen_char(lua_State * L) + } + } + +- + static int lang_pre_exhyphen_char(lua_State * L) + { + struct tex_language **lang_ptr; +@@ -262,7 +259,6 @@ static int do_lang_hyphenate(lua_State * L) + } + + static const struct luaL_Reg langlib_d[] = { +- /* *INDENT-OFF* */ + {"clear_patterns", lang_clear_patterns}, + {"clear_hyphenation", lang_clear_hyphenation}, + {"patterns", lang_patterns}, +@@ -275,13 +271,11 @@ static const struct luaL_Reg langlib_d[] = { + {"sethjcode", lang_sethjcode}, + {"gethjcode", lang_gethjcode}, + {"id", lang_id}, +- /* *INDENT-ON* */ +- {NULL, NULL} /* sentinel */ ++ /*tex sentinel */ ++ {NULL, NULL} + }; + +- + static const struct luaL_Reg langlib[] = { +- /* *INDENT-OFF* */ + {"clear_patterns", lang_clear_patterns}, + {"clear_hyphenation", lang_clear_hyphenation}, + {"patterns", lang_patterns}, +@@ -297,17 +291,19 @@ static const struct luaL_Reg langlib[] = { + {"clean", do_lang_clean}, + {"hyphenate", do_lang_hyphenate}, + {"new", lang_new}, +- /* *INDENT-ON* */ +- {NULL, NULL} /* sentinel */ ++ /*tex sentinel */ ++ {NULL, NULL} + }; + +- + int luaopen_lang(lua_State * L) + { + luaL_newmetatable(L, LANG_METATABLE); +- lua_pushvalue(L, -1); /* push metatable */ +- lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ +- luaL_openlib(L, NULL, langlib_d, 0); /* dict methods */ ++ /*tex push metatable */ ++ lua_pushvalue(L, -1); ++ /*tex metatable.__index = metatable */ ++ lua_setfield(L, -2, "__index"); ++ /*tex set dict methods */ ++ luaL_openlib(L, NULL, langlib_d, 0); + luaL_openlib(L, "lang", langlib, 0); + return 1; + } +diff --git a/texk/web2c/luatexdir/lua/llualib.c b/texk/web2c/luatexdir/lua/llualib.c +index a73e3abf2..ac9b03ffe 100644 +--- a/texk/web2c/luatexdir/lua/llualib.c ++++ b/texk/web2c/luatexdir/lua/llualib.c +@@ -20,14 +20,13 @@ + #include "ptexlib.h" + #include "lua/luatex-api.h" + +- +-#define LOAD_BUF_SIZE 256 ++#define LOAD_BUF_SIZE 64*1024 + #define UINT_MAX32 0xFFFFFFFF + + typedef struct { + unsigned char *buf; + int size; +- int done; ++ /* int done; */ + int alloc; + } bytecode; + +@@ -95,7 +94,7 @@ void undump_luac_registers(void) + lua_bytecode_registers = xmalloc((unsigned) (i * sizeof(bytecode))); + luabytecode_bytes = (unsigned) (i * sizeof(bytecode)); + for (i = 0; i <= (unsigned) luabytecode_max; i++) { +- lua_bytecode_registers[i].done = 0; ++ /* lua_bytecode_registers[i].done = 0; */ + lua_bytecode_registers[i].size = 0; + lua_bytecode_registers[i].buf = NULL; + } +@@ -170,14 +169,18 @@ static int writer(lua_State * L, const void *b, size_t size, void *B) + static const char *reader(lua_State * L, void *ud, size_t * size) + { + bytecode *buf = (bytecode *) ud; +- (void) L; /* for -Wunused */ ++ (void) L; /* for -Wunused */ ++ /* + if (buf->done == buf->size) { + *size = 0; + buf->done = 0; + return NULL; + } ++ */ + *size = (size_t) buf->size; ++ /* + buf->done = buf->size; ++ */ + return (const char *) buf->buf; + } + +@@ -196,7 +199,7 @@ static int get_bytecode(lua_State * L) + #else + "bytecode", NULL)) { + #endif +- return luaL_error(L, "bad bytecode register"); ++ return luaL_error(L, "bad bytecode register"); + } else { + lua_pushvalue(L, -1); + bytecode_register_shadow_set(L, k); +@@ -208,6 +211,41 @@ static int get_bytecode(lua_State * L) + return 1; + } + ++void luabytecodecall(int slot) ++{ ++ int i; ++ int stacktop = lua_gettop(Luas); ++ lua_active++; ++ if (slot < 0 || slot > luabytecode_max) { ++ luaL_error(Luas, "bytecode register out of range"); ++ } else if (bytecode_register_shadow_get(Luas, slot) || lua_bytecode_registers[slot].buf == NULL) { ++ luaL_error(Luas, "undefined bytecode register"); ++ } else if (lua_load(Luas, reader, (void *) (lua_bytecode_registers + slot), ++#ifdef LuajitTeX ++ "bytecode")) ++#else ++ "bytecode", NULL)) ++#endif ++ { ++ luaL_error(Luas, "bytecode register doesn't load well"); ++ } else { ++ 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 */ ++++function_callback_count; /* this will be a dedicated counter */ ++ 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--; ++} ++ + static int set_bytecode(lua_State * L) + { + int k, ltype; +@@ -244,7 +282,7 @@ static int set_bytecode(lua_State * L) + for (i = (unsigned) (luabytecode_max + 1); i <= (unsigned) k; i++) { + lua_bytecode_registers[i].buf = NULL; + lua_bytecode_registers[i].size = 0; +- lua_bytecode_registers[i].done = 0; ++ /* lua_bytecode_registers[i].done = 0; */ + } + luabytecode_max = k; + } +@@ -252,7 +290,7 @@ static int set_bytecode(lua_State * L) + xfree(lua_bytecode_registers[k].buf); + luabytecode_bytes -= (unsigned) lua_bytecode_registers[k].size; + lua_bytecode_registers[k].size = 0; +- lua_bytecode_registers[k].done = 0; ++ /* lua_bytecode_registers[k].done = 0; */ + lua_pushnil(L); + bytecode_register_shadow_set(L, k); + } +@@ -268,7 +306,7 @@ static int set_bytecode(lua_State * L) + lua_dump(L, writer, (void *) (lua_bytecode_registers + k),strip); + #endif + #if LUA_VERSION_NUM == 502 +- lua_dump(L, writer, (void *) (lua_bytecode_registers + k)); ++ lua_dump(L, writer, (void *) (lua_bytecode_registers + k)); + #endif + #endif + } +@@ -329,6 +367,18 @@ static int new_table(lua_State * L) /* hh */ + return 1; + } + ++static int get_stack_top(lua_State * L) /* hh */ ++{ ++ lua_pushinteger(L, lua_gettop(L)); ++ return 1; ++} ++ ++static int get_call_level(lua_State * L) /* hh */ ++{ ++ lua_pushinteger(L, lua_active); ++ return 1; ++} ++ + static const struct luaL_Reg lualib[] = { + /* *INDENT-OFF* */ + {"getluaname", get_luaname}, +@@ -337,6 +387,8 @@ static const struct luaL_Reg lualib[] = { + {"setbytecode", set_bytecode}, + {"newtable", new_table}, + {"get_functions_table",lua_functions_get_table}, ++ {"getstacktop",get_stack_top}, ++ {"getcalllevel", get_call_level}, + /* *INDENT-ON* */ + {NULL, NULL} /* sentinel */ + }; +diff --git a/texk/web2c/luatexdir/lua/lnewtokenlib.c b/texk/web2c/luatexdir/lua/lnewtokenlib.c +index 085129d54..3e879b54a 100644 +--- a/texk/web2c/luatexdir/lua/lnewtokenlib.c ++++ b/texk/web2c/luatexdir/lua/lnewtokenlib.c +@@ -33,10 +33,12 @@ + #include "ptexlib.h" + #include "lua/luatex-api.h" + ++/* + typedef struct lua_token { + int token; + int origin; + } lua_token; ++*/ + + typedef struct saved_tex_scanner { + int token; +@@ -145,33 +147,51 @@ static void push_token(lua_State * L, int tok) + lua_setmetatable(L, -2); + } + +-/* static int run_get_cs_offset(lua_State * L) */ +-/* { */ +-/* lua_pushinteger(L, cs_token_flag); */ +-/* return 1; */ +-/* } */ +- +-/* static int run_get_command_id(lua_State * L) */ +-/* { */ +-/* int cs = -1; */ +-/* if (lua_type(L, -1) == LUA_TSTRING) { */ +-/* cs = get_command_id(lua_tostring(L, -1)); */ +-/* } */ +-/* lua_pushinteger(L, cs); */ +-/* return 1; */ +-/* } */ +- +-/* static int run_get_csname_id(lua_State * L) */ +-/* { */ +-/* const char *s; */ +-/* size_t k, cs = 0; */ +-/* if (lua_type(L, -1) == LUA_TSTRING) { */ +-/* s = lua_tolstring(L, -1, &k); */ +-/* cs = (size_t) string_lookup(s, k); */ +-/* } */ +-/* lua_pushinteger(L, (lua_Number) cs); */ +-/* return 1; */ +-/* } */ ++static int run_get_biggest_char(lua_State * L) ++{ ++ lua_pushinteger(L, biggest_char); ++ return 1; ++} ++ ++/* not that useful: ++ ++static int run_get_cs_offset(lua_State * L) ++{ ++ lua_pushinteger(L, cs_token_flag); ++ return 1; ++} ++ ++*/ ++ ++static int run_get_command_id(lua_State * L) ++{ ++ int id = -1; ++ if (lua_type(L, -1) == LUA_TSTRING) { ++ id = get_command_id(lua_tostring(L, -1)); ++ } ++ if (id >= 0) { ++ lua_pushinteger(L, id); ++ } else { ++ lua_pushnil(L); ++ } ++ return 1; ++} ++ ++/* not that useful: ++ ++static int run_get_csname_id(lua_State * L) ++{ ++ const char *s; ++ size_t k, cs = 0; ++ if (lua_type(L, -1) == LUA_TSTRING) { ++ s = lua_tolstring(L, -1, &k); ++ cs = (size_t) string_lookup(s, k); ++ } ++ lua_pushinteger(L, (lua_Number) cs); ++ return 1; ++} ++ ++*/ + + static int run_get_next(lua_State * L) + { +@@ -296,6 +316,22 @@ static int run_scan_keyword(lua_State * L) + return 1; + } + ++static int run_scan_keyword_cs(lua_State * L) ++{ ++ saved_tex_scanner texstate; ++ const char *s = luaL_checkstring(L, -1); ++ int v = 0; ++ if (s) { ++ save_tex_scanner(texstate); ++ if (scan_keyword_case_sensitive(s)) { ++ v = 1; ++ } ++ unsave_tex_scanner(texstate); ++ } ++ lua_pushboolean(L,v); ++ return 1; ++} ++ + static int run_scan_csname(lua_State * L) + { + unsigned char *s; +@@ -328,6 +364,138 @@ static int run_scan_int(lua_State * L) + return 1; + } + ++/* ++ char * str ; ++*/ ++ ++# define declare_buffer \ ++ unsigned char word[5 + 1]; \ ++ char *uindex = (char *)word; \ ++ luaL_Buffer b ; \ ++ luaL_buffinit(L,&b) ; ++ ++/* ++ str = (char *) uni2str(cur_chr); ++ luaL_addstring(&b,(char *) str); ++ xfree(str); ++*/ ++ ++# define add_to_buffer(chr) \ ++ if (chr <= 255) { \ ++ luaL_addchar(&b,(unsigned) (char) chr); \ ++ } else { \ ++ uindex = uni2string((char *)word,(unsigned) chr); \ ++ *uindex = '\0'; \ ++ luaL_addstring(&b,(char *) word); \ ++ } ++ ++# define push_buffer \ ++ luaL_pushresult(&b); ++ ++static int run_scan_float_indeed(lua_State * L, boolean exponent) ++{ ++ saved_tex_scanner texstate; ++ int ok; ++ boolean negative = false; ++ double d; ++ declare_buffer; ++ save_tex_scanner(texstate); ++ /* we collapse as in scan_dimen */ ++ while(1) { ++ do { ++ get_x_token(); ++ } while (cur_cmd == spacer_cmd); ++ if (cur_tok == minus_token) { ++ negative = !negative; ++ } else if (cur_tok != plus_token) { ++ break; ++ } ++ } ++ if (negative) { ++ add_to_buffer('-'); ++ } ++ /* we accept [.,]digits */ ++ if (cur_tok == point_token || cur_tok == comma_token) { ++ add_to_buffer('.'); ++ while (1) { ++ get_x_token(); ++ if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) { ++ add_to_buffer(cur_chr); ++ } else if (exponent) { ++ goto EXPONENT; ++ } else { ++ back_input(); ++ goto DONE; ++ } ++ } ++ } else { ++ back_input(); ++ while (1) { ++ get_x_token(); ++ if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) { ++ add_to_buffer(cur_chr); ++ } else { ++ if (cur_tok == point_token || cur_tok == comma_token) { ++ add_to_buffer('.'); ++ while (1) { ++ get_x_token(); ++ if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) { ++ add_to_buffer(cur_chr); ++ } else { ++ back_input(); ++ break; ++ } ++ } ++ } else if (exponent) { ++ goto EXPONENT; ++ } else { ++ back_input(); ++ goto DONE; ++ } ++ } ++ } ++ } ++EXPONENT: ++ if ((cur_chr == 'E') || (cur_chr == 'e')) { ++ add_to_buffer(cur_chr); ++ get_x_token(); ++ if ((cur_tok == minus_token) || (cur_tok == plus_token)) { ++ add_to_buffer(cur_chr); ++ } else if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) { ++ add_to_buffer(cur_chr); ++ } ++ while (1) { ++ get_x_token(); ++ if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) { ++ add_to_buffer(cur_chr); ++ } else { ++ break; ++ } ++ } ++ } ++ back_input(); ++DONE: ++ push_buffer; ++ d = lua_tonumberx(L,1,&ok); ++ if (ok) { ++ lua_pushnumber(L,d); ++ } else { ++ lua_pushnil(L); ++ } ++ unsave_tex_scanner(texstate); ++ return 1; ++} ++ ++static int run_scan_float(lua_State * L) ++{ ++ return run_scan_float_indeed(L,true); ++} ++ ++static int run_scan_real(lua_State * L) ++{ ++ return run_scan_float_indeed(L,false); ++} ++ + static int run_scan_dimen(lua_State * L) + { + saved_tex_scanner texstate; +@@ -339,7 +507,7 @@ static int run_scan_dimen(lua_State * L) + if (t>1) + mu = lua_toboolean(L,2); /* mu units required ?*/ + save_tex_scanner(texstate); +- scan_dimen( mu,inf, false); /* arg3 = shortcut */ ++ scan_dimen(mu,inf, false); /* arg3 = shortcut */ + v = cur_val; + o = cur_order; + unsave_tex_scanner(texstate); +@@ -415,25 +583,67 @@ static int run_scan_string(lua_State * L) /* HH */ + } else if (cur_cmd == call_cmd) { + t = token_link(cur_chr); + tokenlist_to_luastring(L,t); ++ } else if (cur_cmd == 11 || cur_cmd == 12 ) { ++ declare_buffer; ++ while (1) { ++ add_to_buffer(cur_chr); ++ get_x_token(); ++ if (cur_cmd != 11 && cur_cmd != 12 ) { ++ break ; ++ } ++ } ++ back_input(); ++ push_buffer; + } else { +- if (cur_cmd == 11 || cur_cmd == 12 ) { +- char * str ; +- luaL_Buffer b ; +- luaL_buffinit(L,&b) ; +- while (1) { +- str = (char *) uni2str(cur_chr); +- luaL_addstring(&b,(char *) str); +- get_x_token(); +- if (cur_cmd != 11 && cur_cmd != 12 ) { +- break ; +- } ++ back_input(); ++ lua_pushnil(L); ++ } ++ unsave_tex_scanner(texstate); ++ return 1; ++} ++ ++static int run_scan_argument(lua_State * L) /* HH */ ++{ /* can be simplified, no need for intermediate list */ ++ saved_tex_scanner texstate; ++ halfword t, saved_defref; ++ save_tex_scanner(texstate); ++ do { ++ get_token(); ++ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); ++ if (cur_cmd == left_brace_cmd) { ++ back_input(); ++ saved_defref = def_ref; ++ (void) scan_toks(false, true); ++ t = def_ref; ++ def_ref = saved_defref; ++ tokenlist_to_luastring(L,t); ++ } else if (cur_cmd == call_cmd) { ++ halfword saved_cur_tok = cur_tok; ++ cur_tok = right_brace_token + '}'; ++ back_input(); ++ cur_tok = saved_cur_tok; ++ back_input(); ++ cur_tok = left_brace_token + '{'; ++ back_input(); ++ saved_defref = def_ref; ++ (void) scan_toks(false, true); ++ t = def_ref; ++ def_ref = saved_defref; ++ tokenlist_to_luastring(L,t); ++ } else if (cur_cmd == 11 || cur_cmd == 12 ) { ++ declare_buffer; ++ while (1) { ++ add_to_buffer(cur_chr); ++ get_x_token(); ++ if (cur_cmd != 11 && cur_cmd != 12 ) { ++ break ; + } +- back_input(); +- luaL_pushresult(&b); +- } else { +- back_input(); +- lua_pushnil(L); + } ++ back_input(); ++ push_buffer; ++ } else { ++ back_input(); ++ lua_pushnil(L); + } + unsave_tex_scanner(texstate); + return 1; +@@ -447,20 +657,16 @@ static int run_scan_word(lua_State * L) /* HH */ + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd == 11 || cur_cmd == 12 ) { +- char *str ; +- luaL_Buffer b ; +- luaL_buffinit(L,&b) ; ++ declare_buffer; + while (1) { +- str = (char *) uni2str(cur_chr); +- luaL_addstring(&b,str); +- xfree(str); ++ add_to_buffer(cur_chr); + get_x_token(); + if (cur_cmd != 11 && cur_cmd != 12 ) { + break ; + } + } + back_input(); +- luaL_pushresult(&b); ++ push_buffer; + } else { + back_input(); + lua_pushnil(L); +@@ -503,12 +709,12 @@ static int lua_tokenlib_is_token(lua_State * L) /* HH */ + return 1; + } + +-/* static int run_expand(lua_State * L) */ +-/* { */ +-/* (void) L; */ +-/* expand(); */ +-/* return 0; */ +-/* } */ ++static int run_expand(lua_State * L) ++{ ++ (void) L; ++ expand(); ++ return 0; ++} + + static int run_lookup(lua_State * L) + { +@@ -529,6 +735,21 @@ static int run_lookup(lua_State * L) + return 1; + } + ++static int lua_tokenlib_is_defined(lua_State * L) ++{ ++ const char *s; ++ size_t l; ++ if (lua_type(L, -1) == LUA_TSTRING) { ++ s = lua_tolstring(L, -1, &l); ++ if (l > 0) { ++ lua_pushboolean(L,string_lookup(s, l) != undefined_control_sequence); ++ return 1; ++ } ++ } ++ lua_pushnil(L); ++ return 1; ++} ++ + static int run_build(lua_State * L) + { + if (lua_type(L, 1) == LUA_TNUMBER) { +@@ -550,6 +771,15 @@ static int run_build(lua_State * L) + } + } + ++static int run_new(lua_State * L) ++{ ++ int cs = 0; ++ int chr = (int) lua_tointeger(L, 1); ++ int cmd = (int) lua_tointeger(L, 2); ++ make_new_token(L, cmd, chr, cs); ++ return 1; ++} ++ + /* token instance functions */ + + static int lua_tokenlib_free(lua_State * L) +@@ -602,7 +832,6 @@ inline static int lua_tokenlib_get_index(lua_State * L) + e -= toks_base; + break; + default: +- e = -1; + break; + } + if ((e >= 0) && (e <= 65535)) { +@@ -630,7 +859,7 @@ inline static int lua_tokenlib_get_cmdname(lua_State * L) + lua_token *n = check_istoken(L, 1); + halfword t = token_info(n->token); + int cmd = (t >= cs_token_flag ? eq_type(t - cs_token_flag) : token_cmd(t)); +- lua_pushstring(L, command_names[cmd].cmd_name); /* can be sped up */ ++ lua_push_string_by_index(L, command_names[cmd].lua); + return 1; + } + +@@ -750,7 +979,7 @@ static int lua_tokenlib_equal(lua_State * L) + n = check_istoken(L, 1); + m = check_istoken(L, 2); + if (token_info(n->token) == token_info(m->token)) { +- lua_pushboolean(L,1); ++ lua_pushboolean(L,1); + return 1; + } + lua_pushboolean(L,0); +@@ -789,6 +1018,19 @@ static int run_scan_token(lua_State * L) + return 1; + } + ++static int run_scan_list(lua_State * L) ++{ ++ saved_tex_scanner texstate; ++ save_tex_scanner(texstate); ++ /*tex ++ This is s tricky call as we are in \LUA\ and therefore ++ mess with the main loop. ++ */ ++ lua_nodelib_push_fast(L, local_scan_box()); ++ unsave_tex_scanner(texstate); ++ return 1; ++} ++ + /* experiment */ + + /* [catcodetable] csname content : \def\csname{content} */ +@@ -844,6 +1086,56 @@ static int get_macro(lua_State * L) + return 0; + } + ++static int set_lua(lua_State *L) ++{ ++ const char *name = null; ++ const char *s = null; ++ size_t lname = 0; ++ int cs; ++ int n = lua_gettop(L); ++ int a = 0; /* global state */ ++ int p = 0; /* protected state */ ++ int f = 0; /* function index */ ++ int nncs = no_new_control_sequence; ++ if (n < 2) { ++ return 0 ; ++ } ++ name = lua_tolstring(L, 1, &lname); ++ if (name == null) { ++ return 0 ; ++ } ++ f = lua_tointeger(L, 2); ++ if (n > 2) { ++ s = lua_tostring(L, 3); ++ if (s) { ++ if (lua_key_eq(s, global)) { ++ a = 4; ++ } else if (lua_key_eq(s, protected)) { ++ p = 1; ++ } ++ } ++ if (n > 3) { ++ s = lua_tostring(L, 4); ++ if (s) { ++ if (lua_key_eq(s, global)) { ++ a = 4; ++ } else if (lua_key_eq(s, protected)) { ++ p = 1; ++ } ++ } ++ } ++ } ++ no_new_control_sequence = false ; ++ cs = string_lookup(name, lname); ++ no_new_control_sequence = nncs; ++ if (p) { ++ define(cs, lua_call_cmd, f); ++ } else { ++ define(cs, lua_expandable_call_cmd, f); ++ } ++ return 0; ++} ++ + static int set_macro(lua_State * L) + { + const char *name = null; +@@ -963,27 +1255,75 @@ static int set_macro(lua_State * L) + return 0; + } + ++static int set_char(lua_State * L) ++{ ++ const char *name = null; ++ const char *s = null; ++ size_t lname = 0; ++ int cs, value; ++ int n = lua_gettop(L); ++ int a = 0 ; /* global state */ ++ int nncs = no_new_control_sequence; ++ if (n < 2) ++ return 0; ++ name = lua_tolstring(L, 1, &lname); ++ if (name == null) ++ return 0; ++ value = lua_tointeger(L, 2); ++ if (value < 0) ++ return 0; ++ if (n > 2) ++ s = lua_tostring(L, 3); ++ if (s && (lua_key_eq(s, global))) { ++ a = 4; ++ } ++ no_new_control_sequence = false ; ++ cs = string_lookup(name, lname); ++ no_new_control_sequence = nncs; ++ define(cs, char_given_cmd, value); ++ return 0; ++} ++ ++static int get_command_names(lua_State * L) ++{ ++ int i; ++ lua_createtable(L,data_cmd+1,0); ++ for (i = 0; command_names[i].lua != 0; i++) { ++ lua_rawgeti(L, LUA_REGISTRYINDEX, command_names[i].lua); ++ lua_rawseti(L, -2, i); ++ } ++ return 1; ++} ++ + static const struct luaL_Reg tokenlib[] = { + { "type", lua_tokenlib_type }, + { "create", run_build }, ++ { "new", run_new }, + { "is_token", lua_tokenlib_is_token }, ++ { "is_defined", lua_tokenlib_is_defined }, ++ { "commands", get_command_names }, ++ { "command_id", run_get_command_id }, ++ { "biggest_char", run_get_biggest_char }, + /* scanners */ + { "get_next", run_get_next }, +- { "put_next", run_put_next }, + { "scan_keyword", run_scan_keyword }, ++ { "scan_keyword_cs", run_scan_keyword_cs }, + { "scan_int", run_scan_int }, ++ { "scan_float", run_scan_float }, ++ { "scan_real", run_scan_real }, + { "scan_dimen", run_scan_dimen }, + { "scan_glue", run_scan_glue }, + { "scan_toks", run_scan_toks }, + { "scan_code", run_scan_code }, + { "scan_string", run_scan_string }, ++ { "scan_argument", run_scan_argument }, + { "scan_word", run_scan_word }, + { "scan_csname", run_scan_csname }, + { "scan_token", run_scan_token }, /* expands next token if needed */ +- /* push into input stream */ +- /* +- { "write",luatwrite }, +- */ ++ { "scan_list", run_scan_list }, ++ /* writers */ ++ { "put_next", run_put_next }, ++ { "expand", run_expand }, + /* getters */ + { "get_command", lua_tokenlib_get_command }, + { "get_index", lua_tokenlib_get_index }, +@@ -995,15 +1335,12 @@ static const struct luaL_Reg tokenlib[] = { + { "get_active", lua_tokenlib_get_active }, + { "get_expandable", lua_tokenlib_get_expandable }, + { "get_protected", lua_tokenlib_get_protected }, +- /* maybe more setters */ +- { "set_macro", set_macro }, + { "get_macro", get_macro }, + { "get_meaning", get_meaning }, +- /* probably never */ +- /* {"expand", run_expand}, */ /* does not work yet! */ +- /* {"csname_id", run_get_csname_id}, */ /* yes or no */ +- /* {"command_id", run_get_command_id}, */ /* yes or no */ +- /* {"cs_offset", run_get_cs_offset}, */ /* not that useful */ ++ /* setters */ ++ { "set_macro", set_macro }, ++ { "set_char", set_char }, ++ { "set_lua", set_lua }, + {NULL, NULL} + }; + +diff --git a/texk/web2c/luatexdir/lua/lnodelib.c b/texk/web2c/luatexdir/lua/lnodelib.c +index 7b4c614ea..d8732df76 100644 +--- a/texk/web2c/luatexdir/lua/lnodelib.c ++++ b/texk/web2c/luatexdir/lua/lnodelib.c +@@ -209,6 +209,80 @@ halfword *check_isnode(lua_State * L, int i) + return NULL; + } + ++static int nodelib_setdir_text(lua_State * L, int i, halfword n) ++{ ++ if (lua_type(L, i) == LUA_TSTRING) { ++ const char *s = lua_tostring(L, i); ++ if (s==lua_key_plus(TLT)) { ++ dir_dir(n) = 0 ; ++ subtype(n) = normal_dir; ++ } else if (s==lua_key_minus(TLT)) { ++ dir_dir(n) = 0 ; ++ subtype(n) = cancel_dir; ++ } else if (s==lua_key_plus(TRT)) { ++ dir_dir(n) = 1 ; ++ subtype(n) = normal_dir; ++ } else if (s==lua_key_minus(TRT)) { ++ dir_dir(n) = 1 ; ++ subtype(n) = cancel_dir; ++ } else if (s==lua_key_plus(LTL)) { ++ dir_dir(n) = 2 ; ++ subtype(n) = normal_dir; ++ } else if (s==lua_key_minus(LTL)) { ++ dir_dir(n) = 2 ; ++ subtype(n) = cancel_dir; ++ } else if (s==lua_key_plus(RTT)) { ++ dir_dir(n) = 3 ; ++ subtype(n) = normal_dir; ++ } else if (s==lua_key_minus(RTT)) { ++ dir_dir(n) = 3 ; ++ subtype(n) = cancel_dir; ++ } else { ++ luaL_error(L, "Bad direction specifier %s", s); ++ } ++ } else { ++ luaL_error(L, "Direction specifiers have to be strings"); ++ } ++ return 0; ++} ++ ++static int nodelib_getdir_par(lua_State * L, int n) ++{ ++ if (lua_type(L, n) == LUA_TSTRING) { ++ const char *s = lua_tostring(L, n); ++ if (s==lua_key(TLT)) ++ return 0 ; ++ else if (s==lua_key(TRT)) ++ return 1 ; ++ else if (s==lua_key(LTL)) ++ return 2 ; ++ else if (s==lua_key(RTT)) ++ return 3 ; ++ else ++ luaL_error(L, "Bad direction specifier %s", s); ++ } else { ++ luaL_error(L, "Direction specifiers have to be strings"); ++ } ++ return 0; ++} ++ ++static int nodelib_getdirection(lua_State * L, int n) ++{ ++ if (lua_type(L, n) == LUA_TNUMBER) { ++ int i = lua_tointeger(L, n); ++ check_dir_value(i); ++ return i; ++ } else { ++ luaL_error(L, "Direction specifiers have to be numbers"); ++ } ++ return 0; ++} ++ ++int nodelib_getdir(lua_State * L, int n) /* the api public one */ ++{ ++ return nodelib_getdir_par(L,n); ++} ++ + /* + + This routine finds the numerical value of a string (or number) at +@@ -216,13 +290,32 @@ halfword *check_isnode(lua_State * L, int i) + + */ + ++/* s = lua_tostring(L, 2); */ ++/* if (lua_key_eq(s, id)) { */ ++ + static int get_node_type_id_from_name(lua_State * L, int n, node_info * data) + { +- int j; +- const char *s = lua_tostring(L, n); +- for (j = 0; data[j].id != -1; j++) { +- if (strcmp(s, data[j].name) == 0) { +- return j; ++ if (data != NULL) { ++ int j; ++ const char *s = lua_tostring(L, n); ++ for (j = 0; data[j].id != -1; j++) { ++ if (s == data[j].name) { ++ return j; ++ } ++ } ++ } ++ return -1; ++} ++ ++static int get_node_subtype_id_from_name(lua_State * L, int n, subtype_info * data) ++{ ++ if (data != NULL) { ++ int j; ++ const char *s = lua_tostring(L, n); ++ for (j = 0; data[j].id != -1; j++) { ++ if (s == data[j].name) { ++ return j; ++ } + } + } + return -1; +@@ -234,7 +327,7 @@ static int get_valid_node_type_id(lua_State * L, int n) + int t = lua_type(L, n); + if (t == LUA_TSTRING) { + i = get_node_type_id_from_name(L,n,node_data); +- if (i<0) { ++ if (i < 0) { + luaL_error(L, "invalid node type id: %s", lua_tostring(L, n)); + } + } else if (t == LUA_TNUMBER) { +@@ -347,6 +440,31 @@ void lua_nodelib_push_fast(lua_State * L, halfword n) + return; + } + ++/* getting and setting fields (helpers) */ ++ ++int nodelib_getlist(lua_State * L, int n) ++{ ++ if (lua_isuserdata(L, n)) { ++ halfword m = *check_isnode(L, n); ++ return m; ++ } else { ++ return null; ++ } ++} ++ ++static str_number nodelib_getstring(lua_State * L, int a) ++{ ++ size_t k; ++ const char *s = lua_tolstring(L, a, &k); ++ return maketexlstring(s, k); ++} ++ ++static int nodelib_cantset(lua_State * L, int n, const char *s) ++{ ++ luaL_error(L,"You cannot set field %s in a node of type %s",s,node_data[type(n)].name); ++ return 0; ++} ++ + /* converts type strings to type ids */ + + static int lua_nodelib_id(lua_State * L) +@@ -439,6 +557,44 @@ static int lua_nodelib_direct_setsubtype(lua_State * L) + return 1; + } + ++/* node.direct.getexpansion */ ++/* node.direct.setexpansion */ ++ ++static int lua_nodelib_direct_getexpansion(lua_State * L) ++{ ++ halfword n = lua_tointeger(L, 1); ++ if (n != null) { ++ halfword t = type(n); ++ if (t == glyph_node) { ++ lua_pushinteger(L, ex_glyph(n)); ++ return 1; ++ } else if (t == kern_node) { ++ lua_pushinteger(L, ex_kern(n)); ++ return 1; ++ } ++ } ++ lua_pushnil(L); ++ return 1; ++} ++ ++static int lua_nodelib_direct_setexpansion(lua_State * L) ++{ ++ halfword n = lua_tointeger(L, 1); ++ if (n) { ++ halfword t = type(n); ++ halfword e = 0; ++ if (lua_type(L, 2) == LUA_TNUMBER) { ++ e = (halfword) lua_tointeger(L, 2); ++ } ++ if (t == glyph_node) { ++ ex_glyph(n) = e; ++ } else if ( t == kern_node) { ++ ex_kern(n) = e; ++ } ++ } ++ return 0; ++} ++ + /* node.direct.getfont */ + /* node.direct.setfont */ + +@@ -530,6 +686,10 @@ static int lua_nodelib_direct_getfam(lua_State * L) + lua_pushinteger(L, math_fam(n)); + } else if (t == delim_node) { + lua_pushinteger(L, small_fam(n)); ++ } else if (t == fraction_noad) { ++ lua_pushinteger(L, fraction_fam(n)); ++ } else if (t == simple_noad) { ++ lua_pushinteger(L, noad_fam(n)); + } else { + lua_pushnil(L); + } +@@ -565,6 +725,10 @@ static int lua_nodelib_direct_setfam(lua_State * L) + math_fam(n) = (halfword) lua_tointeger(L, 2); + } else if (t == delim_node) { + small_fam(n) = (halfword) lua_tointeger(L, 2); ++ } else if (t == fraction_noad) { ++ fraction_fam(n) = (halfword) lua_tointeger(L, 2); ++ } else if (t == simple_noad) { ++ noad_fam(n) = (halfword) lua_tointeger(L, 2); + } + } + return 0; +@@ -572,7 +736,7 @@ static int lua_nodelib_direct_setfam(lua_State * L) + + /* node.getchar */ + +- static int lua_nodelib_getchar(lua_State * L) ++static int lua_nodelib_getchar(lua_State * L) + { + halfword *n = lua_touserdata(L, 1); + if ((n == NULL) || (! lua_getmetatable(L,1))) { +@@ -665,7 +829,8 @@ static int lua_nodelib_direct_setattributelist(lua_State * L) + { + halfword n = lua_tointeger(L, 1); + if ((n) && nodetype_has_attributes(type(n))) { +- if (lua_type(L, 2) == LUA_TNUMBER) { ++ int t = lua_type(L, 2); ++ if (t == LUA_TNUMBER) { + halfword a =lua_tointeger(L, 2); + if (type(a) == attribute_list_node) { + reassign_attribute(n,a); +@@ -674,6 +839,12 @@ static int lua_nodelib_direct_setattributelist(lua_State * L) + } else { + reassign_attribute(n,null); + } ++ } else if (t == LUA_TBOOLEAN) { ++ if (lua_toboolean(L,2)) { ++ reassign_attribute(n,current_attribute_list()); ++ } else { ++ reassign_attribute(n,null); ++ } + } else { + reassign_attribute(n,null); + } +@@ -681,7 +852,6 @@ static int lua_nodelib_direct_setattributelist(lua_State * L) + } + return 0; + } +- + /* node.direct.getpenalty */ + /* node.direct.setpenalty */ + +@@ -827,7 +997,15 @@ static int lua_nodelib_direct_getkern(lua_State * L) + halfword n = lua_tointeger(L, 1); + if (n) { + halfword t = type(n); +- if (t == kern_node || t == margin_kern_node) { ++ if (t == kern_node) { ++ if (lua_toboolean(L,2)) { ++ lua_pushnumber(L, (1+ex_kern(n)/1000) * width(n)); ++ lua_pushinteger(L, ex_kern(n)); ++ return 2; ++ } else { ++ lua_pushinteger(L, width(n)); ++ } ++ } else if (t == margin_kern_node) { + lua_pushinteger(L, width(n)); + } else if (t == math_node) { + lua_pushinteger(L, surround(n)); +@@ -874,7 +1052,7 @@ static int lua_nodelib_direct_getdir(lua_State * L) + if (n) { + halfword t = type(n); + if (t == dir_node) { +- lua_push_dir_text(L, dir_dir(n)); ++ lua_push_dir_text(L, dir_dir(n), subtype(n)); + } else if (t == hlist_node || t == vlist_node) { + lua_push_dir_par(L, box_dir(n)); + } else if (t == rule_node) { +@@ -890,19 +1068,68 @@ static int lua_nodelib_direct_getdir(lua_State * L) + return 1; + } + ++static int lua_nodelib_direct_getdirection(lua_State * L) ++{ ++ halfword n = lua_tointeger(L, 1); ++ if (n) { ++ halfword t = type(n); ++ if (t == dir_node) { ++ lua_push_direction(L, dir_dir(n)); ++ lua_pushboolean(L, subtype(n)); ++ return 2; ++ } else if (t == hlist_node || t == vlist_node) { ++ lua_push_direction(L, box_dir(n)); ++ } else if (t == rule_node) { ++ lua_push_direction(L, rule_dir(n)); ++ } else if (t == local_par_node) { ++ lua_push_direction(L, local_par_dir(n)); ++ } else { ++ lua_pushnil(L); ++ } ++ } else { ++ lua_pushnil(L); ++ } ++ return 1; ++} ++ + static int lua_nodelib_direct_setdir(lua_State * L) + { + halfword n = lua_tointeger(L, 1); + if (n) { + halfword t = type(n); + if (t == dir_node) { +- dir_dir(n) = nodelib_getdir(L, 2, 0); ++ nodelib_setdir_text(L, 2, n); + } else if (t == hlist_node || type(n) == vlist_node) { +- box_dir(n) = nodelib_getdir(L, 2, 1); ++ box_dir(n) = nodelib_getdir_par(L, 2); + } else if (t == rule_node) { +- rule_dir(n) = nodelib_getdir(L, 2, 1); ++ rule_dir(n) = nodelib_getdir_par(L, 2); + } else if (t == local_par_node) { +- local_par_dir(n) = nodelib_getdir(L, 3, 1); ++ local_par_dir(n) = nodelib_getdir_par(L, 2); ++ } ++ } ++ return 0; ++} ++ ++static int lua_nodelib_direct_setdirection(lua_State * L) ++{ ++ halfword n = lua_tointeger(L, 1); ++ if (n) { ++ halfword t = type(n); ++ if (t == dir_node) { ++ dir_dir(n) = nodelib_getdirection(L, 2); ++ if ((lua_type(L, 3) == LUA_TBOOLEAN)) { ++ if (lua_toboolean(L, 3)) { ++ subtype(n) = cancel_dir; ++ } else { ++ subtype(n) = normal_dir; ++ } ++ } ++ } else if (t == hlist_node || type(n) == vlist_node) { ++ box_dir(n) = nodelib_getdirection(L, 2); ++ } else if (t == rule_node) { ++ rule_dir(n) = nodelib_getdirection(L, 2); ++ } else if (t == local_par_node) { ++ local_par_dir(n) = nodelib_getdirection(L, 2); + } + } + return 0; +@@ -1017,15 +1244,24 @@ static int lua_nodelib_direct_setdisc(lua_State * L) + /* node.direct.getwhd */ + /* node.direct.setwhd */ + +-#define push_list_whd(n) \ +- lua_pushinteger(L, width(n)); \ ++#define push_list_whd(n) do { \ ++ lua_pushinteger(L, width(n)); \ + lua_pushinteger(L, height(n)); \ +- lua_pushinteger(L, depth(n)); \ ++ lua_pushinteger(L, depth(n)); \ ++} while (0) ++ ++#define push_char_whd(n) do { \ ++ lua_pushinteger(L, char_width(font(n),character(n))); \ ++ lua_pushinteger(L, char_height(font(n),character(n))); \ ++ lua_pushinteger(L, char_depth(font(n),character(n))); \ ++} while (0) + +-#define push_char_whd(n) \ +- lua_pushinteger(L, char_width(font(n),character(n))); \ ++#define push_char_ehd(n) do { \ ++ lua_pushnumber(L, (1+ex_glyph(n)/1000) * char_width(font(n),character(n))); \ + lua_pushinteger(L, char_height(font(n),character(n))); \ +- lua_pushinteger(L, char_depth(font(n),character(n))); \ ++ lua_pushinteger(L, char_depth(font(n),character(n))); \ ++ lua_pushinteger(L, ex_glyph(n)); \ ++} while (0) + + static int lua_nodelib_direct_getwhd(lua_State * L) + { +@@ -1036,8 +1272,13 @@ static int lua_nodelib_direct_getwhd(lua_State * L) + push_list_whd(n); + return 3; + } else if (t == glyph_node) { +- push_char_whd(n); +- return 3; ++ if (lua_toboolean(L,2)) { ++ push_char_ehd(n); ++ return 4; ++ } else { ++ push_char_whd(n); ++ return 3; ++ } + } else if (t == glue_node) { + halfword l = leader_ptr(n); + if (l != null) { +@@ -1104,8 +1345,13 @@ static int lua_nodelib_direct_setwhd(lua_State * L) + push_list_whd(*n); + return 3; + } else if (t == glyph_node) { +- push_char_whd(*n); +- return 3; ++ if (lua_toboolean(L,2)) { ++ push_char_ehd(*n); ++ return 4; ++ } else { ++ push_char_whd(*n); ++ return 3; ++ } + } else if (t == glue_node) { + halfword l = leader_ptr(*n); + if (l != null) { +@@ -1214,7 +1460,7 @@ static int lua_nodelib_direct_getleader(lua_State * L) + return 1; + } + +- static int lua_nodelib_direct_setleader(lua_State * L) ++static int lua_nodelib_direct_setleader(lua_State * L) + { + halfword n = lua_tointeger(L, 1); + if ((n) && (type(n) == glue_node)) { +@@ -1227,7 +1473,7 @@ static int lua_nodelib_direct_getleader(lua_State * L) + return 0; + } + +- /* node.getleader */ ++/* node.getleader */ + + static int lua_nodelib_getleader(lua_State * L) + { +@@ -1243,6 +1489,241 @@ static int lua_nodelib_direct_getleader(lua_State * L) + return 1; + } + ++/* node.direct.getdata */ ++/* node.direct.setdata */ ++ ++#define get_user_node_direct_value(L, n) do { \ ++ switch (user_node_type(n)) { \ ++ case 'a': \ ++ nodelib_pushdirect(user_node_value(n)); \ ++ break; \ ++ case 'd': \ ++ lua_pushinteger(L, user_node_value(n)); \ ++ break; \ ++ case 'l': \ ++ if (user_node_value(n) != 0) { \ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, user_node_value(n)); \ ++ } else { \ ++ lua_pushnil(L); \ ++ } \ ++ break; \ ++ case 'n': \ ++ nodelib_pushdirect(user_node_value(n)); \ ++ break; \ ++ case 's': \ ++ nodelib_pushstring(L, user_node_value(n)); \ ++ break; \ ++ case 't': \ ++ tokenlist_to_lua(L, user_node_value(n)); \ ++ break; \ ++ default: \ ++ lua_pushinteger(L, user_node_value(n)); \ ++ break; \ ++ } \ ++} while (0) ++ ++#define set_user_node_direct_value(L,n,i) do { \ ++ switch (user_node_type(n)) { \ ++ case 'a': \ ++ user_node_value(n) = nodelib_getlist(L, i); \ ++ break; \ ++ case 'd': \ ++ user_node_value(n) = (halfword) lua_roundnumber(L, i); \ ++ break; \ ++ case 'l': \ ++ lua_pushvalue(L, i); \ ++ if (user_node_value(n) != 0) { \ ++ luaL_unref(L, LUA_REGISTRYINDEX,user_node_value(n)); \ ++ } \ ++ user_node_value(n) = luaL_ref(L, LUA_REGISTRYINDEX); \ ++ break; \ ++ case 'n': \ ++ user_node_value(n) = nodelib_getlist(L, i); \ ++ break; \ ++ case 's': \ ++ user_node_value(n) = nodelib_getstring(L, i); \ ++ break; \ ++ case 't': \ ++ user_node_value(n) = nodelib_gettoks(L, i); \ ++ break; \ ++ default: \ ++ user_node_value(n) = (halfword) lua_roundnumber(L, i); \ ++ break; \ ++ } \ ++} while (0) ++ ++#define get_pdf_literal_direct_value(L,n) do { \ ++ if (pdf_literal_type(n) == lua_refid_literal) { \ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, pdf_literal_data(n)); \ ++ } else if (pdf_literal_type(n) == lua_refid_literal) { \ ++ tokenlist_to_luastring(L, pdf_literal_data(n)); \ ++ } \ ++} while (0) ++ ++#define set_pdf_literal_direct_normal(L,n,i) do { \ ++ if (ini_version) { \ ++ pdf_literal_data(n) = nodelib_gettoks(L, i); \ ++ pdf_literal_type(n) = normal; \ ++ } else { \ ++ lua_pushvalue(L, i); \ ++ pdf_literal_data(n) = luaL_ref(L, LUA_REGISTRYINDEX); \ ++ pdf_literal_type(n) = lua_refid_literal; \ ++ } \ ++} while (0) ++ ++#define set_pdf_literal_direct_token(L,n,i) do { \ ++ pdf_literal_data(n) = nodelib_gettoks(L, i); \ ++} while (0) ++ ++#define cleanup_late_lua(n) do { \ ++ if (late_lua_data(n) != 0) { \ ++ if (late_lua_type(n) == normal) { \ ++ delete_token_ref(late_lua_data(n)); \ ++ } else if (late_lua_type(n) == lua_refid_literal) { \ ++ luaL_unref(L, LUA_REGISTRYINDEX,late_lua_data(n)); \ ++ } \ ++ } \ ++} while (0) ++ ++#define cleanup_late_lua_name(n) do { \ ++ if (late_lua_name(n) != 0) { \ ++ delete_token_ref(late_lua_name(n)); \ ++ } \ ++} while (0) ++ ++#define set_late_lua_direct_normal(L,n,i) do { \ ++ cleanup_late_lua(n) ; \ ++ if (ini_version) { \ ++ late_lua_data(n) = nodelib_gettoks(L, i); \ ++ late_lua_type(n) = normal; \ ++ } else if (lua_type(L, i) == LUA_TNUMBER) { \ ++ late_lua_data(n) = lua_tointeger(L,i); \ ++ late_lua_type(n) = lua_refid_call; \ ++ } else { \ ++ lua_pushvalue(L, i); \ ++ late_lua_data(n) = luaL_ref(L, LUA_REGISTRYINDEX); \ ++ late_lua_type(n) = lua_refid_literal; \ ++ } \ ++} while (0) ++ ++#define set_late_lua_direct_token(L,n,i) do { \ ++ cleanup_late_lua(n) ; \ ++ late_lua_data(n) = nodelib_gettoks(L, i); \ ++ late_lua_type(n) = normal; \ ++} while (0) ++ ++#define get_late_lua_direct_value(L,n) do { \ ++ if (late_lua_type(n) == lua_refid_literal) { \ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, late_lua_data(n)); \ ++ } else if (late_lua_type(n) == lua_refid_call) { \ ++ lua_pushinteger(L, late_lua_data(n)); \ ++ } else if (late_lua_type(n) == normal) { \ ++ tokenlist_to_luastring(L, late_lua_data(n)); \ ++ } \ ++} while (0) ++ ++#define set_special_direct_value(L,n,i) do { \ ++ write_tokens(n) = nodelib_gettoks(L, i); \ ++} while (0) ++ ++#define get_special_direct_value(L,n) do { \ ++ tokenlist_to_luastring(L, write_tokens(n)); \ ++} while (0) ++ ++#define set_write_direct_value(L,n,i) do { \ ++ write_tokens(n) = nodelib_gettoks(L, i); \ ++} while (0) ++ ++#define get_write_direct_value(L,n) do { \ ++ tokenlist_to_lua(L, write_tokens(n)); \ ++} while (0) ++ ++#define set_pdf_setmatrix_direct_value(L,n,i) do { \ ++ pdf_setmatrix_data(n) = nodelib_gettoks(L, i); \ ++} while (0) ++ ++#define get_pdf_setmatrix_direct_value(L,n) do { \ ++ tokenlist_to_luastring(L, pdf_setmatrix_data(n)); \ ++} while (0) ++ ++/*tex ++ ++ These getter and setter get |data| as well as |value| fields. One can ++ make them equivalent to |getvalue| and |setvalue| if needed. ++ ++*/ ++ ++static int lua_nodelib_direct_getdata(lua_State * L) ++{ ++ halfword n = lua_tointeger(L, 1); ++ if (n == null) { ++ lua_pushnil(L); ++ } else { ++ halfword t = type(n) ; ++ if (t == glyph_node) { ++ lua_pushinteger(L,glyph_node_data(n)); ++ } else if (t == boundary_node) { ++ lua_pushinteger(L,boundary_value(n)); ++ } else if (t == whatsit_node) { ++ halfword s = subtype(n); ++ if (s == user_defined_node) { ++ get_user_node_direct_value(L, n); ++ } else if (s == pdf_literal_node) { ++ get_pdf_literal_direct_value(L, n); ++ /*tex A bonus. */ ++ lua_pushinteger(L,pdf_literal_mode(n)); ++ return 2; ++ } else if (s == late_lua_node) { ++ get_late_lua_direct_value(L, n); ++ } else if (s == pdf_setmatrix_node) { ++ get_pdf_setmatrix_direct_value(L, n); ++ } else if (s == special_node) { ++ get_special_direct_value(L, n); ++ } else if (s == write_node) { ++ get_write_direct_value(L, n); ++ } else { ++ lua_pushnil(L); ++ } ++ } else { ++ lua_pushnil(L); ++ } ++ } ++ return 1; ++} ++ ++static int lua_nodelib_direct_setdata(lua_State * L) /* data and value */ ++{ ++ halfword n = lua_tointeger(L, 1); ++ if (n != null) { ++ halfword t = type(n) ; ++ if (t == glyph_node) { ++ glyph_node_data(n) = lua_tointeger(L,2); ++ } else if (t == boundary_node) { ++ boundary_value(n) = lua_tointeger(L,2); ++ } else if (t == whatsit_node) { ++ halfword s = subtype(n); ++ if (s == user_defined_node) { ++ set_user_node_direct_value(L, n, 2); ++ } else if (s == pdf_literal_node) { ++ set_pdf_literal_direct_normal(L, n, 2); ++ if (lua_type(L,2) == LUA_TNUMBER) { ++ /*tex A bonus. */ ++ pdf_literal_mode(n) = lua_tointeger(L,2); ++ } ++ } else if (s == late_lua_node) { ++ set_late_lua_direct_normal(L, n, 2); ++ } else if (s == pdf_setmatrix_node) { ++ set_pdf_setmatrix_direct_value(L, n, 2); ++ } else if (s == special_node) { ++ set_special_direct_value(L, n, 2); ++ } else if (s == write_node) { ++ set_write_direct_value(L,n,2); ++ } ++ } ++ } ++ return 0; ++} ++ + /* node.direct.getnext */ + /* node.direct.setnext */ + +@@ -1488,11 +1969,13 @@ static int lua_nodelib_type(lua_State * L) + if (lua_type(L,1) == LUA_TNUMBER) { + int i = lua_tointeger(L, 1); + if (known_node_type(i)) { +- lua_pushstring(L, node_data[i].name); ++ /* lua_pushstring(L, node_data[i].name); */ ++ lua_push_string_by_index(L, node_data[i].lua); + return 1; + } + } else if (maybe_isnode(L, 1) != NULL) { +- lua_pushstring(L,"node"); ++ /* lua_pushstring(L,"node"); */ ++ lua_push_string_by_name(L,node); + return 1; + } + lua_pushnil(L); +@@ -1501,10 +1984,9 @@ static int lua_nodelib_type(lua_State * L) + + /* node.new (allocate a new node) */ + +-static int lua_nodelib_new(lua_State * L) ++static halfword lua_nodelib_new_node(lua_State * L) + { + int i, j; +- halfword n = null; + int t = lua_type(L, 1); + if (t == LUA_TNUMBER) { + i = lua_tointeger(L,1); +@@ -1536,33 +2018,26 @@ static int lua_nodelib_new(lua_State * L) + } + } else if (t == LUA_TNUMBER) { + j = (int) lua_tointeger(L, 2); ++ } else if (t == LUA_TSTRING) { ++ j = get_node_subtype_id_from_name(L,2,node_data[i].subtypes); + } else { + j = 0; + } +- n = new_node(i, j); ++ return new_node(i, j); ++} ++ ++static int lua_nodelib_new(lua_State * L) ++{ ++ halfword n = lua_nodelib_new_node(L); + lua_nodelib_push_fast(L, n); + return 1; + } + +-/* node.direct.new (still with checking) */ ++/* node.direct.new */ + + static int lua_nodelib_direct_new(lua_State * L) + { +- int j; +- halfword n ; +- int i = get_valid_node_type_id(L, 1); +- if (i == whatsit_node) { +- j = -1; +- if (lua_gettop(L) > 1) +- j = get_valid_node_subtype_id(L, 2); +- if (j < 0) +- luaL_error(L, "Creating a whatsit requires the subtype number as a second argument"); +- } else { +- j = 0; +- if (lua_gettop(L) > 1) +- j = (int) lua_tointeger(L, 2); +- } +- n = new_node(i, j); ++ halfword n = lua_nodelib_new_node(L); + lua_pushinteger(L,n); + return 1; + } +@@ -1704,9 +2179,6 @@ static int lua_nodelib_remove(lua_State * L) + alink(vlink(current)) = t; + current = vlink(current); + } +-#if DEBUG +- show_node_links(head, "after"); +-#endif + /* can be: lua_nodelib_push_fast(L, head); */ + lua_pushinteger(L, head); + lua_nodelib_push(L); +@@ -1843,8 +2315,9 @@ static int lua_nodelib_direct_insert_before(lua_State * L) + current = tail_of_list(head); + if (head != current) { + halfword t = alink(current); +- if (t == null || vlink(t) != current) ++ if (t == null || vlink(t) != current) { + set_t_to_prev(head, current); ++ } + couple_nodes(t, n); + } + couple_nodes(n, current); /* nice but incompatible: couple_nodes(tail_of_list(n),current) */ +@@ -2091,8 +2564,10 @@ static int lua_nodelib_hpack(lua_State * L) + luaL_error(L, "wrong mode in hpack"); + } + if (lua_gettop(L) > 3) { +- if (lua_type(L, 4) == LUA_TSTRING) { +- d = nodelib_getdir(L, 4, 1); ++ if (lua_type(L, 4) == LUA_TNUMBER) { ++ d = nodelib_getdirection(L, 4); ++ } else if (lua_type(L, 4) == LUA_TSTRING) { ++ d = nodelib_getdir_par(L, 4); + } else { + lua_pushstring(L, "incorrect 4th argument"); + } +@@ -2138,8 +2613,10 @@ static int lua_nodelib_direct_hpack(lua_State * L) + lua_pushstring(L, "incorrect 3rd argument"); + } + if (lua_gettop(L) > 3) { +- if (lua_type(L, 4) == LUA_TSTRING) { +- d = nodelib_getdir(L, 4, 1); ++ if (lua_type(L, 4) == LUA_TNUMBER) { ++ d = nodelib_getdirection(L, 4); ++ } else if (lua_type(L, 4) == LUA_TSTRING) { ++ d = nodelib_getdir_par(L, 4); + } else { + lua_pushstring(L, "incorrect 4th argument"); + } +@@ -2177,8 +2654,10 @@ static int lua_nodelib_vpack(lua_State * L) + } + + if (lua_gettop(L) > 3) { +- if (lua_type(L, 4) == LUA_TSTRING) { +- d = nodelib_getdir(L, 4, 1); ++ if (lua_type(L, 4) == LUA_TNUMBER) { ++ d = nodelib_getdirection(L, 4); ++ } else if (lua_type(L, 4) == LUA_TSTRING) { ++ d = nodelib_getdir_par(L, 4); + } else { + lua_pushstring(L, "incorrect 4th argument"); + } +@@ -2222,8 +2701,10 @@ static int lua_nodelib_direct_vpack(lua_State * L) + } + + if (lua_gettop(L) > 3) { +- if (lua_type(L, 4) == LUA_TSTRING) { +- d = nodelib_getdir(L, 4, 1); ++ if (lua_type(L, 4) == LUA_TNUMBER) { ++ d = nodelib_getdirection(L, 4); ++ } else if (lua_type(L, 4) == LUA_TSTRING) { ++ d = nodelib_getdir_par(L, 4); + } else { + lua_pushstring(L, "incorrect 4th argument"); + } +@@ -2269,13 +2750,17 @@ static int lua_nodelib_dimensions(lua_State * L) + n = *(check_isnode(L, i)); + if (lua_gettop(L) > i && !lua_isnil(L, (i + 1))) { + if (lua_type(L, (i + 1)) == LUA_TSTRING) { +- d = nodelib_getdir(L, (i + 1), 1); ++ d = nodelib_getdir_par(L, (i + 1)); + } else { + p = *(check_isnode(L, (i + 1))); + } + } +- if (lua_gettop(L) > (i + 1) && lua_type(L, (i + 2)) == LUA_TSTRING) { +- d = nodelib_getdir(L, (i + 2), 1); ++ if (lua_gettop(L) > (i + 1)) { ++ if (lua_type(L, (i + 2)) == LUA_TNUMBER) { ++ d = nodelib_getdirection(L, (i + 2)); ++ } else if (lua_type(L, (i + 2)) == LUA_TSTRING) { ++ d = nodelib_getdir_par(L, (i + 2)); ++ } + } + siz = natural_sizes(n, p, g_mult, g_sign, g_order, d); + lua_pushinteger(L, siz.wd); +@@ -2322,7 +2807,8 @@ static int lua_nodelib_direct_dimensions(lua_State * L) + int g_order = normal; + int i = 1; + int d = -1; +- halfword n = null, p = null; ++ halfword n = null; ++ halfword p = null; + if (top > 3) { + i += 3; + g_mult = (glue_ratio) lua_tonumber(L, 1); /* integer or float */ +@@ -2332,13 +2818,18 @@ static int lua_nodelib_direct_dimensions(lua_State * L) + n = (halfword) lua_tointeger(L,i); + if (lua_gettop(L) > i && !lua_isnil(L, (i + 1))) { + if (lua_type(L, (i + 1)) == LUA_TSTRING) { +- d = nodelib_getdir(L, (i + 1), 1); ++ d = nodelib_getdir_par(L, (i + 1)); + } else { + p = (halfword) lua_tointeger(L,i+1); + } + } +- if (lua_gettop(L) > (i + 1) && lua_type(L, (i + 2)) == LUA_TSTRING) +- d = nodelib_getdir(L, (i + 2), 1); ++ if (lua_gettop(L) > (i + 1)) { ++ if (lua_type(L, (i + 2)) == LUA_TNUMBER) { ++ d = nodelib_getdirection(L, (i + 2)); ++ } else if (lua_type(L, (i + 2)) == LUA_TSTRING) { ++ d = nodelib_getdir_par(L, (i + 2)); ++ } ++ } + siz = natural_sizes(n, p, g_mult, g_sign, g_order, d); + lua_pushinteger(L, siz.wd); + lua_pushinteger(L, siz.ht); +@@ -2383,7 +2874,7 @@ static int lua_nodelib_mlist_to_hlist(lua_State * L) + luaL_checkany(L, 3); + m = lua_toboolean(L, 3); + mlist_to_hlist(n, m, w); +- alink(vlink(temp_head)) = null; /*hh-ls */ ++ alink(vlink(temp_head)) = null; + lua_nodelib_push_fast(L, vlink(temp_head)); + return 1; + } +@@ -2407,8 +2898,8 @@ static int lua_nodelib_mfont(lua_State * L) + identifiers. It has to do some more work, because not all + identifiers are valid for all types of nodes. + +- If really needed we can optimize this one using a big if .. +- .. else like with the getter and setter. ++ We can make this faster if needed but when this needs to ++ be called often something is wrong with the code. + + */ + +@@ -2438,16 +2929,19 @@ static int get_node_field_id(lua_State * L, int n, int node) + } + } else { + int j; +- const char **fields = node_data[t].fields; ++ field_info *fields ; + if (t == whatsit_node) { + fields = whatsit_node_data[subtype(node)].fields; ++ } else { ++ fields = node_data[t].fields; + } + if (lua_key_eq(s, list)) { + s = lua_key(head); + } + if (fields != NULL) { +- for (j = 0; fields[j] != NULL; j++) { +- if (strcmp(s, fields[j]) == 0) { ++ for (j = 0; fields[j].lua != 0; j++) { ++ // if (strcmp(s, fields[j]) == 0) { ++ if (fields[j].name == s) { + return j + 3; + } + } +@@ -2549,7 +3043,7 @@ static int lua_nodelib_fields(lua_State * L) + { + int i = -1; + int offset = 2; +- const char **fields; ++ field_info *fields; + int t = get_valid_node_type_id(L, 1); + if (t == whatsit_node) { + t = get_valid_node_subtype_id(L, 2); +@@ -2573,26 +3067,63 @@ static int lua_nodelib_fields(lua_State * L) + lua_rawseti(L, -2, -1); + } + if (fields != NULL) { +- for (i = 0; fields[i] != NULL; i++) { +- lua_pushstring(L, fields[i]); /* todo */ ++ for (i = 0; fields[i].lua != 0; i++) { ++ // lua_pushstring(L, fields[i]); /* todo */ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, fields[i].lua); + lua_rawseti(L, -2, (i + offset)); + } + } + return 1; + } + ++static int lua_nodelib_values(lua_State * L) ++{ ++ int i = -1; ++ subtype_info *values = NULL; ++ const char *s ; ++ int t = lua_type(L,1); ++ if (t == LUA_TSTRING) { ++ /* ++ delimiter options (bit set) ++ delimiter modes (bit set) ++ */ ++ s = lua_tostring(L,1); ++ if (lua_key_eq(s,dir)) values = node_values_dir; ++ else if (lua_key_eq(s,direction)) values = node_values_dir; ++ else if (lua_key_eq(s,glue)) values = node_values_fill; ++ /* backend */ ++ else if (lua_key_eq(s,pdf_literal)) values = node_values_pdf_literal; ++ else if (lua_key_eq(s,pdf_action)) values = node_values_pdf_action; ++ else if (lua_key_eq(s,pdf_window)) values = node_values_pdf_window; ++ else if (lua_key_eq(s,color_stack)) values = node_values_color_stack; ++ /* extras */ ++ else if (lua_key_eq(s,pagestate)) values = other_values_page_states; ++ } ++ if (values != NULL) { ++ lua_checkstack(L, 2); ++ lua_newtable(L); ++ for (i = 0; values[i].id >= 0 ; i++) { ++ lua_rawgeti(L, LUA_REGISTRYINDEX, values[i].lua); ++ lua_rawseti(L, -2, values[i].id); ++ } ++ } else { ++ lua_pushnil(L); ++ } ++ return 1; ++} ++ + static int lua_nodelib_subtypes(lua_State * L) + { + int i = -1; +- int l = 0; +- const char **subtypes = NULL; ++ subtype_info *subtypes = NULL; + const char *s ; + int t = lua_type(L,1); + if (t == LUA_TSTRING) { + /* official accessors */ + s = lua_tostring(L,1); + if (lua_key_eq(s,glyph)) subtypes = node_subtypes_glyph; +- else if (lua_key_eq(s,glue)) { subtypes = node_subtypes_glue; l = 1; } ++ else if (lua_key_eq(s,glue)) subtypes = node_subtypes_glue; ++ else if (lua_key_eq(s,dir)) subtypes = node_subtypes_dir; + else if (lua_key_eq(s,boundary)) subtypes = node_subtypes_boundary; + else if (lua_key_eq(s,penalty)) subtypes = node_subtypes_penalty; + else if (lua_key_eq(s,kern)) subtypes = node_subtypes_kern; +@@ -2602,8 +3133,8 @@ static int lua_nodelib_subtypes(lua_State * L) + || lua_key_eq(s,vlist)) subtypes = node_subtypes_list; /* too many but ok as reserved */ + else if (lua_key_eq(s,adjust)) subtypes = node_subtypes_adjust; + else if (lua_key_eq(s,disc)) subtypes = node_subtypes_disc; +- else if (lua_key_eq(s,fill)) subtypes = node_subtypes_fill; +- else if (lua_key_eq(s,leader)) { subtypes = node_subtypes_leader; l = 2; } ++ else if (lua_key_eq(s,fill)) subtypes = node_values_fill; ++ else if (lua_key_eq(s,leader)) subtypes = node_subtypes_leader; + else if (lua_key_eq(s,marginkern)) subtypes = node_subtypes_marginkern; + else if (lua_key_eq(s,math)) subtypes = node_subtypes_math; + else if (lua_key_eq(s,noad)) subtypes = node_subtypes_noad; +@@ -2611,13 +3142,14 @@ static int lua_nodelib_subtypes(lua_State * L) + else if (lua_key_eq(s,accent)) subtypes = node_subtypes_accent; + else if (lua_key_eq(s,fence)) subtypes = node_subtypes_fence; + /* backend */ +- else if (lua_key_eq(s,pdf_destination)) subtypes = node_subtypes_pdf_destination; +- else if (lua_key_eq(s,pdf_literal)) subtypes = node_subtypes_pdf_literal; ++ else if (lua_key_eq(s,pdf_destination)) subtypes = node_values_pdf_destination; ++ else if (lua_key_eq(s,pdf_literal)) subtypes = node_values_pdf_literal; + } else if (t == LUA_TNUMBER) { + /* maybe */ + t = lua_tointeger(L,1); + if (t == glyph_node) subtypes = node_subtypes_glyph; +- else if (t == glue_node) { subtypes = node_subtypes_glue; l = 1; } ++ else if (t == glue_node) subtypes = node_subtypes_glue; ++ else if (t == dir_node) subtypes = node_subtypes_dir; + else if (t == boundary_node) subtypes = node_subtypes_boundary; + else if (t == penalty_node) subtypes = node_subtypes_penalty; + else if (t == kern_node) subtypes = node_subtypes_kern; +@@ -2626,7 +3158,7 @@ static int lua_nodelib_subtypes(lua_State * L) + || (t == vlist_node)) subtypes = node_subtypes_list; + else if (t == adjust_node) subtypes = node_subtypes_adjust; + else if (t == disc_node) subtypes = node_subtypes_disc; +- else if (t == glue_spec_node) subtypes = node_subtypes_fill; ++ else if (t == glue_spec_node) subtypes = node_values_fill; + else if (t == margin_kern_node) subtypes = node_subtypes_marginkern; + else if (t == math_node) subtypes = node_subtypes_math; + else if (t == simple_noad) subtypes = node_subtypes_noad; +@@ -2634,29 +3166,15 @@ static int lua_nodelib_subtypes(lua_State * L) + else if (t == accent_noad) subtypes = node_subtypes_accent; + else if (t == fence_noad) subtypes = node_subtypes_fence; + /* backend */ +- else if (t == pdf_dest_node) subtypes = node_subtypes_pdf_destination; +- else if (t == pdf_literal_node) subtypes = node_subtypes_pdf_literal; ++ else if (t == pdf_dest_node) subtypes = node_values_pdf_destination; ++ else if (t == pdf_literal_node) subtypes = node_values_pdf_literal; + } + if (subtypes != NULL) { + lua_checkstack(L, 2); + lua_newtable(L); +- if (l < 2) { +- for (i = 0; subtypes[i] != NULL; i++) { +- lua_pushstring(L, subtypes[i]); /* todo */ +- lua_rawseti(L, -2, i); +- } +- } +- if (l > 0) { +- /* add math states */ +- for (i = 0; node_subtypes_mathglue[i] != NULL; i++) { +- lua_pushstring(L, node_subtypes_mathglue[i]); /* todo */ +- lua_rawseti(L, -2, 98 + i); +- } +- /* add leaders */ +- for (i = 0; node_subtypes_leader[i] != NULL; i++) { +- lua_pushstring(L, node_subtypes_leader[i]); /* todo */ +- lua_rawseti(L, -2, 100 + i); +- } ++ for (i = 0; subtypes[i].id >= 0 ; i++) { ++ lua_rawgeti(L, LUA_REGISTRYINDEX, subtypes[i].lua); ++ lua_rawseti(L, -2, subtypes[i].id); + } + } else { + lua_pushnil(L); +@@ -2823,7 +3341,10 @@ static int lua_nodelib_get_attribute(lua_State * L) + if (p != null) { + p = vlink(p); + if (p != null) { +- int i = lua_tointeger(L, 2); ++ int i = 0; ++ if (lua_gettop(L) > 1) { ++ i = lua_tointeger(L, 2); ++ } + while (p != null) { + if (attribute_id(p) == i) { + int ret = attribute_value(p); +@@ -2895,7 +3416,10 @@ static int lua_nodelib_direct_get_attribute(lua_State * L) + if (p != null) { + p = vlink(p); + if (p != null) { +- int i = lua_tointeger(L, 2); ++ int i = 0; ++ if (lua_gettop(L) > 1) { ++ i = lua_tointeger(L, 2); ++ } + while (p != null) { + if (attribute_id(p) == i) { + int ret = attribute_value(p); +@@ -3049,10 +3573,24 @@ static int lua_nodelib_direct_getwidth(lua_State * L) + if (t == hlist_node || t == vlist_node || t == rule_node) { + lua_pushinteger(L,width(n)); + } else if (t == glyph_node) { +- lua_pushinteger(L, char_width(font(n),character(n))); ++ if (lua_toboolean(L,2)) { ++ lua_pushnumber(L, (1+ex_glyph(n)/1000) * char_width(font(n),character(n))); ++ lua_pushinteger(L, ex_glyph(n)); ++ return 2; ++ } else { ++ lua_pushinteger(L, char_width(font(n),character(n))); ++ } + } else if (t == glue_node || t == glue_spec_node || t == math_node || t == ins_node) { + lua_pushinteger(L,width(n)); +- } else if (t == kern_node || t == margin_kern_node) { ++ } else if (t == kern_node) { ++ if (lua_toboolean(L,2)) { ++ lua_pushnumber(L, (1+ex_kern(n)/1000) * width(n)); ++ lua_pushinteger(L, ex_kern(n)); ++ return 2; ++ } else { ++ lua_pushinteger(L, width(n)); ++ } ++ } else if (t == margin_kern_node) { + lua_pushinteger(L,width(n)); + } else if (t == unset_node) { + lua_pushinteger(L,width(n)); +@@ -3071,7 +3609,8 @@ static int lua_nodelib_direct_setwidth(lua_State * L) + if (n) { + halfword t = type(n); + if (t == hlist_node || t == vlist_node || t == rule_node || t == glue_node || t == glue_spec_node || t == math_node || +- t == kern_node || t == margin_kern_node || t == ins_node || t == unset_node) { ++ t == kern_node || t == margin_kern_node || t == ins_node || t == unset_node || ++ t == fraction_noad || t == radical_noad ) { + if (lua_type(L, 2) == LUA_TNUMBER) { + width(n) = lua_roundnumber(L,2); + } else { +@@ -3093,6 +3632,8 @@ static int lua_nodelib_direct_getheight(lua_State * L) + lua_pushinteger(L, char_height(font(n),character(n))); + } else if (t == unset_node || t == ins_node) { + lua_pushinteger(L,height(n)); ++ } else if (t == fence_noad) { ++ lua_pushinteger(L,delimiterheight(n)); + } else { + lua_pushnil(L); + } +@@ -3107,12 +3648,14 @@ static int lua_nodelib_direct_setheight(lua_State * L) + halfword n = lua_tointeger(L, 1); + if (n) { + halfword t = type(n); ++ halfword h = 0; ++ if (lua_type(L, 2) == LUA_TNUMBER) { ++ h = lua_roundnumber(L,2); ++ } + if (t == hlist_node || t == vlist_node || t == rule_node || t == unset_node) { +- if (lua_type(L, 2) == LUA_TNUMBER) { +- height(n) = lua_roundnumber(L,2); +- } else { +- height(n) = 0; +- } ++ height(n) = h; ++ } else if (t == fence_noad) { ++ delimiterheight(n) = h; + } + } + return 0; +@@ -3129,6 +3672,8 @@ static int lua_nodelib_direct_getdepth(lua_State * L) + lua_pushinteger(L, char_depth(font(n),character(n))); + } else if (t == unset_node || t == ins_node) { + lua_pushinteger(L,depth(n)); ++ } else if (t == fence_noad) { ++ lua_pushinteger(L,delimiterdepth(n)); + } else { + lua_pushnil(L); + } +@@ -3143,12 +3688,14 @@ static int lua_nodelib_direct_setdepth(lua_State * L) + halfword n = lua_tointeger(L, 1); + if (n) { + halfword t = type(n); ++ halfword d = 0; ++ if (lua_type(L, 2) == LUA_TNUMBER) { ++ d = lua_roundnumber(L,2); ++ } + if (t == hlist_node || t == vlist_node || t == rule_node || t == unset_node) { +- if (lua_type(L, 2) == LUA_TNUMBER) { +- depth(n) = lua_roundnumber(L,2); +- } else { +- depth(n) = 0; +- } ++ depth(n) = d; ++ } else if (t == fence_noad) { ++ delimiterdepth(n) = d; + } + } + return 0; +@@ -3324,9 +3871,51 @@ static int nodelib_aux_nil(lua_State * L) + return 1; + } + +-/* node.direct.traverse_id */ + /* node.direct.traverse */ ++/* node.direct.traverse_id */ + /* node.direct.traverse_char */ ++/* node.direct.traverse_glyph */ ++/* node.direct.traverse_list */ ++ ++static int nodelib_direct_aux_next(lua_State * L) ++{ ++ halfword t; ++ if (lua_isnil(L, 2)) { ++ t = lua_tointeger(L,1) ; ++ lua_settop(L,1); ++ } else { ++ t = lua_tointeger(L,2) ; ++ t = vlink(t); ++ lua_settop(L,2); ++ } ++ if (t == null) { ++ lua_pushnil(L); ++ return 1; ++ } else { ++ lua_pushinteger(L,t); ++ lua_pushinteger(L,type(t)); ++ lua_pushinteger(L,subtype(t)); ++ return 3; ++ } ++} ++ ++static int lua_nodelib_direct_traverse(lua_State * L) ++{ ++ halfword n; ++ if (lua_isnil(L, 1)) { ++ lua_pushcclosure(L, nodelib_aux_nil, 0); ++ return 1; ++ } ++ n = (halfword) lua_tointeger(L, 1); ++ if (n == null) { ++ lua_pushcclosure(L, nodelib_aux_nil, 0); ++ return 1; ++ } ++ lua_pushcclosure(L, nodelib_direct_aux_next, 0); ++ lua_pushinteger(L,n); ++ lua_pushnil(L); ++ return 3; ++} + + static int nodelib_direct_aux_next_filtered(lua_State * L) + { +@@ -3340,18 +3929,17 @@ static int nodelib_direct_aux_next_filtered(lua_State * L) + t = vlink(t); + lua_settop(L,2); + } +- while (1) { +- if (t == null) { +- break; +- } else if (type(t) == i) { +- lua_pushinteger(L,t); +- return 1; +- } else { +- t = vlink(t); +- } ++ while (t != null && type(t) != i) { ++ t = vlink(t); ++ } ++ if (t == null) { ++ lua_pushnil(L); ++ return 1; ++ } else { ++ lua_pushinteger(L,t); ++ lua_pushinteger(L,subtype(t)); ++ return 2; + } +- lua_pushnil(L); +- return 1; + } + + static int lua_nodelib_direct_traverse_filtered(lua_State * L) +@@ -3371,7 +3959,7 @@ static int lua_nodelib_direct_traverse_filtered(lua_State * L) + return 3; + } + +-static int nodelib_direct_aux_next(lua_State * L) ++static int nodelib_direct_aux_next_char(lua_State * L) + { + halfword t; /* traverser */ + if (lua_isnil(L, 2)) { /* first call */ +@@ -3382,15 +3970,21 @@ static int nodelib_direct_aux_next(lua_State * L) + t = vlink(t); + lua_settop(L,2); + } ++ while (! ((t == null) || (type(t) == glyph_node && subtype(t) < 256))) { ++ t = vlink(t); ++ } + if (t == null) { + lua_pushnil(L); ++ return 1; + } else { + lua_pushinteger(L,t); ++ lua_pushinteger(L,character(t)); ++ lua_pushinteger(L,font(t)); ++ return 3; + } +- return 1; + } + +-static int lua_nodelib_direct_traverse(lua_State * L) ++static int lua_nodelib_direct_traverse_char(lua_State * L) + { + halfword n; + if (lua_isnil(L, 1)) { +@@ -3402,13 +3996,13 @@ static int lua_nodelib_direct_traverse(lua_State * L) + lua_pushcclosure(L, nodelib_aux_nil, 0); + return 1; + } +- lua_pushcclosure(L, nodelib_direct_aux_next, 0); ++ lua_pushcclosure(L, nodelib_direct_aux_next_char, 0); + lua_pushinteger(L,n); + lua_pushnil(L); + return 3; + } + +-static int nodelib_direct_aux_next_char(lua_State * L) ++static int nodelib_direct_aux_next_glyph(lua_State * L) + { + halfword t; /* traverser */ + if (lua_isnil(L, 2)) { /* first call */ +@@ -3419,21 +4013,65 @@ static int nodelib_direct_aux_next_char(lua_State * L) + t = vlink(t); + lua_settop(L,2); + } +- while (1) { +- if (t == null) { +- break; +- } else if ((type(t) == glyph_node) && (subtype(t) < 256)){ +- lua_pushinteger(L,t); +- return 1; +- } else { +- t = vlink(t); +- } ++ while (t != null && type(t) != glyph_node) { ++ t = vlink(t); ++ } ++ if (t == null) { ++ lua_pushnil(L); ++ return 1; ++ } else { ++ lua_pushinteger(L,t); ++ lua_pushinteger(L,character(t)); ++ lua_pushinteger(L,font(t)); ++ return 3; + } ++} ++ ++static int lua_nodelib_direct_traverse_glyph(lua_State * L) ++{ ++ halfword n; ++ if (lua_isnil(L, 1)) { ++ lua_pushcclosure(L, nodelib_aux_nil, 0); ++ return 1; ++ } ++ n = (halfword) lua_tointeger(L, 1); ++ if (n == null) { ++ lua_pushcclosure(L, nodelib_aux_nil, 0); ++ return 1; ++ } ++ lua_pushcclosure(L, nodelib_direct_aux_next_glyph, 0); ++ lua_pushinteger(L,n); + lua_pushnil(L); +- return 1; ++ return 3; + } + +-static int lua_nodelib_direct_traverse_char(lua_State * L) ++static int nodelib_direct_aux_next_list(lua_State * L) ++{ ++ halfword t; /* traverser */ ++ if (lua_isnil(L, 2)) { /* first call */ ++ t = lua_tointeger(L,1) ; ++ lua_settop(L,1); ++ } else { ++ t = lua_tointeger(L,2) ; ++ t = vlink(t); ++ lua_settop(L,2); ++ } ++ while (t != null && type(t) != hlist_node && type(t) != vlist_node) { ++ t = vlink(t); ++ } ++ if (t == null) { ++ lua_pushnil(L); ++ return 1; ++ } else { ++ lua_pushinteger(L,t); ++ lua_pushinteger(L,type(t)); ++ lua_pushinteger(L,subtype(t)); ++ nodelib_pushdirect_or_nil(list_ptr(t)); ++ return 4; ++ } ++} ++ ++static int lua_nodelib_direct_traverse_list(lua_State * L) + { + halfword n; + if (lua_isnil(L, 1)) { +@@ -3445,22 +4083,101 @@ static int lua_nodelib_direct_traverse_char(lua_State * L) + lua_pushcclosure(L, nodelib_aux_nil, 0); + return 1; + } +- lua_pushcclosure(L, nodelib_direct_aux_next_char, 0); +- lua_pushinteger(L,n); ++ lua_pushcclosure(L, nodelib_direct_aux_next_list, 1); ++ lua_pushinteger(L, n); ++ lua_pushnil(L); ++ return 3; ++} ++ ++/* node.traverse */ ++/* node.traverse_id */ ++/* node.traverse_char */ ++/* node.traverse_glyph */ ++/* node.traverse_list */ ++ ++static int nodelib_aux_next(lua_State * L) ++{ ++ halfword t; ++ halfword *a; ++ if (lua_isnil(L, 2)) { ++ t = *check_isnode(L, 1); ++ lua_settop(L,1); ++ } else { ++ t = *check_isnode(L, 2); ++ t = vlink(t); ++ lua_settop(L,2); ++ } ++ if (t == null) { ++ lua_pushnil(L); ++ return 1; ++ } else { ++ fast_metatable_top(t); ++ lua_pushinteger(L,type(t)); ++ lua_pushinteger(L,subtype(t)); ++ return 3; ++ } ++} ++ ++static int lua_nodelib_traverse(lua_State * L) ++{ ++ halfword n; ++ if (lua_isnil(L, 1)) { ++ lua_pushcclosure(L, nodelib_aux_nil, 0); ++ return 1; ++ } ++ n = *check_isnode(L, 1); ++ lua_pushcclosure(L, nodelib_aux_next, 0); ++ lua_nodelib_push_fast(L, n); ++ lua_pushnil(L); ++ return 3; ++} ++ ++static int nodelib_aux_next_filtered(lua_State * L) ++{ ++ halfword t; /* traverser */ ++ halfword *a; ++ int i = (int) lua_tointeger(L, lua_upvalueindex(1)); ++ if (lua_isnil(L, 2)) { /* first call */ ++ t = *check_isnode(L, 1); ++ lua_settop(L,1); ++ } else { ++ t = *check_isnode(L, 2); ++ t = vlink(t); ++ lua_settop(L,2); ++ } ++ while (t != null && type(t) != i) { ++ t = vlink(t); ++ } ++ if (t == null) { ++ lua_pushnil(L); ++ return 1; ++ } else { ++ fast_metatable_top(t); ++ lua_pushinteger(L,subtype(t)); ++ return 2; ++ } ++} ++ ++static int lua_nodelib_traverse_filtered(lua_State * L) ++{ ++ halfword n; ++ if (lua_isnil(L, 2)) { ++ lua_pushcclosure(L, nodelib_aux_nil, 0); ++ return 1; ++ } ++ n = *check_isnode(L, 2); ++ lua_pop(L, 1); /* the node, integer remains */ ++ lua_pushcclosure(L, nodelib_aux_next_filtered, 1); ++ lua_nodelib_push_fast(L, n); + lua_pushnil(L); + return 3; + } + +-/* node.traverse_id */ +-/* node.traverse */ +-/* node.traverse_char */ +- +-static int nodelib_aux_next_filtered(lua_State * L) ++static int nodelib_aux_next_char(lua_State * L) + { +- halfword t; /* traverser */ ++ halfword t; /* traverser */ + halfword *a; +- int i = (int) lua_tointeger(L, lua_upvalueindex(1)); +- if (lua_isnil(L, 2)) { /* first call */ ++ if (lua_isnil(L, 2)) { /* first call */ + t = *check_isnode(L, 1); + lua_settop(L,1); + } else { +@@ -3468,36 +4185,38 @@ static int nodelib_aux_next_filtered(lua_State * L) + t = vlink(t); + lua_settop(L,2); + } +- while (t != null && type(t) != i) { ++ while (! ((t == null) || (type(t) == glyph_node && subtype(t) < 256))) { + t = vlink(t); + } + if (t == null) { + lua_pushnil(L); ++ return 1; + } else { + fast_metatable_top(t); ++ lua_pushinteger(L,character(t)); ++ lua_pushinteger(L,font(t)); ++ return 3; + } +- return 1; + } + +-static int lua_nodelib_traverse_filtered(lua_State * L) ++static int lua_nodelib_traverse_char(lua_State * L) + { + halfword n; +- if (lua_isnil(L, 2)) { ++ if (lua_isnil(L, 1)) { + lua_pushcclosure(L, nodelib_aux_nil, 0); + return 1; + } +- n = *check_isnode(L, 2); +- lua_pop(L, 1); /* the node, integer remains */ +- lua_pushcclosure(L, nodelib_aux_next_filtered, 1); ++ n = *check_isnode(L, 1); ++ lua_pushcclosure(L, nodelib_aux_next_char, 0); + lua_nodelib_push_fast(L, n); + lua_pushnil(L); + return 3; + } + +-static int nodelib_aux_next(lua_State * L) ++static int nodelib_aux_next_glyph(lua_State * L) + { + halfword t; /* traverser */ +- halfword *a; /* a or *a */ ++ halfword *a; + if (lua_isnil(L, 2)) { /* first call */ + t = *check_isnode(L, 1); + lua_settop(L,1); +@@ -3506,15 +4225,21 @@ static int nodelib_aux_next(lua_State * L) + t = vlink(t); + lua_settop(L,2); + } ++ while (t != null && type(t) != glyph_node) { ++ t = vlink(t); ++ } + if (t == null) { + lua_pushnil(L); ++ return 1; + } else { + fast_metatable_top(t); ++ lua_pushinteger(L,character(t)); ++ lua_pushinteger(L,font(t)); ++ return 3; + } +- return 1; + } + +-static int lua_nodelib_traverse(lua_State * L) ++static int lua_nodelib_traverse_glyph(lua_State * L) + { + halfword n; + if (lua_isnil(L, 1)) { +@@ -3522,17 +4247,17 @@ static int lua_nodelib_traverse(lua_State * L) + return 1; + } + n = *check_isnode(L, 1); +- lua_pushcclosure(L, nodelib_aux_next, 0); ++ lua_pushcclosure(L, nodelib_aux_next_glyph, 0); + lua_nodelib_push_fast(L, n); + lua_pushnil(L); + return 3; + } + +-static int nodelib_aux_next_char(lua_State * L) ++static int nodelib_aux_next_list(lua_State * L) + { +- halfword t; /* traverser */ ++ halfword t; /* traverser */ + halfword *a; +- if (lua_isnil(L, 2)) { /* first call */ ++ if (lua_isnil(L, 2)) { /* first call */ + t = *check_isnode(L, 1); + lua_settop(L,1); + } else { +@@ -3540,20 +4265,22 @@ static int nodelib_aux_next_char(lua_State * L) + t = vlink(t); + lua_settop(L,2); + } +- while (1) { +- if (t == null) { +- break; +- } else if ((type(t) == glyph_node) && (subtype(t) < 256)){ +- fast_metatable_top(t); +- return 1; +- } else { +- t = vlink(t); +- } ++ while (t != null && type(t) != hlist_node && type(t) != vlist_node) { ++ t = vlink(t); ++ } ++ if (t == null) { ++ lua_pushnil(L); ++ return 1; ++ } else { ++ fast_metatable_top(t); ++ lua_pushinteger(L,type(t)); ++ lua_pushinteger(L,subtype(t)); ++ fast_metatable_or_nil(list_ptr(t)); ++ return 4; + } +- return 1; + } + +-static int lua_nodelib_traverse_char(lua_State * L) ++static int lua_nodelib_traverse_list(lua_State * L) + { + halfword n; + if (lua_isnil(L, 1)) { +@@ -3561,7 +4288,7 @@ static int lua_nodelib_traverse_char(lua_State * L) + return 1; + } + n = *check_isnode(L, 1); +- lua_pushcclosure(L, nodelib_aux_next_char, 0); ++ lua_pushcclosure(L, nodelib_aux_next_list, 1); + lua_nodelib_push_fast(L, n); + lua_pushnil(L); + return 3; +@@ -3638,46 +4365,6 @@ static int lua_nodelib_count(lua_State * L) + return do_lua_nodelib_count(L, m, i, n); + } + +-/* getting and setting fields (helpers) */ +- +-int nodelib_getlist(lua_State * L, int n) +-{ +- if (lua_isuserdata(L, n)) { +- halfword m = *check_isnode(L, n); +- return m; +- } else { +- return null; +- } +-} +- +-int nodelib_getdir(lua_State * L, int n, int absolute_only) +-{ +- if (lua_type(L, n) == LUA_TSTRING) { +- const char *s = lua_tostring(L, n); +- RETURN_DIR_VALUES(TLT); +- RETURN_DIR_VALUES(TRT); +- RETURN_DIR_VALUES(LTL); +- RETURN_DIR_VALUES(RTT); +- luaL_error(L, "Bad direction specifier %s", s); +- } else { +- luaL_error(L, "Direction specifiers have to be strings"); +- } +- return 0; +-} +- +-static str_number nodelib_getstring(lua_State * L, int a) +-{ +- size_t k; +- const char *s = lua_tolstring(L, a, &k); +- return maketexlstring(s, k); +-} +- +-static int nodelib_cantset(lua_State * L, int n, const char *s) +-{ +- luaL_error(L,"You cannot set field %s in a node of type %s",s,node_data[type(n)].name); +- return 0; +-} +- + /* node.direct.getfield */ + + static void lua_nodelib_getfield_whatsit(lua_State * L, int n, const char *s) +@@ -3690,54 +4377,48 @@ static void lua_nodelib_getfield_whatsit(lua_State * L, int n, const char *s) + lua_pushinteger(L, user_node_type(n)); + } else if (lua_key_eq(s, value)) { + switch (user_node_type(n)) { +- case 'a': +- nodelib_pushlist(L, user_node_value(n)); +- break; +- case 'd': +- lua_pushinteger(L, user_node_value(n)); +- break; +- case 'l': +- if (user_node_value(n) != 0) { +- lua_rawgeti(L, LUA_REGISTRYINDEX, user_node_value(n)); +- } else { +- lua_pushnil(L); +- } +- break; +- case 'n': +- nodelib_pushlist(L, user_node_value(n)); +- break; +- case 's': +- nodelib_pushstring(L, user_node_value(n)); +- break; +- case 't': +- tokenlist_to_lua(L, user_node_value(n)); +- break; +- default: +- lua_pushinteger(L, user_node_value(n)); +- break; ++ case 'a': ++ nodelib_pushlist(L, user_node_value(n)); ++ break; ++ case 'd': ++ lua_pushinteger(L, user_node_value(n)); ++ break; ++ case 'l': ++ if (user_node_value(n) != 0) { ++ lua_rawgeti(L, LUA_REGISTRYINDEX, user_node_value(n)); ++ } else { ++ lua_pushnil(L); ++ } ++ break; ++ case 'n': ++ nodelib_pushlist(L, user_node_value(n)); ++ break; ++ case 's': ++ nodelib_pushstring(L, user_node_value(n)); ++ break; ++ case 't': ++ tokenlist_to_lua(L, user_node_value(n)); ++ break; ++ default: ++ lua_pushinteger(L, user_node_value(n)); ++ break; + } + } else { + lua_pushnil(L); + } + } else if (t == pdf_literal_node) { ++ /*tex The |string| key is obsolete. */ + if (lua_key_eq(s, mode)) { + lua_pushinteger(L, pdf_literal_mode(n)); +- } else if (lua_key_eq(s, data)) { +- if (pdf_literal_type(n) == lua_refid_literal) { +- lua_rawgeti(L, LUA_REGISTRYINDEX, pdf_literal_data(n)); +- } else { +- tokenlist_to_luastring(L, pdf_literal_data(n)); +- } ++ } else if (lua_key_eq(s, data) || lua_key_eq(s, token) || lua_key_eq(s, string)) { ++ get_pdf_literal_direct_value(L, n); + } else { + lua_pushnil(L); + } + } else if (t == late_lua_node) { +- if (lua_key_eq(s, string) || lua_key_eq(s, data)) { +- if (late_lua_type(n) == lua_refid_literal) { +- lua_rawgeti(L, LUA_REGISTRYINDEX, late_lua_data(n)); +- } else { +- tokenlist_to_luastring(L, late_lua_data(n)); +- } ++ /*tex The |string| key is obsolete. */ ++ if (lua_key_eq(s, data) || lua_key_eq(s, token) || lua_key_eq(s, string)) { ++ get_late_lua_direct_value(L,n); + } else if (lua_key_eq(s, name)) { + tokenlist_to_luastring(L, late_lua_name(n)); + } else { +@@ -3782,7 +4463,7 @@ static void lua_nodelib_getfield_whatsit(lua_State * L, int n, const char *s) + } + } else if (t == pdf_setmatrix_node) { + if (lua_key_eq(s, data)) { +- tokenlist_to_luastring(L, pdf_setmatrix_data(n)); ++ get_pdf_setmatrix_direct_value(L,n); + } else { + lua_pushnil(L); + } +@@ -3806,13 +4487,13 @@ static void lua_nodelib_getfield_whatsit(lua_State * L, int n, const char *s) + if (lua_key_eq(s, stream)) { + lua_pushinteger(L, write_stream(n)); + } else if (lua_key_eq(s, data)) { +- tokenlist_to_lua(L, write_tokens(n)); ++ get_write_direct_value(L,n); + } else { + lua_pushnil(L); + } + } else if (t == special_node) { + if (lua_key_eq(s, data)) { +- tokenlist_to_luastring(L, write_tokens(n)); ++ get_special_direct_value(L,n); + } else { + lua_pushnil(L); + } +@@ -3978,8 +4659,8 @@ static int lua_nodelib_fast_getfield(lua_State * L) + lua_pushinteger(L, x_displace(n)); + } else if (lua_key_eq(s, yoffset)) { + lua_pushinteger(L, y_displace(n)); +- } else if (lua_key_eq(s, xadvance)) { +- lua_pushinteger(L, x_advance(n)); ++ } else if (lua_key_eq(s, data)) { ++ lua_pushinteger(L, glyph_node_data(n)); + } else if (lua_key_eq(s, width)) { + lua_pushinteger(L, char_width(font(n),character(n))); + } else if (lua_key_eq(s, height)) { +@@ -4013,6 +4694,8 @@ static int lua_nodelib_fast_getfield(lua_State * L) + lua_pushinteger(L, height(n)); + } else if (lua_key_eq(s, depth)) { + lua_pushinteger(L, depth(n)); ++ } else if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, box_dir(n)); + } else if (lua_key_eq(s, dir)) { + lua_push_dir_par(L, box_dir(n)); + } else if (lua_key_eq(s, shift)) { +@@ -4086,6 +4769,12 @@ static int lua_nodelib_fast_getfield(lua_State * L) + lua_pushinteger(L, height(n)); + } else if (lua_key_eq(s, depth)) { + lua_pushinteger(L, depth(n)); ++ } else if (lua_key_eq(s, left)) { ++ lua_pushinteger(L,rule_left(n)); ++ } else if (lua_key_eq(s, right)) { ++ lua_pushinteger(L,rule_right(n)); ++ } else if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, rule_dir(n)); + } else if (lua_key_eq(s, dir)) { + lua_push_dir_par(L, rule_dir(n)); + } else if (lua_key_eq(s, index)) { +@@ -4096,8 +4785,10 @@ static int lua_nodelib_fast_getfield(lua_State * L) + lua_pushnil(L); + } + } else if (t == dir_node) { +- if (lua_key_eq(s, dir)) { +- lua_push_dir_text(L, dir_dir(n)); ++ if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, dir_dir(n)); ++ } else if (lua_key_eq(s, dir)) { ++ lua_push_dir_text(L, dir_dir(n),subtype(n)); + } else if (lua_key_eq(s, level)) { + lua_pushinteger(L, dir_level(n)); + } else if (lua_key_eq(s, subtype)) { /* can be used for anything */ +@@ -4110,6 +4801,8 @@ static int lua_nodelib_fast_getfield(lua_State * L) + lua_pushinteger(L, local_pen_inter(n)); + } else if (lua_key_eq(s, pen_broken)) { + lua_pushinteger(L, local_pen_broken(n)); ++ } else if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, local_par_dir(n)); + } else if (lua_key_eq(s, dir)) { + lua_push_dir_par(L, local_par_dir(n)); + } else if (lua_key_eq(s, box_left)) { +@@ -4241,6 +4934,8 @@ static int lua_nodelib_fast_getfield(lua_State * L) + fast_metatable_or_nil(right_delimiter(n)); + } else if (lua_key_eq(s, middle)) { + fast_metatable_or_nil(middle_delimiter(n)); ++ } else if (lua_key_eq(s, fam)) { ++ lua_pushinteger(L, fraction_fam(n)); + } else if (lua_key_eq(s, options)) { + lua_pushinteger(L, fractionoptions(n)); + } else { +@@ -4409,6 +5104,8 @@ static int lua_nodelib_fast_getfield(lua_State * L) + lua_pushinteger(L, height(n)); + } else if (lua_key_eq(s, depth)) { + lua_pushinteger(L, depth(n)); ++ } else if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, box_dir(n)); + } else if (lua_key_eq(s, dir)) { + lua_push_dir_par(L, box_dir(n)); + } else if (lua_key_eq(s, shrink)) { +@@ -4477,55 +5174,23 @@ static void lua_nodelib_direct_getfield_whatsit(lua_State * L, int n, const char + } else if (lua_key_eq(s, type)) { + lua_pushinteger(L, user_node_type(n)); + } else if (lua_key_eq(s, value)) { +- switch (user_node_type(n)) { +- case 'a': +- nodelib_pushdirect(user_node_value(n)); +- break; +- case 'd': +- lua_pushinteger(L, user_node_value(n)); +- break; +- case 'l': +- if (user_node_value(n) != 0) { +- lua_rawgeti(L, LUA_REGISTRYINDEX, user_node_value(n)); +- } else { +- lua_pushnil(L); +- } +- break; +- case 'n': +- nodelib_pushdirect(user_node_value(n)); +- break; +- case 's': +- nodelib_pushstring(L, user_node_value(n)); +- break; +- case 't': +- tokenlist_to_lua(L, user_node_value(n)); +- break; +- default: +- lua_pushinteger(L, user_node_value(n)); +- break; +- } ++ get_user_node_direct_value(L, n); + } else { + lua_pushnil(L); + } + } else if (t == pdf_literal_node) { ++ /*tex The |string| key is obsolete. */ + if (lua_key_eq(s, mode)) { + lua_pushinteger(L, pdf_literal_mode(n)); +- } else if (lua_key_eq(s, data)) { +- if (pdf_literal_type(n) == lua_refid_literal) { +- lua_rawgeti(L, LUA_REGISTRYINDEX, pdf_literal_data(n)); +- } else { +- tokenlist_to_luastring(L, pdf_literal_data(n)); +- } ++ } else if (lua_key_eq(s, data) || lua_key_eq(s, token) || lua_key_eq(s, string)) { ++ get_pdf_literal_direct_value(L, n); + } else { + lua_pushnil(L); + } + } else if (t == late_lua_node) { +- if (lua_key_eq(s, string) || lua_key_eq(s, data)) { +- if (late_lua_type(n) == lua_refid_literal) { +- lua_rawgeti(L, LUA_REGISTRYINDEX, late_lua_data(n)); +- } else { +- tokenlist_to_luastring(L, late_lua_data(n)); +- } ++ /*tex The |string| key is obsolete. */ ++ if (lua_key_eq(s, data) || lua_key_eq(s, token) || lua_key_eq(s, string)) { ++ get_late_lua_direct_value(L,n); + } else if (lua_key_eq(s, name)) { + tokenlist_to_luastring(L, late_lua_name(n)); + } else { +@@ -4570,7 +5235,7 @@ static void lua_nodelib_direct_getfield_whatsit(lua_State * L, int n, const char + } + } else if (t == pdf_setmatrix_node) { + if (lua_key_eq(s, data)) { +- tokenlist_to_luastring(L, pdf_setmatrix_data(n)); ++ get_pdf_setmatrix_direct_value(L,n); + } else { + lua_pushnil(L); + } +@@ -4594,13 +5259,13 @@ static void lua_nodelib_direct_getfield_whatsit(lua_State * L, int n, const char + if (lua_key_eq(s, stream)) { + lua_pushinteger(L, write_stream(n)); + } else if (lua_key_eq(s, data)) { +- tokenlist_to_lua(L, write_tokens(n)); ++ get_write_direct_value(L,n); + } else { + lua_pushnil(L); + } + } else if (t == special_node) { + if (lua_key_eq(s, data)) { +- tokenlist_to_luastring(L, write_tokens(n)); ++ get_special_direct_value(L,n); + } else { + lua_pushnil(L); + } +@@ -4751,8 +5416,8 @@ static int lua_nodelib_direct_getfield(lua_State * L) + lua_pushinteger(L, x_displace(n)); + } else if (lua_key_eq(s, yoffset)) { + lua_pushinteger(L, y_displace(n)); +- } else if (lua_key_eq(s, xadvance)) { +- lua_pushinteger(L, x_advance(n)); ++ } else if (lua_key_eq(s, data)) { ++ lua_pushinteger(L, glyph_node_data(n)); + } else if (lua_key_eq(s, width)) { + lua_pushinteger(L, char_width(font(n),character(n))); + } else if (lua_key_eq(s, height)) { +@@ -4784,6 +5449,8 @@ static int lua_nodelib_direct_getfield(lua_State * L) + lua_pushinteger(L, height(n)); + } else if (lua_key_eq(s, depth)) { + lua_pushinteger(L, depth(n)); ++ } else if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, box_dir(n)); + } else if (lua_key_eq(s, dir)) { + lua_push_dir_par(L, box_dir(n)); + } else if (lua_key_eq(s, shift)) { +@@ -4847,6 +5514,12 @@ static int lua_nodelib_direct_getfield(lua_State * L) + lua_pushinteger(L, height(n)); + } else if (lua_key_eq(s, depth)) { + lua_pushinteger(L, depth(n)); ++ } else if (lua_key_eq(s, left)) { ++ lua_pushinteger(L,rule_left(n)); ++ } else if (lua_key_eq(s, right)) { ++ lua_pushinteger(L,rule_right(n)); ++ } else if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, rule_dir(n)); + } else if (lua_key_eq(s, dir)) { + lua_push_dir_par(L, rule_dir(n)); + } else if (lua_key_eq(s, index)) { +@@ -4857,8 +5530,10 @@ static int lua_nodelib_direct_getfield(lua_State * L) + lua_pushnil(L); + } + } else if (t == dir_node) { +- if (lua_key_eq(s, dir)) { +- lua_push_dir_text(L, dir_dir(n)); ++ if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, dir_dir(n)); ++ } else if (lua_key_eq(s, dir)) { ++ lua_push_dir_text(L, dir_dir(n), subtype(n)); + } else if (lua_key_eq(s, level)) { + lua_pushinteger(L, dir_level(n)); + } else if (lua_key_eq(s, subtype)) { /* can be used for anything */ +@@ -4871,6 +5546,8 @@ static int lua_nodelib_direct_getfield(lua_State * L) + lua_pushinteger(L, local_pen_inter(n)); + } else if (lua_key_eq(s, pen_broken)) { + lua_pushinteger(L, local_pen_broken(n)); ++ } else if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, local_par_dir(n)); + } else if (lua_key_eq(s, dir)) { + lua_push_dir_par(L, local_par_dir(n)); + } else if (lua_key_eq(s, box_left)) { +@@ -4969,6 +5646,8 @@ static int lua_nodelib_direct_getfield(lua_State * L) + nodelib_pushdirect_or_nil(right_delimiter(n)); + } else if (lua_key_eq(s, middle)) { + nodelib_pushdirect_or_nil(middle_delimiter(n)); ++ } else if (lua_key_eq(s, fam)) { ++ lua_pushinteger(L, fraction_fam(n)); + } else if (lua_key_eq(s, options)) { + lua_pushinteger(L, fractionoptions(n)); + } else { +@@ -5111,6 +5790,8 @@ static int lua_nodelib_direct_getfield(lua_State * L) + lua_pushinteger(L, height(n)); + } else if (lua_key_eq(s, depth)) { + lua_pushinteger(L, depth(n)); ++ } else if (lua_key_eq(s, direction)) { ++ lua_pushinteger(L, box_dir(n)); + } else if (lua_key_eq(s, dir)) { + lua_push_dir_par(L, box_dir(n)); + } else if (lua_key_eq(s, shrink)) { +@@ -5213,7 +5894,7 @@ static int lua_nodelib_equal(lua_State * L) + static int font_tex_ligaturing(lua_State * L) + { + /* on the stack are two nodes and a direction */ +- /* hh-ls: we need to deal with prev nodes when a range starts with a ligature */ ++ /* we need to deal with prev nodes when a range starts with a ligature */ + halfword tmp_head; + halfword h; + halfword t = null; +@@ -5254,7 +5935,7 @@ static int font_tex_ligaturing(lua_State * L) + static int font_tex_direct_ligaturing(lua_State * L) + { + /* on the stack are two nodes and a direction */ +- /* hh-ls: we need to deal with prev nodes when a range starts with a ligature */ ++ /* we need to deal with prev nodes when a range starts with a ligature */ + halfword tmp_head; + halfword h; + halfword t = null; +@@ -5805,57 +6486,29 @@ static int lua_nodelib_direct_tonode(lua_State * L) + + /* node.setfield */ + +-#define cleanup_late_lua(n) do { \ +- if (late_lua_data(n) != 0) { \ +- if (late_lua_type(n) == normal) { \ +- delete_token_ref(late_lua_data(n)); \ +- } else if (late_lua_type(n) == lua_refid_literal) { \ +- luaL_unref(L, LUA_REGISTRYINDEX,late_lua_data(n)); \ +- } \ +- } \ +-} while (0) +- +-#define cleanup_late_lua_name(n) do { \ +- if (late_lua_name(n) != 0) { \ +- delete_token_ref(late_lua_name(n)); \ +- } \ +-} while (0) +- + static int lua_nodelib_setfield_whatsit(lua_State * L, int n, const char *s) + { + int t = subtype(n); + + if (t == pdf_literal_node) { ++ /*tex The |string| key is obsolete. */ + if (lua_key_eq(s, mode)) { + pdf_literal_mode(n) = (quarterword) lua_tointeger(L, 3); +- } else if (lua_key_eq(s, data)) { +- if (ini_version) { +- pdf_literal_data(n) = nodelib_gettoks(L, 3); +- } else { +- lua_pushvalue(L, 3); +- pdf_literal_data(n) = luaL_ref(L, LUA_REGISTRYINDEX); +- pdf_literal_type(n) = lua_refid_literal; +- } ++ } else if (lua_key_eq(s, data) || lua_key_eq(s, string)) { ++ set_pdf_literal_direct_normal(L, n, 3); ++ } else if (lua_key_eq(s, token)) { ++ set_pdf_literal_direct_token(L, n, 3); + } else { + return nodelib_cantset(L, n, s); + } + } else if (t == late_lua_node) { +- if (lua_key_eq(s, string)) { +- cleanup_late_lua(n) ; /* ls-hh */ +- if (ini_version) { +- late_lua_data(n) = nodelib_gettoks(L, 3); +- late_lua_type(n) = normal; +- } else { +- lua_pushvalue(L, 3); +- late_lua_data(n) = luaL_ref(L, LUA_REGISTRYINDEX); +- late_lua_type(n) = lua_refid_literal; +- } +- } else if (lua_key_eq(s, data)) { +- cleanup_late_lua(n) ; /* ls-hh */ +- late_lua_data(n) = nodelib_gettoks(L, 3); +- late_lua_type(n) = normal; ++ /*tex The |string| key is obsolete. */ ++ if (lua_key_eq(s, data) || lua_key_eq(s, string)) { ++ set_late_lua_direct_normal(L, n, 3); ++ } else if (lua_key_eq(s, token)) { ++ set_late_lua_direct_token(L, n, 3); + } else if (lua_key_eq(s, name)) { +- cleanup_late_lua_name(n) ; /* ls-hh */ ++ cleanup_late_lua_name(n) ; + late_lua_name(n) = nodelib_gettoks(L, 3); + } else { + return nodelib_cantset(L, n, s); +@@ -5868,31 +6521,31 @@ static int lua_nodelib_setfield_whatsit(lua_State * L, int n, const char *s) + user_node_type(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, value)) { + switch (user_node_type(n)) { +- case 'a': +- user_node_value(n) = nodelib_getlist(L, 3); +- break; +- case 'd': +- user_node_value(n) = (halfword) lua_roundnumber(L, 3); +- break; +- case 'l': +- lua_pushvalue(L, 3); +- if (user_node_value(n) != 0) { +- luaL_unref(L, LUA_REGISTRYINDEX,user_node_value(n)); +- } +- user_node_value(n) = luaL_ref(L, LUA_REGISTRYINDEX); +- break; +- case 'n': +- user_node_value(n) = nodelib_getlist(L, 3); +- break; +- case 's': +- user_node_value(n) = nodelib_getstring(L, 3); +- break; +- case 't': +- user_node_value(n) = nodelib_gettoks(L, 3); +- break; +- default: +- user_node_value(n) = (halfword) lua_roundnumber(L, 3); +- break; ++ case 'a': ++ user_node_value(n) = nodelib_getlist(L, 3); ++ break; ++ case 'd': ++ user_node_value(n) = (halfword) lua_roundnumber(L, 3); ++ break; ++ case 'l': ++ lua_pushvalue(L, 3); ++ if (user_node_value(n) != 0) { ++ luaL_unref(L, LUA_REGISTRYINDEX,user_node_value(n)); ++ } ++ user_node_value(n) = luaL_ref(L, LUA_REGISTRYINDEX); ++ break; ++ case 'n': ++ user_node_value(n) = nodelib_getlist(L, 3); ++ break; ++ case 's': ++ user_node_value(n) = nodelib_getstring(L, 3); ++ break; ++ case 't': ++ user_node_value(n) = nodelib_gettoks(L, 3); ++ break; ++ default: ++ user_node_value(n) = (halfword) lua_roundnumber(L, 3); ++ break; + } + } else { + return nodelib_cantset(L, n, s); +@@ -5937,7 +6590,7 @@ static int lua_nodelib_setfield_whatsit(lua_State * L, int n, const char *s) + } + } else if (t == pdf_setmatrix_node) { + if (lua_key_eq(s, data)) { +- pdf_setmatrix_data(n) = nodelib_gettoks(L, 3); ++ set_pdf_setmatrix_direct_value(L,n,3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -5967,7 +6620,7 @@ static int lua_nodelib_setfield_whatsit(lua_State * L, int n, const char *s) + if (lua_key_eq(s, stream)) { + write_stream(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, data)) { +- write_tokens(n) = nodelib_gettoks(L, 3); ++ set_write_direct_value(L,n,3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -6023,7 +6676,7 @@ static int lua_nodelib_setfield_whatsit(lua_State * L, int n, const char *s) + } + } else if (t == special_node) { + if (lua_key_eq(s, data)) { +- write_tokens(n) = nodelib_gettoks(L, 3); ++ set_special_direct_value(L,n,3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -6107,8 +6760,8 @@ static int lua_nodelib_fast_setfield(lua_State * L) + x_displace(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, yoffset)) { + y_displace(n) = (halfword) lua_roundnumber(L, 3); +- } else if (lua_key_eq(s, xadvance)) { +- x_advance(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, data)) { ++ glyph_node_data(n) = (halfword) lua_tointeger(L, 3);; + } else if (lua_key_eq(s, width)) { + /* not yet */ + } else if (lua_key_eq(s, height)) { +@@ -6141,8 +6794,10 @@ static int lua_nodelib_fast_setfield(lua_State * L) + height(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, depth)) { + depth(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, direction)) { ++ box_dir(n) = nodelib_getdirection(L, 3); + } else if (lua_key_eq(s, dir)) { +- box_dir(n) = nodelib_getdir(L, 3, 1); ++ box_dir(n) = nodelib_getdir_par(L, 3); + } else if (lua_key_eq(s, shift)) { + shift_amount(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, glue_order)) { +@@ -6213,8 +6868,14 @@ static int lua_nodelib_fast_setfield(lua_State * L) + height(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, depth)) { + depth(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, left)) { ++ rule_left(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, right)) { ++ rule_right(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, direction)) { ++ rule_dir(n) = nodelib_getdirection(L, 3); + } else if (lua_key_eq(s, dir)) { +- rule_dir(n) = nodelib_getdir(L, 3, 1); ++ rule_dir(n) = nodelib_getdir_par(L, 3); + } else if (lua_key_eq(s, index)) { + rule_index(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, transform)) { +@@ -6223,12 +6884,12 @@ static int lua_nodelib_fast_setfield(lua_State * L) + return nodelib_cantset(L, n, s); + } + } else if (t == dir_node) { +- if (lua_key_eq(s, dir)) { +- dir_dir(n) = nodelib_getdir(L, 3, 0); ++ if (lua_key_eq(s, direction)) { ++ dir_dir(n) = nodelib_getdirection(L, 3); ++ } else if (lua_key_eq(s, dir)) { ++ nodelib_setdir_text(L, 3, n); + } else if (lua_key_eq(s, level)) { + dir_level(n) = (halfword) lua_tointeger(L, 3); +- } else if (lua_key_eq(s, subtype)) { /* can be used for anything */ +- subtype(n) = (quarterword) lua_tointeger(L, 3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -6237,8 +6898,10 @@ static int lua_nodelib_fast_setfield(lua_State * L) + local_pen_inter(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, pen_broken)) { + local_pen_broken(n) = (halfword) lua_tointeger(L, 3); ++ } else if (lua_key_eq(s, direction)) { ++ local_par_dir(n) = nodelib_getdirection(L, 3); + } else if (lua_key_eq(s, dir)) { +- local_par_dir(n) = nodelib_getdir(L, 3, 1); ++ local_par_dir(n) = nodelib_getdir_par(L, 3); + } else if (lua_key_eq(s, box_left)) { + local_box_left(n) = nodelib_getlist(L, 3); + } else if (lua_key_eq(s, box_left_width)) { +@@ -6349,6 +7012,8 @@ static int lua_nodelib_fast_setfield(lua_State * L) + right_delimiter(n) = nodelib_getlist(L, 3); + } else if (lua_key_eq(s, middle)) { + middle_delimiter(n) = nodelib_getlist(L, 3); ++ } else if (lua_key_eq(s, fam)) { ++ fraction_fam(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, options)) { + fractionoptions(n) = (halfword) lua_tointeger(L, 3); + } else { +@@ -6517,8 +7182,10 @@ static int lua_nodelib_fast_setfield(lua_State * L) + height(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, depth)) { + depth(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, direction)) { ++ box_dir(n) = nodelib_getdirection(L, 3); + } else if (lua_key_eq(s, dir)) { +- box_dir(n) = nodelib_getdir(L, 3, 1); ++ box_dir(n) = nodelib_getdir_par(L, 3); + } else if (lua_key_eq(s, shrink)) { + glue_shrink(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, glue_order)) { +@@ -6592,36 +7259,24 @@ static int lua_nodelib_direct_setfield_whatsit(lua_State * L, int n, const char + { + int t = subtype(n); + if (t == pdf_literal_node) { ++ /*tex The |string| key is obsolete. */ + if (lua_key_eq(s, mode)) { + pdf_literal_mode(n) = (quarterword) lua_tointeger(L, 3); +- } else if (lua_key_eq(s, data)) { +- if (ini_version) { +- pdf_literal_data(n) = nodelib_gettoks(L, 3); +- } else { +- lua_pushvalue(L, 3); +- pdf_literal_data(n) = luaL_ref(L, LUA_REGISTRYINDEX); +- pdf_literal_type(n) = lua_refid_literal; +- } ++ } else if (lua_key_eq(s, data) || lua_key_eq(s, string)) { ++ set_pdf_literal_direct_normal(L, n, 3); ++ } else if (lua_key_eq(s, token)) { ++ set_pdf_literal_direct_token(L, n, 3); + } else { + return nodelib_cantset(L, n, s); + } + } else if (t == late_lua_node) { +- if (lua_key_eq(s, string)) { +- cleanup_late_lua(n) ; /* ls-hh */ +- if (ini_version) { +- late_lua_data(n) = nodelib_gettoks(L, 3); +- late_lua_type(n) = normal; +- } else { +- lua_pushvalue(L, 3); +- late_lua_data(n) = luaL_ref(L, LUA_REGISTRYINDEX); +- late_lua_type(n) = lua_refid_literal; +- } +- } else if (lua_key_eq(s, data)) { +- cleanup_late_lua(n) ; /* ls-hh */ +- late_lua_data(n) = nodelib_gettoks(L, 3); +- late_lua_type(n) = normal; ++ /*tex The |string| key is obsolete. */ ++ if (lua_key_eq(s, data) || lua_key_eq(s, string)) { ++ set_late_lua_direct_normal(L, n, 3); ++ } else if (lua_key_eq(s, token)) { ++ set_late_lua_direct_token(L, n, 3); + } else if (lua_key_eq(s, name)) { +- cleanup_late_lua_name(n) ; /* ls-hh */ ++ cleanup_late_lua_name(n) ; + late_lua_name(n) = nodelib_gettoks(L, 3); + } else { + return nodelib_cantset(L, n, s); +@@ -6632,33 +7287,7 @@ static int lua_nodelib_direct_setfield_whatsit(lua_State * L, int n, const char + } else if (lua_key_eq(s, type)) { + user_node_type(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, value)) { +- switch (user_node_type(n)) { +- case 'a': +- user_node_value(n) = nodelib_getlist(L, 3); +- break; +- case 'd': +- user_node_value(n) = (halfword) lua_roundnumber(L, 3); +- break; +- case 'l': +- lua_pushvalue(L, 3); +- if (user_node_value(n) != 0) { +- luaL_unref(L, LUA_REGISTRYINDEX,user_node_value(n)); +- } +- user_node_value(n) = luaL_ref(L, LUA_REGISTRYINDEX); +- break; +- case 'n': +- user_node_value(n) = nodelib_getlist(L, 3); +- break; +- case 's': +- user_node_value(n) = nodelib_getstring(L, 3); +- break; +- case 't': +- user_node_value(n) = nodelib_gettoks(L, 3); +- break; +- default: +- user_node_value(n) = (halfword) lua_roundnumber(L, 3); +- break; +- } ++ set_user_node_direct_value(L, n, 3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -6702,7 +7331,7 @@ static int lua_nodelib_direct_setfield_whatsit(lua_State * L, int n, const char + } + } else if (t == pdf_setmatrix_node) { + if (lua_key_eq(s, data)) { +- pdf_setmatrix_data(n) = nodelib_gettoks(L, 3); ++ set_pdf_setmatrix_direct_value(L, n, 3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -6730,9 +7359,9 @@ static int lua_nodelib_direct_setfield_whatsit(lua_State * L, int n, const char + } + } else if (t == write_node) { + if (lua_key_eq(s, stream)) { +- write_stream(n) = (halfword) lua_tointeger(L, 3); ++ set_write_direct_value(L,n,3); + } else if (lua_key_eq(s, data)) { +- write_tokens(n) = nodelib_gettoks(L, 3); ++ set_write_direct_value(L,n,3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -6788,7 +7417,7 @@ static int lua_nodelib_direct_setfield_whatsit(lua_State * L, int n, const char + } + } else if (t == special_node) { + if (lua_key_eq(s, data)) { +- write_tokens(n) = nodelib_gettoks(L, 3); ++ set_special_direct_value(L, n, 3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -6963,8 +7592,8 @@ static int lua_nodelib_direct_setfield(lua_State * L) + x_displace(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, yoffset)) { + y_displace(n) = (halfword) lua_roundnumber(L, 3); +- } else if (lua_key_eq(s, xadvance)) { +- x_advance(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, data)) { ++ glyph_node_data(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, expansion_factor)) { + ex_glyph(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, components)) { +@@ -6997,8 +7626,10 @@ static int lua_nodelib_direct_setfield(lua_State * L) + height(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, depth)) { + depth(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, direction)) { ++ box_dir(n) = nodelib_getdirection(L, 3); + } else if (lua_key_eq(s, dir)) { +- box_dir(n) = nodelib_getdir(L, 3, 1); ++ box_dir(n) = nodelib_getdir_par(L, 3); + } else if (lua_key_eq(s, shift)) { + shift_amount(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, glue_order)) { +@@ -7069,8 +7700,14 @@ static int lua_nodelib_direct_setfield(lua_State * L) + height(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, depth)) { + depth(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, left)) { ++ rule_left(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, right)) { ++ rule_right(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, direction)) { ++ rule_dir(n) = nodelib_getdirection(L, 3); + } else if (lua_key_eq(s, dir)) { +- rule_dir(n) = nodelib_getdir(L, 3, 1); ++ rule_dir(n) = nodelib_getdir_par(L, 3); + } else if (lua_key_eq(s, index)) { + rule_index(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, transform)) { +@@ -7079,12 +7716,12 @@ static int lua_nodelib_direct_setfield(lua_State * L) + return nodelib_cantset(L, n, s); + } + } else if (t == dir_node) { +- if (lua_key_eq(s, dir)) { +- dir_dir(n) = nodelib_getdir(L, 3, 0); ++ if (lua_key_eq(s, direction)) { ++ dir_dir(n) = nodelib_getdirection(L, 3); ++ } else if (lua_key_eq(s, dir)) { ++ nodelib_setdir_text(L, 3, n); + } else if (lua_key_eq(s, level)) { + dir_level(n) = (halfword) lua_tointeger(L, 3); +- } else if (lua_key_eq(s, subtype)) { /* can be used for anything */ +- subtype(n) = (quarterword) lua_tointeger(L, 3); + } else { + return nodelib_cantset(L, n, s); + } +@@ -7099,8 +7736,10 @@ static int lua_nodelib_direct_setfield(lua_State * L) + local_pen_inter(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, pen_broken)) { + local_pen_broken(n) = (halfword) lua_tointeger(L, 3); ++ } else if (lua_key_eq(s, direction)) { ++ local_par_dir(n) = nodelib_getdirection(L, 3); + } else if (lua_key_eq(s, dir)) { +- local_par_dir(n) = nodelib_getdir(L, 3, 1); ++ local_par_dir(n) = nodelib_getdir_par(L, 3); + } else if (lua_key_eq(s, box_left)) { + local_box_left(n) = nodelib_getlist(L, 3); + } else if (lua_key_eq(s, box_left_width)) { +@@ -7205,6 +7844,8 @@ static int lua_nodelib_direct_setfield(lua_State * L) + right_delimiter(n) = nodelib_popdirect(3); + } else if (lua_key_eq(s, middle)) { + middle_delimiter(n) = nodelib_popdirect(3); ++ } else if (lua_key_eq(s, fam)) { ++ fraction_fam(n) = (halfword) lua_tointeger(L, 3); + } else if (lua_key_eq(s, options)) { + fractionoptions(n) = (halfword) lua_tointeger(L, 3); + } else { +@@ -7373,8 +8014,10 @@ static int lua_nodelib_direct_setfield(lua_State * L) + height(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, depth)) { + depth(n) = (halfword) lua_roundnumber(L, 3); ++ } else if (lua_key_eq(s, direction)) { ++ box_dir(n) = nodelib_getdirection(L, 3); + } else if (lua_key_eq(s, dir)) { +- box_dir(n) = nodelib_getdir(L, 3, 1); ++ box_dir(n) = nodelib_getdir_par(L, 3); + } else if (lua_key_eq(s, shrink)) { + glue_shrink(n) = (halfword) lua_roundnumber(L, 3); + } else if (lua_key_eq(s, glue_order)) { +@@ -7516,6 +8159,8 @@ static int lua_nodelib_direct_setbox(lua_State * L) + + /* node.is_node(n) */ + ++/* ++ + static int lua_nodelib_is_node(lua_State * L) + { + if (maybe_isnode(L,1) == NULL) +@@ -7525,6 +8170,18 @@ static int lua_nodelib_is_node(lua_State * L) + return 1; + } + ++*/ ++ ++static int lua_nodelib_is_node(lua_State * L) ++{ ++ halfword *p = maybe_isnode(L, 1); ++ if (p == NULL) ++ lua_pushboolean(L,0); ++ else ++ lua_pushinteger(L, *((halfword *)p)); ++ return 1; ++} ++ + /* node.direct.is_direct(n) (handy for mixed usage testing) */ + + static int lua_nodelib_direct_is_direct(lua_State * L) +@@ -7571,7 +8228,7 @@ static int lua_nodelib_direct_is_node(lua_State * L) + mechanism. And, one can always sweep the table empty. + */ + +-static int lua_nodelib_properties_set_mode(lua_State * L) /* hh */ ++static int lua_nodelib_properties_set_mode(lua_State * L) + { /* */ + if (lua_isboolean(L,1)) { + lua_properties_enabled = lua_toboolean(L,1); +@@ -7584,7 +8241,7 @@ static int lua_nodelib_properties_set_mode(lua_State * L) /* hh */ + + /* We used to have variants in assigned defaults but they made no sense. */ + +-static int lua_nodelib_properties_flush_table(lua_State * L) /* hh */ ++static int lua_nodelib_properties_flush_table(lua_State * L) + { /* */ + lua_get_metatablelua(node_properties); + lua_pushnil(L); /* initializes lua_next */ +@@ -7599,7 +8256,7 @@ static int lua_nodelib_properties_flush_table(lua_State * L) /* hh */ + + /* maybe we should allocate a proper index 0..var_mem_max but not now */ + +-static int lua_nodelib_get_property(lua_State * L) /* hh */ ++static int lua_nodelib_get_property(lua_State * L) + { /* */ + halfword n = *((halfword *) lua_touserdata(L, 1)); + if (n == null) { +@@ -7611,7 +8268,7 @@ static int lua_nodelib_get_property(lua_State * L) /* hh */ + return 1; + } + +-static int lua_nodelib_direct_get_property(lua_State * L) /* hh */ ++static int lua_nodelib_direct_get_property(lua_State * L) + { /* */ + halfword n = lua_tointeger(L, 1); + if (n == null) { +@@ -7623,7 +8280,7 @@ static int lua_nodelib_direct_get_property(lua_State * L) /* hh */ + return 1; + } + +-static int lua_nodelib_set_property(lua_State * L) /* hh */ ++static int lua_nodelib_set_property(lua_State * L) + { + /* */ + halfword n = *((halfword *) lua_touserdata(L, 1)); +@@ -7638,7 +8295,7 @@ static int lua_nodelib_set_property(lua_State * L) /* hh */ + return 0; + } + +-static int lua_nodelib_direct_set_property(lua_State * L) /* hh */ ++static int lua_nodelib_direct_set_property(lua_State * L) + { + /* */ + halfword n = lua_tointeger(L, 1); +@@ -7653,13 +8310,13 @@ static int lua_nodelib_direct_set_property(lua_State * L) /* hh */ + return 0; + } + +-static int lua_nodelib_direct_properties_get_table(lua_State * L) /* hh */ ++static int lua_nodelib_direct_properties_get_table(lua_State * L) + { /* */ + lua_get_metatablelua(node_properties); + return 1; + } + +-static int lua_nodelib_properties_get_table(lua_State * L) /* hh */ ++static int lua_nodelib_properties_get_table(lua_State * L) + { /* */ + lua_get_metatablelua(node_properties_indirect); + return 1; +@@ -7667,7 +8324,7 @@ static int lua_nodelib_properties_get_table(lua_State * L) /* hh */ + + /* bonus */ + +-static int lua_nodelib_get_property_t(lua_State * L) /* hh */ ++static int lua_nodelib_get_property_t(lua_State * L) + { /* */ + halfword n = *((halfword *) lua_touserdata(L, 2)); + if (n == null) { +@@ -7678,7 +8335,7 @@ static int lua_nodelib_get_property_t(lua_State * L) /* hh */ + return 1; + } + +-static int lua_nodelib_set_property_t(lua_State * L) /* hh */ ++static int lua_nodelib_set_property_t(lua_State * L) + { + /*
          */ + halfword n = *((halfword *) lua_touserdata(L, 2)); +@@ -7804,7 +8461,7 @@ static int lua_nodelib_direct_flatten_discretionaries(lua_State * L) + try_couple_nodes(alink(current),h); + } + vlink(n) = null ; +- //tlink(n) = null; ++ /*tlink(n) = null; */ + } else { + if (current == head) { + head = next; +@@ -7870,7 +8527,7 @@ static int lua_nodelib_flatten_discretionaries(lua_State * L) + try_couple_nodes(alink(current),h); + } + vlink(n) = null ; +- //tlink(n) = null; ++ /*tlink(n) = null; */ + } else { + if (current == head) { + head = next; +@@ -7967,6 +8624,115 @@ static int lua_nodelib_direct_get_synctex_fields(lua_State * L) + return 0; + } + ++/* helper, assumes one node, returns node and delta .. to be tested */ ++ ++static int lua_nodelib_prepend_prevdepth(lua_State * L) ++{ ++ halfword *a; ++ halfword p; ++ halfword prevdepth; ++ boolean mirrored; ++ halfword n = *check_isnode(L, 1); ++ if (!(type(n) == hlist_node || type(n) == vlist_node)) { ++ lua_pushnil(L); ++ return 1; ++ } ++ prevdepth = lua_tointeger(L,2); ++ mirrored = (type(n) == hlist_node) && is_mirrored(box_dir(n)) ; ++ if (prevdepth > ignore_depth) { ++ halfword d; ++ if (mirrored) { ++ d = width(baseline_skip_par) - prevdepth - depth(n); ++ } else { ++ d = width(baseline_skip_par) - prevdepth - height(n); ++ } ++ 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(p,n); ++ fast_metatable_or_nil(p); /* glue */ ++ } else { ++ fast_metatable_or_nil(n); /* node */ ++ } ++ if (mirrored) { ++ prevdepth = height(n); ++ } else { ++ prevdepth = depth(n); ++ } ++ lua_pushinteger(L,prevdepth); /* new prevdepth */ ++ return 2; ++} ++ ++static int lua_nodelib_direct_prepend_prevdepth(lua_State * L) ++{ ++ halfword p; ++ halfword prevdepth; ++ boolean mirrored; ++ halfword n = lua_tointeger(L, 1); ++ if (type(n) == hlist_node || type(n) == vlist_node) { ++ lua_pushnil(L); ++ return 1; ++ } ++ prevdepth = lua_tointeger(L,2); ++ mirrored = (type(n) == hlist_node) && is_mirrored(box_dir(n)) ; ++ if (prevdepth > ignore_depth) { ++ halfword d; ++ if (mirrored) { ++ d = width(baseline_skip_par) - prevdepth - depth(n); ++ } else { ++ d = width(baseline_skip_par) - prevdepth - height(n); ++ } ++ 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(p,n); ++ lua_pushinteger(L,p); /* glue */ ++ } else { ++ lua_pushinteger(L,n); /* node */ ++ } ++ if (mirrored) { ++ prevdepth = height(n); ++ } else { ++ prevdepth = depth(n); ++ } ++ lua_pushinteger(L,prevdepth); /* new prevdepth */ ++ return 2; ++} ++ ++static int lua_nodelib_make_extensible(lua_State * L) ++{ ++ int top = lua_gettop(L); ++ if (top >= 3) { ++ halfword fnt = lua_tointeger(L,1); ++ halfword chr = lua_tointeger(L,2); ++ halfword size = lua_tointeger(L,3); ++ halfword overlap = 65536 ; ++ halfword attlist = null; ++ halfword b = null; ++ int horizontal = 0; ++ if (top >= 4) { ++ overlap = lua_tointeger(L,4); ++ } ++ if (top >= 5) { ++ horizontal = lua_toboolean(L,5); ++ } ++ if (top >= 6) { ++ attlist = *check_isnode(L, 6); ++ } ++ b = make_extensible(fnt,chr,size,overlap,horizontal,attlist); ++ nodelib_pushlist(L,b); ++ } else { ++ lua_pushnil(L); ++ } ++ return 1; ++} ++ + /* done */ + + static const struct luaL_Reg nodelib_p[] = { +@@ -8022,6 +8788,7 @@ static const struct luaL_Reg direct_nodelib_f[] = { + {"getshift", lua_nodelib_direct_getshift}, + {"getfield", lua_nodelib_direct_getfield}, + {"getfont", lua_nodelib_direct_getfont}, ++ {"getexpansion", lua_nodelib_direct_getexpansion}, + {"getfam", lua_nodelib_direct_getfam}, + {"getid", lua_nodelib_direct_getid}, + {"getnext", lua_nodelib_direct_getnext}, +@@ -8029,11 +8796,13 @@ static const struct luaL_Reg direct_nodelib_f[] = { + {"getboth", lua_nodelib_direct_getboth}, + {"getlist", lua_nodelib_direct_getlist}, + {"getleader", lua_nodelib_direct_getleader}, ++ {"getdata", lua_nodelib_direct_getdata}, + {"getsubtype", lua_nodelib_direct_getsubtype}, + {"getattributelist", lua_nodelib_direct_getattributelist}, + {"getnucleus", lua_nodelib_direct_getnucleus}, + {"getsub", lua_nodelib_direct_getsub}, + {"getsup", lua_nodelib_direct_getsup}, ++ {"getdirection", lua_nodelib_direct_getdirection}, + {"has_glyph", lua_nodelib_direct_has_glyph}, + {"has_attribute", lua_nodelib_direct_has_attribute}, + {"get_attribute", lua_nodelib_direct_get_attribute}, +@@ -8064,12 +8833,14 @@ static const struct luaL_Reg direct_nodelib_f[] = { + {"setfield", lua_nodelib_direct_setfield}, + {"setchar", lua_nodelib_direct_setchar}, + {"setfont", lua_nodelib_direct_setfont}, ++ {"setexpansion", lua_nodelib_direct_setexpansion}, + {"setfam", lua_nodelib_direct_setfam}, + {"setcomponents", lua_nodelib_direct_setcomponents}, + {"setlang", lua_nodelib_direct_setlang}, + {"setkern", lua_nodelib_direct_setkern}, + {"setpenalty", lua_nodelib_direct_setpenalty}, + {"setdir", lua_nodelib_direct_setdir}, ++ {"setdirection", lua_nodelib_direct_setdirection}, + {"setoffsets", lua_nodelib_direct_setoffsets}, + {"setdisc", lua_nodelib_direct_setdisc}, + {"setwhd", lua_nodelib_direct_setwhd}, +@@ -8084,6 +8855,7 @@ static const struct luaL_Reg direct_nodelib_f[] = { + {"setsplit", lua_nodelib_direct_setsplit}, + {"setlist", lua_nodelib_direct_setlist}, + {"setleader", lua_nodelib_direct_setleader}, ++ {"setdata", lua_nodelib_direct_setdata}, + {"setsubtype", lua_nodelib_direct_setsubtype}, + {"setattributelist", lua_nodelib_direct_setattributelist}, + {"setnucleus", lua_nodelib_direct_setnucleus}, +@@ -8097,6 +8869,8 @@ static const struct luaL_Reg direct_nodelib_f[] = { + {"traverse", lua_nodelib_direct_traverse}, + {"traverse_id", lua_nodelib_direct_traverse_filtered}, + {"traverse_char", lua_nodelib_direct_traverse_char}, ++ {"traverse_glyph", lua_nodelib_direct_traverse_glyph}, ++ {"traverse_list", lua_nodelib_direct_traverse_list}, + /* {"type", lua_nodelib_type}, */ /* no node argument */ + /* {"types", lua_nodelib_types}, */ /* no node argument */ + {"unprotect_glyphs", lua_nodelib_direct_unprotect_glyphs}, +@@ -8110,14 +8884,15 @@ static const struct luaL_Reg direct_nodelib_f[] = { + /* {"whatsits", lua_nodelib_whatsits}, */ /* no node argument */ + {"write", lua_nodelib_direct_append}, + {"set_properties_mode",lua_nodelib_properties_set_mode}, +- {"flush_properties_table",lua_nodelib_properties_flush_table}, /* hh experiment */ +- {"get_properties_table",lua_nodelib_direct_properties_get_table}, /* hh experiment */ ++ {"flush_properties_table",lua_nodelib_properties_flush_table}, ++ {"get_properties_table",lua_nodelib_direct_properties_get_table}, + {"getproperty", lua_nodelib_direct_get_property}, + {"setproperty", lua_nodelib_direct_set_property}, + {"effective_glue", lua_nodelib_direct_effective_glue}, + {"check_discretionary", lua_nodelib_direct_check_discretionary}, + {"check_discretionaries", lua_nodelib_direct_check_discretionaries}, + {"flatten_discretionaries",lua_nodelib_direct_flatten_discretionaries}, ++ {"prepend_prevdepth",lua_nodelib_direct_prepend_prevdepth}, + /* done */ + {"set_synctex_fields", lua_nodelib_direct_set_synctex_fields}, + {"get_synctex_fields", lua_nodelib_direct_get_synctex_fields}, +@@ -8139,6 +8914,7 @@ static const struct luaL_Reg nodelib_f[] = { + {"family_font", lua_nodelib_mfont}, + {"fields", lua_nodelib_fields}, + {"subtypes", lua_nodelib_subtypes}, ++ {"values", lua_nodelib_values}, + {"first_glyph", lua_nodelib_first_glyph}, + {"flush_list", lua_nodelib_flush_list}, + {"flush_node", lua_nodelib_flush_node}, +@@ -8191,33 +8967,34 @@ static const struct luaL_Reg nodelib_f[] = { + {"traverse", lua_nodelib_traverse}, + {"traverse_id", lua_nodelib_traverse_filtered}, + {"traverse_char", lua_nodelib_traverse_char}, ++ {"traverse_glyph", lua_nodelib_traverse_glyph}, ++ {"traverse_list", lua_nodelib_traverse_list}, + {"type", lua_nodelib_type}, + {"types", lua_nodelib_types}, + {"unprotect_glyphs", lua_nodelib_unprotect_glyphs}, + {"unprotect_glyph", lua_nodelib_unprotect_glyph}, + {"unset_attribute", lua_nodelib_unset_attribute}, +- {"setglue",lua_nodelib_set_glue}, +- {"getglue",lua_nodelib_get_glue}, +- {"is_zero_glue",lua_nodelib_is_zero_glue}, ++ {"setglue", lua_nodelib_set_glue}, ++ {"getglue", lua_nodelib_get_glue}, ++ {"is_zero_glue", lua_nodelib_is_zero_glue}, + {"usedlist", lua_nodelib_usedlist}, + {"vpack", lua_nodelib_vpack}, + {"whatsits", lua_nodelib_whatsits}, + {"write", lua_nodelib_append}, + /* experiment */ +- /* {"attributes_to_table",lua_nodelib_attributes_to_table}, */ /* hh experiment */ ++ /* {"attributes_to_table",lua_nodelib_attributes_to_table}, */ + /* experiment */ +- {"set_properties_mode",lua_nodelib_properties_set_mode}, /* hh experiment */ +- {"flush_properties_table",lua_nodelib_properties_flush_table}, /* hh experiment */ +- {"get_properties_table",lua_nodelib_properties_get_table}, /* bonus */ /* hh experiment */ +- {"getproperty", lua_nodelib_get_property}, /* hh experiment */ +- {"setproperty", lua_nodelib_set_property}, /* hh experiment */ ++ {"set_properties_mode", lua_nodelib_properties_set_mode}, ++ {"flush_properties_table", lua_nodelib_properties_flush_table}, ++ {"get_properties_table", lua_nodelib_properties_get_table}, ++ {"getproperty", lua_nodelib_get_property}, ++ {"setproperty", lua_nodelib_set_property}, + {"effective_glue", lua_nodelib_effective_glue}, + {"check_discretionary", lua_nodelib_check_discretionary}, + {"check_discretionaries", lua_nodelib_check_discretionaries}, +- {"flatten_discretionaries",lua_nodelib_flatten_discretionaries}, +- /* done */ +- /* {"set_synctex_fields", lua_nodelib_set_synctex_fields}, */ +- /* {"get_synctex_fields", lua_nodelib_get_synctex_fields}, */ ++ {"flatten_discretionaries", lua_nodelib_flatten_discretionaries}, ++ {"prepend_prevdepth", lua_nodelib_prepend_prevdepth}, ++ {"make_extensible", lua_nodelib_make_extensible}, + /* done */ + {"fix_node_lists", lua_nodelib_fix_node_lists}, + {NULL, NULL} /* sentinel */ +@@ -8255,12 +9032,12 @@ void nodelist_to_lua(lua_State * L, int n) + lua_nodelib_push(L); + } + +-int nodelist_from_lua(lua_State * L) ++int nodelist_from_lua(lua_State * L, int n) + { +- if (lua_isnil(L, -1)) { ++ if (lua_isnil(L, n)) { + return null; + } else { +- halfword n= *check_isnode(L, -1); +- return (n ? n : null); ++ halfword list = *check_isnode(L, n); ++ return (list ? list : null); + } + } +diff --git a/texk/web2c/luatexdir/lua/loslibext.c b/texk/web2c/luatexdir/lua/loslibext.c +index 309d636c4..979f72472 100644 +--- a/texk/web2c/luatexdir/lua/loslibext.c ++++ b/texk/web2c/luatexdir/lua/loslibext.c +@@ -703,28 +703,49 @@ static int uname(struct utsname *uts) + GetVersionEx(&osver); + GetSystemInfo(&sysinfo); + ++ ++ /* ++ Windows 10 10.0* ++ Windows Server 2016 10.0* ++ Windows 8.1 6.3* ++ Windows Server 2012 R2 6.3* ++ Windows 8 6.2 ++ Windows Server 2012 6.2 ++ Windows 7 6.1 ++ Windows Server 2008 R2 6.1 ++ Windows Server 2008 6.0 ++ Windows Vista 6.0 ++ Windows Server 2003 R2 5.2 ++ Windows Server 2003 5.2 ++ Windows XP 64-Bit Edition 5.2 ++ Windows XP 5.1 ++ Windows 2000 5.0 ++ */ ++ + switch (osver.dwPlatformId) { +- case VER_PLATFORM_WIN32_NT: /* NT, Windows 2000 or Windows XP */ ++ case VER_PLATFORM_WIN32_NT: + if (osver.dwMajorVersion == 4) +- strcpy(uts->sysname, "Windows NT4x"); /* NT4x */ ++ strcpy(uts->sysname, "Windows NT 4"); + else if (osver.dwMajorVersion <= 3) +- strcpy(uts->sysname, "Windows NT3x"); /* NT3x */ ++ strcpy(uts->sysname, "Windows NT 3"); + else if (osver.dwMajorVersion == 5) { + if (osver.dwMinorVersion == 0) +- strcpy(uts->sysname, "Windows 2000"); /* 2k */ ++ strcpy(uts->sysname, "Windows 2000"); + else if (osver.dwMinorVersion == 1) +- strcpy(uts->sysname, "Windows XP"); /* XP */ ++ strcpy(uts->sysname, "Windows XP"); + else if (osver.dwMinorVersion == 2) +- strcpy(uts->sysname, "Windows Server 2003"); /* Server 2003 */ ++ strcpy(uts->sysname, "Windows XP 64-Bit"); + } else if (osver.dwMajorVersion == 6) { +- /* +- if( osver.wProductType == VER_NT_WORKSTATION ) +- */ +- strcpy(uts->sysname, "Windows Vista"); /* Vista */ +- /* +- else +- strcpy (uts->sysname, "Windows Server 2008"); +- */ ++ if (osver.dwMinorVersion == 0) ++ strcpy(uts->sysname, "Windows Vista"); ++ else if (osver.dwMinorVersion == 1) ++ strcpy(uts->sysname, "Windows 7"); ++ else if (osver.dwMinorVersion == 2) ++ strcpy(uts->sysname, "Windows 8"); ++ else if (osver.dwMinorVersion == 3) ++ strcpy(uts->sysname, "Windows 8.1"); ++ } else if (osver.dwMajorVersion == 10) { ++ strcpy(uts->sysname, "Windows 10"); + } + os = WinNT; + break; +diff --git a/texk/web2c/luatexdir/lua/lpdflib.c b/texk/web2c/luatexdir/lua/lpdflib.c +index 096600de3..d8e051f3f 100644 +--- a/texk/web2c/luatexdir/lua/lpdflib.c ++++ b/texk/web2c/luatexdir/lua/lpdflib.c +@@ -22,7 +22,7 @@ + #include "lua/luatex-api.h" + #include "pdf/pdftables.h" + +-static int luapdfprint(lua_State * L) ++int luapdfprint(lua_State * L) + { + int n; + const_lstring st; +@@ -82,6 +82,7 @@ static int luapdfprint(lua_State * L) + } + st.s = lua_tolstring(L, n, &st.l); + pdf_out_block(static_pdf, st.s, st.l); ++/* pdf_out(pdf, '\n'); */ + return 0; + } + +@@ -193,6 +194,7 @@ static int table_obj(lua_State * L) + const_lstring attr, st; + lstring buf; + int immediate = 0; /* default: not immediate */ ++ int nolength = 0; + attr.s = st.s = NULL; + attr.l = 0; + assert(lua_istable(L, 1)); /* t */ +@@ -213,6 +215,12 @@ static int table_obj(lua_State * L) + immediate = lua_toboolean(L, -1); /* 0 or 1 */ + } + lua_pop(L, 1); /* t */ ++ lua_key_rawgeti(nolength); ++ if (!lua_isnil(L, -1)) { /* b? t */ ++ if (lua_isboolean(L, -1)) /* !b t */ ++ nolength = lua_toboolean(L, -1); /* 0 or 1 */ ++ } ++ lua_pop(L, 1); /* t */ + + /* is a reserved object referenced by "objnum"? */ + +@@ -335,19 +343,29 @@ static int table_obj(lua_State * L) + } else { + if (immediate == 1) { + pdf_begin_obj(static_pdf, k, OBJSTM_NEVER); /* 0 = not an object stream candidate! */ +- pdf_begin_dict(static_pdf); +- if (attr.s != NULL) { ++ if (nolength && attr.s != NULL) { ++ /* we have a direct copy possible with compressed data */ ++ pdf_begin_dict(static_pdf); + pdf_out_block(static_pdf, attr.s, attr.l); +- if (attr.s[attr.l - 1] != '\n') +- pdf_out(static_pdf, '\n'); ++ static_pdf->compress_level = 0; ++ static_pdf->stream_deflate = false; ++ pdf_end_dict(static_pdf); ++ } else { ++ pdf_begin_dict(static_pdf); ++ if (attr.s != NULL) { ++ pdf_check_space(static_pdf); ++ pdf_out_block(static_pdf, attr.s, attr.l); ++ pdf_set_space(static_pdf); ++ } ++ if (compress_level > -1) ++ static_pdf->compress_level = compress_level; ++ pdf_dict_add_streaminfo(static_pdf); ++ pdf_end_dict(static_pdf); + } +- if (compress_level > -1) +- static_pdf->compress_level = compress_level; +- pdf_dict_add_streaminfo(static_pdf); +- pdf_end_dict(static_pdf); + pdf_begin_stream(static_pdf); + } else { + set_obj_obj_is_stream(static_pdf, k); ++ set_obj_obj_no_length(static_pdf, k); + if (compress_level > -1) + obj_obj_pdfcompresslevel(static_pdf, k) = compress_level; + } +@@ -404,42 +422,42 @@ static int orig_obj(lua_State * L) + obj_data_ptr(static_pdf, k) = pdf_get_mem(static_pdf, pdfmem_obj_size); + init_obj_obj(static_pdf, k); + switch (n - first_arg + 1) { +- case 0: +- luaL_error(L, "pdf.obj() needs at least one argument"); +- break; +- case 1: +- if (!lua_isstring(L, first_arg)) /* or number */ +- luaL_error(L, "pdf.obj() 1st argument must be string"); +- break; +- case 2: +- case 3: +- if (lua_type(L, first_arg) != LUA_TSTRING) +- luaL_error(L, "pdf.obj() 1st argument must be string"); +- if (!lua_isstring(L, first_arg + 1)) /* or number */ +- luaL_error(L, "pdf.obj() 2nd argument must be string"); +- st_s = lua_tostring(L, first_arg); +- if (lua_key_eq(st_s, file)) { +- if (n == first_arg + 2) +- luaL_error(L, "pdf.obj() 3rd argument forbidden in file mode"); +- set_obj_obj_is_file(static_pdf, k); +- } else { +- if (n == first_arg + 2) { /* write attr text */ +- if (!lua_isstring(L, -1)) /* or number */ +- luaL_error(L, "pdf.obj() 3rd argument must be string"); +- obj_obj_stream_attr(static_pdf, k) = +- luaL_ref(Luas, LUA_REGISTRYINDEX); +- } +- if (lua_key_eq(st_s, stream)) { +- set_obj_obj_is_stream(static_pdf, k); +- } else if (lua_key_eq(st_s, streamfile)) { +- set_obj_obj_is_stream(static_pdf, k); ++ case 0: ++ luaL_error(L, "pdf.obj() needs at least one argument"); ++ break; ++ case 1: ++ if (!lua_isstring(L, first_arg)) /* or number */ ++ luaL_error(L, "pdf.obj() 1st argument must be string"); ++ break; ++ case 2: ++ case 3: ++ if (lua_type(L, first_arg) != LUA_TSTRING) ++ luaL_error(L, "pdf.obj() 1st argument must be string"); ++ if (!lua_isstring(L, first_arg + 1)) /* or number */ ++ luaL_error(L, "pdf.obj() 2nd argument must be string"); ++ st_s = lua_tostring(L, first_arg); ++ if (lua_key_eq(st_s, file)) { ++ if (n == first_arg + 2) ++ luaL_error(L, "pdf.obj() 3rd argument forbidden in file mode"); + set_obj_obj_is_file(static_pdf, k); +- } else +- luaL_error(L, "pdf.obj() invalid argument"); +- } +- break; +- default: +- luaL_error(L, "pdf.obj() allows max. 3 arguments"); ++ } else { ++ if (n == first_arg + 2) { /* write attr text */ ++ if (!lua_isstring(L, -1)) /* or number */ ++ luaL_error(L, "pdf.obj() 3rd argument must be string"); ++ obj_obj_stream_attr(static_pdf, k) = ++ luaL_ref(Luas, LUA_REGISTRYINDEX); ++ } ++ if (lua_key_eq(st_s, stream)) { ++ set_obj_obj_is_stream(static_pdf, k); ++ } else if (lua_key_eq(st_s, streamfile)) { ++ set_obj_obj_is_stream(static_pdf, k); ++ set_obj_obj_is_file(static_pdf, k); ++ } else ++ luaL_error(L, "pdf.obj() invalid argument"); ++ } ++ break; ++ default: ++ luaL_error(L, "pdf.obj() allows max. 3 arguments"); + } + obj_obj_data(static_pdf, k) = luaL_ref(L, LUA_REGISTRYINDEX); + return k; +@@ -478,23 +496,23 @@ static int l_reserveobj(lua_State * L) + const char *st_s = NULL; + n = lua_gettop(L); + switch (n) { +- case 0: +- static_pdf->obj_count++; +- pdf_last_obj = pdf_create_obj(static_pdf, obj_type_obj, static_pdf->obj_ptr + 1); +- break; +- case 1: +- if (lua_type(L, -1) != LUA_TSTRING) +- luaL_error(L, "pdf.reserveobj() optional argument must be string"); +- st_s = luaL_checkstring(L, 1); +- if (lua_key_eq(st_s, annot)) { +- pdf_last_annot = pdf_create_obj(static_pdf, obj_type_annot, 0); +- } else { +- luaL_error(L, "pdf.reserveobj() optional string must be \"annot\""); +- } +- lua_pop(L, 1); +- break; +- default: +- luaL_error(L, "pdf.reserveobj() allows max. 1 argument"); ++ case 0: ++ static_pdf->obj_count++; ++ pdf_last_obj = pdf_create_obj(static_pdf, obj_type_obj, static_pdf->obj_ptr + 1); ++ break; ++ case 1: ++ if (lua_type(L, -1) != LUA_TSTRING) ++ luaL_error(L, "pdf.reserveobj() optional argument must be string"); ++ st_s = luaL_checkstring(L, 1); ++ if (lua_key_eq(st_s, annot)) { ++ pdf_last_annot = pdf_create_obj(static_pdf, obj_type_annot, 0); ++ } else { ++ luaL_error(L, "pdf.reserveobj() optional string must be \"annot\""); ++ } ++ lua_pop(L, 1); ++ break; ++ default: ++ luaL_error(L, "pdf.reserveobj() allows max. 1 argument"); + } + lua_pushinteger(L, static_pdf->obj_ptr); + return 1; +@@ -505,16 +523,16 @@ static int l_registerannot(lua_State * L) + int n, i; + n = lua_gettop(L); + switch (n) { +- case 1: +- if (global_shipping_mode == NOT_SHIPPING) +- luaL_error(L, "pdf.registerannot() can only be used in late lua"); +- i = (int) luaL_checkinteger(L, 1); +- if (i <= 0) +- luaL_error(L, "pdf.registerannot() can only register positive object numbers"); +- addto_page_resources(static_pdf, obj_type_annot, i); +- break; +- default: +- luaL_error(L, "pdf.registerannot() needs exactly 1 argument"); ++ case 1: ++ if (global_shipping_mode == NOT_SHIPPING) ++ luaL_error(L, "pdf.registerannot() can only be used in late lua"); ++ i = (int) luaL_checkinteger(L, 1); ++ if (i <= 0) ++ luaL_error(L, "pdf.registerannot() can only register positive object numbers"); ++ addto_page_resources(static_pdf, obj_type_annot, i); ++ break; ++ default: ++ luaL_error(L, "pdf.registerannot() needs exactly 1 argument"); + } + return 0; + } +@@ -599,55 +617,30 @@ static int l_set_xformresources (lua_State * L) { l_set_pdf_value(xformresources + static int l_set_xformattributes(lua_State * L) { l_set_pdf_value(xformattributes); } + static int l_set_trailerid (lua_State * L) { l_set_pdf_value(trailerid); } + +-/* +- +-static int setpdf(lua_State * L) ++static int getpdfobjtype(lua_State * L) + { +- const char *s ; +- if (lua_gettop(L) != 3) { +- return 0; +- } +- if (lua_type(L, -2) == LUA_TSTRING) { +- s = lua_tostring(L, -1); +- if (valid_pdf_key) { +- lua_get_metatablelua(pdf_data); +- lua_replace(L, -4); ++ if (lua_type(L, 1) == LUA_TNUMBER) { ++ int n = (int) lua_tointeger(L, 1); ++ if (n > 0 && n <= static_pdf->obj_ptr) { ++ lua_pushstring(L, pdf_obj_typenames[obj_type(static_pdf, n)]); ++ return 1; + } + } +- lua_rawset(L, -3); +- return 0; +-} +- +-*/ +- +-static int l_objtype(lua_State * L) +-{ +- int n = lua_gettop(L); +- if (n != 1) +- luaL_error(L, "pdf.objtype() needs exactly 1 argument"); +- n = (int) luaL_checkinteger(L, 1); +- if (n < 0 || n > static_pdf->obj_ptr) +- lua_pushnil(L); +- else +- lua_pushstring(L, pdf_obj_typenames[obj_type(static_pdf, n)]); ++ lua_pushnil(L); + return 1; + } + +-static int l_maxobjnum(lua_State * L) ++static int getpdfmaxobjnum(lua_State * L) + { +- int n = lua_gettop(L); +- if (n != 0) +- luaL_error(L, "pdf.maxobjnum() needs 0 arguments"); + lua_pushinteger(L, static_pdf->obj_ptr); + return 1; + } + + static int l_mapfile(lua_State * L) + { +- char *s; + const char *st; +- if ((lua_type(L,-1) == LUA_TSTRING) && (st = lua_tostring(L, -1)) != NULL) { +- s = xstrdup(st); ++ if ((lua_type(L, 1) == LUA_TSTRING) && (st = lua_tostring(L, 1)) != NULL) { ++ char *s = xstrdup(st); + process_map_item(s, MAPFILE); + free(s); + } +@@ -656,29 +649,15 @@ static int l_mapfile(lua_State * L) + + static int l_mapline(lua_State * L) + { +- char *s; + const char *st; +- if ((lua_type(L,-1) == LUA_TSTRING) && (st = lua_tostring(L, -1)) != NULL) { +- s = xstrdup(st); ++ if ((lua_type(L, 1) == LUA_TSTRING) && (st = lua_tostring(L, 1)) != NULL) { ++ char *s = xstrdup(st); + process_map_item(s, MAPLINE); + free(s); + } + return 0; + } + +-static int l_pageref(lua_State * L) +-{ +- int n = lua_gettop(L); +- if (n != 1) +- luaL_error(L, "pdf.pageref() needs exactly 1 argument"); +- n = (int) luaL_checkinteger(L, 1); +- if (n <= 0) +- luaL_error(L, "pdf.pageref() needs page number > 0"); +- n = pdf_get_obj(static_pdf, obj_type_page, n, false); +- lua_pushinteger(L, n); +- return 1; +-} +- + static int l_getpos(lua_State * L) + { + lua_pushinteger(L, static_pdf->posstruct->pos.h); +@@ -770,6 +749,12 @@ static int l_get_obj_compress_level(lua_State * L) + return 1 ; + } + ++static int l_get_recompress(lua_State * L) ++{ ++ lua_pushinteger(L, (pdf_recompress)); ++ return 1 ; ++} ++ + static int l_set_compress_level(lua_State * L) + { + if (lua_type(L, 1) == LUA_TNUMBER) { +@@ -796,6 +781,19 @@ static int l_set_obj_compress_level(lua_State * L) + return 0 ; + } + ++ static int l_set_recompress(lua_State * L) ++{ ++ if (lua_type(L, 1) == LUA_TNUMBER) { ++ int c = (int) lua_tointeger(L, 1); ++ if (c<0) ++ c = 0 ; ++ else if (c>9) ++ c = 9 ; ++ set_pdf_recompress(c); ++ } ++ return 0 ; ++} ++ + /* fonts */ + + static int getpdfgentounicode(lua_State * L) +@@ -826,6 +824,18 @@ static int setpdfomitcidset(lua_State * L) + return 0 ; + } + ++/* for tracing purposes when no pages are flushed */ ++ ++static int setforcefile(lua_State * L) ++{ ++ if (lua_type(L, 1) == LUA_TBOOLEAN) { ++ static_pdf->force_file = lua_toboolean(L,1); ++ } else { ++ static_pdf->force_file = 0; ++ } ++ return 0 ; ++} ++ + /* accuracy */ + + static int l_get_decimal_digits(lua_State * L) +@@ -879,9 +889,10 @@ static int getpdffontname(lua_State * L) + int c, ff ; + if (lua_type(L, 1) == LUA_TNUMBER) { + c = (int) lua_tointeger(L, 1); +- pdf_check_vf(c); +- if (!font_used(c)) ++ /* pdf_check_vf(c); */ ++ if (!font_used(c)) { + pdf_init_font(static_pdf,c); ++ } + set_ff(c); + lua_pushinteger(L, (obj_info(static_pdf, pdf_font_num(ff)))); + } else { +@@ -895,9 +906,10 @@ static int getpdffontobjnum(lua_State * L) + if (lua_type(L, 1) == LUA_TNUMBER) { + int ff; + int c = (int) lua_tointeger(L, 1); +- pdf_check_vf(c); +- if (!font_used(c)) ++ /* pdf_check_vf(c); */ ++ if (!font_used(c)) { + pdf_init_font(static_pdf,c); ++ } + set_ff(c); + lua_pushinteger(L, (pdf_font_num(ff))); + } else { +@@ -917,21 +929,19 @@ static int getpdffontsize(lua_State * L) + return 1 ; + } + +-/* +- + static int getpdfpageref(lua_State * L) + { + if (lua_type(L, 1) == LUA_TNUMBER) { + int c = (int) lua_tointeger(L, 1); +- lua_pushinteger(L, (pdf_get_obj(static_pdf, obj_type_page, c, false))); +- } else { +- lua_pushnil(L); ++ if (c > 0) { ++ lua_pushinteger(L, (pdf_get_obj(static_pdf, obj_type_page, c, false))); ++ return 1; ++ } + } ++ lua_pushnil(L); + return 1 ; + } + +-*/ +- + static int getpdfxformname(lua_State * L) + { + if (lua_type(L, 1) == LUA_TNUMBER) { +@@ -944,12 +954,6 @@ static int getpdfxformname(lua_State * L) + return 1 ; + } + +-static int getpdfversion(lua_State * L) +-{ +- lua_pushinteger(L,1); +- return 1 ; +-} +- + static int getpdfcreationdate(lua_State * L) + { + initialize_start_time(static_pdf); +@@ -1169,6 +1173,76 @@ static int l_set_font_attributes(lua_State * L) + return 0; + } + ++static int pdfincludechar(lua_State * L) ++{ ++ int f = lua_tointeger(L, 1); ++ if (lua_type(L,2) == LUA_TTABLE) { ++ int i, c; ++ int n = lua_rawlen(L, 2); ++ for (i=1; i<=n; i++) { ++ lua_rawgeti(L, 2, i); ++ c = lua_tointeger(L, 3); ++ pdf_mark_char(f,c); ++ lua_pop(L, 1); ++ } ++ } else { ++ int c = lua_tointeger(L, 2); ++ pdf_mark_char(f,c); ++ } ++ return 0; ++} ++ ++static int pdfincludefont(lua_State * L) ++{ ++ int f = lua_tointeger(L, 1); ++ pdf_init_font(static_pdf,f); ++ return 0; ++} ++ ++static int pdfincludeimage(lua_State * L) ++{ ++ /*tex How to check for a valid entry? */ ++ image_dict *idict = idict_array[lua_tointeger(L,1)]; ++ int objnum = img_objnum(idict); ++ if (img_state(idict) < DICT_OUTIMG) { ++ img_state(idict) = DICT_OUTIMG; ++ } ++ if (! is_obj_written(static_pdf, objnum)) { ++ pdf_write_image(static_pdf, objnum); ++ } ++ lua_pushinteger(L,img_type(idict)); ++ lua_pushinteger(L,img_xorig(idict)); ++ lua_pushinteger(L,img_yorig(idict)); ++ lua_pushinteger(L,img_xsize(idict)); ++ lua_pushinteger(L,img_ysize(idict)); ++ lua_pushinteger(L,img_rotation(idict)); ++ lua_pushinteger(L,objnum); ++ if (img_type(idict) == IMG_TYPE_PNG) { ++ lua_pushinteger(L,img_group_ref(idict)); ++ } else { ++ lua_pushnil(L); ++ } ++ return 8; ++} ++ ++static int getpdfnofobjects(lua_State * L) ++{ ++ int k; ++ int written = 0; ++ int dropped = 0; ++ for (k = 1; k <= static_pdf->obj_ptr; k++) { ++ if (is_obj_written(static_pdf, k)) { ++ written += 1; ++ } else { ++ dropped += 1; ++ } ++ } ++ lua_pushinteger(L,written); ++ lua_pushinteger(L,dropped); ++ return 2; ++} ++ ++/*tex For normal output see |pdflistout.c|: */ + + static const struct luaL_Reg pdflib[] = { + { "gethpos", l_gethpos }, +@@ -1179,11 +1253,10 @@ static const struct luaL_Reg pdflib[] = { + { "registerannot", l_registerannot }, + { "reserveobj", l_reserveobj }, + { "getpos", l_getpos }, +- /* { "pageref", getpdfpageref }, */ +- { "maxobjnum", l_maxobjnum }, +- { "pageref", l_pageref }, ++ { "getpageref", getpdfpageref }, ++ { "getmaxobjnum", getpdfmaxobjnum }, + { "print", luapdfprint }, +- { "objtype", l_objtype }, ++ { "getobjtype", getpdfobjtype }, + { "getmatrix", l_getmatrix }, + { "hasmatrix", l_hasmatrix }, + { "setfontattributes", l_set_font_attributes }, +@@ -1213,20 +1286,20 @@ static const struct luaL_Reg pdflib[] = { + { "getlastannot", l_get_lastannot }, + { "getcompresslevel", l_get_compress_level }, + { "getobjcompresslevel", l_get_obj_compress_level }, ++ { "getrecompress", l_get_recompress }, + { "setcompresslevel", l_set_compress_level }, + { "setobjcompresslevel", l_set_obj_compress_level }, ++ { "setrecompress", l_set_recompress }, + { "getdecimaldigits", l_get_decimal_digits }, + { "setdecimaldigits", l_set_decimal_digits }, + { "getpkresolution", l_get_pk_resolution }, + { "setpkresolution", l_set_pk_resolution }, + { "getsuppressoptionalinfo", l_get_suppress_optional_info }, + { "setsuppressoptionalinfo", l_set_suppress_optional_info }, +- /* moved from tex table */ +- { "fontname", getpdffontname }, +- { "fontobjnum", getpdffontobjnum }, +- { "fontsize", getpdffontsize }, +- { "xformname", getpdfxformname }, +- { "getversion", getpdfversion }, ++ { "getfontname", getpdffontname }, ++ { "getfontobjnum", getpdffontobjnum }, ++ { "getfontsize", getpdffontsize }, ++ { "getxformname", getpdfxformname }, + { "getcreationdate", getpdfcreationdate }, + { "getmajorversion", getpdfmajorversion }, + { "setmajorversion", setpdfmajorversion }, +@@ -1253,8 +1326,22 @@ static const struct luaL_Reg pdflib[] = { + { "setignoreunknownimages", setpdfignoreunknownimages }, + { "setgentounicode", setpdfgentounicode }, + { "setomitcidset", setpdfomitcidset }, ++ { "setforcefile", setforcefile }, + { "mapfile", l_mapfile }, + { "mapline", l_mapline }, ++ { "includechar", pdfincludechar }, ++ { "includefont", pdfincludefont }, ++ /* might go, used when sanitizing backend */ ++ { "includeimage", pdfincludeimage }, ++ { "getnofobjects", getpdfnofobjects }, ++ /* for a while */ ++ { "maxobjnum", getpdfmaxobjnum }, ++ { "pageref", getpdfpageref }, ++ { "objtype", getpdfobjtype }, ++ { "fontname", getpdffontname }, ++ { "fontobjnum", getpdffontobjnum }, ++ { "fontsize", getpdffontsize }, ++ { "xformname", getpdfxformname }, + /* sentinel */ + {NULL, NULL} + }; +diff --git a/texk/web2c/luatexdir/lua/lpdfscannerlib.cc b/texk/web2c/luatexdir/lua/lpdfscannerlib.cc +index 8bafd6122..56452b2ff 100644 +--- a/texk/web2c/luatexdir/lua/lpdfscannerlib.cc ++++ b/texk/web2c/luatexdir/lua/lpdfscannerlib.cc +@@ -17,6 +17,70 @@ + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see . */ + ++/* converted example in manual: ++ ++local operatortable = { } ++ ++operatortable.Do = function(scanner,info) ++ local resources = info.resources ++ if resources then ++ local val = scanner:pop() ++ local name = val[2] ++ local xobject = pdfe.dictionarytotable(resources.XObject[2]) ++ print(info.space .. "Uses XObject " .. name) ++ local kind, ++ entry = unpack(xobject[name]) ++ local dict ++ if kind == 10 then -- reference ++ kind, ++ entry, ++ dict = pdfe.getfromreference(entry) ++ end ++ if kind == 9 then -- stream ++ dict = pdfe.dictionarytotable(dict) ++ local resources = dict.Resources ++ if resources then ++ local newinfo = { ++ space = info.space .. " " , ++ resources = Resources[2], ++ } ++ pdfscanner.scan(entry, operatortable, newinfo) ++ end ++ end ++ end ++end ++ ++local function Analyze(filename) ++ local doc = pdfe.open(filename) ++ if doc then ++ local pagenum = 1 ++ local n = 1 -- pdfe.getnofpages(doc) ++ for i=1,n do ++ local page = pdfe.getpage(doc,i) ++ local kind, ++ data = pdfe.getfromdictionarybyname(page,"Resources") ++ if kind == 10 then -- reference ++ kind, data = pdfe.getfromreference(data) ++ end ++ data = pdfe.dictionarytotable(data) ++ local info = { ++ space = " " , ++ resources = data, ++ } ++ print("Page " .. i) ++ kind, data = pdfe.getfromdictionarybyname(page,"Contents") ++ if kind == 10 then -- reference ++ kind, data = pdfe.getfromreference(data) ++ end ++ pdfscanner.scan(data,operatortable,info) ++ end ++ end ++end ++ ++Analyze("e:/tmp/oeps.pdf") ++ ++*/ ++ + # include + # include + # include +@@ -28,26 +92,9 @@ extern "C" { + # include + # include + # include +-} + +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include +-# include ++# include "luapplib/pplib.h" ++} + + # include + +@@ -57,48 +104,47 @@ extern "C" { + #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_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; ++ pdf_token_type type; ++ double value; ++ char *string; + } Token; + + typedef struct ObjectList { +- struct ObjectList *next; +- Object *stream; ++ struct ObjectList *next; ++ ppstream *stream; + } ObjectList; + + typedef struct scannerdata { +- int _ininlineimage; +- int _nextoperand; +- Token ** _operandstack; +- Object * _stream; +- ObjectList * _streams; ++ int _ininlineimage; ++ int _nextoperand; ++ Token ** _operandstack; ++ ppstream * _stream; ++ ObjectList * _streams; ++ const char *buffer; ++ size_t position; ++ size_t size; + } scannerdata; + +-typedef enum { ALLOC_POPPLER, ALLOC_LEPDF } alloctype; +- +-#define M_Object "Object" +-#define M_Stream "Stream" ++#define PDFE_METATABLE_ARRAY "luatex.pdfe.array" ++#define PDFE_METATABLE_STREAM "luatex.pdfe.stream" + + typedef struct { + void *d; +- alloctype atype; // was it allocated by poppler or the lepdflib.cc? + void *pd; // reference to PdfDocument, or NULL +- unsigned long pc; // counter to detect PDFDoc change ++ unsigned long pc; // counter to detect PDFDoc change + } udstruct; + + static void clear_operand_stack (scannerdata *self, int from); +@@ -119,8 +165,8 @@ static void *priv_xrealloc (void *old_ptr, size_t size) + { + void *new_mem = (void *)realloc(old_ptr, size); + if (new_mem == NULL) { +- fprintf(stderr,"fatal: memory exhausted (realloc of %lu bytes).\n", (unsigned long)size); +- exit(EXIT_FAILURE); ++ fprintf(stderr,"fatal: memory exhausted (realloc of %lu bytes).\n", (unsigned long)size); ++ exit(EXIT_FAILURE); + } + return new_mem; + } +@@ -129,20 +175,20 @@ static void *priv_xrealloc (void *old_ptr, size_t size) + + #define INITBUFSIZE 64 + +-#define define_buffer(a) \ +- char *a = (char *)priv_xmalloc (INITBUFSIZE); \ +- int a##_size = INITBUFSIZE; \ +- int a##index = 0; \ ++#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) ++#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) +@@ -155,769 +201,814 @@ static scannerdata * scanner_push(lua_State * L) + + 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; ++ 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); ++ 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; ++ int i = self->_nextoperand-1; ++ while (i>=from) { ++ if (self->_operandstack[i]) { ++ free_token(self->_operandstack[i]); ++ self->_operandstack[i] = NULL; ++ } ++ i--; + } +- i--; +- } +- self->_nextoperand = from; ++ 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; ++ 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; ++ Token *token = (Token *)priv_xmalloc(sizeof(Token)); ++ memset (token, 0, sizeof(Token)); ++ token->type = c; ++ return token; ++} ++ ++static void _nextStream (scannerdata *self) ++{ ++ if (self->buffer != NULL) { ++ ppstream_done(self->_stream); ++ } ++ ObjectList *rover = self->_streams; ++ self->_stream = rover->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 void _nextStream (scannerdata *self) { +- self->_stream->streamClose(); +- ObjectList *rover = self->_streams; +- self->_stream = rover->stream; +- self->_stream->streamReset(); +- self->_streams = rover->next; +- free(rover); ++static int streamLookChar (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 streamGetChar (scannerdata *self) { +- int i = self->_stream->streamGetChar(); +- if (i<0 && self->_streams) { +- _nextStream(self); +- i = streamGetChar(self); +- } +- return i; ++static void streamReset (scannerdata *self) ++{ ++ self->buffer = (const char*) ppstream_all(self->_stream,&self->size,1); ++ self->position = 0; + } + +-static int streamLookChar (scannerdata *self) { +- int i= self->_stream->streamLookChar(); +- if (i<0 && self->_streams) { +- _nextStream(self); +- i = streamLookChar(self); +- } +- return i; ++static void streamClose (scannerdata *self) { ++ ppstream_done(self->_stream); ++ self->buffer = NULL; ++ self->_stream = NULL; + } + ++/* end of stream interface */ ++ + static Token * _parseSpace (scannerdata *self) + { +- return _parseToken (self,streamGetChar(self)); ++ return _parseToken (self,streamGetChar(self)); + } + + static Token * _parseString (scannerdata *self, int c) + { +- // local token = {type = pdf_string,value = ''} +- define_buffer(found); +- int level = 1; +- while (1) { +- c = streamGetChar(self); +- if (c == '(') { +- level = level + 1 ; +- } +- if (c == ')') { +- level = level - 1 ; +- if (level < 1) break; +- } +- 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') { +- next = next - '0'; +- int next2 = streamLookChar(self); +- if (next2 >= '0' && next2 <= '7') { +- next2 = streamGetChar(self); +- next2 = next2 - '0'; +- int 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 *token = new_operand(pdf_string); +- token->value = foundindex; +- token->string = found; +- return token; ++ define_buffer(found); ++ int 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') { ++ next = next - '0'; ++ int next2 = streamLookChar(self); ++ if (next2 >= '0' && next2 <= '7') { ++ next2 = streamGetChar(self); ++ next2 = next2 - '0'; ++ int 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 *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; +- 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 *token = new_operand(type); +- token->value = value; +- return token; ++ double value = 0; ++ pdf_token_type type = pdf_integer; ++ int isfraction = 0; ++ int isnegative = 0; ++ int i = 0; ++ 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 *token = new_operand(type); ++ token->value = value; ++ return token; + } + +- + static Token *_parseName (scannerdata *self, int c) + { +- 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 ; ++ define_buffer(found); + c = streamGetChar(self); +- } +- Token *token = new_operand(pdf_name); +- token->string = found; +- token->value = strlen(found); +- return token; ++ 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 *token = new_operand(pdf_name); ++ token->string = found; ++ token->value = strlen(found); ++ return token; + } + +-#define hexdigit(c) \ ++#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; +- 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); ++ int isodd = 1; ++ int hexval = 0; ++ 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); + } +- c = streamGetChar(self); +- } +- Token *token = new_operand(pdf_string); +- token->value = foundindex; +- token->string = found; +- return token; ++ Token *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') + + // -- this is rather horrible ++ + static Token *_parseInlineImage (scannerdata *self, int c) + { +- define_buffer(found); +- if (c == ' ') { // 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)) { +- found[--foundindex] = '\0'; /* I */ +- found[--foundindex] = '\0'; /* E */ +- /* 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; ++ define_buffer(found); ++ if (c == ' ') { // first space can be ignored ++ c = streamGetChar(self); + } +- } +- Token *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); +- } +- // 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); ++ 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)) { ++ found[--foundindex] = '\0'; /* I */ ++ found[--foundindex] = '\0'; /* E */ ++ /* 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 *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); ++ } ++ // 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)); ++ 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); +- } ++ 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; +- } ++ 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; ++ fprintf(stderr, "stray %c [%d] in stream", c, c); ++ return NULL; + } + + static Token *_parseStartarray () + { +- return new_operand (pdf_startarray); ++ return new_operand (pdf_startarray); + } + + static Token *_parseStoparray () + { +- return new_operand (pdf_stoparray); ++ 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) { +- self->_ininlineimage = 0; +- Token *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); +- } +- } ++ if (self->_ininlineimage==1) { ++ self->_ininlineimage = 2; ++ return _parseInlineImage(self,c); ++ } else if (self->_ininlineimage==2) { ++ self->_ininlineimage = 0; ++ Token *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))); +- // 4 = self +- if (lua_type(L,1)== LUA_TTABLE) { +- udstruct *uin; +- int i = 1; +- while (1) { +- lua_rawgeti(L,1,i); +- if (lua_type(L,-1)== LUA_TUSERDATA) { +- uin = (udstruct *) luaL_checkudata(L, -1, M_Object); +- if (((Object *) uin->d)->isStream()) { +- ObjectList *rover = self->_streams; +- ObjectList *item = (ObjectList *)priv_xmalloc (sizeof(ObjectList)); +- item->stream = ((Object *) uin->d); +- item->next = NULL; +- if (!rover) { +- rover = item; +- self->_streams = rover; +- } else { +- while (rover->next) +- rover = rover->next; +- rover->next = item; +- } +- } +- } else { // done +- 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; +- luaL_checktype(L, 1, LUA_TUSERDATA); +- uin = (udstruct *) luaL_checkudata(L, 1, M_Object); +- if (((Object *) uin->d)->isStream()) { +- self->_stream = ((Object *) uin->d); +- } else if (((Object *) uin->d)->isArray()) { +- Array *arrayref = ((Object *) uin->d)->getArray(); +- int count = arrayref->getLength(); +- int i; +- for (i=0;iget(i); +- if (val->isStream()) { +- ObjectList *rover = self->_streams; +- ObjectList *item = (ObjectList *)priv_xmalloc (sizeof(ObjectList)); +- item->stream = val; +- item->next = NULL; +- if (!rover) { +- rover = item; +- self->_streams = rover; +- } else { +- while (rover->next) +- rover = rover->next; +- rover->next = item; +- } +- } +- } +- ObjectList *rover = self->_streams; +- self->_stream = rover->stream; +- self->_streams = rover->next; +- } +- +- } +- assert (lua_gettop(L) == 4); +- self->_stream->streamReset(); +- token = _parseToken(self,streamGetChar(self)); +- while (token) { +- if (token->type == pdf_operator) { +- lua_pushstring(L, token->string); +- free_token(token); +- lua_rawget(L,2); // operator table +- if (lua_isfunction(L,-1)) { +- lua_pushvalue(L,4); +- lua_pushvalue(L,3); +- (void)lua_call(L,2,0); +- } else { +- lua_pop(L,1); // nil +- } +- clear_operand_stack(self,0); +- } else { +- push_operand(self, token); ++ Token *token; ++ scannerdata *self; ++ if (lua_gettop(L) != 3) { ++ return 0; + } +- if (!self->_stream) { +- break; ++ 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))); ++ // 4 = self ++ 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) { ++ uin = (udstruct *) ud; ++ ObjectList *rover = self->_streams; ++ ObjectList *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) { ++ uin = (udstruct *) ud; ++ pparray * array = (pparray *) uin->d; ++ int count = array->size; ++ int i; ++ for (i=0;itype == 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; ++ } ++ } ++ } ++ ObjectList *rover = self->_streams; ++ self->_stream = rover->stream; ++ self->_streams = rover->next; ++ } ++ } + } ++ streamReset(self); + token = _parseToken(self,streamGetChar(self)); +- } +- /* wrap up */ +- if (self->_stream) { +- self->_stream->streamClose(); +- self->_stream = NULL; +- } +- clear_operand_stack(self,0); +- free(self->_operandstack); +- return 0; ++ while (token) { ++ if (token->type == pdf_operator) { ++ lua_pushstring(L, token->string); ++ free_token(token); ++ lua_rawget(L,2); // operator table ++ if (lua_isfunction(L,-1)) { ++ lua_pushvalue(L,4); ++ lua_pushvalue(L,3); ++ (void)lua_call(L,2,0); ++ } else { ++ lua_pop(L,1); // nil ++ } ++ clear_operand_stack(self,0); ++ } else { ++ push_operand(self, token); ++ } ++ if (!self->_stream) { ++ break; ++ } ++ token = _parseToken(self,streamGetChar(self)); ++ } ++ /* 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; ++ int c; ++ scannerdata *self = scanner_check(L,1); ++ while ((c=streamGetChar(self))>=0) ; ++ return 0; + } + + // 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; ++ 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) + { +- int balance = 1; // nesting tracking +- int index = 1; // lua array index +- 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++); ++ int balance = 1; // nesting tracking ++ int index = 1; // lua array index ++ 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++]; + } +- token = self->_operandstack[self->_nextoperand++]; +- } + } + + + static void push_dict (lua_State *L, scannerdata *self) + { +- int balance = 1; // nesting tracking +- int needskey = 1; // toggle between lua value and lua key +- 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); +- } ++ int balance = 1; // nesting tracking ++ int needskey = 1; // toggle between lua value and lua key ++ 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++]; + } +- token = self->_operandstack[self->_nextoperand++]; +- } + } + +-const char *typenames[pdf_stopdict+1] = +- { "unknown", "integer", "real", "boolean", "name", "operator", +- "string", "array", "array", "dict", "dict" }; ++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) { +- lua_pushnumber(L, token->value); /* integer or float */ +- } 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); ++ 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) { ++ lua_pushnumber(L, token->value); /* integer or float */ ++ } 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) { +- int clear = 0; // how much of the operand stack needs deleting +- scannerdata *self = scanner_check(L,1); +- if (self->_nextoperand==0) { +- return 0; +- } +- clear = self->_nextoperand-1; +- Token *token = self->_operandstack[self->_nextoperand-1]; +- if (token ==NULL || (token->type != token_type )) { +- return 0; +- } +- // 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); ++ int clear = 0; // how much of the operand stack needs deleting ++ scannerdata *self = scanner_check(L,1); ++ if (self->_nextoperand==0) { ++ return 0; ++ } + clear = self->_nextoperand-1; +- push_token(L, self); +- lua_rawgeti(L,-1,2); +- } else if (token_type == pdf_real || token_type == pdf_integer) { +- lua_pushnumber(L, token->value); /* integer or float */ +- } 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; ++ Token *token = self->_operandstack[self->_nextoperand-1]; ++ if (token ==NULL || (token->type != token_type )) { ++ return 0; ++ } ++ // 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) { ++ lua_pushnumber(L, token->value); /* integer or float */ ++ } 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) { +- int clear = 0; // how much of the operand stack needs deleting +- scannerdata *self = scanner_check(L,1); +- if (self->_nextoperand==0) { +- return 0; +- } +- clear = self->_nextoperand-1; +- Token *token = self->_operandstack[self->_nextoperand-1]; +- if (token ==NULL) { +- return 0; +- } +- int token_type = token->type; +- // 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); ++ int clear = 0; // how much of the operand stack needs deleting ++ scannerdata *self = scanner_check(L,1); ++ if (self->_nextoperand==0) { ++ return 0; ++ } + clear = self->_nextoperand-1; +- push_token(L, self); +- } else { +- push_token(L, self); +- } +- clear_operand_stack(self,clear); +- return 1; ++ Token *token = self->_operandstack[self->_nextoperand-1]; ++ if (token ==NULL) { ++ return 0; ++ } ++ int token_type = token->type; ++ // 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); ++ 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)) ++ if(scanner_popsingular(L,pdf_boolean)) ++ return 1; ++ lua_pushnil(L); + return 1; +- lua_pushnil(L); +- return 1; + } + + static int scanner_popstring(lua_State * L) + { +- if (scanner_popsingular(L,pdf_string)) ++ if (scanner_popsingular(L,pdf_string)) ++ return 1; ++ lua_pushnil(L); + return 1; +- lua_pushnil(L); +- return 1; + } + + static int scanner_popname(lua_State * L) + { +- if (scanner_popsingular(L,pdf_name)) ++ if (scanner_popsingular(L,pdf_name)) ++ return 1; ++ lua_pushnil(L); + return 1; +- lua_pushnil(L); +- return 1; + } + + static int scanner_poparray(lua_State * L) + { +- if (scanner_popsingular(L,pdf_stoparray)) ++ if (scanner_popsingular(L,pdf_stoparray)) ++ return 1; ++ lua_pushnil(L); + return 1; +- lua_pushnil(L); +- return 1; + } + + static int scanner_popdictionary(lua_State * L) + { +- if (scanner_popsingular(L,pdf_stopdict)) ++ if (scanner_popsingular(L,pdf_stopdict)) ++ return 1; ++ lua_pushnil(L); + return 1; +- lua_pushnil(L); +- return 1; + } + + static int scanner_popany(lua_State * L) + { +- if (scanner_popanything(L)) ++ if (scanner_popanything(L)) ++ return 1; ++ lua_pushnil(L); + return 1; +- lua_pushnil(L); +- return 1; + } + + static const luaL_Reg scannerlib_meta[] = { +- {0, 0} ++ {0, 0} + }; + + static const struct luaL_Reg scannerlib_m[] = { +@@ -929,10 +1020,9 @@ static const struct luaL_Reg scannerlib_m[] = { + {"popDict", scanner_popdictionary}, + {"popBool", scanner_popboolean}, + {"pop", scanner_popany}, +- {NULL, NULL} /* sentinel */ ++ {NULL, NULL} /* sentinel */ + }; + +- + static const luaL_Reg scannerlib[] = { + {"scan", scanner_scan}, + {NULL, NULL} +diff --git a/texk/web2c/luatexdir/lua/lstatslib.c b/texk/web2c/luatexdir/lua/lstatslib.c +index 43f33008d..5f3b72262 100644 +--- a/texk/web2c/luatexdir/lua/lstatslib.c ++++ b/texk/web2c/luatexdir/lua/lstatslib.c +@@ -147,13 +147,15 @@ static lua_Number get_luatexhashchars(void) + static const char *get_luatexhashtype(void) + { + #ifdef LuajitTeX +- return (const char *)jithash_hashname; ++ if (jithash_hashname) ++ return (const char *)jithash_hashname; ++ else ++ return "???"; + #else + return "lua"; + #endif + } + +- + static lua_Number get_pdf_gone(void) + { + if (static_pdf != NULL) +@@ -266,8 +268,22 @@ static lua_Number get_development_id(void) + return (lua_Number) luatex_svn_revision ; + } + ++static lua_Number get_dvi_gone(void) ++{ ++ if (static_pdf != NULL) ++ return (lua_Number) dvi_get_status_gone(static_pdf); ++ return (lua_Number) 0; ++} ++ ++static lua_Number get_dvi_ptr(void) ++{ ++ if (static_pdf != NULL) ++ return (lua_Number) dvi_get_status_ptr(static_pdf); ++ return (lua_Number) 0; ++} + + /* temp, for backward compat */ ++ + static int init_pool_ptr = 0; + + static struct statistic stats[] = { +@@ -291,8 +307,9 @@ static struct statistic stats[] = { + + {"pdf_gone", 'N', &get_pdf_gone}, + {"pdf_ptr", 'N', &get_pdf_ptr}, +- {"dvi_gone", 'g', &dvi_offset}, +- {"dvi_ptr", 'g', &dvi_ptr}, ++ {"dvi_gone", 'g', &get_dvi_gone}, ++ {"dvi_ptr", 'g', &get_dvi_ptr}, ++ + {"total_pages", 'g', &total_pages}, + {"output_file_name", 'S', (void *) &get_output_file_name}, + {"log_name", 'S', (void *) &getlogname}, +@@ -355,9 +372,14 @@ static struct statistic stats[] = { + {"luabytecodes", 'g', &luabytecode_max}, + {"luabytecode_bytes", 'g', &luabytecode_bytes}, + {"luastate_bytes", 'g', &luastate_bytes}, ++ + {"callbacks", 'g', &callback_count}, ++ {"indirect_callbacks", 'g', &saved_callback_count}, /* these are file io callbacks */ + +- {"indirect_callbacks", 'g', &saved_callback_count}, ++ {"saved_callbacks", 'g', &saved_callback_count}, ++ {"late_callbacks", 'g', &late_callback_count}, ++ {"direct_callbacks", 'g', &direct_callback_count}, ++ {"function_callbacks", 'g', &function_callback_count}, + + {"lc_ctype", 'S', (void *) &get_lc_ctype}, + {"lc_collate", 'S', (void *) &get_lc_collate}, +diff --git a/texk/web2c/luatexdir/lua/ltexiolib.c b/texk/web2c/luatexdir/lua/ltexiolib.c +index f0fa5d369..5f1786c65 100644 +--- a/texk/web2c/luatexdir/lua/ltexiolib.c ++++ b/texk/web2c/luatexdir/lua/ltexiolib.c +@@ -155,10 +155,26 @@ static int texio_setescape(lua_State * L) + return 0 ; + } + ++static int texio_closeinput(lua_State * L) ++{ ++ /* ++ printf("before, first %i, index %i, iname %i, inopen %i, pointer %i\n",istart,iindex,iname,in_open,input_ptr); ++ */ ++ if (iindex > 0) { ++ end_token_list(); ++ end_file_reading(); ++ /* ++ printf("after, first %i, index %i, iname %i, inopen %i, pointer %i\n",istart,iindex,iname,in_open,input_ptr); ++ */ ++ } ++ return 0 ; ++} ++ + static const struct luaL_Reg texiolib[] = { + {"write", texio_print}, + {"write_nl", texio_printnl}, + {"setescape", texio_setescape}, ++ {"closeinput",texio_closeinput}, + {NULL, NULL} + }; + +diff --git a/texk/web2c/luatexdir/lua/ltexlib.c b/texk/web2c/luatexdir/lua/ltexlib.c +index 65e55705e..c3e459b22 100644 +--- a/texk/web2c/luatexdir/lua/ltexlib.c ++++ b/texk/web2c/luatexdir/lua/ltexlib.c +@@ -35,7 +35,8 @@ typedef struct { + void *next; + boolean partial; + int cattable; +- /* halfword tok; */ ++ halfword tok; ++ halfword nod; + } rope; + + typedef struct { +@@ -54,33 +55,64 @@ static int spindle_size = 0; + static spindle *spindles = NULL; + static int spindle_index = 0; + +-static void luac_store(lua_State * L, int i, int partial, int cattable) ++static int luac_store(lua_State * L, int i, int partial, int cattable) + { +- const char *sttemp; +- char *st; +- size_t tsize; ++ char *st = NULL; ++ size_t tsize = 0; + rope *rn = NULL; +- sttemp = lua_tolstring(L, i, &tsize); +- st = xmalloc((unsigned) (tsize + 1)); +- memcpy(st, sttemp, (tsize + 1)); +- if (st) { +- luacstrings++; +- rn = (rope *) xmalloc(sizeof(rope)); +- rn->text = st; +- rn->tsize = (unsigned) tsize; +- rn->partial = partial; +- rn->cattable = cattable; +- rn->next = NULL; +- /* rn->tok = 0; */ +- if (write_spindle.head == NULL) { +- assert(write_spindle.tail == NULL); +- write_spindle.head = rn; ++ halfword tok = null; ++ halfword nod = null; ++ int t = lua_type(L, i); ++ if (t == LUA_TNUMBER || t == LUA_TSTRING) { ++ const char *sttemp; ++ sttemp = lua_tolstring(L, i, &tsize); ++ st = xmalloc((unsigned) (tsize + 1)); ++ memcpy(st, sttemp, (tsize + 1)); ++ } else if (t == LUA_TUSERDATA) { ++ void *p ; ++ p = lua_touserdata(L, i); ++ if (p == NULL) { ++ return 0; ++ } else if (lua_getmetatable(L, i)) { ++ lua_get_metatablelua(luatex_token); ++ if (lua_rawequal(L, -1, -2)) { ++ tok = (halfword) token_info((*((lua_token *)p)).token); ++ lua_pop(L, 2); ++ } else { ++ lua_get_metatablelua(luatex_node); ++ if (lua_rawequal(L, -1, -3)) { ++ nod = *((halfword *)p); ++ lua_pop(L, 3); ++ } else { ++ lua_pop(L, 3); ++ return 0; ++ } ++ } + } else { +- write_spindle.tail->next = rn; ++ return 0; + } +- write_spindle.tail = rn; +- write_spindle.complete = 0; ++ } else { ++ return 0; ++ } ++ /* common */ ++ luacstrings++; ++ rn = (rope *) xmalloc(sizeof(rope)); ++ rn->text = st; ++ rn->tsize = (unsigned) tsize; ++ rn->tok = tok; ++ rn->nod = nod; ++ rn->next = NULL; ++ rn->partial = partial; ++ rn->cattable = cattable; ++ /* add */ ++ if (write_spindle.head == NULL) { ++ write_spindle.head = rn; ++ } else { ++ write_spindle.tail->next = rn; + } ++ write_spindle.tail = rn; ++ write_spindle.complete = 0; ++ return 1; + } + + static int do_luacprint(lua_State * L, int partial, int deftable) +@@ -93,47 +125,33 @@ static int do_luacprint(lua_State * L, int partial, int deftable) + cattable = lua_tointeger(L, 1); + startstrings = 2; + if (cattable != -1 && cattable != -2 && !valid_catcode_table(cattable)) { +- cattable = DEFAULT_CAT_TABLE; +- } ++ cattable = DEFAULT_CAT_TABLE; ++ } + } + } + if (lua_type(L, startstrings) == LUA_TTABLE) { + int i; + for (i = 1;; i++) { + lua_rawgeti(L, startstrings, i); +- if (lua_isstring(L,-1)) { /* or number */ +- luac_store(L, -1, partial, cattable); ++ if (luac_store(L, -1, partial, cattable)) { + lua_pop(L, 1); + } else { ++ lua_pop(L, 1); + break; + } + } + } else { + int i; + for (i = startstrings; i <= n; i++) { +- if (!lua_isstring(L,i)) { /* or number */ +- luaL_error(L, "no string to print"); +- } + luac_store(L, i, partial, cattable); + } +- /* hh: We could use this but it makes not much different, apart from allocating more ropes so less +- memory. To be looked into: lua 5.2 buffer mechanism as now we still hash the concatination. This +- test was part of the why-eis-luajit-so-slow on crited experiments. */ +- /* +- if (startstrings == n) { +- luac_store(L, n, partial, cattable); +- } else { +- lua_concat(L,n-startstrings+1); +- luac_store(L, startstrings, partial, cattable); +- } +- */ + } + return 0; + } + +-/* ++/* the next one writes a raw token (number) */ + +-// some first experiments .. somewhat tricky at the other end ++/* + + int luatwrite(lua_State * L) + { +@@ -148,6 +166,7 @@ int luatwrite(lua_State * L) + rn->cattable = DEFAULT_CAT_TABLE; + rn->next = NULL; + rn->tok = 0; ++ rn->nod = 0; + if (write_spindle.head == NULL) { + write_spindle.head = rn; + } else { +@@ -165,6 +184,7 @@ int luatwrite(lua_State * L) + r->cattable = DEFAULT_CAT_TABLE; + r->next = NULL; + r->tok = 0; ++ r->nod = 0; + rn->next = r; + rn = r; + write_spindle.tail = rn; +@@ -179,21 +199,79 @@ int luatwrite(lua_State * L) + + */ + ++/* the next one writes a raw node (number) */ ++ ++/* ++ ++int luanwrite(lua_State * L) ++{ ++ int top = lua_gettop(L); ++ if (top>0) { ++ rope *rn = xmalloc(sizeof(rope)); // overkill ++ int i = 1 ; ++ luacstrings++; // should be luactokens ++ rn->text = NULL; ++ rn->tsize = 0; ++ rn->partial = 0; ++ rn->cattable = DEFAULT_CAT_TABLE; ++ rn->next = NULL; ++ rn->tok = 0; ++ rn->nod = 0; ++ if (write_spindle.head == NULL) { ++ write_spindle.head = rn; ++ } else { ++ write_spindle.tail->next = rn; ++ } ++ write_spindle.tail = rn; ++ write_spindle.complete = 0; ++ while (1) { ++ rn->nod = lua_tointeger(L,i); ++ if (itext = NULL; ++ r->tsize = 0; ++ r->partial = 0; ++ r->cattable = DEFAULT_CAT_TABLE; ++ r->next = NULL; ++ r->tok = 0; ++ r->nod = 0; ++ rn->next = r; ++ rn = r; ++ write_spindle.tail = rn; ++ i++; ++ } else { ++ break; ++ } ++ } ++ } ++ return 0; ++} ++ ++*/ ++ ++/* lua.write */ ++ + static int luacwrite(lua_State * L) + { + return do_luacprint(L, FULL_LINE, NO_CAT_TABLE); + } + ++/* lua.print */ ++ + static int luacprint(lua_State * L) + { + return do_luacprint(L, FULL_LINE, DEFAULT_CAT_TABLE); + } + ++/* lua.sprint */ ++ + static int luacsprint(lua_State * L) + { + return do_luacprint(L, PARTIAL_LINE, DEFAULT_CAT_TABLE); + } + ++/* lua.cprint */ ++ + static int luaccprint(lua_State * L) + { + /* so a negative value is a specific catcode with offset 1 */ +@@ -207,10 +285,10 @@ static int luaccprint(lua_State * L) + int i; + for (i = 1;; i++) { + lua_rawgeti(L, 2, i); +- if (lua_isstring(L,-1)) { /* or number */ +- luac_store(L, -1, PARTIAL_LINE, cattable); ++ if (luac_store(L, -1, PARTIAL_LINE, cattable)) { + lua_pop(L, 1); + } else { ++ lua_pop(L, 1); + break; + } + } +@@ -218,15 +296,14 @@ static int luaccprint(lua_State * L) + int i; + int n = lua_gettop(L); + for (i = 2; i <= n; i++) { +- if (!lua_isstring(L,i)) { /* or number */ +- luaL_error(L, "no string to print"); +- } + luac_store(L, i, PARTIAL_LINE, cattable); + } + } + return 0; + } + ++/* lua.tprint */ ++ + static int luactprint(lua_State * L) + { + int i, j; +@@ -238,7 +315,7 @@ static int luactprint(lua_State * L) + if (lua_type(L, i) != LUA_TTABLE) { + luaL_error(L, "no string to print"); + } +- lua_pushvalue(L, i); /* push the table */ ++ lua_pushvalue(L, i); /* push the table */ + lua_pushinteger(L, 1); + lua_gettable(L, -2); + if (lua_type(L, -1) == LUA_TNUMBER) { +@@ -252,15 +329,14 @@ static int luactprint(lua_State * L) + for (j = startstrings;; j++) { + lua_pushinteger(L, j); + lua_gettable(L, -2); +- if (lua_isstring(L, -1)) { /* or number */ +- luac_store(L, -1, PARTIAL_LINE, cattable); ++ if (luac_store(L, -1, PARTIAL_LINE, cattable)) { + lua_pop(L, 1); + } else { + lua_pop(L, 1); + break; + } + } +- lua_pop(L, 1); /* pop the table */ ++ lua_pop(L, 1); /* pop the table */ + } + return 0; + } +@@ -280,7 +356,7 @@ int luacstring_final_line(void) + return (read_spindle.tail->next == NULL); + } + +-int luacstring_input(void) ++int luacstring_input(halfword *n) + { + rope *t = read_spindle.head; + int ret = 1 ; +@@ -308,12 +384,15 @@ int luacstring_input(void) + } + free(t->text); + t->text = NULL; +- /* + } else if (t->tok > 0) { +- ret = - t->tok; +- */ +- } +- if (read_spindle.tail != NULL) { /* not a one-liner */ ++ *n = t->tok; ++ ret = 2; ++ } else if (t->nod > 0) { ++ *n = t->nod; ++ ret = 3; ++ } ++ if (read_spindle.tail != NULL) { ++ /* not a one-liner */ + free(read_spindle.tail); + } + read_spindle.tail = t; +@@ -322,11 +401,13 @@ int luacstring_input(void) + } + + /* open for reading, and make a new one for writing */ ++ + void luacstring_start(int n) + { +- (void) n; /* for -W */ ++ (void) n; /* for -W */ + spindle_index++; +- if (spindle_size == spindle_index) { /* add a new one */ ++ if (spindle_size == spindle_index) { ++ /* add a new one */ + spindles = xrealloc(spindles, (unsigned) (sizeof(spindle) * (unsigned) (spindle_size + 1))); + spindles[spindle_index].head = NULL; + spindles[spindle_index].tail = NULL; +@@ -340,7 +421,7 @@ void luacstring_start(int n) + void luacstring_close(int n) + { + rope *next, *t; +- (void) n; /* for -W */ ++ (void) n; /* for -W */ + next = read_spindle.head; + while (next != NULL) { + if (next->text != NULL) +@@ -378,15 +459,15 @@ void luacstring_close(int n) + + static const char *scan_integer_part(lua_State * L, const char *ss, int *ret, int *radix_ret) + { +- boolean negative = false; /* should the answer be negated? */ +- int m = 214748364; /* |$2^{31}$ / radix|, the threshold of danger */ +- int d; /* the digit just scanned */ +- boolean vacuous = true; /* have no digits appeared? */ +- boolean OK_so_far = true; /* has an error message been issued? */ +- int radix1 = 10; /* the radix of the integer */ +- int c = 0; /* the current character */ +- const char *s; /* where we stopped in the string |ss| */ +- integer val = 0; /* return value */ ++ boolean negative = false; /* should the answer be negated? */ ++ int m = 214748364; /* |$2^{31}$ / radix|, the threshold of danger */ ++ int d; /* the digit just scanned */ ++ boolean vacuous = true; /* have no digits appeared? */ ++ boolean OK_so_far = true; /* has an error message been issued? */ ++ int radix1 = 10; /* the radix of the integer */ ++ int c = 0; /* the current character */ ++ const char *s; /* where we stopped in the string |ss| */ ++ integer val = 0; /* return value */ + s = ss; + do { + do { +@@ -448,9 +529,9 @@ static const char *scan_integer_part(lua_State * L, const char *ss, int *ret, in + + #define set_conversion(A,B) do { num=(A); denom=(B); } while(0) + ++/* sets |cur_val| to a dimension */ + + static const char *scan_dimen_part(lua_State * L, const char *ss, int *ret) +-/* sets |cur_val| to a dimension */ + { + boolean negative = false; /* should the answer be negated? */ + int f = 0; /* numerator of a fraction whose denominator is $2^{16}$ */ +@@ -460,7 +541,7 @@ static const char *scan_dimen_part(lua_State * L, const char *ss, int *ret) + int save_cur_val; /* temporary storage of |cur_val| */ + int c; /* the current character */ + const char *s = ss; /* where we are in the string */ +- int radix1 = 0; /* the current radix */ ++ int radix1 = 0; /* the current radix */ + int rdig[18]; /* to save the |dig[]| array */ + int saved_tex_remainder; /* to save |tex_remainder| */ + int saved_arith_error; /* to save |arith_error| */ +@@ -468,9 +549,9 @@ static const char *scan_dimen_part(lua_State * L, const char *ss, int *ret) + saved_tex_remainder = tex_remainder; + saved_arith_error = arith_error; + saved_cur_val = cur_val; +- /* Get the next non-blank non-sign... */ ++ /* get the next non-blank non-sign */ + do { +- /* Get the next non-blank non-call token */ ++ /* get the next non-blank non-call token */ + do { + c = *s++; + } while (c && c == ' '); +@@ -493,16 +574,18 @@ static const char *scan_dimen_part(lua_State * L, const char *ss, int *ret) + if (c == ',') + c = '.'; + if ((radix1 == 10) && (c == '.')) { +- /* Scan decimal fraction */ ++ /* scan decimal fraction */ + for (k = 0; k < 18; k++) + rdig[k] = dig[k]; + k = 0; +- s++; /* get rid of the '.' */ ++ s++; ++ /* get rid of the '.' */ + while (1) { + c = *s++; + if ((c > '0' + 9) || (c < '0')) + break; +- if (k < 17) { /* digits for |k>=17| cannot affect the result */ ++ if (k < 17) { ++ /* digits for |k>=17| cannot affect the result */ + dig[k++] = c - '0'; + } + } +@@ -516,8 +599,10 @@ static const char *scan_dimen_part(lua_State * L, const char *ss, int *ret) + negative = !negative; + cur_val = -cur_val; + } +- /* Scan for (u)units that are internal dimensions; +- |goto attach_sign| with |cur_val| set if found */ ++ /* ++ 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... */ + do { +@@ -570,8 +655,10 @@ static const char *scan_dimen_part(lua_State * L, const char *ss, int *ret) + s += 2; + goto ATTACH_FRACTION; /* the easy case */ + } +- /* Scan for (a)all other units and adjust |cur_val| and |f| accordingly; +- |goto done| in the case of scaled points */ ++ /* ++ Scan for (a)all other units and adjust |cur_val| and |f| accordingly; |goto done| ++ in the case of scaled points ++ */ + if (strncmp(s, "mm", 2) == 0) { + s += 2; + set_conversion(7227, 2540); +@@ -715,7 +802,7 @@ static int get_item_index(lua_State * L, int i, int base) + s = lua_tolstring(L, i, &kk); + cur_cs1 = string_lookup(s, kk); + if (cur_cs1 == undefined_control_sequence || cur_cs1 == undefined_cs_cmd) +- k = -1; /* guarandeed invalid */ ++ k = -1; /* guarandeed invalid */ + else + k = (equiv(cur_cs1) - base); + break; +@@ -724,7 +811,7 @@ static int get_item_index(lua_State * L, int i, int base) + break; + default: + luaL_error(L, "argument must be a string or a number"); +- k = -1; /* not a valid index */ ++ k = -1; /* not a valid index */ + } + return k; + } +@@ -1311,7 +1398,7 @@ static int vsetbox(lua_State * L, int is_global) + } else if (t == LUA_TNIL) { + j = null; + } else { +- j = nodelist_from_lua(L); ++ j = nodelist_from_lua(L,-1); + if (j != null && type(j) != hlist_node && type(j) != vlist_node) { + luaL_error(L, "setbox: incompatible node type (%s)\n", get_node_name(type(j), subtype(j))); + return 0; +@@ -2112,6 +2199,7 @@ static int gettex(lua_State * L) + case assign_int_cmd: + case assign_attr_cmd: + case assign_dir_cmd: ++ case assign_direction_cmd: + case assign_dimen_cmd: + case set_aux_cmd: + case set_prev_graf_cmd: +@@ -2141,8 +2229,9 @@ static int gettex(lua_State * L) + static int getlist(lua_State * L) + { + const char *str; +- if (lua_type(L,2) == LUA_TSTRING) { +- str = lua_tostring(L, 2); ++ int top = lua_gettop(L); ++ if (lua_type(L,top) == LUA_TSTRING) { ++ str = lua_tostring(L, top); + if (lua_key_eq(str,page_ins_head)) { + if (vlink(page_ins_head) == page_ins_head) + lua_pushinteger(L, null); +@@ -2203,17 +2292,18 @@ static int getlist(lua_State * L) + + static int setlist(lua_State * L) + { +- if (lua_type(L,2) == LUA_TSTRING) { +- const char *str = lua_tostring(L, 2); ++ int top = (lua_type(L,1) == LUA_TTABLE) ? 2 : 1 ; ++ if (lua_type(L,top) == LUA_TSTRING) { ++ const char *str = lua_tostring(L, top); + if (lua_key_eq(str,best_size)) { +- best_size = (int) lua_tointeger(L, 3); ++ best_size = (int) lua_tointeger(L, top+1); + } else if (lua_key_eq(str,least_page_cost)) { +- least_page_cost = (int) lua_tointeger(L, 3); ++ least_page_cost = (int) lua_tointeger(L, top+1); + } else { + halfword *n_ptr; + halfword n = 0; +- if (!lua_isnil(L, 3)) { +- n_ptr = check_isnode(L, 3); ++ if (!lua_isnil(L, top+1)) { ++ n_ptr = check_isnode(L, top+1); + n = *n_ptr; + } + if (lua_key_eq(str,page_ins_head)) { +@@ -2359,24 +2449,32 @@ static void init_nest_lib(lua_State * L) + static int getnest(lua_State * L) + { + list_state_record **nestitem; +- int t = lua_type(L, 2); +- if (t == LUA_TNUMBER) { +- int ptr = lua_tointeger(L, 2); +- if (ptr >= 0 && ptr <= nest_ptr) { +- nestitem = lua_newuserdata(L, sizeof(list_state_record *)); +- *nestitem = &nest[ptr]; +- luaL_getmetatable(L, NEST_METATABLE); +- lua_setmetatable(L, -2); +- } else { +- lua_pushnil(L); +- } +- } else if (t == LUA_TSTRING) { +- const char *s = lua_tostring(L, 2); +- if (lua_key_eq(s,ptr)) { +- lua_pushinteger(L, nest_ptr); +- } else { +- lua_pushnil(L); ++ int n = lua_gettop(L); ++ int p = -1 ; ++ if (n == 0) { ++ p = nest_ptr; ++ } else { ++ int t = lua_type(L, n); ++ if (t == LUA_TNUMBER) { ++ int ptr = lua_tointeger(L, n); ++ if (ptr >= 0 && ptr <= nest_ptr) { ++ p = ptr; ++ } ++ } else if (t == LUA_TSTRING) { ++ const char *s = lua_tostring(L, n); ++ if (lua_key_eq(s,top)) { ++ p = nest_ptr; ++ } else if (lua_key_eq(s,ptr)) { ++ lua_pushinteger(L, nest_ptr); ++ return 1; ++ } + } ++ } ++ if (p > -1) { ++ nestitem = lua_newuserdata(L, sizeof(list_state_record *)); ++ *nestitem = &nest[p]; ++ luaL_getmetatable(L, NEST_METATABLE); ++ lua_setmetatable(L, -2); + } else { + lua_pushnil(L); + } +@@ -2386,7 +2484,7 @@ static int getnest(lua_State * L) + static int setnest(lua_State * L) + { + luaL_error(L, "You can't modify the semantic nest array directly"); +- return 2; ++ return 0; + } + + static int do_integer_error(double m) +@@ -2859,7 +2957,7 @@ static int tex_run_linebreak(lua_State * L) + } + lua_key_rawgeti(pardir); + if (lua_type(L, -1) == LUA_TSTRING) { +- paragraph_dir = nodelib_getdir(L, -1, 1); ++ paragraph_dir = nodelib_getdir(L, -1); + } + lua_pop(L, 1); + +@@ -3117,8 +3215,21 @@ static int tex_save_box_resource(lua_State * L) + int type = 0; + int margin = pdf_xform_margin; + boolean immediate = false; ++ /* more or less same as scanner variant */ ++ if (lua_type(L,1) == LUA_TNUMBER) { ++ halfword boxnumber = lua_tointeger(L,1); ++ boxdata = box(boxnumber); ++ box(boxnumber) = null; ++ } else { ++ boxdata = nodelist_from_lua(L,1); ++ if (type(boxdata) != hlist_node && type(boxdata) != vlist_node) { ++ normal_error("pdf backend", "xforms can only be used with a box or [h|v]list"); ++ } ++ } ++ if (boxdata == null) { ++ normal_error("pdf backend", "xforms cannot be used with a void box or empty [h|v]list"); ++ } + /* box attributes resources */ +- halfword boxnumber = lua_tointeger(L,1); + if (lua_type(L,2) == LUA_TSTRING) { + lua_pushvalue(L, 2); + attributes = luaL_ref(L, LUA_REGISTRYINDEX); +@@ -3136,10 +3247,6 @@ static int tex_save_box_resource(lua_State * L) + if (lua_type(L,6) == LUA_TNUMBER) { + margin = lua_tointeger(L, 6); + } +- /* more or less same as scanner variant */ +- boxdata = box(boxnumber); +- if (boxdata == null) +- normal_error("pdf backend", "xforms cannot be used with a void box"); + static_pdf->xform_count++; + index = pdf_create_obj(static_pdf, obj_type_xform, static_pdf->xform_count); + set_obj_data_ptr(static_pdf, index, pdf_get_mem(static_pdf, pdfmem_xform_size)); +@@ -3153,7 +3260,6 @@ static int tex_save_box_resource(lua_State * L) + set_obj_xform_depth(static_pdf, index, depth(boxdata)); + set_obj_xform_type(static_pdf, index, type); + set_obj_xform_margin(static_pdf, index, margin); +- box(boxnumber) = null; + last_saved_box_index = index; + lua_pushinteger(L, index); + if (immediate) { +@@ -3229,12 +3335,35 @@ static int tex_get_box_resource_dimensions(lua_State * L) + return 4; + } + ++static int tex_get_box_resource_box(lua_State * L) ++{ ++ /* no checking yet as this might go */ ++ halfword b; ++ int index = lua_tointeger(L,1); ++ check_obj_type(static_pdf, obj_type_xform, index); ++ b = obj_xform_box(static_pdf, index); ++ nodelist_to_lua(L, b); ++ return 1; ++} ++ + static int tex_build_page(lua_State * L) + { + build_page(); + return 0; + } + ++static int lua_get_page_state(lua_State * L) ++{ ++ lua_pushinteger(L,page_contents); ++ return 1; ++} ++ ++static int lua_get_local_level(lua_State * L) ++{ ++ lua_pushinteger(L,current_local_level()); ++ return 1; ++} ++ + /* synctex */ + + static int lua_set_synctex_mode(lua_State * L) +@@ -3296,6 +3425,82 @@ static int lua_set_synctex_no_files(lua_State * L) + return 0; + } + ++/* ++ This is experimental and might change. In version 10 we hope to have the ++ final version available. It actually took quite a bit of time to understand ++ the implications of mixing lua prints in here. The current variant is (so far) ++ the most robust (wrt crashes and side effects). ++*/ ++ ++#define mode mode_par ++ ++/* ++ When we add save levels then we can get crashes when one flushed bad ++ groups due to out of order flushing. So we play safe! But still we can ++ have issues so best make sure you're in hmode. ++*/ ++ ++static int forcehmode(lua_State * L) ++{ ++ if (abs(mode) == vmode) { ++ if (lua_type(L,1) == LUA_TBOOLEAN) { ++ new_graf(lua_toboolean(L,1)); ++ } else { ++ new_graf(1); ++ } ++ } ++ return 0; ++} ++ ++static int runtoks(lua_State * L) ++{ ++ if (lua_type(L,1) == LUA_TFUNCTION) { ++ int old_mode = mode; ++ int ref; ++ pointer r = get_avail(); ++ pointer t = get_avail(); ++ token_info(r) = token_val(extension_cmd,end_local_code); ++ lua_pushvalue(L, 1); ++ ref = luaL_ref(L,LUA_REGISTRYINDEX); ++ token_info(t) = token_val(lua_local_call_cmd, ref); ++ begin_token_list(r,inserted); ++ begin_token_list(t,inserted); ++ if (luacstrings > 0) { ++ lua_string_start(); ++ } ++ if (tracing_nesting_par > 2) { ++ local_control_message("entering token scanner via function"); ++ } ++ mode = -hmode; ++ local_control(); ++ mode = old_mode; ++ luaL_unref(L,LUA_REGISTRYINDEX,ref); ++ } else { ++ int k = get_item_index(L, lua_gettop(L), toks_base); ++ halfword t = toks(k); ++ check_index_range(k, "gettoks"); ++ if (t != null) { ++ int old_mode = mode; ++ pointer r = get_avail(); ++ token_info(r) = token_val(extension_cmd,end_local_code); ++ begin_token_list(r,inserted); ++ /* new_save_level(semi_simple_group); */ ++ begin_token_list(t,local_text); ++ if (luacstrings > 0) { ++ lua_string_start(); ++ } ++ if (tracing_nesting_par > 2) { ++ local_control_message("entering token scanner via register"); ++ } ++ mode = -hmode; ++ local_control(); ++ mode = old_mode; ++ /* unsave(); */ ++ } ++ } ++ return 0; ++} ++ + /* till here */ + + void init_tex_table(lua_State * L) +@@ -3315,10 +3520,14 @@ static const struct luaL_Reg texlib[] = { + { "finish", tex_run_end }, /* may be needed */ + { "write", luacwrite }, + { "print", luacprint }, ++ { "sprint", luacsprint }, + { "tprint", luactprint }, + { "cprint", luaccprint }, ++ /* ++ { "twrite", luatwrite }, ++ { "nwrite", luanwrite }, ++ */ + { "error", texerror }, +- { "sprint", luacsprint }, + { "set", settex }, + { "get", gettex }, + { "isdimen", isdimen }, +@@ -3327,11 +3536,13 @@ static const struct luaL_Reg texlib[] = { + { "isskip", isskip }, + { "setskip", setskip }, + { "getskip", getskip }, ++ { "isglue", isskip }, + { "setglue", setglue }, + { "getglue", getglue }, + { "ismuskip", ismuskip }, + { "setmuskip", setmuskip }, + { "getmuskip", getmuskip }, ++ { "ismuglue", ismuskip }, + { "setmuglue", setmuglue }, + { "getmuglue", getmuglue }, + { "isattribute", isattribute }, +@@ -3350,7 +3561,7 @@ static const struct luaL_Reg texlib[] = { + { "splitbox", splitbox }, + { "setlist", setlist }, + { "getlist", getlist }, +- { "setnest", setnest }, ++ { "setnest", setnest }, /* only a message */ + { "getnest", getnest }, + { "setcatcode", setcatcode }, + { "getcatcode", getcatcode }, +@@ -3395,8 +3606,12 @@ static const struct luaL_Reg texlib[] = { + { "saveboxresource", tex_save_box_resource }, + { "useboxresource", tex_use_box_resource }, + { "getboxresourcedimensions", tex_get_box_resource_dimensions }, ++ /* might go, used when sanitizing backend */ ++ { "getboxresourcebox", tex_get_box_resource_box }, + /* just for testing: it will probably stay but maybe with options */ + { "triggerbuildpage", tex_build_page }, ++ { "getpagestate", lua_get_page_state }, ++ { "getlocallevel", lua_get_local_level }, + /* not the best place but better than in node */ + { "set_synctex_mode", lua_set_synctex_mode }, + { "get_synctex_mode", lua_get_synctex_mode }, +@@ -3407,6 +3622,9 @@ static const struct luaL_Reg texlib[] = { + { "force_synctex_line", lua_force_synctex_line }, + { "set_synctex_line", lua_set_synctex_line }, + { "get_synctex_line", lua_get_synctex_line }, ++ /* test */ ++ { "runtoks", runtoks }, ++ { "forcehmode", forcehmode }, + /* sentinel */ + { NULL, NULL } + }; +@@ -3451,6 +3669,8 @@ int luaopen_tex(lua_State * L) + spindles[0].tail = NULL; + spindle_size = 1; + /* a somewhat odd place for this assert, maybe */ +- assert(command_names[data_cmd].command_offset == data_cmd); ++ if (command_names[data_cmd].id != data_cmd) { ++ fatal_error("mismatch between tex and lua command name tables"); ++ }; + return 1; + } +diff --git a/texk/web2c/luatexdir/lua/luatex-api.h b/texk/web2c/luatexdir/lua/luatex-api.h +index 36b2f5979..399dccb04 100644 +--- a/texk/web2c/luatexdir/lua/luatex-api.h ++++ b/texk/web2c/luatexdir/lua/luatex-api.h +@@ -35,8 +35,10 @@ extern int draft_mode_value; + /* 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 output_mode get_o_mode(void); + extern void fix_o_mode(void); ++*/ + + /* till here */ + +@@ -66,8 +68,7 @@ typedef struct LoadS { + + extern lua_State *Luas; + +-extern void make_table(lua_State * L, const char *tab, const char *mttab, const char *getfunc, +- const char *setfunc); ++extern void make_table(lua_State * L, const char *tab, const char *mttab, const char *getfunc, const char *setfunc); + + extern int luac_main(int argc, char *argv[]); + +@@ -76,6 +77,8 @@ extern int luaopen_pdf(lua_State * L); + extern int luaopen_texio(lua_State * L); + extern int luaopen_lang(lua_State * L); + ++extern int luapdfprint(lua_State * L); ++ + # define LUA_TEXFILEHANDLE "TEXFILE*" + + extern lua_State *luatex_error(lua_State * L, int fatal); +@@ -85,6 +88,7 @@ extern int luaopen_zip(lua_State * L); + extern int luaopen_lfs(lua_State * L); + extern int luaopen_lpeg(lua_State * L); + extern int luaopen_md5(lua_State * L); ++extern int luaopen_sha2(lua_State * L); + + #ifndef LuajitTeX + extern int luaopen_ffi(lua_State * L); +@@ -101,14 +105,13 @@ extern void luatex_socketlua_open(lua_State * L); + + extern int luaopen_img(lua_State * L); + extern int l_new_image(lua_State * L); +-extern int luaopen_epdf(lua_State * L); ++extern int luaopen_pdfe(lua_State * L); + extern int luaopen_pdfscanner(lua_State * L); + extern int luaopen_mplib(lua_State * L); + extern int luaopen_fio(lua_State * L); + + extern void open_oslibext(lua_State * L); + extern void open_strlibext(lua_State * L); +-extern void open_lfslibext(lua_State * L); + + extern void initfilecallbackids(int max); + extern void setinputfilecallbackid(int n, int i); +@@ -118,6 +121,8 @@ extern int getreadfilecallbackid(int n); + + extern void lua_initialize(int ac, char **av); + ++extern void luacall_vf(int p, int f, int c); ++ + extern int luaopen_kpse(lua_State * L); + + extern int luaopen_callback(lua_State * L); +@@ -138,12 +143,12 @@ extern void tokenlist_to_luastring(lua_State * L, int p); + extern int tokenlist_from_lua(lua_State * L); + + extern void lua_nodelib_push(lua_State * L); +-extern int nodelib_getdir(lua_State * L, int n, int absolute_only); ++extern int nodelib_getdir(lua_State * L, int n); + extern int nodelib_getlist(lua_State * L, int n); + + extern int luaopen_node(lua_State * L); + extern void nodelist_to_lua(lua_State * L, int n); +-extern int nodelist_from_lua(lua_State * L); ++extern int nodelist_from_lua(lua_State * L, int n); + + extern int dimen_to_number(lua_State * L, const char *s); + +@@ -158,8 +163,6 @@ extern const char *lc_ctype; + extern const char *lc_collate; + extern const char *lc_numeric; + +- +- + #ifdef LuajitTeX + extern int luajiton; + extern char *jithash_hashname ; +@@ -177,9 +180,9 @@ extern void unhide_lua_value(lua_State * lua, const char *name, const char *item + extern int hide_lua_value(lua_State * lua, const char *name, const char *item); + + typedef struct command_item_ { +- const char *cmd_name; +- int command_offset; +- const char **commands; ++ int id; ++ const char *name; ++ int lua; + } command_item; + + extern command_item command_names[]; +@@ -193,6 +196,9 @@ extern int luastate_bytes; + + extern int callback_count; + extern int saved_callback_count; ++extern int direct_callback_count; ++extern int late_callback_count; ++extern int function_callback_count; + + extern const char *luatex_banner; + extern const char *engine_name; +@@ -256,7 +262,13 @@ extern char **environ; + } + #endif + ++typedef struct lua_token { ++ int token; ++ int origin; ++} lua_token; ++ + extern int luatwrite(lua_State * L); ++extern int luanwrite(lua_State * L); + + /* + Same as in lnodelib.c, but with prefix G_ for now. +@@ -342,22 +354,58 @@ preassign these at startup time. */ + #define LOCAL_PAR_SIZE 5 + #define MATH_STYLE_NAME_SIZE 8 + #define APPEND_LIST_SIZE 5 +-#define DIR_PAR_SIZE 8 +-#define DIR_TEXT_SIZE 8 ++#define DIR_PAR_SIZE 4 ++#define DIR_TEXT_SIZE 4 + + extern int l_pack_type_index [PACK_TYPE_SIZE]; + extern int l_group_code_index [GROUP_CODE_SIZE]; + extern int l_local_par_index [LOCAL_PAR_SIZE]; + extern int l_math_style_name_index [MATH_STYLE_NAME_SIZE]; + extern int l_dir_par_index [DIR_PAR_SIZE]; +-extern int l_dir_text_index [DIR_TEXT_SIZE]; ++extern int l_dir_text_index_normal [DIR_TEXT_SIZE]; ++extern int l_dir_text_index_cancel [DIR_TEXT_SIZE]; + + #define lua_push_pack_type(L,pack_type) lua_rawgeti(L, LUA_REGISTRYINDEX, l_pack_type_index[pack_type] ); + #define lua_push_group_code(L,group_code) lua_rawgeti(L, LUA_REGISTRYINDEX, l_group_code_index[group_code]); + #define lua_push_local_par_mode(L,par_mode) lua_rawgeti(L, LUA_REGISTRYINDEX, l_local_par_index[par_mode]); + #define lua_push_math_style_name(L,style_name) lua_rawgeti(L, LUA_REGISTRYINDEX, l_math_style_name_index[style_name]); +-#define lua_push_dir_par(L,dir) lua_rawgeti(L, LUA_REGISTRYINDEX, l_dir_par_index[dir+dir_swap]) +-#define lua_push_dir_text(L,dir) lua_rawgeti(L, LUA_REGISTRYINDEX, l_dir_text_index[dir+dir_swap]) ++ ++#define lua_push_direction(L,direction) \ ++ if (direction < 0) { \ ++ lua_pushnil(L); \ ++ } else { \ ++ lua_pushinteger(L,direction); \ ++ } ++ ++#define lua_push_dir_par(L,dir) \ ++ if (dir < 0) { \ ++ lua_pushnil(L); \ ++ } else { \ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, l_dir_par_index[dir]); \ ++ } ++ ++#define lua_push_dir_text_normal(L,dir) \ ++ if (dir < 0) { \ ++ lua_pushnil(L); \ ++ } else { \ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, l_dir_text_index_normal[dir]); \ ++ } ++ ++#define lua_push_dir_text_cancel(L,dir) \ ++ if (dir < 0) { \ ++ lua_pushnil(L); \ ++ } else { \ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, l_dir_text_index_cancel[dir]); \ ++ } ++ ++#define lua_push_dir_text(L,dir,sub) \ ++ if (dir < 0) { \ ++ lua_pushnil(L); \ ++ } else if (sub) { \ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, l_dir_text_index_cancel[dir]); \ ++ } else { \ ++ lua_rawgeti(L, LUA_REGISTRYINDEX, l_dir_text_index_normal[dir]); \ ++ } + + #define lua_push_string_by_index(L,index) lua_rawgeti(L, LUA_REGISTRYINDEX, index) + #define lua_push_string_by_name(L,index) lua_rawgeti(L, LUA_REGISTRYINDEX, lua_key_index(index)) +@@ -414,21 +462,17 @@ l_math_style_name_index[cramped_script_script_style] = lua_key_index(crampedscri + l_dir_par_index[dir_TLT] = lua_key_index(TLT);\ + l_dir_par_index[dir_TRT] = lua_key_index(TRT);\ + l_dir_par_index[dir_LTL] = lua_key_index(LTL);\ +-l_dir_par_index[dir_RTT] = lua_key_index(RTT);\ +-l_dir_par_index[dir_TLT+4] = lua_key_index(TLT);\ +-l_dir_par_index[dir_TRT+4] = lua_key_index(TRT);\ +-l_dir_par_index[dir_LTL+4] = lua_key_index(LTL);\ +-l_dir_par_index[dir_RTT+4] = lua_key_index(RTT);\ ++l_dir_par_index[dir_RTT] = lua_key_index(RTT); + + #define set_l_dir_text_index \ +-l_dir_text_index[dir_TLT] = lua_key_index(mTLT);\ +-l_dir_text_index[dir_TRT] = lua_key_index(mTRT);\ +-l_dir_text_index[dir_LTL] = lua_key_index(mLTL);\ +-l_dir_text_index[dir_RTT] = lua_key_index(mRTT);\ +-l_dir_text_index[dir_TLT+4] = lua_key_index(pTLT);\ +-l_dir_text_index[dir_TRT+4] = lua_key_index(pTRT);\ +-l_dir_text_index[dir_LTL+4] = lua_key_index(pLTL);\ +-l_dir_text_index[dir_RTT+4] = lua_key_index(pRTT);\ ++l_dir_text_index_normal[dir_TLT] = lua_key_index(pTLT);\ ++l_dir_text_index_normal[dir_TRT] = lua_key_index(pTRT);\ ++l_dir_text_index_normal[dir_LTL] = lua_key_index(pLTL);\ ++l_dir_text_index_normal[dir_RTT] = lua_key_index(pRTT);\ ++l_dir_text_index_cancel[dir_TLT] = lua_key_index(mTLT);\ ++l_dir_text_index_cancel[dir_TRT] = lua_key_index(mTRT);\ ++l_dir_text_index_cancel[dir_LTL] = lua_key_index(mLTL);\ ++l_dir_text_index_cancel[dir_RTT] = lua_key_index(mRTT); + + #define img_parms_max 25 + #define img_pageboxes_max 6 +@@ -438,6 +482,7 @@ extern int img_pageboxes [img_pageboxes_max]; + + # define set_l_img_keys_index \ + img_parms[ 0] = lua_key_index(attr); \ ++img_parms[ 0] = lua_key_index(attribute_list); \ + img_parms[ 1] = lua_key_index(bbox); \ + img_parms[ 2] = lua_key_index(colordepth); \ + img_parms[ 3] = lua_key_index(colorspace); \ +@@ -474,17 +519,16 @@ img_pageboxes[5] = lua_key_index(art); \ + #define lua_push_img_key(L,key) lua_rawgeti(L, LUA_REGISTRYINDEX, img_parms[key] ); + #define lua_push_img_pagebox(L,box) lua_rawgeti(L, LUA_REGISTRYINDEX, img_pageboxes[box]); + +-extern int lua_show_valid_list(lua_State *L, const char **list, int max); ++extern int lua_show_valid_list(lua_State *L, const char **list, int offset, int max); + extern int lua_show_valid_keys(lua_State *L, int *list, int max); + + #define set_make_keys \ +-make_lua_key(cmdname);make_lua_key(expandable);make_lua_key(protected);\ +-make_lua_key(LTL);\ +-make_lua_key(MathConstants);\ +-make_lua_key(RTT);\ +-make_lua_key(TLT);\ +-make_lua_key(TRT);\ ++make_lua_key(__index);\ ++make_lua_key(above);\ ++make_lua_key(abovedisplayshortskip);\ ++make_lua_key(abovedisplayskip);\ + make_lua_key(accent);\ ++make_lua_key(accentkern);\ + make_lua_key(action);\ + make_lua_key(action_id);\ + make_lua_key(action_type);\ +@@ -496,53 +540,107 @@ make_lua_key(adjust_head);\ + make_lua_key(adjusted_hbox);\ + make_lua_key(adjustspacing);\ + make_lua_key(advance);\ ++make_lua_key(after_assignment);\ + make_lua_key(after_display);\ ++make_lua_key(after_group);\ + make_lua_key(after_output);\ ++make_lua_key(afterdisplaypenalty);\ + make_lua_key(align);\ + make_lua_key(align_head);\ ++make_lua_key(align_record);\ + make_lua_key(align_set);\ ++make_lua_key(align_stack);\ + make_lua_key(alignment);\ ++make_lua_key(always);\ + make_lua_key(annot);\ + make_lua_key(area);\ + make_lua_key(art);\ ++make_lua_key(assign_attr);\ ++make_lua_key(assign_box_dir);\ ++make_lua_key(assign_box_direction);\ ++make_lua_key(assign_dimen);\ ++make_lua_key(assign_dir);\ ++make_lua_key(assign_direction);\ ++make_lua_key(assign_font_dimen);\ ++make_lua_key(assign_font_int);\ ++make_lua_key(assign_glue);\ ++make_lua_key(assign_hang_indent);\ ++make_lua_key(assign_int);\ ++make_lua_key(assign_local_box);\ ++make_lua_key(assign_mu_glue);\ ++make_lua_key(assign_toks);\ + make_lua_key(attr);\ ++make_lua_key(attribute);\ ++make_lua_key(attribute_list);\ + make_lua_key(attributes);\ ++make_lua_key(automatic);\ ++make_lua_key(baselineskip);\ + make_lua_key(bbox);\ + make_lua_key(before_display);\ ++make_lua_key(beforedisplaypenalty);\ ++make_lua_key(begin_group);\ ++make_lua_key(beginmath);\ ++make_lua_key(belowdisplayshortskip);\ ++make_lua_key(belowdisplayskip);\ + make_lua_key(best_ins_ptr);\ + make_lua_key(best_page_break);\ + make_lua_key(best_size);\ ++make_lua_key(bin);\ + make_lua_key(bleed);\ + make_lua_key(bot);\ + make_lua_key(bot_accent);\ ++make_lua_key(bothflexible);\ + make_lua_key(bottom_left);\ + make_lua_key(bottom_right);\ + make_lua_key(boundary);\ + make_lua_key(box);\ + make_lua_key(box_left);\ + make_lua_key(box_left_width);\ ++make_lua_key(box_ref);\ + make_lua_key(box_right);\ + make_lua_key(box_right_width);\ ++make_lua_key(box_there);\ ++make_lua_key(break_penalty);\ + make_lua_key(broken_ins);\ + make_lua_key(broken_ptr);\ + make_lua_key(brokenpenalty);\ + make_lua_key(cache);\ + make_lua_key(cal_expand_ratio);\ ++make_lua_key(call);\ ++make_lua_key(cancel);\ ++make_lua_key(car_ret);\ ++make_lua_key(case_shift);\ ++make_lua_key(Catalog);\ + make_lua_key(catalog);\ ++make_lua_key(cell);\ + make_lua_key(char);\ ++make_lua_key(char_ghost);\ ++make_lua_key(char_given);\ ++make_lua_key(char_num);\ ++make_lua_key(character);\ + make_lua_key(characters);\ + make_lua_key(checksum);\ ++make_lua_key(choice);\ + make_lua_key(cidinfo);\ + make_lua_key(class);\ ++make_lua_key(cleaders);\ ++make_lua_key(close);\ + make_lua_key(clubpenalty);\ ++make_lua_key(cmd);\ ++make_lua_key(cmdname);\ ++make_lua_key(color_stack);\ + make_lua_key(colordepth);\ + make_lua_key(colorspace);\ ++make_lua_key(combinetoks);\ + make_lua_key(command);\ + make_lua_key(commands);\ + make_lua_key(comment);\ + make_lua_key(components);\ + make_lua_key(compresslevel);\ ++make_lua_key(conditionalmathskip);\ + make_lua_key(contrib_head);\ ++make_lua_key(convert);\ ++make_lua_key(copy_font);\ + make_lua_key(core);\ + make_lua_key(cost);\ + make_lua_key(count);\ +@@ -551,11 +649,21 @@ make_lua_key(crampedscript);\ + make_lua_key(crampedscriptscript);\ + make_lua_key(crampedtext);\ + make_lua_key(crop);\ ++make_lua_key(cs_name);\ + make_lua_key(csname);\ ++make_lua_key(current);\ + make_lua_key(data);\ ++make_lua_key(def);\ ++make_lua_key(def_char_code);\ ++make_lua_key(def_del_code);\ ++make_lua_key(def_family);\ ++make_lua_key(def_font);\ ++make_lua_key(def_lua_call);\ + make_lua_key(degree);\ + make_lua_key(delim);\ ++make_lua_key(delim_num);\ + make_lua_key(delimptr);\ ++make_lua_key(delta);\ + make_lua_key(demerits);\ + make_lua_key(denom);\ + make_lua_key(depth);\ +@@ -568,46 +676,92 @@ make_lua_key(direct);\ + make_lua_key(direction);\ + make_lua_key(dirs);\ + make_lua_key(disc);\ ++make_lua_key(discretionary);\ + make_lua_key(display);\ ++make_lua_key(divide);\ ++make_lua_key(dont_expand);\ + make_lua_key(doublehyphendemerits);\ + make_lua_key(down);\ + make_lua_key(embedding);\ + make_lua_key(emergencystretch);\ ++make_lua_key(empty);\ + make_lua_key(empty_string);\ + make_lua_key(encodingbytes);\ + make_lua_key(encodingname);\ + make_lua_key(end);\ +-make_lua_key(etex);\ ++make_lua_key(end_cs_name);\ ++make_lua_key(end_group);\ ++make_lua_key(end_template);\ ++make_lua_key(endmath);\ ++make_lua_key(endv);\ ++make_lua_key(eq_no);\ + make_lua_key(equation);\ + make_lua_key(equation_number);\ ++make_lua_key(equationnumber);\ ++make_lua_key(equationnumberpenalty);\ ++make_lua_key(etex);\ ++make_lua_key(ex_space);\ + make_lua_key(exactly);\ ++make_lua_key(expand_after);\ ++make_lua_key(expand_font);\ ++make_lua_key(expandable);\ + make_lua_key(expansion_factor);\ ++make_lua_key(explicit);\ ++make_lua_key(expr_stack);\ + make_lua_key(ext);\ ++make_lua_key(extdef_del_code);\ ++make_lua_key(extdef_math_code);\ + make_lua_key(extend);\ + make_lua_key(extender);\ + make_lua_key(extensible);\ ++make_lua_key(extension);\ + make_lua_key(extra_space);\ + make_lua_key(fam);\ + make_lua_key(fast);\ ++make_lua_key(feedback);\ + make_lua_key(fence);\ ++make_lua_key(fi);\ ++make_lua_key(fi_or_else);\ ++make_lua_key(fil);\ + make_lua_key(file);\ + make_lua_key(filename);\ + make_lua_key(filepath);\ + make_lua_key(fill);\ ++make_lua_key(filll);\ ++make_lua_key(fillll);\ + make_lua_key(fin_row);\ + make_lua_key(finalhyphendemerits);\ ++make_lua_key(finalpenalty);\ ++make_lua_key(first);\ ++make_lua_key(fit);\ ++make_lua_key(fitb);\ ++make_lua_key(fitbh);\ ++make_lua_key(fitbv);\ ++make_lua_key(fith);\ ++make_lua_key(fitr);\ ++make_lua_key(fitv);\ ++make_lua_key(fixedboth);\ ++make_lua_key(fixedbottom);\ ++make_lua_key(fixedtop);\ + make_lua_key(font);\ ++make_lua_key(fontkern);\ + make_lua_key(fonts);\ + make_lua_key(format);\ + make_lua_key(fraction);\ + make_lua_key(fullname);\ ++make_lua_key(ghost);\ ++make_lua_key(gleaders);\ + make_lua_key(global);\ + make_lua_key(glue);\ + make_lua_key(glue_order);\ ++make_lua_key(glue_ref);\ + make_lua_key(glue_set);\ + make_lua_key(glue_sign);\ ++make_lua_key(glue_spec);\ + make_lua_key(glyph);\ ++make_lua_key(goto);\ + make_lua_key(h);\ ++make_lua_key(halign);\ + make_lua_key(hangafter);\ + make_lua_key(hangindent);\ + make_lua_key(hbox);\ +@@ -615,20 +769,37 @@ make_lua_key(head);\ + make_lua_key(height);\ + make_lua_key(hlist);\ + make_lua_key(hmode_par);\ ++make_lua_key(hmove);\ + make_lua_key(hold_head);\ + make_lua_key(horiz_variants);\ ++make_lua_key(hrule);\ + make_lua_key(hsize);\ ++make_lua_key(hskip);\ ++make_lua_key(hyph_data);\ ++make_lua_key(hyphenated);\ + make_lua_key(hyphenchar);\ + make_lua_key(id);\ + make_lua_key(identity);\ ++make_lua_key(if_stack);\ ++make_lua_key(if_test);\ ++make_lua_key(ignore_spaces);\ + make_lua_key(image);\ + make_lua_key(imagetype);\ + make_lua_key(immediate);\ ++make_lua_key(in_stream);\ ++make_lua_key(indent);\ + make_lua_key(index);\ + make_lua_key(info);\ ++make_lua_key(Info);\ ++make_lua_key(inner);\ ++make_lua_key(input);\ ++make_lua_key(ins);\ + make_lua_key(insert);\ ++make_lua_key(inserts_only);\ + make_lua_key(interlinepenalty);\ ++make_lua_key(ital_corr);\ + make_lua_key(italic);\ ++make_lua_key(italiccorrection);\ + make_lua_key(keepopen);\ + make_lua_key(kern);\ + make_lua_key(kerns);\ +@@ -636,95 +807,191 @@ make_lua_key(lang);\ + make_lua_key(large_char);\ + make_lua_key(large_fam);\ + make_lua_key(last_ins_ptr);\ ++make_lua_key(last_item);\ + make_lua_key(lastlinefit);\ ++make_lua_key(late_lua);\ + make_lua_key(leader);\ ++make_lua_key(leader_ship);\ ++make_lua_key(leaders);\ + make_lua_key(least_page_cost);\ + make_lua_key(left);\ + make_lua_key(left_boundary);\ ++make_lua_key(left_brace);\ + make_lua_key(left_protruding);\ ++make_lua_key(left_right);\ + make_lua_key(leftskip);\ ++make_lua_key(let);\ ++make_lua_key(letter);\ ++make_lua_key(letterspace_font);\ + make_lua_key(level);\ ++make_lua_key(ligature);\ + make_lua_key(ligatures);\ ++make_lua_key(limit_switch);\ ++make_lua_key(line);\ ++make_lua_key(linebreakpenalty);\ + make_lua_key(linepenalty);\ ++make_lua_key(lineskip);\ + make_lua_key(link_attr);\ + make_lua_key(list);\ + make_lua_key(local_box);\ ++make_lua_key(local_par);\ + make_lua_key(log);\ ++make_lua_key(long_call);\ ++make_lua_key(long_outer_call);\ + make_lua_key(looseness);\ ++make_lua_key(LTL);\ + make_lua_key(lua);\ ++make_lua_key(lua_bytecode_call);\ + make_lua_key(lua_bytecodes_indirect);\ ++make_lua_key(lua_call);\ ++make_lua_key(lua_expandable_call);\ ++make_lua_key(lua_local_call);\ ++make_lua_key(lua_function_call);\ + make_lua_key(lua_functions);\ + make_lua_key(luatex);\ + make_lua_key(luatex_node);\ + make_lua_key(luatex_token);\ +-make_lua_key(mLTL);\ +-make_lua_key(mRTT);\ +-make_lua_key(mTLT);\ +-make_lua_key(mTRT);\ ++make_lua_key(luatex_pdfe);\ ++make_lua_key(luatex_pdfe_dictionary);\ ++make_lua_key(luatex_pdfe_array);\ ++make_lua_key(luatex_pdfe_stream);\ ++make_lua_key(luatex_pdfe_reference);\ ++make_lua_key(mac_param);\ ++make_lua_key(make_box);\ ++make_lua_key(margin_kern);\ + make_lua_key(marginkern);\ + make_lua_key(mark);\ + make_lua_key(math);\ ++make_lua_key(math_accent);\ ++make_lua_key(math_char);\ ++make_lua_key(math_char_num);\ + make_lua_key(math_choice);\ ++make_lua_key(math_comp);\ ++make_lua_key(math_given);\ + make_lua_key(math_left);\ + make_lua_key(math_shift);\ ++make_lua_key(math_shift_cs);\ ++make_lua_key(math_style);\ ++make_lua_key(math_sub_box);\ ++make_lua_key(math_sub_mlist);\ ++make_lua_key(math_text_char);\ ++make_lua_key(MathConstants);\ + make_lua_key(mathdir);\ + make_lua_key(mathkern);\ ++make_lua_key(mathskip);\ + make_lua_key(mathstyle);\ + make_lua_key(media);\ ++make_lua_key(medmuskip);\ ++make_lua_key(message);\ + make_lua_key(mid);\ + make_lua_key(middle);\ ++make_lua_key(mkern);\ ++make_lua_key(mLTL);\ + make_lua_key(mode);\ + make_lua_key(modeline);\ ++make_lua_key(movement_stack);\ ++make_lua_key(mRTT);\ ++make_lua_key(mskip);\ ++make_lua_key(mTLT);\ ++make_lua_key(mTRT);\ ++make_lua_key(muglue);\ ++make_lua_key(multiply);\ + make_lua_key(name);\ + make_lua_key(named_id);\ + make_lua_key(names);\ ++make_lua_key(nested_list);\ ++make_lua_key(new);\ + make_lua_key(new_graf);\ + make_lua_key(new_window);\ + make_lua_key(next);\ + make_lua_key(no);\ ++make_lua_key(nolength);\ + make_lua_key(no_align);\ ++make_lua_key(no_expand);\ ++make_lua_key(no_super_sub_script);\ + make_lua_key(noad);\ ++make_lua_key(noadpenalty);\ + make_lua_key(node);\ + make_lua_key(node_properties);\ + make_lua_key(node_properties_indirect);\ ++make_lua_key(nohrule);\ + make_lua_key(nomath);\ ++make_lua_key(non_script);\ + make_lua_key(none);\ ++make_lua_key(nonew);\ + make_lua_key(nop);\ ++make_lua_key(normal);\ ++make_lua_key(novrule);\ + make_lua_key(nucleus);\ + make_lua_key(num);\ + make_lua_key(number);\ + make_lua_key(objcompression);\ + make_lua_key(objnum);\ + make_lua_key(oldmath);\ +-make_lua_key(ordering);\ ++make_lua_key(omit);\ ++make_lua_key(opdisplaylimits);\ ++make_lua_key(open);\ ++make_lua_key(oplimits);\ ++make_lua_key(opnolimits);\ ++make_lua_key(option);\ + make_lua_key(options);\ ++make_lua_key(ord);\ ++make_lua_key(ordering);\ + make_lua_key(orientation);\ + make_lua_key(origin);\ ++make_lua_key(other_char);\ ++make_lua_key(outer_call);\ ++make_lua_key(outline);\ + make_lua_key(output);\ ++make_lua_key(over);\ + make_lua_key(overlay_accent);\ +-make_lua_key(pLTL);\ +-make_lua_key(pRTT);\ +-make_lua_key(pTLT);\ +-make_lua_key(pTRT);\ ++make_lua_key(ownerpassword);\ + make_lua_key(page);\ +-make_lua_key(pages);\ + make_lua_key(page_discards_head);\ + make_lua_key(page_head);\ + make_lua_key(page_ins_head);\ ++make_lua_key(page_insert);\ + make_lua_key(pageattributes);\ + make_lua_key(pagebox);\ + make_lua_key(pageresources);\ ++make_lua_key(pages);\ ++make_lua_key(Pages);\ + make_lua_key(pagesattributes);\ ++make_lua_key(pagestate);\ ++make_lua_key(par_end);\ + make_lua_key(parameters);\ + make_lua_key(pardir);\ ++make_lua_key(parfillskip);\ + make_lua_key(parshape);\ ++make_lua_key(parskip);\ ++make_lua_key(passive);\ + make_lua_key(pdf);\ ++make_lua_key(pdfe);\ ++make_lua_key(pdf_action);\ ++make_lua_key(pdf_annot);\ ++make_lua_key(pdf_colorstack);\ + make_lua_key(pdf_data);\ ++make_lua_key(pdf_dest);\ + make_lua_key(pdf_destination);\ ++make_lua_key(pdf_end_link);\ ++make_lua_key(pdf_end_thread);\ ++make_lua_key(pdf_link_data);\ + make_lua_key(pdf_literal);\ ++make_lua_key(pdf_refobj);\ ++make_lua_key(pdf_restore);\ ++make_lua_key(pdf_save);\ ++make_lua_key(pdf_setmatrix);\ ++make_lua_key(pdf_setobj);\ ++make_lua_key(pdf_start);\ ++make_lua_key(pdf_start_link);\ ++make_lua_key(pdf_start_thread);\ ++make_lua_key(pdf_thread);\ ++make_lua_key(pdf_thread_data);\ ++make_lua_key(pdf_window);\ + make_lua_key(pen_broken);\ + make_lua_key(pen_inter);\ + make_lua_key(penalty);\ ++make_lua_key(pLTL);\ + make_lua_key(pop);\ + make_lua_key(post);\ + make_lua_key(post_linebreak);\ +@@ -734,35 +1001,71 @@ make_lua_key(pre_adjust_head);\ + make_lua_key(pre_align);\ + make_lua_key(pre_box);\ + make_lua_key(preamble);\ ++make_lua_key(prefix);\ + make_lua_key(pretolerance);\ + make_lua_key(prev);\ + make_lua_key(prevdepth);\ + make_lua_key(prevgraf);\ ++make_lua_key(protected);\ + make_lua_key(protrudechars);\ ++make_lua_key(protrusion);\ ++make_lua_key(pRTT);\ ++make_lua_key(pseudo_file);\ ++make_lua_key(pseudo_line);\ + make_lua_key(psname);\ ++make_lua_key(pTLT);\ + make_lua_key(ptr);\ ++make_lua_key(pTRT);\ ++make_lua_key(punct);\ + make_lua_key(push);\ + make_lua_key(quad);\ + make_lua_key(radical);\ + make_lua_key(raw);\ ++make_lua_key(read_to_cs);\ ++make_lua_key(recompress);\ + make_lua_key(ref_count);\ + make_lua_key(reg);\ ++make_lua_key(register);\ + make_lua_key(registry);\ ++make_lua_key(regular);\ ++make_lua_key(rel);\ ++make_lua_key(relax);\ ++make_lua_key(remove_item);\ + make_lua_key(renew);\ + make_lua_key(rep);\ + make_lua_key(replace);\ + make_lua_key(resources);\ + make_lua_key(right);\ + make_lua_key(right_boundary);\ ++make_lua_key(right_brace);\ + make_lua_key(right_protruding);\ + make_lua_key(rightskip);\ + make_lua_key(rotation);\ ++make_lua_key(RTT);\ + make_lua_key(rule);\ ++make_lua_key(save_pos);\ + make_lua_key(scale);\ + make_lua_key(script);\ + make_lua_key(scriptscript);\ ++make_lua_key(second);\ + make_lua_key(semi_simple);\ ++make_lua_key(set);\ ++make_lua_key(set_aux);\ ++make_lua_key(set_box);\ ++make_lua_key(set_box_dimen);\ ++make_lua_key(set_etex_shape);\ ++make_lua_key(set_font);\ ++make_lua_key(set_font_id);\ ++make_lua_key(set_interaction);\ ++make_lua_key(set_math_param);\ ++make_lua_key(set_page_dimen);\ ++make_lua_key(set_page_int);\ ++make_lua_key(set_prev_graf);\ ++make_lua_key(set_tex_shape);\ ++make_lua_key(shape);\ ++make_lua_key(shape_ref);\ + make_lua_key(shift);\ ++make_lua_key(shorthand_def);\ + make_lua_key(shrink);\ + make_lua_key(shrink_order);\ + make_lua_key(simple);\ +@@ -776,13 +1079,22 @@ make_lua_key(space);\ + make_lua_key(space_shrink);\ + make_lua_key(space_stretch);\ + make_lua_key(spacefactor);\ ++make_lua_key(spacer);\ ++make_lua_key(spaceskip);\ ++make_lua_key(span);\ ++make_lua_key(spec);\ + make_lua_key(special);\ + make_lua_key(split_discards_head);\ ++make_lua_key(split_insert);\ + make_lua_key(split_keep);\ + make_lua_key(split_off);\ ++make_lua_key(splittopskip);\ ++make_lua_key(squeeze);\ + make_lua_key(stack);\ + make_lua_key(start);\ ++make_lua_key(start_par);\ + make_lua_key(step);\ ++make_lua_key(stop);\ + make_lua_key(stream);\ + make_lua_key(streamfile);\ + make_lua_key(streamprovider);\ +@@ -791,72 +1103,122 @@ make_lua_key(stretch_order);\ + make_lua_key(string);\ + make_lua_key(style);\ + make_lua_key(sub);\ ++make_lua_key(sub_box);\ ++make_lua_key(sub_mark);\ ++make_lua_key(sub_mlist);\ + make_lua_key(subst_ex_font);\ + make_lua_key(subtype);\ + make_lua_key(sup);\ ++make_lua_key(sup_mark);\ ++make_lua_key(super_sub_script);\ + make_lua_key(supplement);\ + make_lua_key(surround);\ ++make_lua_key(tab_mark);\ ++make_lua_key(tabskip);\ + make_lua_key(tail);\ ++make_lua_key(temp);\ + make_lua_key(temp_head);\ + make_lua_key(term);\ + make_lua_key(term_and_log);\ + make_lua_key(tex);\ + make_lua_key(text);\ ++make_lua_key(the);\ ++make_lua_key(thickmuskip);\ ++make_lua_key(thinmuskip);\ ++make_lua_key(thread);\ + make_lua_key(thread_attr);\ + make_lua_key(thread_id);\ +-make_lua_key(tolerance);\ ++make_lua_key(TLT);\ + make_lua_key(tok);\ + make_lua_key(token);\ ++make_lua_key(toks_register);\ ++make_lua_key(tolerance);\ + make_lua_key(top);\ + make_lua_key(top_accent);\ ++make_lua_key(top_bot_mark);\ + make_lua_key(top_left);\ + make_lua_key(top_right);\ ++make_lua_key(topskip);\ + make_lua_key(tounicode);\ + make_lua_key(tracingparagraphs);\ + make_lua_key(trailer);\ ++make_lua_key(Trailer);\ + make_lua_key(trailerid);\ + make_lua_key(transform);\ + make_lua_key(trim);\ ++make_lua_key(TRT);\ + make_lua_key(type);\ + make_lua_key(uchyph);\ ++make_lua_key(udelimiterover);\ ++make_lua_key(udelimiterunder);\ ++make_lua_key(un_hbox);\ ++make_lua_key(un_vbox);\ ++make_lua_key(undefined_cs);\ ++make_lua_key(under);\ ++make_lua_key(unhyphenated);\ + make_lua_key(units_per_em);\ ++make_lua_key(unknown);\ ++make_lua_key(unset);\ ++make_lua_key(uoverdelimiter);\ ++make_lua_key(uradical);\ ++make_lua_key(uroot);\ + make_lua_key(used);\ ++make_lua_key(user);\ ++make_lua_key(userpassword);\ ++make_lua_key(user_defined);\ + make_lua_key(user_id);\ ++make_lua_key(userkern);\ ++make_lua_key(userpenalty);\ ++make_lua_key(userskip);\ ++make_lua_key(uunderdelimiter);\ + make_lua_key(v);\ ++make_lua_key(vadjust);\ ++make_lua_key(valign);\ + make_lua_key(value);\ ++make_lua_key(variable);\ + make_lua_key(vbox);\ + make_lua_key(vcenter);\ + make_lua_key(version);\ + make_lua_key(vert_italic);\ + make_lua_key(vert_variants);\ +-make_lua_key(vmode_par);\ + make_lua_key(visiblefilename);\ + make_lua_key(vlist);\ ++make_lua_key(vmode_par);\ ++make_lua_key(vmove);\ ++make_lua_key(vrule);\ ++make_lua_key(vskip);\ + make_lua_key(vtop);\ ++make_lua_key(whatsit);\ + make_lua_key(widowpenalty);\ + make_lua_key(width);\ ++make_lua_key(word);\ ++make_lua_key(wordpenalty);\ ++make_lua_key(write); \ ++make_lua_key(writingmode); \ + make_lua_key(x_height);\ + make_lua_key(xadvance);\ +-make_lua_key(xformresources);\ + make_lua_key(xformattributes);\ ++make_lua_key(xformresources);\ ++make_lua_key(xleaders);\ ++make_lua_key(xmath_given);\ + make_lua_key(xoffset);\ ++make_lua_key(xray);\ + make_lua_key(xres);\ + make_lua_key(xsize);\ ++make_lua_key(xspaceskip);\ ++make_lua_key(xyz);\ + make_lua_key(xyz_zoom);\ + make_lua_key(yoffset); \ + make_lua_key(yres); \ +-make_lua_key(ysize); \ +-make_lua_key(writingmode); \ +-make_lua_key(__index) ++make_lua_key(ysize); + + #define set_init_keys \ +-init_lua_key(cmdname);init_lua_key(expandable);init_lua_key(protected);\ +-init_lua_key(LTL);\ +-init_lua_key(MathConstants);\ +-init_lua_key(RTT);\ +-init_lua_key(TLT);\ +-init_lua_key(TRT);\ ++init_lua_key(__index);\ ++init_lua_key(above);\ ++init_lua_key(abovedisplayshortskip);\ ++init_lua_key(abovedisplayskip);\ + init_lua_key(accent);\ ++init_lua_key(accentkern);\ + init_lua_key(action);\ + init_lua_key(action_id);\ + init_lua_key(action_type);\ +@@ -868,53 +1230,107 @@ init_lua_key(adjust_head);\ + init_lua_key(adjusted_hbox);\ + init_lua_key(adjustspacing);\ + init_lua_key(advance);\ ++init_lua_key(after_assignment);\ + init_lua_key(after_display);\ ++init_lua_key(after_group);\ + init_lua_key(after_output);\ ++init_lua_key(afterdisplaypenalty);\ + init_lua_key(align);\ + init_lua_key(align_head);\ ++init_lua_key(align_record);\ + init_lua_key(align_set);\ ++init_lua_key(align_stack);\ + init_lua_key(alignment);\ ++init_lua_key(always);\ + init_lua_key(annot);\ + init_lua_key(area);\ + init_lua_key(art);\ ++init_lua_key(assign_attr);\ ++init_lua_key(assign_box_dir);\ ++init_lua_key(assign_box_direction);\ ++init_lua_key(assign_dimen);\ ++init_lua_key(assign_dir);\ ++init_lua_key(assign_direction);\ ++init_lua_key(assign_font_dimen);\ ++init_lua_key(assign_font_int);\ ++init_lua_key(assign_glue);\ ++init_lua_key(assign_hang_indent);\ ++init_lua_key(assign_int);\ ++init_lua_key(assign_local_box);\ ++init_lua_key(assign_mu_glue);\ ++init_lua_key(assign_toks);\ + init_lua_key(attr);\ ++init_lua_key(attribute);\ ++init_lua_key(attribute_list);\ + init_lua_key(attributes);\ ++init_lua_key(automatic);\ ++init_lua_key(baselineskip);\ + init_lua_key(bbox);\ + init_lua_key(before_display);\ ++init_lua_key(beforedisplaypenalty);\ ++init_lua_key(begin_group);\ ++init_lua_key(beginmath);\ ++init_lua_key(belowdisplayshortskip);\ ++init_lua_key(belowdisplayskip);\ + init_lua_key(best_ins_ptr);\ + init_lua_key(best_page_break);\ + init_lua_key(best_size);\ ++init_lua_key(bin);\ + init_lua_key(bleed);\ + init_lua_key(bot);\ + init_lua_key(bot_accent);\ ++init_lua_key(bothflexible);\ + init_lua_key(bottom_left);\ + init_lua_key(bottom_right);\ + init_lua_key(boundary);\ + init_lua_key(box);\ + init_lua_key(box_left);\ + init_lua_key(box_left_width);\ ++init_lua_key(box_ref);\ + init_lua_key(box_right);\ + init_lua_key(box_right_width);\ ++init_lua_key(box_there);\ ++init_lua_key(break_penalty);\ + init_lua_key(broken_ins);\ + init_lua_key(broken_ptr);\ + init_lua_key(brokenpenalty);\ + init_lua_key(cache);\ + init_lua_key(cal_expand_ratio);\ ++init_lua_key(call);\ ++init_lua_key(cancel);\ ++init_lua_key(car_ret);\ ++init_lua_key(case_shift);\ ++init_lua_key(Catalog);\ + init_lua_key(catalog);\ ++init_lua_key(cell);\ + init_lua_key(char);\ ++init_lua_key(char_ghost);\ ++init_lua_key(char_given);\ ++init_lua_key(char_num);\ ++init_lua_key(character);\ + init_lua_key(characters);\ + init_lua_key(checksum);\ ++init_lua_key(choice);\ + init_lua_key(cidinfo);\ + init_lua_key(class);\ ++init_lua_key(cleaders);\ ++init_lua_key(close);\ + init_lua_key(clubpenalty);\ ++init_lua_key(cmd);\ ++init_lua_key(cmdname);\ ++init_lua_key(color_stack);\ + init_lua_key(colordepth);\ + init_lua_key(colorspace);\ ++init_lua_key(combinetoks);\ + init_lua_key(command);\ + init_lua_key(commands);\ + init_lua_key(comment);\ + init_lua_key(components);\ + init_lua_key(compresslevel);\ ++init_lua_key(conditionalmathskip);\ + init_lua_key(contrib_head);\ ++init_lua_key(convert);\ ++init_lua_key(copy_font);\ + init_lua_key(core);\ + init_lua_key(cost);\ + init_lua_key(count);\ +@@ -923,11 +1339,21 @@ init_lua_key(crampedscript);\ + init_lua_key(crampedscriptscript);\ + init_lua_key(crampedtext);\ + init_lua_key(crop);\ ++init_lua_key(cs_name);\ + init_lua_key(csname);\ ++init_lua_key(current);\ + init_lua_key(data);\ ++init_lua_key(def);\ ++init_lua_key(def_char_code);\ ++init_lua_key(def_del_code);\ ++init_lua_key(def_family);\ ++init_lua_key(def_font);\ ++init_lua_key(def_lua_call);\ + init_lua_key(degree);\ + init_lua_key(delim);\ ++init_lua_key(delim_num);\ + init_lua_key(delimptr);\ ++init_lua_key(delta);\ + init_lua_key(demerits);\ + init_lua_key(denom);\ + init_lua_key(depth);\ +@@ -940,45 +1366,91 @@ init_lua_key(direct);\ + init_lua_key(direction);\ + init_lua_key(dirs);\ + init_lua_key(disc);\ ++init_lua_key(discretionary);\ + init_lua_key(display);\ ++init_lua_key(divide);\ ++init_lua_key(dont_expand);\ + init_lua_key(doublehyphendemerits);\ + init_lua_key(down);\ + init_lua_key(embedding);\ + init_lua_key(emergencystretch);\ ++init_lua_key(empty);\ + init_lua_key(encodingbytes);\ + init_lua_key(encodingname);\ + init_lua_key(end);\ +-init_lua_key(etex);\ ++init_lua_key(end_cs_name);\ ++init_lua_key(end_group);\ ++init_lua_key(end_template);\ ++init_lua_key(endmath);\ ++init_lua_key(endv);\ ++init_lua_key(eq_no);\ + init_lua_key(equation);\ + init_lua_key(equation_number);\ ++init_lua_key(equationnumber);\ ++init_lua_key(equationnumberpenalty);\ ++init_lua_key(etex);\ ++init_lua_key(ex_space);\ + init_lua_key(exactly);\ ++init_lua_key(expand_after);\ ++init_lua_key(expand_font);\ ++init_lua_key(expandable);\ + init_lua_key(expansion_factor);\ ++init_lua_key(explicit);\ ++init_lua_key(expr_stack);\ + init_lua_key(ext);\ ++init_lua_key(extdef_del_code);\ ++init_lua_key(extdef_math_code);\ + init_lua_key(extend);\ + init_lua_key(extender);\ + init_lua_key(extensible);\ ++init_lua_key(extension);\ + init_lua_key(extra_space);\ + init_lua_key(fam);\ + init_lua_key(fast);\ ++init_lua_key(feedback);\ + init_lua_key(fence);\ ++init_lua_key(fi);\ ++init_lua_key(fi_or_else);\ ++init_lua_key(fil);\ + init_lua_key(file);\ + init_lua_key(filename);\ + init_lua_key(filepath);\ + init_lua_key(fill);\ ++init_lua_key(filll);\ ++init_lua_key(fillll);\ + init_lua_key(fin_row);\ + init_lua_key(finalhyphendemerits);\ ++init_lua_key(finalpenalty);\ ++init_lua_key(first);\ ++init_lua_key(fit);\ ++init_lua_key(fitb);\ ++init_lua_key(fitbh);\ ++init_lua_key(fitbv);\ ++init_lua_key(fith);\ ++init_lua_key(fitr);\ ++init_lua_key(fitv);\ ++init_lua_key(fixedboth);\ ++init_lua_key(fixedbottom);\ ++init_lua_key(fixedtop);\ + init_lua_key(font);\ ++init_lua_key(fontkern);\ + init_lua_key(fonts);\ + init_lua_key(format);\ + init_lua_key(fraction);\ + init_lua_key(fullname);\ ++init_lua_key(ghost);\ ++init_lua_key(gleaders);\ + init_lua_key(global);\ + init_lua_key(glue);\ + init_lua_key(glue_order);\ ++init_lua_key(glue_ref);\ + init_lua_key(glue_set);\ + init_lua_key(glue_sign);\ ++init_lua_key(glue_spec);\ + init_lua_key(glyph);\ ++init_lua_key(goto);\ + init_lua_key(h);\ ++init_lua_key(halign);\ + init_lua_key(hangafter);\ + init_lua_key(hangindent);\ + init_lua_key(hbox);\ +@@ -986,20 +1458,37 @@ init_lua_key(head);\ + init_lua_key(height);\ + init_lua_key(hlist);\ + init_lua_key(hmode_par);\ ++init_lua_key(hmove);\ + init_lua_key(hold_head);\ + init_lua_key(horiz_variants);\ ++init_lua_key(hrule);\ + init_lua_key(hsize);\ ++init_lua_key(hskip);\ ++init_lua_key(hyph_data);\ ++init_lua_key(hyphenated);\ + init_lua_key(hyphenchar);\ + init_lua_key(id);\ + init_lua_key(identity);\ ++init_lua_key(if_stack);\ ++init_lua_key(if_test);\ ++init_lua_key(ignore_spaces);\ + init_lua_key(image);\ + init_lua_key(imagetype);\ + init_lua_key(immediate);\ ++init_lua_key(in_stream);\ ++init_lua_key(indent);\ + init_lua_key(index);\ + init_lua_key(info);\ ++init_lua_key(Info);\ ++init_lua_key(inner);\ ++init_lua_key(input);\ ++init_lua_key(ins);\ + init_lua_key(insert);\ ++init_lua_key(inserts_only);\ + init_lua_key(interlinepenalty);\ ++init_lua_key(ital_corr);\ + init_lua_key(italic);\ ++init_lua_key(italiccorrection);\ + init_lua_key(keepopen);\ + init_lua_key(kern);\ + init_lua_key(kerns);\ +@@ -1007,82 +1496,179 @@ init_lua_key(lang);\ + init_lua_key(large_char);\ + init_lua_key(large_fam);\ + init_lua_key(last_ins_ptr);\ ++init_lua_key(last_item);\ + init_lua_key(lastlinefit);\ +-init_lua_key(leftskip);\ ++init_lua_key(late_lua);\ + init_lua_key(leader);\ ++init_lua_key(leader_ship);\ ++init_lua_key(leaders);\ + init_lua_key(least_page_cost);\ + init_lua_key(left);\ + init_lua_key(left_boundary);\ ++init_lua_key(left_brace);\ + init_lua_key(left_protruding);\ ++init_lua_key(left_right);\ + init_lua_key(leftskip);\ ++init_lua_key(let);\ ++init_lua_key(letter);\ ++init_lua_key(letterspace_font);\ + init_lua_key(level);\ ++init_lua_key(ligature);\ + init_lua_key(ligatures);\ +-init_lua_key(leftskip);\ ++init_lua_key(limit_switch);\ ++init_lua_key(line);\ ++init_lua_key(linebreakpenalty);\ + init_lua_key(linepenalty);\ ++init_lua_key(lineskip);\ + init_lua_key(link_attr);\ + init_lua_key(list);\ + init_lua_key(local_box);\ ++init_lua_key(local_par);\ + init_lua_key(log);\ ++init_lua_key(long_call);\ ++init_lua_key(long_outer_call);\ + init_lua_key(looseness);\ ++init_lua_key(LTL);\ + init_lua_key(lua);\ ++init_lua_key(lua_bytecode_call);\ + init_lua_key(lua_bytecodes_indirect);\ ++init_lua_key(lua_call);\ ++init_lua_key(lua_expandable_call);\ ++init_lua_key(lua_local_call);\ ++init_lua_key(lua_function_call);\ + init_lua_key(lua_functions);\ + init_lua_key(luatex);\ + init_lua_key(luatex_node);\ + init_lua_key(luatex_token);\ ++init_lua_key(luatex_pdfe);\ ++init_lua_key(luatex_pdfe_dictionary);\ ++init_lua_key(luatex_pdfe_array);\ ++init_lua_key(luatex_pdfe_stream);\ ++init_lua_key(luatex_pdfe_reference);\ ++init_lua_key(mac_param);\ ++init_lua_key(make_box);\ ++init_lua_key(margin_kern);\ + init_lua_key(marginkern);\ + init_lua_key(mark);\ + init_lua_key(math);\ ++init_lua_key(math_accent);\ ++init_lua_key(math_char);\ ++init_lua_key(math_char_num);\ + init_lua_key(math_choice);\ ++init_lua_key(math_comp);\ ++init_lua_key(math_given);\ + init_lua_key(math_left);\ + init_lua_key(math_shift);\ ++init_lua_key(math_shift_cs);\ ++init_lua_key(math_style);\ ++init_lua_key(math_sub_box);\ ++init_lua_key(math_sub_mlist);\ ++init_lua_key(math_text_char);\ ++init_lua_key(MathConstants);\ + init_lua_key(mathdir);\ + init_lua_key(mathkern);\ ++init_lua_key(mathskip);\ + init_lua_key(mathstyle);\ + init_lua_key(media);\ ++init_lua_key(medmuskip);\ ++init_lua_key(message);\ + init_lua_key(mid);\ + init_lua_key(middle);\ ++init_lua_key(mkern);\ + init_lua_key(mode);\ + init_lua_key(modeline);\ ++init_lua_key(movement_stack);\ ++init_lua_key(mskip);\ ++init_lua_key(muglue);\ ++init_lua_key(multiply);\ + init_lua_key(name);\ + init_lua_key(named_id);\ + init_lua_key(names);\ ++init_lua_key(nested_list);\ ++init_lua_key(new);\ + init_lua_key(new_graf);\ + init_lua_key(new_window);\ + init_lua_key(next);\ + init_lua_key(no);\ ++init_lua_key(nolength);\ + init_lua_key(no_align);\ ++init_lua_key(no_expand);\ ++init_lua_key(no_super_sub_script);\ + init_lua_key(noad);\ ++init_lua_key(noadpenalty);\ + init_lua_key(node);\ ++init_lua_key(nohrule);\ + init_lua_key(nomath);\ ++init_lua_key(non_script);\ + init_lua_key(none);\ ++init_lua_key(nonew);\ + init_lua_key(nop);\ ++init_lua_key(normal);\ ++init_lua_key(novrule);\ + init_lua_key(nucleus);\ + init_lua_key(num);\ + init_lua_key(number);\ + init_lua_key(objcompression);\ + init_lua_key(objnum);\ + init_lua_key(oldmath);\ ++init_lua_key(omit);\ ++init_lua_key(opdisplaylimits);\ ++init_lua_key(open);\ ++init_lua_key(oplimits);\ ++init_lua_key(opnolimits);\ ++init_lua_key(option);\ + init_lua_key(options);\ ++init_lua_key(ord);\ ++init_lua_key(ordering);\ + init_lua_key(orientation);\ + init_lua_key(origin);\ +-init_lua_key(ordering);\ ++init_lua_key(other_char);\ ++init_lua_key(outer_call);\ ++init_lua_key(outline);\ + init_lua_key(output);\ ++init_lua_key(over);\ + init_lua_key(overlay_accent);\ ++init_lua_key(ownerpassword);\ + init_lua_key(page);\ +-init_lua_key(pages);\ + init_lua_key(page_discards_head);\ + init_lua_key(page_head);\ + init_lua_key(page_ins_head);\ ++init_lua_key(page_insert);\ + init_lua_key(pageattributes);\ + init_lua_key(pagebox);\ + init_lua_key(pageresources);\ ++init_lua_key(pages);\ ++init_lua_key(Pages);\ + init_lua_key(pagesattributes);\ ++init_lua_key(pagestate);\ ++init_lua_key(par_end);\ + init_lua_key(parameters);\ + init_lua_key(pardir);\ ++init_lua_key(parfillskip);\ + init_lua_key(parshape);\ ++init_lua_key(parskip);\ ++init_lua_key(passive);\ ++init_lua_key(pdfe);\ ++init_lua_key(pdf_action);\ ++init_lua_key(pdf_annot);\ ++init_lua_key(pdf_colorstack);\ ++init_lua_key(pdf_dest);\ + init_lua_key(pdf_destination);\ ++init_lua_key(pdf_end_link);\ ++init_lua_key(pdf_end_thread);\ ++init_lua_key(pdf_link_data);\ + init_lua_key(pdf_literal);\ ++init_lua_key(pdf_refobj);\ ++init_lua_key(pdf_restore);\ ++init_lua_key(pdf_save);\ ++init_lua_key(pdf_setmatrix);\ ++init_lua_key(pdf_setobj);\ ++init_lua_key(pdf_start);\ ++init_lua_key(pdf_start_link);\ ++init_lua_key(pdf_start_thread);\ ++init_lua_key(pdf_thread);\ ++init_lua_key(pdf_thread_data);\ ++init_lua_key(pdf_window);\ + init_lua_key(pen_broken);\ + init_lua_key(pen_inter);\ + init_lua_key(penalty);\ +@@ -1095,35 +1681,68 @@ init_lua_key(pre_adjust_head);\ + init_lua_key(pre_align);\ + init_lua_key(pre_box);\ + init_lua_key(preamble);\ ++init_lua_key(prefix);\ + init_lua_key(pretolerance);\ + init_lua_key(prev);\ + init_lua_key(prevdepth);\ + init_lua_key(prevgraf);\ ++init_lua_key(protected);\ + init_lua_key(protrudechars);\ ++init_lua_key(protrusion);\ ++init_lua_key(pseudo_file);\ ++init_lua_key(pseudo_line);\ + init_lua_key(psname);\ + init_lua_key(ptr);\ ++init_lua_key(punct);\ + init_lua_key(push);\ + init_lua_key(quad);\ + init_lua_key(radical);\ + init_lua_key(raw);\ ++init_lua_key(read_to_cs);\ + init_lua_key(ref_count);\ ++init_lua_key(recompress);\ + init_lua_key(reg);\ ++init_lua_key(register);\ + init_lua_key(registry);\ ++init_lua_key(regular);\ ++init_lua_key(rel);\ ++init_lua_key(relax);\ ++init_lua_key(remove_item);\ + init_lua_key(renew);\ + init_lua_key(rep);\ + init_lua_key(replace);\ + init_lua_key(resources);\ + init_lua_key(right);\ + init_lua_key(right_boundary);\ ++init_lua_key(right_brace);\ + init_lua_key(right_protruding);\ + init_lua_key(rightskip);\ + init_lua_key(rotation);\ ++init_lua_key(RTT);\ + init_lua_key(rule);\ ++init_lua_key(save_pos);\ + init_lua_key(scale);\ + init_lua_key(script);\ + init_lua_key(scriptscript);\ ++init_lua_key(second);\ + init_lua_key(semi_simple);\ ++init_lua_key(set);\ ++init_lua_key(set_aux);\ ++init_lua_key(set_box);\ ++init_lua_key(set_box_dimen);\ ++init_lua_key(set_etex_shape);\ ++init_lua_key(set_font);\ ++init_lua_key(set_font_id);\ ++init_lua_key(set_interaction);\ ++init_lua_key(set_math_param);\ ++init_lua_key(set_page_dimen);\ ++init_lua_key(set_page_int);\ ++init_lua_key(set_prev_graf);\ ++init_lua_key(set_tex_shape);\ ++init_lua_key(shape);\ ++init_lua_key(shape_ref);\ + init_lua_key(shift);\ ++init_lua_key(shorthand_def);\ + init_lua_key(shrink);\ + init_lua_key(shrink_order);\ + init_lua_key(simple);\ +@@ -1137,13 +1756,22 @@ init_lua_key(space);\ + init_lua_key(space_shrink);\ + init_lua_key(space_stretch);\ + init_lua_key(spacefactor);\ ++init_lua_key(spacer);\ ++init_lua_key(spaceskip);\ ++init_lua_key(span);\ ++init_lua_key(spec);\ + init_lua_key(special);\ + init_lua_key(split_discards_head);\ ++init_lua_key(split_insert);\ + init_lua_key(split_keep);\ + init_lua_key(split_off);\ ++init_lua_key(splittopskip);\ ++init_lua_key(squeeze);\ + init_lua_key(stack);\ + init_lua_key(start);\ ++init_lua_key(start_par);\ + init_lua_key(step);\ ++init_lua_key(stop);\ + init_lua_key(stream);\ + init_lua_key(streamfile);\ + init_lua_key(streamprovider);\ +@@ -1152,67 +1780,123 @@ init_lua_key(stretch_order);\ + init_lua_key(string);\ + init_lua_key(style);\ + init_lua_key(sub);\ ++init_lua_key(sub_box);\ ++init_lua_key(sub_mark);\ ++init_lua_key(sub_mlist);\ + init_lua_key(subst_ex_font);\ + init_lua_key(subtype);\ + init_lua_key(sup);\ ++init_lua_key(sup_mark);\ ++init_lua_key(super_sub_script);\ + init_lua_key(supplement);\ + init_lua_key(surround);\ ++init_lua_key(tab_mark);\ ++init_lua_key(tabskip);\ + init_lua_key(tail);\ ++init_lua_key(temp);\ + init_lua_key(temp_head);\ + init_lua_key(term);\ + init_lua_key(tex);\ + init_lua_key(text);\ ++init_lua_key(the);\ ++init_lua_key(thickmuskip);\ ++init_lua_key(thinmuskip);\ ++init_lua_key(thread);\ + init_lua_key(thread_attr);\ + init_lua_key(thread_id);\ +-init_lua_key(tolerance);\ ++init_lua_key(TLT);\ + init_lua_key(tok);\ + init_lua_key(token);\ ++init_lua_key(toks_register);\ ++init_lua_key(tolerance);\ + init_lua_key(top);\ + init_lua_key(top_accent);\ ++init_lua_key(top_bot_mark);\ + init_lua_key(top_left);\ + init_lua_key(top_right);\ ++init_lua_key(topskip);\ + init_lua_key(tounicode);\ + init_lua_key(tracingparagraphs);\ + init_lua_key(trailer);\ ++init_lua_key(Trailer);\ + init_lua_key(trailerid);\ + init_lua_key(transform);\ + init_lua_key(trim);\ ++init_lua_key(TRT);\ + init_lua_key(type);\ + init_lua_key(uchyph);\ ++init_lua_key(udelimiterover);\ ++init_lua_key(udelimiterunder);\ ++init_lua_key(un_hbox);\ ++init_lua_key(un_vbox);\ ++init_lua_key(undefined_cs);\ ++init_lua_key(under);\ ++init_lua_key(unhyphenated);\ + init_lua_key(units_per_em);\ ++init_lua_key(unknown);\ ++init_lua_key(unset);\ ++init_lua_key(uoverdelimiter);\ ++init_lua_key(uradical);\ ++init_lua_key(uroot);\ + init_lua_key(used);\ ++init_lua_key(user);\ ++init_lua_key(userpassword);\ ++init_lua_key(user_defined);\ + init_lua_key(user_id);\ ++init_lua_key(userkern);\ ++init_lua_key(userpenalty);\ ++init_lua_key(userskip);\ ++init_lua_key(uunderdelimiter);\ + init_lua_key(v);\ ++init_lua_key(vadjust);\ ++init_lua_key(valign);\ + init_lua_key(value);\ ++init_lua_key(variable);\ + init_lua_key(vbox);\ + init_lua_key(vcenter);\ + init_lua_key(version);\ + init_lua_key(vert_italic);\ + init_lua_key(vert_variants);\ +-init_lua_key(vmode_par);\ + init_lua_key(visiblefilename);\ + init_lua_key(vlist);\ ++init_lua_key(vmode_par);\ ++init_lua_key(vmove);\ ++init_lua_key(vrule);\ ++init_lua_key(vskip);\ + init_lua_key(vtop);\ ++init_lua_key(whatsit);\ + init_lua_key(widowpenalty);\ + init_lua_key(width);\ ++init_lua_key(word);\ ++init_lua_key(wordpenalty);\ ++init_lua_key(write);\ ++init_lua_key(writingmode);\ + init_lua_key(x_height);\ + init_lua_key(xadvance);\ +-init_lua_key(xformresources);\ + init_lua_key(xformattributes);\ ++init_lua_key(xformresources);\ ++init_lua_key(xleaders);\ ++init_lua_key(xmath_given);\ + init_lua_key(xoffset);\ ++init_lua_key(xray);\ + init_lua_key(xres);\ + init_lua_key(xsize);\ ++init_lua_key(xspaceskip);\ ++init_lua_key(xyz);\ + init_lua_key(xyz_zoom);\ + init_lua_key(yoffset);\ + init_lua_key(yres);\ + init_lua_key(ysize);\ +-init_lua_key(writingmode);\ +-init_lua_key(__index);\ + init_lua_key_alias(empty_string,"");\ + init_lua_key_alias(lua_bytecodes_indirect,"lua.bytecodes.indirect");\ + init_lua_key_alias(lua_functions,"lua.functions");\ + init_lua_key_alias(luatex_node, "luatex.node");\ + init_lua_key_alias(luatex_token, "luatex.token");\ ++init_lua_key_alias(luatex_pdfe, "luatex.pdfe");\ ++init_lua_key_alias(luatex_pdfe_dictionary, "luatex.pdfe.dictionary");\ ++init_lua_key_alias(luatex_pdfe_array, "luatex.pdfe.array");\ ++init_lua_key_alias(luatex_pdfe_stream, "luatex.pdfe.stream");\ ++init_lua_key_alias(luatex_pdfe_reference, "luatex.pdfe.reference");\ + init_lua_key_alias(mLTL,"-LTL");\ + init_lua_key_alias(mRTT,"-RTT");\ + init_lua_key_alias(mTLT,"-TLT");\ +@@ -1268,14 +1952,12 @@ extern FILE *_cairo_win_tmpfile( void ); + /* These keys have to available to different files */ + /* */ + +-use_lua_key(cmdname);use_lua_key(expandable);use_lua_key(protected); +- +-use_lua_key(LTL); +-use_lua_key(MathConstants); +-use_lua_key(RTT); +-use_lua_key(TLT); +-use_lua_key(TRT); ++use_lua_key(__index); ++use_lua_key(above); ++use_lua_key(abovedisplayshortskip); ++use_lua_key(abovedisplayskip); + use_lua_key(accent); ++use_lua_key(accentkern); + use_lua_key(action); + use_lua_key(action_id); + use_lua_key(action_type); +@@ -1287,53 +1969,107 @@ use_lua_key(adjust_head); + use_lua_key(adjusted_hbox); + use_lua_key(adjustspacing); + use_lua_key(advance); ++use_lua_key(after_assignment); + use_lua_key(after_display); ++use_lua_key(after_group); + use_lua_key(after_output); ++use_lua_key(afterdisplaypenalty); + use_lua_key(align); + use_lua_key(align_head); ++use_lua_key(align_record); + use_lua_key(align_set); ++use_lua_key(align_stack); + use_lua_key(alignment); ++use_lua_key(always); + use_lua_key(annot); + use_lua_key(area); + use_lua_key(art); ++use_lua_key(assign_attr); ++use_lua_key(assign_box_dir); ++use_lua_key(assign_box_direction); ++use_lua_key(assign_dimen); ++use_lua_key(assign_dir); ++use_lua_key(assign_direction); ++use_lua_key(assign_font_dimen); ++use_lua_key(assign_font_int); ++use_lua_key(assign_glue); ++use_lua_key(assign_hang_indent); ++use_lua_key(assign_int); ++use_lua_key(assign_local_box); ++use_lua_key(assign_mu_glue); ++use_lua_key(assign_toks); + use_lua_key(attr); ++use_lua_key(attribute); ++use_lua_key(attribute_list); + use_lua_key(attributes); ++use_lua_key(automatic); ++use_lua_key(baselineskip); + use_lua_key(bbox); + use_lua_key(before_display); ++use_lua_key(beforedisplaypenalty); ++use_lua_key(begin_group); ++use_lua_key(beginmath); ++use_lua_key(belowdisplayshortskip); ++use_lua_key(belowdisplayskip); + use_lua_key(best_ins_ptr); + use_lua_key(best_page_break); + use_lua_key(best_size); ++use_lua_key(bin); + use_lua_key(bleed); + use_lua_key(bot); + use_lua_key(bot_accent); ++use_lua_key(bothflexible); + use_lua_key(bottom_left); + use_lua_key(bottom_right); + use_lua_key(boundary); + use_lua_key(box); + use_lua_key(box_left); + use_lua_key(box_left_width); ++use_lua_key(box_ref); + use_lua_key(box_right); + use_lua_key(box_right_width); ++use_lua_key(box_there); ++use_lua_key(break_penalty); + use_lua_key(broken_ins); + use_lua_key(broken_ptr); + use_lua_key(brokenpenalty); + use_lua_key(cache); + use_lua_key(cal_expand_ratio); ++use_lua_key(call); ++use_lua_key(cancel); ++use_lua_key(car_ret); ++use_lua_key(case_shift); ++use_lua_key(Catalog); + use_lua_key(catalog); ++use_lua_key(cell); + use_lua_key(char); ++use_lua_key(char_ghost); ++use_lua_key(char_given); ++use_lua_key(char_num); ++use_lua_key(character); + use_lua_key(characters); + use_lua_key(checksum); ++use_lua_key(choice); + use_lua_key(cidinfo); + use_lua_key(class); ++use_lua_key(cleaders); ++use_lua_key(close); + use_lua_key(clubpenalty); ++use_lua_key(cmd); ++use_lua_key(cmdname); ++use_lua_key(color_stack); + use_lua_key(colordepth); + use_lua_key(colorspace); ++use_lua_key(combinetoks); + use_lua_key(command); + use_lua_key(commands); + use_lua_key(comment); + use_lua_key(components); + use_lua_key(compresslevel); ++use_lua_key(conditionalmathskip); + use_lua_key(contrib_head); ++use_lua_key(convert); ++use_lua_key(copy_font); + use_lua_key(core); + use_lua_key(cost); + use_lua_key(count); +@@ -1342,11 +2078,21 @@ use_lua_key(crampedscript); + use_lua_key(crampedscriptscript); + use_lua_key(crampedtext); + use_lua_key(crop); ++use_lua_key(cs_name); + use_lua_key(csname); ++use_lua_key(current); + use_lua_key(data); ++use_lua_key(def); ++use_lua_key(def_char_code); ++use_lua_key(def_del_code); ++use_lua_key(def_family); ++use_lua_key(def_font); ++use_lua_key(def_lua_call); + use_lua_key(degree); + use_lua_key(delim); ++use_lua_key(delim_num); + use_lua_key(delimptr); ++use_lua_key(delta); + use_lua_key(demerits); + use_lua_key(denom); + use_lua_key(depth); +@@ -1359,46 +2105,92 @@ use_lua_key(direct); + use_lua_key(direction); + use_lua_key(dirs); + use_lua_key(disc); ++use_lua_key(discretionary); + use_lua_key(display); ++use_lua_key(divide); ++use_lua_key(dont_expand); + use_lua_key(doublehyphendemerits); + use_lua_key(down); + use_lua_key(embedding); + use_lua_key(emergencystretch); ++use_lua_key(empty); + use_lua_key(empty_string); + use_lua_key(encodingbytes); + use_lua_key(encodingname); + use_lua_key(end); +-use_lua_key(etex); ++use_lua_key(end_cs_name); ++use_lua_key(end_group); ++use_lua_key(end_template); ++use_lua_key(endmath); ++use_lua_key(endv); ++use_lua_key(eq_no); + use_lua_key(equation);\ + use_lua_key(equation_number);\ ++use_lua_key(equationnumber); ++use_lua_key(equationnumberpenalty); ++use_lua_key(etex); ++use_lua_key(ex_space); + use_lua_key(exactly); ++use_lua_key(expand_after); ++use_lua_key(expand_font); ++use_lua_key(expandable); + use_lua_key(expansion_factor); ++use_lua_key(explicit); ++use_lua_key(expr_stack); + use_lua_key(ext); ++use_lua_key(extdef_del_code); ++use_lua_key(extdef_math_code); + use_lua_key(extend); + use_lua_key(extender); + use_lua_key(extensible); ++use_lua_key(extension); + use_lua_key(extra_space); + use_lua_key(fam); + use_lua_key(fast); ++use_lua_key(feedback); + use_lua_key(fence); ++use_lua_key(fi); ++use_lua_key(fi_or_else); ++use_lua_key(fil); + use_lua_key(file); + use_lua_key(filename); + use_lua_key(filepath); + use_lua_key(fill); ++use_lua_key(filll); ++use_lua_key(fillll); + use_lua_key(fin_row); + use_lua_key(finalhyphendemerits); ++use_lua_key(finalpenalty); ++use_lua_key(first); ++use_lua_key(fit); ++use_lua_key(fitb); ++use_lua_key(fitbh); ++use_lua_key(fitbv); ++use_lua_key(fith); ++use_lua_key(fitr); ++use_lua_key(fitv); ++use_lua_key(fixedboth); ++use_lua_key(fixedbottom); ++use_lua_key(fixedtop); + use_lua_key(font); ++use_lua_key(fontkern); + use_lua_key(fonts); + use_lua_key(format); + use_lua_key(fraction); + use_lua_key(fullname); ++use_lua_key(ghost); ++use_lua_key(gleaders); + use_lua_key(global); + use_lua_key(glue); + use_lua_key(glue_order); ++use_lua_key(glue_ref); + use_lua_key(glue_set); + use_lua_key(glue_sign); ++use_lua_key(glue_spec); + use_lua_key(glyph); ++use_lua_key(goto); + use_lua_key(h); ++use_lua_key(halign); + use_lua_key(hangafter); + use_lua_key(hangindent); + use_lua_key(hbox); +@@ -1406,20 +2198,37 @@ use_lua_key(head); + use_lua_key(height); + use_lua_key(hlist); + use_lua_key(hmode_par); ++use_lua_key(hmove); + use_lua_key(hold_head); + use_lua_key(horiz_variants); ++use_lua_key(hrule); + use_lua_key(hsize); ++use_lua_key(hskip); ++use_lua_key(hyph_data); ++use_lua_key(hyphenated); + use_lua_key(hyphenchar); + use_lua_key(id); + use_lua_key(identity); ++use_lua_key(if_stack); ++use_lua_key(if_test); ++use_lua_key(ignore_spaces); + use_lua_key(image); + use_lua_key(imagetype); + use_lua_key(immediate); ++use_lua_key(in_stream); ++use_lua_key(indent); + use_lua_key(index); + use_lua_key(info); ++use_lua_key(Info); ++use_lua_key(inner); ++use_lua_key(input); ++use_lua_key(ins); + use_lua_key(insert); ++use_lua_key(inserts_only); + use_lua_key(interlinepenalty); ++use_lua_key(ital_corr); + use_lua_key(italic); ++use_lua_key(italiccorrection); + use_lua_key(keepopen); + use_lua_key(kern); + use_lua_key(kerns); +@@ -1427,95 +2236,191 @@ use_lua_key(lang); + use_lua_key(large_char); + use_lua_key(large_fam); + use_lua_key(last_ins_ptr); ++use_lua_key(last_item); + use_lua_key(lastlinefit); ++use_lua_key(late_lua); + use_lua_key(leader); ++use_lua_key(leader_ship); ++use_lua_key(leaders); + use_lua_key(least_page_cost); + use_lua_key(left); + use_lua_key(left_boundary); ++use_lua_key(left_brace); + use_lua_key(left_protruding); ++use_lua_key(left_right); + use_lua_key(leftskip); ++use_lua_key(let); ++use_lua_key(letter); ++use_lua_key(letterspace_font); + use_lua_key(level); ++use_lua_key(ligature); + use_lua_key(ligatures); ++use_lua_key(limit_switch); ++use_lua_key(line); ++use_lua_key(linebreakpenalty); + use_lua_key(linepenalty); ++use_lua_key(lineskip); + use_lua_key(link_attr); + use_lua_key(list); + use_lua_key(local_box); ++use_lua_key(local_par); + use_lua_key(log); ++use_lua_key(long_call); ++use_lua_key(long_outer_call); + use_lua_key(looseness); ++use_lua_key(LTL); + use_lua_key(lua); ++use_lua_key(lua_bytecode_call); + use_lua_key(lua_bytecodes_indirect); ++use_lua_key(lua_call); ++use_lua_key(lua_expandable_call); ++use_lua_key(lua_local_call); ++use_lua_key(lua_function_call); + use_lua_key(lua_functions); + use_lua_key(luatex); + use_lua_key(luatex_node); + use_lua_key(luatex_token); +-use_lua_key(mLTL); +-use_lua_key(mRTT); +-use_lua_key(mTLT); +-use_lua_key(mTRT); ++use_lua_key(luatex_pdfe); ++use_lua_key(luatex_pdfe_dictionary); ++use_lua_key(luatex_pdfe_array); ++use_lua_key(luatex_pdfe_stream); ++use_lua_key(luatex_pdfe_reference); ++use_lua_key(mac_param); ++use_lua_key(make_box); ++use_lua_key(margin_kern); + use_lua_key(marginkern); + use_lua_key(mark); + use_lua_key(math); ++use_lua_key(math_accent); ++use_lua_key(math_char); ++use_lua_key(math_char_num); + use_lua_key(math_choice); ++use_lua_key(math_comp); ++use_lua_key(math_given); + use_lua_key(math_left); + use_lua_key(math_shift); ++use_lua_key(math_shift_cs); ++use_lua_key(math_style); ++use_lua_key(math_sub_box); ++use_lua_key(math_sub_mlist); ++use_lua_key(math_text_char); ++use_lua_key(MathConstants); + use_lua_key(mathdir); + use_lua_key(mathkern); ++use_lua_key(mathskip); + use_lua_key(mathstyle); + use_lua_key(media); ++use_lua_key(medmuskip); ++use_lua_key(message); + use_lua_key(mid); + use_lua_key(middle); ++use_lua_key(mkern); ++use_lua_key(mLTL); + use_lua_key(mode); + use_lua_key(modeline); ++use_lua_key(movement_stack); ++use_lua_key(mRTT); ++use_lua_key(mskip); ++use_lua_key(mTLT); ++use_lua_key(mTRT); ++use_lua_key(muglue); ++use_lua_key(multiply); + use_lua_key(name); + use_lua_key(named_id); + use_lua_key(names); ++use_lua_key(nested_list); ++use_lua_key(new); + use_lua_key(new_graf); + use_lua_key(new_window); + use_lua_key(next); + use_lua_key(no); ++use_lua_key(nolength); + use_lua_key(no_align); ++use_lua_key(no_expand); ++use_lua_key(no_super_sub_script); + use_lua_key(noad); ++use_lua_key(noadpenalty); + use_lua_key(node); + use_lua_key(node_properties); + use_lua_key(node_properties_indirect); ++use_lua_key(nohrule); + use_lua_key(nomath); ++use_lua_key(non_script); + use_lua_key(none); ++use_lua_key(nonew); + use_lua_key(nop); ++use_lua_key(normal); ++use_lua_key(novrule); + use_lua_key(nucleus); + use_lua_key(num); + use_lua_key(number); + use_lua_key(objcompression); + use_lua_key(objnum); + use_lua_key(oldmath); ++use_lua_key(omit); ++use_lua_key(opdisplaylimits); ++use_lua_key(open); ++use_lua_key(oplimits); ++use_lua_key(opnolimits); ++use_lua_key(option); + use_lua_key(options); ++use_lua_key(ord); ++use_lua_key(ordering); + use_lua_key(orientation); + use_lua_key(origin); +-use_lua_key(ordering); ++use_lua_key(other_char); ++use_lua_key(outer_call); ++use_lua_key(outline); + use_lua_key(output); ++use_lua_key(over); + use_lua_key(overlay_accent); +-use_lua_key(pLTL); +-use_lua_key(pRTT); +-use_lua_key(pTLT); +-use_lua_key(pTRT); ++use_lua_key(ownerpassword); + use_lua_key(page); +-use_lua_key(pages); + use_lua_key(page_discards_head); + use_lua_key(page_head); + use_lua_key(page_ins_head); +-use_lua_key(pagebox); ++use_lua_key(page_insert); + use_lua_key(pageattributes); ++use_lua_key(pagebox); + use_lua_key(pageresources); ++use_lua_key(pages); ++use_lua_key(Pages); + use_lua_key(pagesattributes); ++use_lua_key(pagestate); ++use_lua_key(par_end); + use_lua_key(parameters); + use_lua_key(pardir); ++use_lua_key(parfillskip); + use_lua_key(parshape); ++use_lua_key(parskip); ++use_lua_key(passive); + use_lua_key(pdf); ++use_lua_key(pdfe); ++use_lua_key(pdf_action); ++use_lua_key(pdf_annot); ++use_lua_key(pdf_colorstack); + use_lua_key(pdf_data); ++use_lua_key(pdf_dest); + use_lua_key(pdf_destination); ++use_lua_key(pdf_end_link); ++use_lua_key(pdf_end_thread); ++use_lua_key(pdf_link_data); + use_lua_key(pdf_literal); ++use_lua_key(pdf_refobj); ++use_lua_key(pdf_restore); ++use_lua_key(pdf_save); ++use_lua_key(pdf_setmatrix); ++use_lua_key(pdf_setobj); ++use_lua_key(pdf_start); ++use_lua_key(pdf_start_link); ++use_lua_key(pdf_start_thread); ++use_lua_key(pdf_thread); ++use_lua_key(pdf_thread_data); ++use_lua_key(pdf_window); + use_lua_key(pen_broken); + use_lua_key(pen_inter); + use_lua_key(penalty); ++use_lua_key(pLTL); + use_lua_key(pop); + use_lua_key(post); + use_lua_key(post_linebreak); +@@ -1525,35 +2430,71 @@ use_lua_key(pre_adjust_head); + use_lua_key(pre_align); + use_lua_key(pre_box); + use_lua_key(preamble); ++use_lua_key(prefix); + use_lua_key(pretolerance); + use_lua_key(prev); + use_lua_key(prevdepth); + use_lua_key(prevgraf); ++use_lua_key(protected); + use_lua_key(protrudechars); ++use_lua_key(protrusion); ++use_lua_key(pRTT); ++use_lua_key(pseudo_file); ++use_lua_key(pseudo_line); + use_lua_key(psname); ++use_lua_key(pTLT); + use_lua_key(ptr); ++use_lua_key(pTRT); ++use_lua_key(punct); + use_lua_key(push); + use_lua_key(quad); + use_lua_key(radical); + use_lua_key(raw); ++use_lua_key(read_to_cs); + use_lua_key(ref_count); ++use_lua_key(recompress); + use_lua_key(reg); ++use_lua_key(register); + use_lua_key(registry); ++use_lua_key(regular); ++use_lua_key(rel); ++use_lua_key(relax); ++use_lua_key(remove_item); + use_lua_key(renew); + use_lua_key(rep); + use_lua_key(replace); + use_lua_key(resources); + use_lua_key(right); + use_lua_key(right_boundary); ++use_lua_key(right_brace); + use_lua_key(right_protruding); + use_lua_key(rightskip); + use_lua_key(rotation); ++use_lua_key(RTT); + use_lua_key(rule); ++use_lua_key(save_pos); + use_lua_key(scale); + use_lua_key(script); + use_lua_key(scriptscript); ++use_lua_key(second); + use_lua_key(semi_simple); ++use_lua_key(set); ++use_lua_key(set_aux); ++use_lua_key(set_box); ++use_lua_key(set_box_dimen); ++use_lua_key(set_etex_shape); ++use_lua_key(set_font); ++use_lua_key(set_font_id); ++use_lua_key(set_interaction); ++use_lua_key(set_math_param); ++use_lua_key(set_page_dimen); ++use_lua_key(set_page_int); ++use_lua_key(set_prev_graf); ++use_lua_key(set_tex_shape); ++use_lua_key(shape); ++use_lua_key(shape_ref); + use_lua_key(shift); ++use_lua_key(shorthand_def); + use_lua_key(shrink); + use_lua_key(shrink_order); + use_lua_key(simple); +@@ -1567,13 +2508,22 @@ use_lua_key(space); + use_lua_key(space_shrink); + use_lua_key(space_stretch); + use_lua_key(spacefactor); ++use_lua_key(spacer); ++use_lua_key(spaceskip); ++use_lua_key(span); ++use_lua_key(spec); + use_lua_key(special); + use_lua_key(split_discards_head); ++use_lua_key(split_insert); + use_lua_key(split_keep); + use_lua_key(split_off); ++use_lua_key(splittopskip); ++use_lua_key(squeeze); + use_lua_key(stack); + use_lua_key(start); ++use_lua_key(start_par); + use_lua_key(step); ++use_lua_key(stop); + use_lua_key(stream); + use_lua_key(streamfile); + use_lua_key(streamprovider); +@@ -1582,60 +2532,111 @@ use_lua_key(stretch_order); + use_lua_key(string); + use_lua_key(style); + use_lua_key(sub); ++use_lua_key(sub_box); ++use_lua_key(sub_mark); ++use_lua_key(sub_mlist); + use_lua_key(subst_ex_font); + use_lua_key(subtype); + use_lua_key(sup); ++use_lua_key(sup_mark); ++use_lua_key(super_sub_script); + use_lua_key(supplement); + use_lua_key(surround); ++use_lua_key(tab_mark); ++use_lua_key(tabskip); + use_lua_key(tail); ++use_lua_key(temp); + use_lua_key(temp_head); + use_lua_key(term); + use_lua_key(term_and_log); + use_lua_key(tex); + use_lua_key(text); ++use_lua_key(the); ++use_lua_key(thickmuskip); ++use_lua_key(thinmuskip); ++use_lua_key(thread); + use_lua_key(thread_attr); + use_lua_key(thread_id); +-use_lua_key(tolerance); ++use_lua_key(TLT); + use_lua_key(tok); + use_lua_key(token); ++use_lua_key(toks_register); ++use_lua_key(tolerance); + use_lua_key(top); + use_lua_key(top_accent); ++use_lua_key(top_bot_mark); + use_lua_key(top_left); + use_lua_key(top_right); ++use_lua_key(topskip); + use_lua_key(tounicode); + use_lua_key(tracingparagraphs); + use_lua_key(trailer); ++use_lua_key(Trailer); + use_lua_key(trailerid); + use_lua_key(transform); + use_lua_key(trim); ++use_lua_key(TRT); + use_lua_key(type); + use_lua_key(uchyph); ++use_lua_key(udelimiterover); ++use_lua_key(udelimiterunder); ++use_lua_key(un_hbox); ++use_lua_key(un_vbox); ++use_lua_key(undefined_cs); ++use_lua_key(under); ++use_lua_key(unhyphenated); + use_lua_key(units_per_em); ++use_lua_key(unknown); ++use_lua_key(unset); ++use_lua_key(uoverdelimiter); ++use_lua_key(uradical); ++use_lua_key(uroot); + use_lua_key(used); ++use_lua_key(user); ++use_lua_key(userpassword); ++use_lua_key(user_defined); + use_lua_key(user_id); ++use_lua_key(userkern); ++use_lua_key(userpenalty); ++use_lua_key(userskip); ++use_lua_key(uunderdelimiter); + use_lua_key(v); ++use_lua_key(vadjust); ++use_lua_key(valign); + use_lua_key(value); ++use_lua_key(variable); + use_lua_key(vbox); + use_lua_key(vcenter); + use_lua_key(version); + use_lua_key(vert_italic); + use_lua_key(vert_variants); +-use_lua_key(vmode_par); + use_lua_key(visiblefilename); + use_lua_key(vlist); ++use_lua_key(vmode_par); ++use_lua_key(vmove); ++use_lua_key(vrule); ++use_lua_key(vskip); + use_lua_key(vtop); ++use_lua_key(whatsit); + use_lua_key(widowpenalty); + use_lua_key(width); ++use_lua_key(word); ++use_lua_key(wordpenalty); ++use_lua_key(write); ++use_lua_key(writingmode); + use_lua_key(x_height); + use_lua_key(xadvance); +-use_lua_key(xformresources); + use_lua_key(xformattributes); ++use_lua_key(xformresources); ++use_lua_key(xleaders); ++use_lua_key(xmath_given); + use_lua_key(xoffset); ++use_lua_key(xray); + use_lua_key(xres); + use_lua_key(xsize); ++use_lua_key(xspaceskip); ++use_lua_key(xyz); + use_lua_key(xyz_zoom); + use_lua_key(yoffset); + use_lua_key(yres); + use_lua_key(ysize); +-use_lua_key(writingmode); +-use_lua_key(__index); +diff --git a/texk/web2c/luatexdir/lua/luatex-core.c b/texk/web2c/luatexdir/lua/luatex-core.c +index 6551b0bc4..5cb58af65 100644 +--- a/texk/web2c/luatexdir/lua/luatex-core.c ++++ b/texk/web2c/luatexdir/lua/luatex-core.c +@@ -17,7 +17,7 @@ int load_luatex_core_lua (lua_State * L) + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x5b, 0x27, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x2d, + 0x63, 0x6f, 0x72, 0x65, 0x27, 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x2e, +- 0x30, 0x30, 0x35, 0x2c, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6d, 0x6d, ++ 0x30, 0x38, 0x30, 0x2c, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x27, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, + 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x27, 0x2c, 0x0a, 0x2d, + 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x20, 0x20, 0x20, 0x20, +@@ -27,536 +27,609 @@ int load_luatex_core_lua (lua_State * L) + 0x3d, 0x20, 0x27, 0x4c, 0x75, 0x61, 0x54, 0x65, 0x58, 0x20, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x54, 0x65, 0x61, 0x6d, 0x27, 0x2c, 0x0a, 0x2d, 0x2d, 0x20, + 0x7d, 0x0a, 0x0a, 0x4c, 0x55, 0x41, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x52, 0x45, 0x56, 0x45, 0x52, +- 0x53, 0x49, 0x4f, 0x4e, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x30, 0x35, 0x0a, 0x0a, 0x2d, 0x2d, +- 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x6c, +- 0x6f, 0x61, 0x64, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x4c, 0x75, 0x61, 0x20, 0x66, 0x75, +- 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x61, +- 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x70, +- 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x0a, +- 0x2d, 0x2d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, +- 0x20, 0x61, 0x73, 0x20, 0x4c, 0x75, 0x61, 0x54, 0x65, 0x58, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x2e, +- 0x30, 0x34, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x20, +- 0x74, 0x68, 0x69, 0x73, 0x20, 0x77, 0x61, 0x79, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x73, +- 0x20, 0x75, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x20, 0x74, 0x68, 0x65, 0x0a, +- 0x2d, 0x2d, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x20, 0x6c, +- 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x2e, 0x20, +- 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, +- 0x72, 0x6f, 0x62, 0x61, 0x62, 0x6c, 0x79, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x62, +- 0x69, 0x74, 0x20, 0x62, 0x65, 0x74, 0x74, 0x65, 0x72, 0x20, 0x6e, 0x6f, 0x77, 0x2e, 0x0a, 0x0a, +- 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x6e, 0x65, 0x78, 0x74, +- 0x2c, 0x20, 0x67, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, +- 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, +- 0x6e, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, +- 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x0a, 0x6c, 0x6f, 0x63, 0x61, +- 0x6c, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x2c, 0x20, 0x67, 0x73, 0x75, 0x62, 0x2c, 0x20, 0x66, 0x6f, +- 0x72, 0x6d, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69, +- 0x6e, 0x64, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x2c, +- 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x0a, 0x0a, +- 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x2e, 0x6f, +- 0x70, 0x65, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x5f, 0x70, 0x6f, 0x70, +- 0x65, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, +- 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, +- 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x0a, 0x0a, 0x6c, +- 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, +- 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x2e, 0x72, +- 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, +- 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, +- 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x65, 0x72, +- 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, +- 0x6f, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, +- 0x20, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, +- 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x74, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, +- 0x28, 0x69, 0x6f, 0x2e, 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61, +- 0x6c, 0x20, 0x6d, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, 0x74, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, +- 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x61, 0x66, 0x65, 0x72, 0x6f, 0x70, 0x74, 0x69, +- 0x6f, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, +- 0x74, 0x75, 0x73, 0x2e, 0x73, 0x61, 0x66, 0x65, 0x72, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, +- 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x65, 0x73, 0x63, 0x61, +- 0x70, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, +- 0x74, 0x75, 0x73, 0x2e, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x5f, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, +- 0x20, 0x2d, 0x2d, 0x20, 0x30, 0x20, 0x28, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x29, +- 0x20, 0x31, 0x20, 0x28, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x29, 0x20, 0x32, 0x20, +- 0x28, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x29, 0x0a, 0x6c, 0x6f, 0x63, +- 0x61, 0x6c, 0x20, 0x6b, 0x70, 0x73, 0x65, 0x75, 0x73, 0x65, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, +- 0x6b, 0x70, 0x73, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, +- 0x30, 0x20, 0x31, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, +- 0x5f, 0x6e, 0x6c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, +- 0x20, 0x74, 0x65, 0x78, 0x69, 0x6f, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6e, 0x6c, 0x0a, +- 0x0a, 0x69, 0x6f, 0x2e, 0x73, 0x61, 0x76, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x5f, +- 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x2d, 0x2d, 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, +- 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x0a, 0x6d, 0x74, 0x2e, 0x73, 0x61, 0x76, 0x65, +- 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x2d, 0x2d, +- 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, +- 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, +- 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, +- 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, +- 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x27, 0x72, 0x27, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, +- 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, +- 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x66, +- 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, +- 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x68, 0x6f, 0x77, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x73, +- 0x74, 0x72, 0x69, 0x6e, 0x67, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x28, +- 0x68, 0x6f, 0x77, 0x2c, 0x27, 0x77, 0x27, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, +- 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6e, 0x61, 0x6d, +- 0x65, 0x2c, 0x27, 0x77, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, +- 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, +- 0x6d, 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x72, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x0a, 0x65, 0x6e, +- 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, +- 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, +- 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, +- 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x74, +- 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, +- 0x3d, 0x20, 0x27, 0x72, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x67, 0x73, 0x75, +- 0x62, 0x28, 0x68, 0x6f, 0x77, 0x2c, 0x27, 0x5b, 0x5e, 0x72, 0x62, 0x5d, 0x27, 0x2c, 0x27, 0x27, +- 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x68, 0x6f, 0x77, +- 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x27, 0x72, +- 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, +- 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, +- 0x66, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, +- 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x66, 0x20, 0x74, +- 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6f, 0x5f, +- 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6e, +- 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x72, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x0a, 0x65, 0x6e, +- 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, +- 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x70, 0x6f, 0x70, 0x65, +- 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x6b, 0x61, 0x79, 0x2c, 0x20, 0x66, 0x6f, 0x75, 0x6e, +- 0x64, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x65, 0x72, +- 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x20, 0x20, +- 0x20, 0x20, 0x69, 0x66, 0x20, 0x6f, 0x6b, 0x61, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x6f, +- 0x75, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6f, 0x5f, 0x70, 0x6f, 0x70, 0x65, 0x6e, +- 0x28, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x6c, 0x6f, 0x63, 0x61, +- 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, +- 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, +- 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, +- 0x61, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x69, 0x6f, +- 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x20, 0x6f, +- 0x72, 0x20, 0x27, 0x72, 0x27, 0x29, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x69, 0x66, 0x20, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x2d, 0x2d, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, +- 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x0a, 0x2d, 0x2d, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, +- 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x28, +- 0x29, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x2d, 0x2d, 0x20, +- 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x73, 0x6f, 0x6d, 0x65, +- 0x20, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x63, 0x20, 0x64, +- 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x6b, 0x69, 0x63, 0x6b, 0x20, 0x69, 0x6e, 0x20, 0x73, +- 0x6f, 0x20, 0x77, 0x65, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6c, 0x6f, +- 0x73, 0x65, 0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x74, 0x6c, 0x79, 0x0a, 0x2d, 0x2d, 0x20, 0x73, +- 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, +- 0x65, 0x20, 0x69, 0x73, 0x20, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x6c, +- 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, +- 0x20, 0x3d, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, +- 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, +- 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x28, 0x6e, +- 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, +- 0x74, 0x79, 0x70, 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x73, +- 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x69, 0x6f, +- 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x20, 0x6f, +- 0x72, 0x20, 0x27, 0x72, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, ++ 0x53, 0x49, 0x4f, 0x4e, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x30, 0x20, 0x2d, 0x2d, 0x20, ++ 0x77, 0x65, 0x20, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, ++ 0x75, 0x61, 0x74, 0x65, 0x78, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x68, ++ 0x65, 0x72, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x68, 0x61, 0x70, 0x70, ++ 0x65, 0x6e, 0x65, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, ++ 0x6c, 0x65, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x20, 0x73, 0x6f, 0x6d, ++ 0x65, 0x20, 0x4c, 0x75, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, ++ 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x76, 0x61, ++ 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x74, ++ 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, ++ 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x61, 0x73, 0x20, 0x4c, 0x75, 0x61, 0x54, ++ 0x65, 0x58, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x34, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, ++ 0x6f, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x77, 0x61, 0x79, ++ 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x75, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x6b, ++ 0x65, 0x65, 0x70, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, ++ 0x6e, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, ++ 0x20, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, ++ 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x6c, 0x79, 0x20, ++ 0x65, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x62, 0x69, 0x74, 0x20, 0x62, 0x65, 0x74, 0x74, 0x65, ++ 0x72, 0x20, 0x6e, 0x6f, 0x77, 0x2e, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x57, 0x65, 0x20, 0x74, 0x65, ++ 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, ++ 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x64, ++ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, ++ 0x65, 0x20, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x61, 0x64, ++ 0x20, 0x6f, 0x6e, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x0a, 0x2d, 0x2d, 0x20, 0x61, 0x72, ++ 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, ++ 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, ++ 0x2e, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x6e, ++ 0x65, 0x78, 0x74, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, ++ 0x65, 0x2c, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, ++ 0x65, 0x2c, 0x20, 0x6e, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, ++ 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x0a, 0x6c, ++ 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x2c, 0x20, 0x67, 0x73, 0x75, 0x62, 0x2c, ++ 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, ++ 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, ++ 0x75, 0x62, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, ++ 0x74, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, ++ 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x5f, ++ 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, ++ 0x6c, 0x20, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, ++ 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, ++ 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x66, 0x69, ++ 0x6f, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, ++ 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, ++ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, ++ 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, ++ 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, ++ 0x61, 0x6d, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x72, ++ 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, ++ 0x20, 0x6d, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, ++ 0x62, 0x6c, 0x65, 0x28, 0x69, 0x6f, 0x2e, 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x29, 0x0a, 0x6c, ++ 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, 0x74, 0x2e, 0x6c, 0x69, ++ 0x6e, 0x65, 0x73, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x61, 0x66, 0x65, 0x72, 0x6f, ++ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, ++ 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x73, 0x61, 0x66, 0x65, 0x72, 0x5f, 0x6f, 0x70, 0x74, ++ 0x69, 0x6f, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x65, ++ 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, ++ 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x5f, 0x65, 0x73, 0x63, ++ 0x61, 0x70, 0x65, 0x20, 0x2d, 0x2d, 0x20, 0x30, 0x20, 0x28, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, ++ 0x65, 0x64, 0x29, 0x20, 0x31, 0x20, 0x28, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x29, ++ 0x20, 0x32, 0x20, 0x28, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x29, 0x0a, ++ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6b, 0x70, 0x73, 0x65, 0x75, 0x73, 0x65, 0x64, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, ++ 0x75, 0x73, 0x2e, 0x6b, 0x70, 0x73, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x20, 0x20, 0x20, 0x20, ++ 0x2d, 0x2d, 0x20, 0x30, 0x20, 0x31, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x77, 0x72, ++ 0x69, 0x74, 0x65, 0x5f, 0x6e, 0x6c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x69, 0x6f, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, ++ 0x6e, 0x6c, 0x0a, 0x0a, 0x69, 0x6f, 0x2e, 0x73, 0x61, 0x76, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, ++ 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, ++ 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x2d, 0x2d, 0x20, 0x61, 0x6c, 0x77, 0x61, ++ 0x79, 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x0a, 0x6d, 0x74, 0x2e, 0x73, ++ 0x61, 0x76, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, ++ 0x20, 0x2d, 0x2d, 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6f, ++ 0x6e, 0x6c, 0x79, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, ++ 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, ++ 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x27, ++ 0x72, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, ++ 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, ++ 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, +- 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x20, 0x3d, +- 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 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, 0x66, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 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, 0x20, 0x20, 0x20, +- 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x68, 0x6f, 0x77, 0x29, 0x20, 0x3d, 0x3d, ++ 0x20, 0x27, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x69, ++ 0x6e, 0x64, 0x28, 0x68, 0x6f, 0x77, 0x2c, 0x27, 0x77, 0x27, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6f, ++ 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, ++ 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x77, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x20, +- 0x77, 0x68, 0x6f, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x69, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, +- 0x20, 0x77, 0x61, 0x79, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, +- 0x20, 0x27, 0x69, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x27, 0x20, 0x63, 0x61, 0x6e, 0x27, +- 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x27, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x6e, 0x61, 0x6d, +- 0x65, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x27, 0x22, 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, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6f, +- 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, ++ 0x20, 0x20, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, ++ 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x72, 0x27, 0x29, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, +- 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x72, +- 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, +- 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, +- 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x6f, 0x2e, +- 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, +- 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x0a, 0x6d, 0x74, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, +- 0x20, 0x3d, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, +- 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x57, 0x65, 0x20, 0x61, 0x73, 0x73, +- 0x75, 0x6d, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, +- 0x6f, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, +- 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, +- 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x70, 0x73, 0x65, 0x2e, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, +- 0x73, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6e, +- 0x20, 0x43, 0x6f, 0x6e, 0x54, 0x65, 0x58, 0x74, 0x2e, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6b, 0x70, +- 0x73, 0x65, 0x75, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x74, 0x68, 0x65, 0x6e, +- 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x3d, +- 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x6c, +- 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x0a, +- 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x73, 0x61, 0x66, 0x65, 0x72, 0x6f, 0x70, 0x74, +- 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, +- 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, +- 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x73, +- 0x74, 0x72, 0x2c, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, +- 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, +- 0x61, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, +- 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x2e, 0x2e, 0x2e, +- 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, +- 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x74, 0x68, +- 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6e, 0x6c, 0x28, 0x66, 0x6f, 0x72, 0x6d, +- 0x61, 0x74, 0x28, 0x22, 0x73, 0x61, 0x66, 0x65, 0x72, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, +- 0x20, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x25, +- 0x71, 0x20, 0x69, 0x73, 0x20, 0x25, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, +- 0x72, 0x2c, 0x66, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x22, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, +- 0x22, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x29, +- 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, +- 0x65, 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, 0x69, 0x66, +- 0x20, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, ++ 0x70, 0x65, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x28, 0x6e, 0x61, 0x6d, ++ 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x68, 0x6f, ++ 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, ++ 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x27, 0x72, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, ++ 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x20, ++ 0x67, 0x73, 0x75, 0x62, 0x28, 0x68, 0x6f, 0x77, 0x2c, 0x27, 0x5b, 0x5e, 0x72, 0x62, 0x5d, 0x27, ++ 0x2c, 0x27, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, ++ 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, ++ 0x20, 0x27, 0x72, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, ++ 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, ++ 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, ++ 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, ++ 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, ++ 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x72, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, ++ 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, ++ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x70, ++ 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x6b, 0x61, 0x79, 0x2c, 0x20, 0x66, ++ 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, ++ 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6f, 0x6b, 0x61, 0x79, 0x20, 0x61, 0x6e, 0x64, ++ 0x20, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6f, 0x5f, 0x70, 0x6f, ++ 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x6c, ++ 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, ++ 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x28, 0x6e, 0x61, ++ 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, ++ 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x2d, 0x2d, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, ++ 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, ++ 0x77, 0x20, 0x6f, 0x72, 0x20, 0x27, 0x72, 0x27, 0x29, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x2d, ++ 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, ++ 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x0a, ++ 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, ++ 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x2d, 0x2d, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x2d, 0x2d, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, ++ 0x65, 0x73, 0x28, 0x29, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, ++ 0x2d, 0x2d, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x73, ++ 0x6f, 0x6d, 0x65, 0x20, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, ++ 0x63, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x6b, 0x69, 0x63, 0x6b, 0x20, 0x69, ++ 0x6e, 0x20, 0x73, 0x6f, 0x20, 0x77, 0x65, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, ++ 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x74, 0x6c, 0x79, 0x0a, 0x2d, ++ 0x2d, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, ++ 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x65, 0x64, 0x2e, ++ 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2c, 0x20, 0x74, ++ 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2c, 0x20, 0x74, 0x79, 0x70, ++ 0x65, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, ++ 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, ++ 0x73, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x3d, 0x3d, ++ 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, ++ 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, ++ 0x77, 0x20, 0x6f, 0x72, 0x20, 0x27, 0x72, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x69, 0x66, 0x20, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, +- 0x28, 0x2e, 0x2e, 0x2e, 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, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, +- 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, +- 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x28, 0x73, 0x74, 0x72, 0x2c, 0x66, +- 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, +- 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, +- 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, +- 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, +- 0x75, 0x74, 0x65, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x73, 0x70, 0x61, +- 0x77, 0x6e, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, +- 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x22, 0x29, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x20, 0x20, 0x20, 0x20, 0x3d, +- 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, +- 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, +- 0x73, 0x65, 0x74, 0x65, 0x6e, 0x76, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, +- 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x65, 0x6e, +- 0x76, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x64, +- 0x69, 0x72, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, +- 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x64, 0x69, 0x72, 0x22, 0x29, 0x0a, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, +- 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, +- 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, +- 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, +- 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, +- 0x6e, 0x22, 0x2c, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, +- 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, +- 0x20, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, +- 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x72, +- 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x72, +- 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, +- 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, +- 0x22, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x74, 0x6d, 0x70, 0x66, 0x69, +- 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, +- 0x79, 0x28, 0x22, 0x69, 0x6f, 0x2e, 0x74, 0x6d, 0x70, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x29, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x20, 0x3d, +- 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x69, +- 0x6f, 0x2e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x6c, 0x66, 0x73, 0x2e, 0x63, 0x68, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, +- 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6c, 0x66, 0x73, 0x2e, 0x63, +- 0x68, 0x64, 0x69, 0x72, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x6c, +- 0x6f, 0x63, 0x6b, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, +- 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6c, 0x66, 0x73, 0x2e, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x29, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x20, 0x20, +- 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, +- 0x6c, 0x66, 0x73, 0x2e, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x6c, 0x66, 0x73, 0x2e, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, +- 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6c, 0x66, 0x73, 0x2e, 0x72, +- 0x6d, 0x64, 0x69, 0x72, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x6d, +- 0x6b, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, +- 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6c, 0x66, 0x73, 0x2e, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x22, +- 0x29, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x73, 0x61, 0x66, 0x65, 0x72, +- 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x6f, 0x72, 0x20, 0x73, +- 0x68, 0x65, 0x6c, 0x6c, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x7e, 0x3d, 0x20, 0x31, 0x20, +- 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x66, 0x69, 0x20, 0x3d, 0x20, +- 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x66, 0x66, 0x69, 0x27, 0x29, 0x0a, 0x20, +- 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, 0x20, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x6e, +- 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x66, 0x69, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x20, 0x7e, 0x3d, 0x20, 0x27, 0x67, 0x63, 0x27, ++ 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, ++ 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, ++ 0x28, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 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, 0x66, 0x3a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 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, ++ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 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, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x6f, ++ 0x73, 0x65, 0x20, 0x77, 0x68, 0x6f, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x69, 0x74, 0x20, 0x74, ++ 0x68, 0x69, 0x73, 0x20, 0x77, 0x61, 0x79, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x70, 0x61, 0x74, 0x63, ++ 0x68, 0x65, 0x64, 0x20, 0x27, 0x69, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x27, 0x20, 0x63, ++ 0x61, 0x6e, 0x27, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x27, 0x22, 0x20, 0x2e, 0x2e, 0x20, ++ 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x27, 0x22, 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, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, ++ 0x20, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, ++ 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, ++ 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, ++ 0x6e, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, ++ 0x72, 0x6e, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, ++ 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, ++ 0x69, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, ++ 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x0a, 0x6d, 0x74, 0x2e, 0x6c, 0x69, ++ 0x6e, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, ++ 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x57, 0x65, 0x20, ++ 0x61, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, ++ 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, ++ 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, ++ 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x70, 0x73, 0x65, 0x2e, 0x20, 0x54, 0x68, 0x69, ++ 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x63, 0x61, 0x73, 0x65, ++ 0x20, 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x54, 0x65, 0x58, 0x74, 0x2e, 0x0a, 0x0a, 0x69, 0x66, ++ 0x20, 0x6b, 0x70, 0x73, 0x65, 0x75, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x74, ++ 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, ++ 0x20, 0x20, 0x3d, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, ++ 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, ++ 0x3d, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x70, 0x6f, 0x70, 0x65, ++ 0x6e, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x73, 0x61, 0x66, 0x65, 0x72, ++ 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x74, 0x68, 0x65, 0x6e, ++ 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, ++ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, ++ 0x79, 0x28, 0x73, 0x74, 0x72, 0x2c, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, ++ 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, ++ 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x66, 0x66, 0x69, 0x5b, 0x6b, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, +- 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x66, 0x69, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, +- 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x6f, 0x73, 0x2e, 0x5b, 0x65, 0x78, +- 0x65, 0x63, 0x75, 0x74, 0x65, 0x7c, 0x6f, 0x73, 0x2e, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x7c, 0x6f, +- 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x5d, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, +- 0x61, 0x72, 0x65, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, +- 0x61, 0x77, 0x61, 0x72, 0x65, 0x29, 0x0a, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6d, 0x64, 0x35, 0x20, +- 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, +- 0x73, 0x75, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x67, 0x73, 0x75, 0x62, 0x20, +- 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, +- 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, +- 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x0a, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x64, +- 0x35, 0x2e, 0x73, 0x75, 0x6d, 0x68, 0x65, 0x78, 0x61, 0x28, 0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x67, 0x73, 0x75, +- 0x62, 0x28, 0x73, 0x75, 0x6d, 0x28, 0x6b, 0x29, 0x2c, 0x20, 0x22, 0x2e, 0x22, 0x2c, 0x20, 0x66, +- 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, +- 0x72, 0x6d, 0x61, 0x74, 0x28, 0x22, 0x25, 0x30, 0x32, 0x78, 0x22, 0x2c, 0x62, 0x79, 0x74, 0x65, +- 0x28, 0x63, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, +- 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, +- 0x48, 0x45, 0x58, 0x41, 0x28, 0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x75, 0x6d, +- 0x28, 0x6b, 0x29, 0x2c, 0x20, 0x22, 0x2e, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, +- 0x6f, 0x6e, 0x28, 0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6e, 0x6c, 0x28, 0x66, ++ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x22, 0x73, 0x61, 0x66, 0x65, 0x72, 0x20, 0x6f, 0x70, 0x74, ++ 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, ++ 0x6e, 0x20, 0x25, 0x71, 0x20, 0x69, 0x73, 0x20, 0x25, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x73, 0x74, 0x72, 0x2c, 0x66, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x22, 0x6c, 0x69, 0x6d, 0x69, ++ 0x74, 0x65, 0x64, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, ++ 0x64, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, ++ 0x74, 0x72, 0x75, 0x65, 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, 0x69, 0x66, 0x20, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, ++ 0x6e, 0x20, 0x66, 0x28, 0x2e, 0x2e, 0x2e, 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, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, ++ 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x28, 0x73, 0x74, ++ 0x72, 0x2c, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, ++ 0x61, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x61, ++ 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x6f, 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x6e, ++ 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x65, ++ 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, ++ 0x73, 0x70, 0x61, 0x77, 0x6e, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, ++ 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x73, 0x70, 0x61, 0x77, 0x6e, ++ 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x20, 0x20, ++ 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, ++ 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x6f, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x76, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, ++ 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x73, 0x65, ++ 0x74, 0x65, 0x6e, 0x76, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x74, 0x65, ++ 0x6d, 0x70, 0x64, 0x69, 0x72, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, ++ 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x64, 0x69, 0x72, ++ 0x22, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, ++ 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, ++ 0x79, 0x28, 0x22, 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x22, 0x29, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, ++ 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x69, 0x6f, 0x2e, ++ 0x6f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, ++ 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x29, 0x0a, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x20, 0x3d, ++ 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, ++ 0x73, 0x2e, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, ++ 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, ++ 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x6d, ++ 0x6f, 0x76, 0x65, 0x22, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x74, 0x6d, ++ 0x70, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, ++ 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x69, 0x6f, 0x2e, 0x74, 0x6d, 0x70, 0x66, 0x69, 0x6c, 0x65, ++ 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, ++ 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, ++ 0x28, 0x22, 0x69, 0x6f, 0x2e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x29, 0x0a, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x63, 0x68, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, ++ 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6c, 0x66, ++ 0x73, 0x2e, 0x63, 0x68, 0x64, 0x69, 0x72, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, ++ 0x73, 0x2e, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, ++ 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6c, 0x66, 0x73, 0x2e, 0x6c, 0x6f, 0x63, ++ 0x6b, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x74, 0x6f, 0x75, 0x63, ++ 0x68, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, ++ 0x79, 0x28, 0x22, 0x6c, 0x66, 0x73, 0x2e, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x22, 0x29, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, ++ 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6c, 0x66, ++ 0x73, 0x2e, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, ++ 0x73, 0x2e, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, ++ 0x6c, 0x6c, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x28, 0x22, 0x6c, 0x66, 0x73, 0x2e, 0x6d, 0x6b, 0x64, ++ 0x69, 0x72, 0x22, 0x29, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x73, 0x61, ++ 0x66, 0x65, 0x72, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x6f, ++ 0x72, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x7e, 0x3d, ++ 0x20, 0x31, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x66, 0x69, ++ 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x66, 0x66, 0x69, 0x27, ++ 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, 0x20, 0x76, 0x20, 0x69, ++ 0x6e, 0x20, 0x6e, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x66, 0x69, 0x20, 0x64, 0x6f, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x20, 0x7e, 0x3d, 0x20, 0x27, ++ 0x67, 0x63, 0x27, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x66, 0x69, 0x5b, 0x6b, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x69, ++ 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x66, 0x69, 0x20, 0x3d, 0x20, ++ 0x6e, 0x69, 0x6c, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x6f, 0x73, 0x2e, ++ 0x5b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x7c, 0x6f, 0x73, 0x2e, 0x73, 0x70, 0x61, 0x77, ++ 0x6e, 0x7c, 0x6f, 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x5d, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, ++ 0x64, 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x65, 0x73, 0x63, 0x61, ++ 0x70, 0x65, 0x20, 0x61, 0x77, 0x61, 0x72, 0x65, 0x29, 0x0a, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6d, ++ 0x64, 0x35, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, ++ 0x61, 0x6c, 0x20, 0x73, 0x75, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, 0x64, 0x35, 0x2e, ++ 0x73, 0x75, 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x67, 0x73, ++ 0x75, 0x62, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, ++ 0x75, 0x62, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x72, ++ 0x6d, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x6f, 0x72, ++ 0x6d, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x79, ++ 0x74, 0x65, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x62, 0x79, ++ 0x74, 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6d, ++ 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, 0x68, 0x65, 0x78, 0x61, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, ++ 0x20, 0x6d, 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, 0x68, 0x65, 0x78, 0x61, 0x28, 0x6b, 0x29, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, ++ 0x72, 0x6e, 0x20, 0x28, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x75, 0x6d, 0x28, 0x6b, 0x29, 0x2c, ++ 0x20, 0x22, 0x2e, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, ++ 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, +- 0x22, 0x25, 0x30, 0x32, 0x58, 0x22, 0x2c, 0x62, 0x79, 0x74, 0x65, 0x28, 0x63, 0x29, 0x29, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x29, 0x0a, 0x20, 0x20, +- 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x63, +- 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x3a, 0x20, 0x74, 0x68, +- 0x69, 0x73, 0x20, 0x6d, 0x69, 0x67, 0x68, 0x74, 0x20, 0x67, 0x6f, 0x20, 0x61, 0x77, 0x61, 0x79, +- 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x20, +- 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x20, +- 0x3d, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x0a, 0x65, +- 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, +- 0x67, 0x65, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x6c, 0x6f, 0x61, 0x64, +- 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, +- 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x73, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, +- 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x74, +- 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x74, 0x72, 0x69, +- 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, ++ 0x22, 0x25, 0x30, 0x32, 0x78, 0x22, 0x2c, 0x62, 0x79, 0x74, 0x65, 0x28, 0x63, 0x29, 0x29, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, ++ 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, ++ 0x74, 0x20, 0x6d, 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, 0x48, 0x45, 0x58, 0x41, 0x20, 0x74, 0x68, ++ 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, ++ 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, 0x48, 0x45, 0x58, 0x41, 0x28, ++ 0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, ++ 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x75, 0x6d, 0x28, ++ 0x6b, 0x29, 0x2c, 0x20, 0x22, 0x2e, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, ++ 0x6e, 0x28, 0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, ++ 0x61, 0x74, 0x28, 0x22, 0x25, 0x30, 0x32, 0x58, 0x22, 0x2c, 0x62, 0x79, 0x74, 0x65, 0x28, 0x63, ++ 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, ++ 0x6e, 0x64, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, + 0x2d, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x3a, +- 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x69, 0x67, 0x68, 0x74, 0x20, 0x73, 0x74, 0x61, 0x79, +- 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x6c, 0x75, 0x61, 0x20, 0x35, 0x2e, 0x32, 0x3a, +- 0x20, 0x77, 0x65, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6b, 0x61, 0x79, 0x0a, 0x0a, 0x65, 0x6c, 0x73, +- 0x65, 0x69, 0x66, 0x20, 0x75, 0x74, 0x66, 0x38, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, +- 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x6c, 0x75, 0x61, 0x20, 0x35, 0x2e, 0x33, 0x3a, 0x20, 0x20, +- 0x62, 0x69, 0x74, 0x77, 0x69, 0x73, 0x65, 0x2e, 0x6c, 0x75, 0x61, 0x2c, 0x20, 0x76, 0x20, 0x31, +- 0x2e, 0x32, 0x34, 0x20, 0x32, 0x30, 0x31, 0x34, 0x2f, 0x31, 0x32, 0x2f, 0x32, 0x36, 0x20, 0x31, +- 0x37, 0x3a, 0x32, 0x30, 0x3a, 0x35, 0x33, 0x20, 0x72, 0x6f, 0x62, 0x65, 0x72, 0x74, 0x6f, 0x0a, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x61, +- 0x64, 0x20, 0x28, 0x20, 0x5b, 0x5b, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x65, 0x6c, +- 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x2d, 0x2d, 0x20, +- 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6f, 0x66, 0x3a, 0x20, 0x61, 0x72, 0x67, 0x20, +- 0x3d, 0x20, 0x7b, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x7d, 0x0a, 0x0a, 0x62, 0x69, 0x74, 0x33, 0x32, +- 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x62, 0x6e, 0x6f, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, +- 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, +- 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7e, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, +- 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x62, +- 0x61, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, +- 0x78, 0x2c, 0x20, 0x79, 0x2c, 0x20, 0x7a, 0x2c, 0x20, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, +- 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x7a, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x28, 0x78, +- 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x26, 0x20, 0x28, 0x79, 0x20, 0x6f, 0x72, 0x20, +- 0x2d, 0x31, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x78, 0x20, 0x26, +- 0x20, 0x79, 0x20, 0x26, 0x20, 0x7a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, +- 0x20, 0x69, 0x3d, 0x31, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x22, 0x23, 0x22, 0x2c, +- 0x2e, 0x2e, 0x2e, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x73, 0x65, 0x6c, 0x65, +- 0x63, 0x74, 0x28, 0x69, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, +- 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, +- 0x0a, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, ++ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x69, 0x67, 0x68, 0x74, 0x20, 0x67, 0x6f, 0x20, 0x61, ++ 0x77, 0x61, 0x79, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x75, 0x6e, 0x70, 0x61, ++ 0x63, 0x6b, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6e, 0x70, 0x61, ++ 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x75, 0x6e, 0x70, 0x61, 0x63, ++ 0x6b, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x70, 0x61, ++ 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, ++ 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x6c, ++ 0x6f, 0x61, 0x64, 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, ++ 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x65, 0x72, 0x73, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, ++ 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x74, 0x72, 0x69, 0x6e, ++ 0x67, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x73, ++ 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x0a, 0x65, 0x6e, 0x64, ++ 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, ++ 0x74, 0x79, 0x3a, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x69, 0x67, 0x68, 0x74, 0x20, 0x73, ++ 0x74, 0x61, 0x79, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, 0x74, 0x68, ++ 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x6c, 0x75, 0x61, 0x20, 0x35, ++ 0x2e, 0x32, 0x3a, 0x20, 0x77, 0x65, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6b, 0x61, 0x79, 0x0a, 0x0a, ++ 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x75, 0x74, 0x66, 0x38, 0x20, 0x74, 0x68, 0x65, 0x6e, ++ 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x6c, 0x75, 0x61, 0x20, 0x35, 0x2e, 0x33, ++ 0x3a, 0x20, 0x20, 0x62, 0x69, 0x74, 0x77, 0x69, 0x73, 0x65, 0x2e, 0x6c, 0x75, 0x61, 0x2c, 0x20, ++ 0x76, 0x20, 0x31, 0x2e, 0x32, 0x34, 0x20, 0x32, 0x30, 0x31, 0x34, 0x2f, 0x31, 0x32, 0x2f, 0x32, ++ 0x36, 0x20, 0x31, 0x37, 0x3a, 0x32, 0x30, 0x3a, 0x35, 0x33, 0x20, 0x72, 0x6f, 0x62, 0x65, 0x72, ++ 0x74, 0x6f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, 0x3d, 0x20, ++ 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x28, 0x20, 0x5b, 0x5b, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, ++ 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, ++ 0x2d, 0x2d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6f, 0x66, 0x3a, 0x20, 0x61, ++ 0x72, 0x67, 0x20, 0x3d, 0x20, 0x7b, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x7d, 0x0a, 0x0a, 0x62, 0x69, ++ 0x74, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x62, 0x6e, 0x6f, 0x74, 0x20, 0x3d, ++ 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x29, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7e, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, ++ 0x20, 0x20, 0x62, 0x61, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x20, 0x79, 0x2c, 0x20, 0x7a, 0x2c, 0x20, 0x2e, 0x2e, 0x2e, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x7a, 0x20, 0x74, 0x68, + 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, +- 0x28, 0x28, 0x78, 0x20, 0x6f, 0x72, 0x20, 0x30, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x79, 0x20, 0x6f, +- 0x72, 0x20, 0x30, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x78, 0x20, +- 0x7c, 0x20, 0x79, 0x20, 0x7c, 0x20, 0x7a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, +- 0x72, 0x20, 0x69, 0x3d, 0x31, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x22, 0x23, 0x22, +- 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x20, 0x7c, 0x20, 0x73, 0x65, 0x6c, +- 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, +- 0x6e, 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, +- 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x78, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, +- 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x20, 0x79, 0x2c, 0x20, 0x7a, 0x2c, 0x20, 0x2e, 0x2e, +- 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x7a, 0x20, +- 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, +- 0x6e, 0x20, 0x28, 0x28, 0x78, 0x20, 0x6f, 0x72, 0x20, 0x30, 0x29, 0x20, 0x7e, 0x20, 0x28, 0x79, +- 0x20, 0x6f, 0x72, 0x20, 0x30, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, ++ 0x28, 0x28, 0x78, 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x26, 0x20, 0x28, 0x79, 0x20, ++ 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, +- 0x78, 0x20, 0x7e, 0x20, 0x79, 0x20, 0x7e, 0x20, 0x7a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x78, 0x20, 0x26, 0x20, 0x79, 0x20, 0x26, 0x20, 0x7a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x69, 0x3d, 0x31, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x22, + 0x23, 0x22, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x20, 0x7e, 0x20, 0x73, ++ 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, +- 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x74, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, ++ 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, ++ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x20, 0x79, 0x2c, 0x20, 0x7a, 0x2c, 0x20, 0x2e, ++ 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x7a, ++ 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, ++ 0x72, 0x6e, 0x20, 0x28, 0x28, 0x78, 0x20, 0x6f, 0x72, 0x20, 0x30, 0x29, 0x20, 0x7c, 0x20, 0x28, ++ 0x79, 0x20, 0x6f, 0x72, 0x20, 0x30, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, ++ 0x20, 0x78, 0x20, 0x7c, 0x20, 0x79, 0x20, 0x7c, 0x20, 0x7a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x3d, 0x31, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, ++ 0x22, 0x23, 0x22, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x20, 0x7c, 0x20, ++ 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, ++ 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, ++ 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x78, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x20, 0x79, 0x2c, 0x20, 0x7a, 0x2c, + 0x20, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x7a, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, +- 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x28, 0x28, 0x78, 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x29, +- 0x20, 0x26, 0x20, 0x28, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x29, 0x29, 0x20, 0x26, 0x20, +- 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x30, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x78, 0x20, 0x26, 0x20, +- 0x79, 0x20, 0x26, 0x20, 0x7a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, +- 0x69, 0x3d, 0x31, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x22, 0x23, 0x22, 0x2c, 0x2e, +- 0x2e, 0x2e, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x73, 0x65, 0x6c, +- 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, +- 0x6e, 0x20, 0x28, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x46, 0x46, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, +- 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x6c, 0x73, 0x68, 0x69, 0x66, +- 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, +- 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, +- 0x28, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x29, +- 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x72, 0x73, 0x68, +- 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, +- 0x61, 0x2c, 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, +- 0x20, 0x28, 0x28, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x62, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, +- 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x61, ++ 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x28, 0x78, 0x20, 0x6f, 0x72, 0x20, 0x30, 0x29, 0x20, 0x7e, ++ 0x20, 0x28, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x30, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, ++ 0x20, 0x3d, 0x20, 0x78, 0x20, 0x7e, 0x20, 0x79, 0x20, 0x7e, 0x20, 0x7a, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x3d, 0x31, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, ++ 0x74, 0x28, 0x22, 0x23, 0x22, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x20, ++ 0x7e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, ++ 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x74, 0x65, 0x73, 0x74, 0x20, 0x3d, ++ 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x20, 0x79, 0x2c, ++ 0x20, 0x7a, 0x2c, 0x20, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, ++ 0x6e, 0x6f, 0x74, 0x20, 0x7a, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x28, 0x28, 0x78, 0x20, 0x6f, 0x72, 0x20, ++ 0x2d, 0x31, 0x29, 0x20, 0x26, 0x20, 0x28, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x29, 0x29, ++ 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x29, 0x20, 0x7e, ++ 0x3d, 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x78, ++ 0x20, 0x26, 0x20, 0x79, 0x20, 0x26, 0x20, 0x7a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, ++ 0x6f, 0x72, 0x20, 0x69, 0x3d, 0x31, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x22, 0x23, ++ 0x22, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, ++ 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, ++ 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x72, 0x65, 0x73, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x6c, 0x73, ++ 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, ++ 0x28, 0x61, 0x2c, 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, ++ 0x6e, 0x20, 0x28, 0x28, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, ++ 0x46, 0x46, 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, + 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, +- 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, 0x3d, +- 0x20, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, +- 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x62, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x20, 0x6f, 0x72, +- 0x20, 0x28, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, +- 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x61, 0x20, 0x3e, 0x3e, 0x20, 0x62, +- 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, +- 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, +- 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x28, 0x61, 0x20, 0x3e, 0x3e, 0x20, 0x62, 0x29, 0x20, 0x7c, +- 0x20, 0x7e, 0x28, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x20, 0x3e, 0x3e, +- 0x20, 0x62, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, +- 0x0a, 0x20, 0x20, 0x6c, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, +- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x20, 0x2c, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, +- 0x20, 0x62, 0x20, 0x3d, 0x20, 0x62, 0x20, 0x26, 0x20, 0x33, 0x31, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, ++ 0x75, 0x72, 0x6e, 0x20, 0x28, 0x28, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x62, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, ++ 0x20, 0x20, 0x61, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, ++ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x61, 0x20, 0x3d, 0x20, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x28, 0x61, 0x20, 0x3c, 0x3c, +- 0x20, 0x62, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x61, 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x33, 0x32, 0x20, +- 0x2d, 0x20, 0x62, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, +- 0x20, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, +- 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x72, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, +- 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, +- 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x2d, 0x62, 0x20, 0x26, 0x20, +- 0x33, 0x31, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x20, 0x26, 0x20, 0x30, +- 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, +- 0x3d, 0x20, 0x28, 0x61, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x61, 0x20, +- 0x3e, 0x3e, 0x20, 0x28, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x62, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, +- 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, +- 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, +- 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, +- 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, 0x66, 0x2c, 0x20, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, +- 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x61, 0x20, 0x3e, 0x3e, 0x20, 0x66, 0x29, +- 0x20, 0x26, 0x20, 0x7e, 0x28, 0x2d, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x77, 0x20, 0x6f, 0x72, +- 0x20, 0x31, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x72, 0x65, +- 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, +- 0x20, 0x28, 0x61, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x66, 0x2c, 0x20, 0x77, 0x29, 0x0a, 0x20, 0x20, +- 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x7e, +- 0x28, 0x2d, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x77, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x29, 0x29, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x28, 0x61, 0x20, +- 0x26, 0x20, 0x7e, 0x28, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3c, 0x3c, 0x20, 0x66, 0x29, 0x29, 0x20, +- 0x7c, 0x20, 0x28, 0x28, 0x76, 0x20, 0x26, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x20, 0x3c, 0x3c, +- 0x20, 0x66, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, +- 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x5d, 0x5d, 0x20, 0x29, 0x0a, 0x0a, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, +- 0x62, 0x69, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, +- 0x20, 0x6c, 0x75, 0x61, 0x6a, 0x69, 0x74, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x77, +- 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x6c, +- 0x6f, 0x61, 0x64, 0x20, 0x28, 0x20, 0x5b, 0x5b, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, +- 0x61, 0x6e, 0x64, 0x2c, 0x20, 0x62, 0x6e, 0x6f, 0x74, 0x2c, 0x20, 0x72, 0x73, 0x68, 0x69, 0x66, +- 0x74, 0x2c, 0x20, 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x2e, +- 0x62, 0x61, 0x6e, 0x64, 0x2c, 0x20, 0x62, 0x69, 0x74, 0x2e, 0x62, 0x6e, 0x6f, 0x74, 0x2c, 0x20, +- 0x62, 0x69, 0x74, 0x2e, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x2c, 0x20, 0x62, 0x69, 0x74, 0x2e, +- 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x0a, 0x0a, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, 0x3d, 0x20, +- 0x7b, 0x0a, 0x20, 0x20, 0x61, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x62, 0x69, +- 0x74, 0x2e, 0x61, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x61, 0x6e, +- 0x64, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x62, 0x61, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x62, +- 0x6e, 0x6f, 0x74, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x62, 0x6e, 0x6f, 0x74, 0x2c, 0x0a, 0x20, +- 0x20, 0x62, 0x6f, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x2e, 0x62, +- 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x78, 0x6f, 0x72, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, +- 0x62, 0x69, 0x74, 0x2e, 0x62, 0x78, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x74, 0x65, 0x73, +- 0x74, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x2e, +- 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, +- 0x61, 0x6e, 0x64, 0x28, 0x2e, 0x2e, 0x2e, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x30, 0x0a, 0x20, 0x20, +- 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x3d, +- 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x66, 0x2c, 0x77, 0x29, +- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x61, 0x6e, 0x64, +- 0x28, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x28, 0x61, 0x2c, 0x66, 0x29, 0x2c, 0x32, 0x5e, 0x28, +- 0x77, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x29, 0x2d, 0x31, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, +- 0x2c, 0x0a, 0x20, 0x20, 0x6c, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x62, 0x69, +- 0x74, 0x2e, 0x72, 0x6f, 0x6c, 0x2c, 0x0a, 0x20, 0x20, 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, +- 0x20, 0x3d, 0x20, 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x70, +- 0x6c, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, +- 0x61, 0x2c, 0x76, 0x2c, 0x66, 0x2c, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, +- 0x61, 0x6c, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x32, 0x5e, 0x28, 0x77, 0x20, 0x6f, +- 0x72, 0x20, 0x31, 0x29, 0x2d, 0x31, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, +- 0x6e, 0x20, 0x62, 0x61, 0x6e, 0x64, 0x28, 0x61, 0x2c, 0x62, 0x6e, 0x6f, 0x74, 0x28, 0x6c, 0x73, +- 0x68, 0x69, 0x66, 0x74, 0x28, 0x6d, 0x61, 0x73, 0x6b, 0x2c, 0x66, 0x29, 0x29, 0x29, 0x2b, 0x6c, +- 0x73, 0x68, 0x69, 0x66, 0x74, 0x28, 0x62, 0x61, 0x6e, 0x64, 0x28, 0x76, 0x2c, 0x6d, 0x61, 0x73, +- 0x6b, 0x29, 0x2c, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x72, +- 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x2e, 0x72, 0x6f, 0x72, +- 0x2c, 0x0a, 0x20, 0x20, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x20, 0x3d, 0x20, 0x72, 0x73, +- 0x68, 0x69, 0x66, 0x74, 0x2c, 0x0a, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x5d, 0x5d, 0x20, 0x29, 0x0a, 0x0a, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x2d, 0x2d, 0x20, 0x68, 0x6f, 0x70, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, +- 0x62, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x0a, 0x0a, 0x20, 0x20, +- 0x20, 0x20, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, +- 0x65, 0x28, 0x22, 0x62, 0x69, 0x74, 0x33, 0x32, 0x22, 0x29, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, +- 0x0a, 0x2d, 0x2d, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x65, 0x65, 0x64, +- 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x72, +- 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x29, +- 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x0a, 0x0a, 0x64, 0x6f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, +- 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x70, +- 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x0a, 0x0a, 0x20, +- 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, +- 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x61, +- 0x64, 0x65, 0x64, 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x61, +- 0x64, 0x65, 0x64, 0x5b, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, +- 0x22, 0x5d, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, +- 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x2e, 0x6d, 0x69, 0x6d, 0x65, 0x20, 0x20, 0x20, +- 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x2e, 0x6d, 0x69, 0x6d, 0x65, +- 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5b, 0x22, 0x6d, 0x69, 0x6d, +- 0x65, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x5d, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, +- 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x73, 0x6f, 0x20, 0x66, 0x61, 0x72, 0x0a, 0x0a, 0x00 ++ 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x62, 0x20, 0x3c, 0x3d, 0x20, 0x30, ++ 0x20, 0x6f, 0x72, 0x20, 0x28, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, ++ 0x30, 0x30, 0x30, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x61, 0x20, 0x3e, ++ 0x3e, 0x20, 0x62, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, ++ 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x28, 0x61, 0x20, 0x3e, 0x3e, 0x20, 0x62, ++ 0x29, 0x20, 0x7c, 0x20, 0x7e, 0x28, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, ++ 0x20, 0x3e, 0x3e, 0x20, 0x62, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, ++ 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x6c, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, ++ 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x20, 0x2c, 0x62, 0x29, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x62, 0x20, 0x26, 0x20, 0x33, 0x31, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x28, 0x61, ++ 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x61, 0x20, 0x3e, 0x3e, 0x20, 0x28, ++ 0x33, 0x32, 0x20, 0x2d, 0x20, 0x62, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, ++ 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, ++ 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x72, 0x72, 0x6f, 0x74, ++ 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, ++ 0x61, 0x2c, 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x2d, 0x62, ++ 0x20, 0x26, 0x20, 0x33, 0x31, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x20, ++ 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x61, 0x20, 0x3d, 0x20, 0x28, 0x61, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x29, 0x20, 0x7c, 0x20, ++ 0x28, 0x61, 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x62, 0x29, 0x29, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x26, 0x20, 0x30, ++ 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, ++ 0x0a, 0x20, 0x20, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, ++ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, 0x66, 0x2c, 0x20, 0x77, 0x29, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x61, 0x20, 0x3e, 0x3e, ++ 0x20, 0x66, 0x29, 0x20, 0x26, 0x20, 0x7e, 0x28, 0x2d, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x77, ++ 0x20, 0x6f, 0x72, 0x20, 0x31, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, ++ 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, ++ 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x66, 0x2c, 0x20, 0x77, 0x29, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, ++ 0x3d, 0x20, 0x7e, 0x28, 0x2d, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x77, 0x20, 0x6f, 0x72, 0x20, ++ 0x31, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, ++ 0x28, 0x61, 0x20, 0x26, 0x20, 0x7e, 0x28, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3c, 0x3c, 0x20, 0x66, ++ 0x29, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x76, 0x20, 0x26, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x29, ++ 0x20, 0x3c, 0x3c, 0x20, 0x66, 0x29, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, ++ 0x46, 0x46, 0x46, 0x46, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x7d, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5d, 0x5d, 0x20, 0x29, 0x0a, 0x0a, 0x65, 0x6c, 0x73, 0x65, ++ 0x69, 0x66, 0x20, 0x62, 0x69, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x2d, 0x2d, 0x20, 0x6c, 0x75, 0x61, 0x6a, 0x69, 0x74, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x20, ++ 0x6e, 0x6f, 0x77, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, ++ 0x3d, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x28, 0x20, 0x5b, 0x5b, 0x0a, 0x6c, 0x6f, 0x63, 0x61, ++ 0x6c, 0x20, 0x62, 0x61, 0x6e, 0x64, 0x2c, 0x20, 0x62, 0x6e, 0x6f, 0x74, 0x2c, 0x20, 0x72, 0x73, ++ 0x68, 0x69, 0x66, 0x74, 0x2c, 0x20, 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x62, ++ 0x69, 0x74, 0x2e, 0x62, 0x61, 0x6e, 0x64, 0x2c, 0x20, 0x62, 0x69, 0x74, 0x2e, 0x62, 0x6e, 0x6f, ++ 0x74, 0x2c, 0x20, 0x62, 0x69, 0x74, 0x2e, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x2c, 0x20, 0x62, ++ 0x69, 0x74, 0x2e, 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x0a, 0x0a, 0x62, 0x69, 0x74, 0x33, 0x32, ++ 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x61, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, ++ 0x20, 0x62, 0x69, 0x74, 0x2e, 0x61, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x2c, 0x0a, 0x20, 0x20, ++ 0x62, 0x61, 0x6e, 0x64, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x62, 0x61, 0x6e, 0x64, 0x2c, 0x0a, ++ 0x20, 0x20, 0x62, 0x6e, 0x6f, 0x74, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x62, 0x6e, 0x6f, 0x74, ++ 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x62, 0x69, ++ 0x74, 0x2e, 0x62, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x62, 0x78, 0x6f, 0x72, 0x20, 0x20, 0x20, ++ 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x2e, 0x62, 0x78, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x62, ++ 0x74, 0x65, 0x73, 0x74, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, ++ 0x6e, 0x28, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, ++ 0x6e, 0x20, 0x62, 0x61, 0x6e, 0x64, 0x28, 0x2e, 0x2e, 0x2e, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x30, ++ 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, ++ 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x66, ++ 0x2c, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, ++ 0x61, 0x6e, 0x64, 0x28, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x28, 0x61, 0x2c, 0x66, 0x29, 0x2c, ++ 0x32, 0x5e, 0x28, 0x77, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x29, 0x2d, 0x31, 0x29, 0x0a, 0x20, 0x20, ++ 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x6c, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, ++ 0x20, 0x62, 0x69, 0x74, 0x2e, 0x72, 0x6f, 0x6c, 0x2c, 0x0a, 0x20, 0x20, 0x6c, 0x73, 0x68, 0x69, ++ 0x66, 0x74, 0x20, 0x20, 0x3d, 0x20, 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x2c, 0x0a, 0x20, 0x20, ++ 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, ++ 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x76, 0x2c, 0x66, 0x2c, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x32, 0x5e, 0x28, ++ 0x77, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x29, 0x2d, 0x31, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, ++ 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x61, 0x6e, 0x64, 0x28, 0x61, 0x2c, 0x62, 0x6e, 0x6f, 0x74, ++ 0x28, 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x28, 0x6d, 0x61, 0x73, 0x6b, 0x2c, 0x66, 0x29, 0x29, ++ 0x29, 0x2b, 0x6c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x28, 0x62, 0x61, 0x6e, 0x64, 0x28, 0x76, 0x2c, ++ 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x2c, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, ++ 0x20, 0x20, 0x72, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x2e, ++ 0x72, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x20, 0x3d, ++ 0x20, 0x72, 0x73, 0x68, 0x69, 0x66, 0x74, 0x2c, 0x0a, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x5d, 0x5d, 0x20, 0x29, 0x0a, 0x0a, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x68, 0x6f, 0x70, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, ++ 0x68, 0x65, 0x20, 0x62, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x0a, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x69, 0x74, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, ++ 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x62, 0x69, 0x74, 0x33, 0x32, 0x22, 0x29, 0x0a, 0x0a, 0x65, ++ 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, ++ 0x65, 0x65, 0x64, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x65, 0x74, 0x74, 0x69, 0x6e, ++ 0x67, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65, ++ 0x74, 0x22, 0x29, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x0a, 0x0a, 0x64, 0x6f, 0x0a, 0x0a, 0x20, ++ 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, ++ 0x3d, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, ++ 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x61, ++ 0x64, 0x65, 0x64, 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, ++ 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x3d, 0x20, ++ 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5b, 0x22, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, ++ 0x6f, 0x72, 0x65, 0x22, 0x5d, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, ++ 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x2e, 0x6d, 0x69, 0x6d, 0x65, ++ 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x2e, 0x6d, ++ 0x69, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5b, 0x22, ++ 0x6d, 0x69, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x5d, 0x20, 0x20, 0x20, 0x65, 0x6e, ++ 0x64, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x64, 0x6f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, ++ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x66, 0x73, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, ++ 0x74, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x61, 0x74, ++ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, ++ 0x61, 0x6c, 0x20, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, ++ 0x75, 0x74, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x73, 0x79, 0x6d, 0x6c, 0x69, ++ 0x6e, 0x6b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x6e, ++ 0x6f, 0x77, 0x20, 0x62, 0x65, 0x20, 0x64, 0x6f, 0x6e, 0x65, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, ++ 0x20, 0x6c, 0x66, 0x73, 0x20, 0x28, 0x77, 0x61, 0x73, 0x20, 0x64, 0x65, 0x61, 0x64, 0x20, 0x73, ++ 0x6c, 0x6f, 0x77, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x69, 0x73, 0x66, 0x69, ++ 0x6c, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x69, 0x73, 0x66, ++ 0x69, 0x6c, 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x20, 0x3d, 0x20, ++ 0x6c, 0x66, 0x73, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x28, 0x6e, 0x61, ++ 0x6d, 0x65, 0x2c, 0x22, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x20, ++ 0x3d, 0x3d, 0x20, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x20, 0x3d, ++ 0x3d, 0x20, 0x22, 0x6c, 0x69, 0x6e, 0x6b, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x69, 0x73, 0x64, ++ 0x69, 0x72, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x69, 0x73, 0x64, ++ 0x69, 0x72, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x20, 0x3d, 0x20, 0x6c, ++ 0x66, 0x73, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x28, 0x6e, 0x61, 0x6d, ++ 0x65, 0x2c, 0x22, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x20, 0x3d, ++ 0x3d, 0x20, 0x22, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x0a, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, ++ 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x6e, ++ 0x61, 0x6d, 0x65, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x62, ++ 0x65, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, ++ 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x6b, 0x70, 0x73, 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x73, 0x68, 0x6f, 0x72, ++ 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x66, 0x73, 0x2e, ++ 0x73, 0x68, 0x6f, 0x72, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, ++ 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x20, ++ 0x20, 0x2d, 0x2d, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, ++ 0x20, 0x61, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x2c, ++ 0x20, 0x73, 0x6f, 0x20, 0x2e, 0x2e, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, ++ 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x6b, ++ 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, ++ 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x6c, ++ 0x69, 0x6e, 0x6b, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x79, 0x6d, ++ 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x28, 0x6e, ++ 0x61, 0x6d, 0x65, 0x2c, 0x22, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x29, 0x20, 0x6f, 0x72, ++ 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, ++ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, ++ 0x2d, 0x20, 0x73, 0x6f, 0x20, 0x66, 0x61, 0x72, 0x0a, 0x0a, 0x00 + }; + return luaL_dostring(L, (const char*) luatex_core_lua); + } +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/lua/luatex-core.lua b/texk/web2c/luatexdir/lua/luatex-core.lua +index 35005d1c8..f2d55fd99 100644 +--- a/texk/web2c/luatexdir/lua/luatex-core.lua ++++ b/texk/web2c/luatexdir/lua/luatex-core.lua +@@ -1,419 +1,465 @@ +--- luatex-core security and io overloads ........... +- +--- if not modules then modules = { } end modules ['luatex-core'] = { +--- version = 1.005, +--- comment = 'companion to luatex', +--- author = 'Hans Hagen & Luigi Scarso', +--- copyright = 'LuaTeX Development Team', +--- } +- +-LUATEXCOREVERSION = 1.005 +- +--- This file overloads some Lua functions. The readline variants provide the same +--- functionality as LuaTeX <= 1.04 and doing it this way permits us to keep the +--- original io libraries clean. Performance is probably even a bit better now. +- +-local type, next, getmetatable, require = type, next, getmetatable, require +-local find, gsub, format = string.find, string.gsub, string.format +- +-local io_open = io.open +-local io_popen = io.popen +-local io_lines = io.lines +- +-local fio_readline = fio.readline +-local fio_checkpermission = fio.checkpermission +-local fio_recordfilename = fio.recordfilename +- +-local mt = getmetatable(io.stderr) +-local mt_lines = mt.lines +-local saferoption = status.safer_option +-local shellescape = status.shell_escape -- 0 (disabled) 1 (anything) 2 (restricted) +-local kpseused = status.kpse_used -- 0 1 +- +-local write_nl = texio.write_nl +- +-io.saved_lines = io_lines -- always readonly +-mt.saved_lines = mt_lines -- always readonly +- +-local function luatex_io_open(name,how) +- if not how then +- how = 'r' +- end +- local f = io_open(name,how) +- if f then +- if type(how) == 'string' and find(how,'w') then +- fio_recordfilename(name,'w') +- else +- fio_recordfilename(name,'r') +- end +- end +- return f +-end +- +-local function luatex_io_open_readonly(name,how) +- if how then +- how = 'r' +- else +- how = gsub(how,'[^rb]','') +- if how == '' then +- how = 'r' +- end +- end +- local f = io_open(name,how) +- if f then +- fio_recordfilename(name,'r') +- end +- return f +-end +- +-local function luatex_io_popen(name,...) +- local okay, found = fio_checkpermission(name) +- if okay and found then +- return io_popen(found,...) +- end +-end +- +--- local function luatex_io_lines(name,how) +--- if name then +--- local f = io_open(name,how or 'r') +--- if f then +--- return function() +--- return fio_readline(f) +--- end +--- end +--- else +--- return io_lines() +--- end +--- end +- +--- For some reason the gc doesn't kick in so we need to close explitly +--- so that the handle is flushed. +- +-local error, type = error, type +- +-local function luatex_io_lines(name,how) +- if type(name) == "string" then +- local f = io_open(name,how or 'r') +- if f then +- return function() +- local l = fio_readline(f) +- if not l then +- f:close() +- end +- return l +- end +- else +- -- for those who like it this way: +- error("patched 'io.lines' can't open '" .. name .. "'") +- end +- else +- return io_lines() +- end +-end +- +-local function luatex_io_readline(f) +- return function() +- return fio_readline(f) +- end +-end +- +-io.lines = luatex_io_lines +-mt.lines = luatex_io_readline +- +--- We assume management to be provided by the replacement of kpse. This is the +--- case in ConTeXt. +- +-if kpseused == 1 then +- +- io.open = luatex_io_open +- io.popen = luatex_io_popen +- +-end +- +-if saferoption == 1 then +- +- local function installdummy(str,f) +- local reported = false +- return function(...) +- if not reported then +- write_nl(format("safer option set, function %q is %s", +- str,f and "limited" or "disabled")) +- reported = true +- end +- if f then +- return f(...) +- end +- end +- end +- +- local function installlimit(str,f) +- local reported = false +- end +- +- os.execute = installdummy("os.execute") +- os.spawn = installdummy("os.spawn") +- os.exec = installdummy("os.exec") +- os.setenv = installdummy("os.setenv") +- os.tempdir = installdummy("os.tempdir") +- +- io.popen = installdummy("io.popen") +- io.open = installdummy("io.open",luatex_io_open_readonly) +- +- os.rename = installdummy("os.rename") +- os.remove = installdummy("os.remove") +- +- io.tmpfile = installdummy("io.tmpfile") +- io.output = installdummy("io.output") +- +- lfs.chdir = installdummy("lfs.chdir") +- lfs.lock = installdummy("lfs.lock") +- lfs.touch = installdummy("lfs.touch") +- lfs.rmdir = installdummy("lfs.rmdir") +- lfs.mkdir = installdummy("lfs.mkdir") +- +-end +- +-if saferoption == 1 or shellescape ~= 1 then +- +- ffi = require('ffi') +- for k, v in next, ffi do +- if k ~= 'gc' then +- ffi[k] = nil +- end +- end +- ffi = nil +- +-end +- +--- os.[execute|os.spawn|os.exec] already are shellescape aware) +- +- +-if md5 then +- +- local sum = md5.sum +- local gsub = string.gsub +- local format = string.format +- local byte = string.byte +- +- function md5.sumhexa(k) +- return (gsub(sum(k), ".", function(c) +- return format("%02x",byte(c)) +- end)) +- end +- +- function md5.sumHEXA(k) +- return (gsub(sum(k), ".", function(c) +- return format("%02X",byte(c)) +- end)) +- end +- +-end +- +--- compatibility: this might go away +- +-if not unpack then +- unpack = table.unpack +-end +- +-if not package.loaders then +- package.loaders = package.searchers +-end +- +-if not loadstring then +- loadstring = load +-end +- +--- compatibility: this might stay +- +-if bit32 then +- +- -- lua 5.2: we're okay +- +-elseif utf8 then +- +- -- lua 5.3: bitwise.lua, v 1.24 2014/12/26 17:20:53 roberto +- +- bit32 = load ( [[ +-local select = select -- instead of: arg = { ... } +- +-bit32 = { +- bnot = function (a) +- return ~a & 0xFFFFFFFF +- end, +- band = function (x, y, z, ...) +- if not z then +- return ((x or -1) & (y or -1)) & 0xFFFFFFFF +- else +- local res = x & y & z +- for i=1,select("#",...) do +- res = res & select(i,...) +- end +- return res & 0xFFFFFFFF +- end +- end, +- bor = function (x, y, z, ...) +- if not z then +- return ((x or 0) | (y or 0)) & 0xFFFFFFFF +- else +- local res = x | y | z +- for i=1,select("#",...) do +- res = res | select(i,...) +- end +- return res & 0xFFFFFFFF +- end +- end, +- bxor = function (x, y, z, ...) +- if not z then +- return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF +- else +- local res = x ~ y ~ z +- for i=1,select("#",...) do +- res = res ~ select(i,...) +- end +- return res & 0xFFFFFFFF +- end +- end, +- btest = function (x, y, z, ...) +- if not z then +- return (((x or -1) & (y or -1)) & 0xFFFFFFFF) ~= 0 +- else +- local res = x & y & z +- for i=1,select("#",...) do +- res = res & select(i,...) +- end +- return (res & 0xFFFFFFFF) ~= 0 +- end +- end, +- lshift = function (a, b) +- return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF +- end, +- rshift = function (a, b) +- return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF +- end, +- arshift = function (a, b) +- a = a & 0xFFFFFFFF +- if b <= 0 or (a & 0x80000000) == 0 then +- return (a >> b) & 0xFFFFFFFF +- else +- return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF +- end +- end, +- lrotate = function (a ,b) +- b = b & 31 +- a = a & 0xFFFFFFFF +- a = (a << b) | (a >> (32 - b)) +- return a & 0xFFFFFFFF +- end, +- rrotate = function (a, b) +- b = -b & 31 +- a = a & 0xFFFFFFFF +- a = (a << b) | (a >> (32 - b)) +- return a & 0xFFFFFFFF +- end, +- extract = function (a, f, w) +- return (a >> f) & ~(-1 << (w or 1)) +- end, +- replace = function (a, v, f, w) +- local mask = ~(-1 << (w or 1)) +- return ((a & ~(mask << f)) | ((v & mask) << f)) & 0xFFFFFFFF +- end, +-} +- ]] ) +- +-elseif bit then +- +- -- luajit (for now) +- +- bit32 = load ( [[ +-local band, bnot, rshift, lshift = bit.band, bit.bnot, bit.rshift, bit.lshift +- +-bit32 = { +- arshift = bit.arshift, +- band = band, +- bnot = bnot, +- bor = bit.bor, +- bxor = bit.bxor, +- btest = function(...) +- return band(...) ~= 0 +- end, +- extract = function(a,f,w) +- return band(rshift(a,f),2^(w or 1)-1) +- end, +- lrotate = bit.rol, +- lshift = lshift, +- replace = function(a,v,f,w) +- local mask = 2^(w or 1)-1 +- return band(a,bnot(lshift(mask,f)))+lshift(band(v,mask),f) +- end, +- rrotate = bit.ror, +- rshift = rshift, +-} +- ]] ) +- +-else +- +- -- hope for the best or fail +- +- bit32 = require("bit32") +- +-end +- +--- this is needed for getting require("socket") right +- +-do +- +- local loaded = package.loaded +- +- if not loaded.socket then loaded.socket = loaded["socket.core"] end +- if not loaded.mime then loaded.mime = loaded["mime.core"] end +- +-end +- +--- so far +- +-if utilities and utilities.merger and utilities.merger.compact then +- +- local byte, format, gmatch = string.byte, string.format, string.gmatch +- local concat = table.concat +- +- local data = gsub(io.loaddata('luatex-core.lua'),'if%s+utilities.*','') +- +- local t = { } +- local r = { } +- local n = 0 +- local d = gsub(data,'\r\n','\n') -- be nice for unix +- local s = utilities.merger.compact(d) -- no comments and less spaces +- +- t[#t+1] = '/* generated from and by luatex-core.lua */' +- t[#t+1] = '' +- -- t[#t+1] = format('/*\n\n%s\n\n*/',d) +- -- t[#t+1] = '' +- t[#t+1] = '#include "lua.h"' +- t[#t+1] = '#include "lauxlib.h"' +- t[#t+1] = '' +- t[#t+1] = 'int load_luatex_core_lua (lua_State * L);' +- t[#t+1] = '' +- t[#t+1] = 'int load_luatex_core_lua (lua_State * L)' +- t[#t+1] = '{' +- t[#t+1] = ' static unsigned char luatex_core_lua[] = {' +- for c in gmatch(d,'.') do +- if n == 16 then +- n = 1 +- t[#t+1] = ' ' .. concat(r,', ') .. ',' +- else +- n = n + 1 +- end +- r[n] = format('0x%02x',byte(c)) +- end +- n = n + 1 +- r[n] = '0x00' +- t[#t+1] = ' ' .. concat(r,', ',1,n) +- t[#t+1] = ' };' +- -- t[#t+1] = format('unsigned int luatex_core_lua_len = 0x%x;',#d+1) +- t[#t+1] = ' return luaL_dostring(L, (const char*) luatex_core_lua);' +- t[#t+1] = '}' +- +- io.savedata('luatex-core.c',concat(t,'\n')) +- io.savedata('luatex-core-stripped.lua',s) +- +-end ++-- luatex-core security and io overloads ........... ++ ++-- if not modules then modules = { } end modules ['luatex-core'] = { ++-- version = 1.080, ++-- comment = 'companion to luatex', ++-- author = 'Hans Hagen & Luigi Scarso', ++-- copyright = 'LuaTeX Development Team', ++-- } ++ ++LUATEXCOREVERSION = 1.080 -- we reflect the luatex version where changes happened ++ ++-- This file overloads some Lua functions. The readline variants provide the same ++-- functionality as LuaTeX <= 1.04 and doing it this way permits us to keep the ++-- original io libraries clean. Performance is probably even a bit better now. ++ ++-- We test for functions already being defined so that we don't overload ones that ++-- are provided in the startup script. ++ ++local type, next, getmetatable, require = type, next, getmetatable, require ++local find, gsub, format = string.find, string.gsub, string.format ++ ++local io_open = io.open ++local io_popen = io.popen ++local io_lines = io.lines ++ ++local fio_readline = fio.readline ++local fio_checkpermission = fio.checkpermission ++local fio_recordfilename = fio.recordfilename ++ ++local mt = getmetatable(io.stderr) ++local mt_lines = mt.lines ++local saferoption = status.safer_option ++local shellescape = status.shell_escape -- 0 (disabled) 1 (anything) 2 (restricted) ++local kpseused = status.kpse_used -- 0 1 ++ ++local write_nl = texio.write_nl ++ ++io.saved_lines = io_lines -- always readonly ++mt.saved_lines = mt_lines -- always readonly ++ ++local function luatex_io_open(name,how) ++ if not how then ++ how = 'r' ++ end ++ local f = io_open(name,how) ++ if f then ++ if type(how) == 'string' and find(how,'w') then ++ fio_recordfilename(name,'w') ++ else ++ fio_recordfilename(name,'r') ++ end ++ end ++ return f ++end ++ ++local function luatex_io_open_readonly(name,how) ++ if how then ++ how = 'r' ++ else ++ how = gsub(how,'[^rb]','') ++ if how == '' then ++ how = 'r' ++ end ++ end ++ local f = io_open(name,how) ++ if f then ++ fio_recordfilename(name,'r') ++ end ++ return f ++end ++ ++local function luatex_io_popen(name,...) ++ local okay, found = fio_checkpermission(name) ++ if okay and found then ++ return io_popen(found,...) ++ end ++end ++ ++-- local function luatex_io_lines(name,how) ++-- if name then ++-- local f = io_open(name,how or 'r') ++-- if f then ++-- return function() ++-- return fio_readline(f) ++-- end ++-- end ++-- else ++-- return io_lines() ++-- end ++-- end ++ ++-- For some reason the gc doesn't kick in so we need to close explitly ++-- so that the handle is flushed. ++ ++local error, type = error, type ++ ++local function luatex_io_lines(name,how) ++ if type(name) == "string" then ++ local f = io_open(name,how or 'r') ++ if f then ++ return function() ++ local l = fio_readline(f) ++ if not l then ++ f:close() ++ end ++ return l ++ end ++ else ++ -- for those who like it this way: ++ error("patched 'io.lines' can't open '" .. name .. "'") ++ end ++ else ++ return io_lines() ++ end ++end ++ ++local function luatex_io_readline(f) ++ return function() ++ return fio_readline(f) ++ end ++end ++ ++io.lines = luatex_io_lines ++mt.lines = luatex_io_readline ++ ++-- We assume management to be provided by the replacement of kpse. This is the ++-- case in ConTeXt. ++ ++if kpseused == 1 then ++ ++ io.open = luatex_io_open ++ io.popen = luatex_io_popen ++ ++end ++ ++if saferoption == 1 then ++ ++ local function installdummy(str,f) ++ local reported = false ++ return function(...) ++ if not reported then ++ write_nl(format("safer option set, function %q is %s", ++ str,f and "limited" or "disabled")) ++ reported = true ++ end ++ if f then ++ return f(...) ++ end ++ end ++ end ++ ++ local function installlimit(str,f) ++ local reported = false ++ end ++ ++ os.execute = installdummy("os.execute") ++ os.spawn = installdummy("os.spawn") ++ os.exec = installdummy("os.exec") ++ os.setenv = installdummy("os.setenv") ++ os.tempdir = installdummy("os.tempdir") ++ ++ io.popen = installdummy("io.popen") ++ io.open = installdummy("io.open",luatex_io_open_readonly) ++ ++ os.rename = installdummy("os.rename") ++ os.remove = installdummy("os.remove") ++ ++ io.tmpfile = installdummy("io.tmpfile") ++ io.output = installdummy("io.output") ++ ++ lfs.chdir = installdummy("lfs.chdir") ++ lfs.lock = installdummy("lfs.lock") ++ lfs.touch = installdummy("lfs.touch") ++ lfs.rmdir = installdummy("lfs.rmdir") ++ lfs.mkdir = installdummy("lfs.mkdir") ++ ++end ++ ++if saferoption == 1 or shellescape ~= 1 then ++ ++ ffi = require('ffi') ++ for k, v in next, ffi do ++ if k ~= 'gc' then ++ ffi[k] = nil ++ end ++ end ++ ffi = nil ++ ++end ++ ++-- os.[execute|os.spawn|os.exec] already are shellescape aware) ++ ++ ++if md5 then ++ ++ local sum = md5.sum ++ local gsub = string.gsub ++ local format = string.format ++ local byte = string.byte ++ ++ if not md5.sumhexa then ++ function md5.sumhexa(k) ++ return (gsub(sum(k), ".", function(c) ++ return format("%02x",byte(c)) ++ end)) ++ end ++ end ++ ++ if not md5.sumHEXA then ++ function md5.sumHEXA(k) ++ return (gsub(sum(k), ".", function(c) ++ return format("%02X",byte(c)) ++ end)) ++ end ++ end ++ ++end ++ ++-- compatibility: this might go away ++ ++if not unpack then ++ unpack = table.unpack ++end ++ ++if not package.loaders then ++ package.loaders = package.searchers ++end ++ ++if not loadstring then ++ loadstring = load ++end ++ ++-- compatibility: this might stay ++ ++if bit32 then ++ ++ -- lua 5.2: we're okay ++ ++elseif utf8 then ++ ++ -- lua 5.3: bitwise.lua, v 1.24 2014/12/26 17:20:53 roberto ++ ++ bit32 = load ( [[ ++local select = select -- instead of: arg = { ... } ++ ++bit32 = { ++ bnot = function (a) ++ return ~a & 0xFFFFFFFF ++ end, ++ band = function (x, y, z, ...) ++ if not z then ++ return ((x or -1) & (y or -1)) & 0xFFFFFFFF ++ else ++ local res = x & y & z ++ for i=1,select("#",...) do ++ res = res & select(i,...) ++ end ++ return res & 0xFFFFFFFF ++ end ++ end, ++ bor = function (x, y, z, ...) ++ if not z then ++ return ((x or 0) | (y or 0)) & 0xFFFFFFFF ++ else ++ local res = x | y | z ++ for i=1,select("#",...) do ++ res = res | select(i,...) ++ end ++ return res & 0xFFFFFFFF ++ end ++ end, ++ bxor = function (x, y, z, ...) ++ if not z then ++ return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF ++ else ++ local res = x ~ y ~ z ++ for i=1,select("#",...) do ++ res = res ~ select(i,...) ++ end ++ return res & 0xFFFFFFFF ++ end ++ end, ++ btest = function (x, y, z, ...) ++ if not z then ++ return (((x or -1) & (y or -1)) & 0xFFFFFFFF) ~= 0 ++ else ++ local res = x & y & z ++ for i=1,select("#",...) do ++ res = res & select(i,...) ++ end ++ return (res & 0xFFFFFFFF) ~= 0 ++ end ++ end, ++ lshift = function (a, b) ++ return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF ++ end, ++ rshift = function (a, b) ++ return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF ++ end, ++ arshift = function (a, b) ++ a = a & 0xFFFFFFFF ++ if b <= 0 or (a & 0x80000000) == 0 then ++ return (a >> b) & 0xFFFFFFFF ++ else ++ return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF ++ end ++ end, ++ lrotate = function (a ,b) ++ b = b & 31 ++ a = a & 0xFFFFFFFF ++ a = (a << b) | (a >> (32 - b)) ++ return a & 0xFFFFFFFF ++ end, ++ rrotate = function (a, b) ++ b = -b & 31 ++ a = a & 0xFFFFFFFF ++ a = (a << b) | (a >> (32 - b)) ++ return a & 0xFFFFFFFF ++ end, ++ extract = function (a, f, w) ++ return (a >> f) & ~(-1 << (w or 1)) ++ end, ++ replace = function (a, v, f, w) ++ local mask = ~(-1 << (w or 1)) ++ return ((a & ~(mask << f)) | ((v & mask) << f)) & 0xFFFFFFFF ++ end, ++} ++ ]] ) ++ ++elseif bit then ++ ++ -- luajit (for now) ++ ++ bit32 = load ( [[ ++local band, bnot, rshift, lshift = bit.band, bit.bnot, bit.rshift, bit.lshift ++ ++bit32 = { ++ arshift = bit.arshift, ++ band = band, ++ bnot = bnot, ++ bor = bit.bor, ++ bxor = bit.bxor, ++ btest = function(...) ++ return band(...) ~= 0 ++ end, ++ extract = function(a,f,w) ++ return band(rshift(a,f),2^(w or 1)-1) ++ end, ++ lrotate = bit.rol, ++ lshift = lshift, ++ replace = function(a,v,f,w) ++ local mask = 2^(w or 1)-1 ++ return band(a,bnot(lshift(mask,f)))+lshift(band(v,mask),f) ++ end, ++ rrotate = bit.ror, ++ rshift = rshift, ++} ++ ]] ) ++ ++else ++ ++ -- hope for the best or fail ++ ++ bit32 = require("bit32") ++ ++end ++ ++-- this is needed for getting require("socket") right ++ ++do ++ ++ local loaded = package.loaded ++ ++ if not loaded.socket then loaded.socket = loaded["socket.core"] end ++ if not loaded.mime then loaded.mime = loaded["mime.core"] end ++ ++end ++ ++do ++ ++ local lfsattributes = lfs.attributes ++ local symlinkattributes = lfs.symlinkattributes ++ ++ -- these can now be done using lfs (was dead slow before) ++ ++ if not lfs.isfile then ++ function lfs.isfile(name) ++ local m = lfsattributes(name,"mode") ++ return m == "file" or m == "link" ++ end ++ end ++ ++ if not lfs.isdir then ++ function lfs.isdir(name) ++ local m = lfsattributes(name,"mode") ++ return m == "directory" ++ end ++ end ++ ++ -- shortnames have also be sort of dropped from kpse ++ ++ if not lfs.shortname then ++ function lfs.shortname(name) ++ return name ++ end ++ end ++ ++ -- now there is a target field, so ... ++ ++ if not lfs.readlink then ++ function lfs.readlink(name) ++ return symlinkattributes(name,"target") or nil ++ end ++ end ++ ++end ++ ++-- so far ++ ++if utilities and utilities.merger and utilities.merger.compact then ++ ++ local byte, format, gmatch = string.byte, string.format, string.gmatch ++ local concat = table.concat ++ ++ local data = gsub(io.loaddata('luatex-core.lua'),'if%s+utilities.*','') ++ ++ local t = { } ++ local r = { } ++ local n = 0 ++ local d = gsub(data,'\r\n','\n') -- be nice for unix ++ local s = utilities.merger.compact(d) -- no comments and less spaces ++ ++ t[#t+1] = '/* generated from and by luatex-core.lua */' ++ t[#t+1] = '' ++ -- t[#t+1] = format('/*\n\n%s\n\n*/',d) ++ -- t[#t+1] = '' ++ t[#t+1] = '#include "lua.h"' ++ t[#t+1] = '#include "lauxlib.h"' ++ t[#t+1] = '' ++ t[#t+1] = 'int load_luatex_core_lua (lua_State * L);' ++ t[#t+1] = '' ++ t[#t+1] = 'int load_luatex_core_lua (lua_State * L)' ++ t[#t+1] = '{' ++ t[#t+1] = ' static unsigned char luatex_core_lua[] = {' ++ for c in gmatch(d,'.') do ++ if n == 16 then ++ n = 1 ++ t[#t+1] = ' ' .. concat(r,', ') .. ',' ++ else ++ n = n + 1 ++ end ++ r[n] = format('0x%02x',byte(c)) ++ end ++ n = n + 1 ++ r[n] = '0x00' ++ t[#t+1] = ' ' .. concat(r,', ',1,n) ++ t[#t+1] = ' };' ++ -- t[#t+1] = format('unsigned int luatex_core_lua_len = 0x%x;',#d+1) ++ t[#t+1] = ' return luaL_dostring(L, (const char*) luatex_core_lua);' ++ t[#t+1] = '}' ++ ++ io.savedata('luatex-core.c',concat(t,'\n')) ++ io.savedata('luatex-core-stripped.lua',s) ++ ++end +diff --git a/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsepfa.c b/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsepfa.c +index 1cff6a485..df369b45e 100644 +--- a/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsepfa.c ++++ b/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsepfa.c +@@ -2667,9 +2667,9 @@ static void FontInfoFree(struct fontinfo *fi) { + void PSFontFree(FontDict *fd) { + int i; + +- if ( fd->encoding!=NULL ) +- for ( i=0; i<256; ++i ) +- free( fd->encoding[i]); ++ /*if ( fd->encoding!=NULL ): useless: fd->encoding is *char[256] */ ++ for ( i=0; i<256; ++i ) ++ free( fd->encoding[i]); + free(fd->fontname); + free(fd->cidfontname); + free(fd->registry); +diff --git a/texk/web2c/luatexdir/luafontloader/src/luafflib.c b/texk/web2c/luatexdir/luafontloader/src/luafflib.c +index 8717fe8e3..434413b00 100644 +--- a/texk/web2c/luatexdir/luafontloader/src/luafflib.c ++++ b/texk/web2c/luatexdir/luafontloader/src/luafflib.c +@@ -358,7 +358,7 @@ static void dump_intfield(lua_State * L, const char *name, long int field) + { + lua_checkstack(L, 2); + lua_pushstring(L, name); +- lua_pushnumber(L, field); ++ lua_pushinteger(L, field); + lua_rawset(L, -3); + } + +@@ -366,7 +366,7 @@ static void dump_uintfield(lua_State * L, const char *name, unsigned int field) + { + lua_checkstack(L, 2); + lua_pushstring(L, name); +- lua_pushnumber(L, field); ++ lua_pushinteger(L, field); + lua_rawset(L, -3); + } + +@@ -465,7 +465,7 @@ static void dump_subtable_name(lua_State * L, const char *name, struct lookup_su + next = b; \ + while (next != NULL) { \ + lua_checkstack(L,2); \ +- lua_pushnumber(L,k); k++; \ ++ lua_pushinteger(L,k); k++; \ + lua_createtable(L,0,c); \ + a(L, next); \ + lua_rawset(L,-3); \ +@@ -478,7 +478,7 @@ static void dump_subtable_name(lua_State * L, const char *name, struct lookup_su + next = b; \ + while (next != NULL) { \ + lua_checkstack(L,2); \ +- lua_pushnumber(L,k); k++; \ ++ lua_pushinteger(L,k); k++; \ + lua_createtable(L,0,d); \ + if (a(L, next, c)) \ + lua_rawset(L,-3); \ +@@ -498,7 +498,7 @@ static void do_handle_scriptlanglist(lua_State * L, struct scriptlanglist *sl) + lua_newtable(L); + for (k = 0; k < MAX_LANG; k++) { + if (sl->langs[k] != 0) { +- lua_pushnumber(L, (k + 1)); ++ lua_pushinteger(L, (k + 1)); + lua_pushstring(L, make_tag_string(sl->langs[k])); + lua_rawset(L, -3); + } +@@ -506,7 +506,7 @@ static void do_handle_scriptlanglist(lua_State * L, struct scriptlanglist *sl) + + if (sl->lang_cnt >= MAX_LANG) { + for (k = MAX_LANG; k < sl->lang_cnt; k++) { +- lua_pushnumber(L, (k + 1)); ++ lua_pushinteger(L, (k + 1)); + lua_pushstring(L, make_tag_string(sl->morelangs[k - MAX_LANG])); + lua_rawset(L, -3); + } +@@ -672,7 +672,7 @@ static void handle_splinecharlist(lua_State * L, struct splinecharlist *scl) + lua_checkstack(L, 10); + while (next != NULL) { + if (next->sc != NULL) { +- lua_pushnumber(L, k); ++ lua_pushinteger(L, k); + k++; + lua_pushstring(L, next->sc->name); + lua_rawset(L, -3); +@@ -772,8 +772,8 @@ static void do_handle_generic_pst(lua_State * L, struct generic_pst *pst) + } + } else if (pst->type == pst_lcaret) { + for (k = 0; k < pst->u.lcaret.cnt; k++) { +- lua_pushnumber(L, (k + 1)); +- lua_pushnumber(L, pst->u.lcaret.carets[k]); ++ lua_pushinteger(L, (k + 1)); ++ lua_pushinteger(L, pst->u.lcaret.carets[k]); + lua_rawset(L, -3); + } + } +@@ -800,7 +800,7 @@ static void handle_generic_pst(lua_State * L, struct generic_pst *pst) + lua_getfield(L, -1, next->subtable->subtable_name); + } + k = lua_rawlen(L, -1) + 1; +- lua_pushnumber(L, k); ++ lua_pushinteger(L, k); + lua_createtable(L, 0, 4); + do_handle_generic_pst(L, next); + lua_rawset(L, -3); +@@ -808,7 +808,7 @@ static void handle_generic_pst(lua_State * L, struct generic_pst *pst) + lua_pop(L, 1); /* pop the subtable */ + } else { + /* Found a pst without subtable, or without subtable name */ +- lua_pushnumber(L, l); ++ lua_pushinteger(L, l); + l++; + lua_createtable(L, 0, 4); + do_handle_generic_pst(L, next); +@@ -970,17 +970,17 @@ static void handle_splinechar(lua_State * L, struct splinechar *glyph, int hasvm + dump_stringfield(L, "name", glyph->name); + dump_intfield(L, "unicode", glyph->unicodeenc); + lua_createtable(L, 4, 0); +- lua_pushnumber(L, 1); +- lua_pushnumber(L, glyph->xmin); ++ lua_pushinteger(L, 1); ++ lua_pushinteger(L, glyph->xmin); + lua_rawset(L, -3); +- lua_pushnumber(L, 2); +- lua_pushnumber(L, glyph->ymin); ++ lua_pushinteger(L, 2); ++ lua_pushinteger(L, glyph->ymin); + lua_rawset(L, -3); +- lua_pushnumber(L, 3); +- lua_pushnumber(L, glyph->xmax); ++ lua_pushinteger(L, 3); ++ lua_pushinteger(L, glyph->xmax); + lua_rawset(L, -3); +- lua_pushnumber(L, 4); +- lua_pushnumber(L, glyph->ymax); ++ lua_pushinteger(L, 4); ++ lua_pushinteger(L, glyph->ymax); + lua_rawset(L, -3); + lua_setfield(L, -2, "boundingbox"); + if (hasvmetrics) { +@@ -1260,21 +1260,21 @@ static void handle_pfminfo(lua_State * L, struct pfminfo pfm) + dump_intfield(L, "os2_breakchar", pfm.os2_breakchar); + if (pfm.hascodepages) { + lua_newtable(L); +- lua_pushnumber(L, pfm.codepages[0]); ++ lua_pushinteger(L, pfm.codepages[0]); + lua_rawseti(L, -2, 1); +- lua_pushnumber(L, pfm.codepages[1]); ++ lua_pushinteger(L, pfm.codepages[1]); + lua_rawseti(L, -2, 2); + lua_setfield(L, -2, "codepages"); + } + if (pfm.hasunicoderanges) { + lua_newtable(L); +- lua_pushnumber(L, pfm.unicoderanges[0]); ++ lua_pushinteger(L, pfm.unicoderanges[0]); + lua_rawseti(L, -2, 1); +- lua_pushnumber(L, pfm.unicoderanges[1]); ++ lua_pushinteger(L, pfm.unicoderanges[1]); + lua_rawseti(L, -2, 2); +- lua_pushnumber(L, pfm.unicoderanges[2]); ++ lua_pushinteger(L, pfm.unicoderanges[2]); + lua_rawseti(L, -2, 3); +- lua_pushnumber(L, pfm.unicoderanges[3]); ++ lua_pushinteger(L, pfm.unicoderanges[3]); + lua_rawseti(L, -2, 4); + lua_setfield(L, -2, "unicoderanges"); + } +@@ -1292,8 +1292,8 @@ static char *do_handle_enc(lua_State * L, struct enc *enc) + if (enc->char_cnt && enc->unicode != NULL) { + lua_createtable(L, enc->char_cnt, 1); + for (i = 0; i < enc->char_cnt; i++) { +- lua_pushnumber(L, i); +- lua_pushnumber(L, enc->unicode[i]); ++ lua_pushinteger(L, i); ++ lua_pushinteger(L, enc->unicode[i]); + lua_rawset(L, -3); + } + lua_setfield(L, -2, "unicode"); +@@ -1302,7 +1302,7 @@ static char *do_handle_enc(lua_State * L, struct enc *enc) + if (enc->char_cnt && enc->psnames != NULL) { + lua_createtable(L, enc->char_cnt, 1); + for (i = 0; i < enc->char_cnt; i++) { +- lua_pushnumber(L, i); ++ lua_pushinteger(L, i); + lua_pushstring(L, enc->psnames[i]); + lua_rawset(L, -3); + } +@@ -1363,13 +1363,13 @@ static void handle_encmap(lua_State * L, struct encmap *map, int notdef_loc) + for (i = 0; i < map->encmax; i++) { + if (map->map[i] != -1) { + int l = map->map[i]; +- lua_pushnumber(L, i); ++ lua_pushinteger(L, i); + /* + if (l < notdef_loc) +- lua_pushnumber(L, (l + 1)); ++ lua_pushinteger(L, (l + 1)); + else + */ +- lua_pushnumber(L, l); ++ lua_pushinteger(L, l); + lua_rawset(L, -3); + } + } +@@ -1382,11 +1382,11 @@ static void handle_encmap(lua_State * L, struct encmap *map, int notdef_loc) + if (map->backmap[i] != -1) { + /* + if (i < notdef_loc) +- lua_pushnumber(L, (i + 1)); ++ lua_pushinteger(L, (i + 1)); + else + */ +- lua_pushnumber(L, i); +- lua_pushnumber(L, map->backmap[i]); ++ lua_pushinteger(L, i); ++ lua_pushinteger(L, map->backmap[i]); + lua_rawset(L, -3); + } + } +@@ -1488,7 +1488,7 @@ static int do_handle_kernclass(lua_State * L, struct kernclass *kerns, const cha + lua_checkstack(L, 4); + lua_createtable(L, kerns->first_cnt, 1); + for (k = 0; k < kerns->first_cnt; k++) { +- lua_pushnumber(L, (k + 1)); ++ lua_pushinteger(L, (k + 1)); + lua_pushstring(L, kerns->firsts[k]); + lua_rawset(L, -3); + } +@@ -1496,7 +1496,7 @@ static int do_handle_kernclass(lua_State * L, struct kernclass *kerns, const cha + + lua_createtable(L, kerns->second_cnt, 1); + for (k = 0; k < kerns->second_cnt; k++) { +- lua_pushnumber(L, (k + 1)); ++ lua_pushinteger(L, (k + 1)); + lua_pushstring(L, kerns->seconds[k]); + lua_rawset(L, -3); + } +@@ -1508,8 +1508,8 @@ static int do_handle_kernclass(lua_State * L, struct kernclass *kerns, const cha + lua_createtable(L, kerns->second_cnt * kerns->first_cnt, 1); + for (k = 0; k < (kerns->second_cnt * kerns->first_cnt); k++) { + if (kerns->offsets[k] != 0) { +- lua_pushnumber(L, (k + 1)); +- lua_pushnumber(L, kerns->offsets[k]); ++ lua_pushinteger(L, (k + 1)); ++ lua_pushinteger(L, kerns->offsets[k]); + lua_rawset(L, -3); + } + } +@@ -1529,8 +1529,8 @@ static void handle_kernclass(lua_State * L, struct kernclass *kerns, const char + int kk; \ + lua_newtable(L); \ + for (kk=0;kklookup_cnt > 0) { + lua_newtable(L); + for (k = 0; k < rule->lookup_cnt; k++) { +- lua_pushnumber(L, (rule->lookups[k].seq + 1)); ++ lua_pushinteger(L, (rule->lookups[k].seq + 1)); + if (rule->lookups[k].lookup != NULL) { + lua_pushstring(L, rule->lookups[k].lookup->lookup_name); + } else { +@@ -1650,7 +1650,7 @@ static void do_handle_generic_fpst(lua_State * L, struct generic_fpst *fpst) + if (fpst->rule_cnt > 0) { + lua_createtable(L, fpst->rule_cnt, 1); + for (k = 0; k < fpst->rule_cnt; k++) { +- lua_pushnumber(L, (k + 1)); ++ lua_pushinteger(L, (k + 1)); + lua_newtable(L); + handle_fpst_rule(L, &(fpst->rules[k]), fpst->format); + lua_rawset(L, -3); +@@ -1668,7 +1668,7 @@ static void handle_generic_fpst(lua_State * L, struct generic_fpst *fpst) + if (fpst->subtable != NULL && fpst->subtable->subtable_name != NULL) { + lua_pushstring(L, fpst->subtable->subtable_name); + } else { +- lua_pushnumber(L, k); ++ lua_pushinteger(L, k); + k++; + } + lua_createtable(L, 0, 10); +@@ -1680,7 +1680,7 @@ static void handle_generic_fpst(lua_State * L, struct generic_fpst *fpst) + if (next->subtable != NULL && next->subtable->subtable_name != NULL) { + lua_pushstring(L, next->subtable->subtable_name); + } else { +- lua_pushnumber(L, k); ++ lua_pushinteger(L, k); + k++; + } + lua_createtable(L, 0, 10); +@@ -1801,9 +1801,9 @@ static void handle_base(lua_State * L, struct Base *Base) + lua_newtable(L); + for (i = 0; i < Base->baseline_cnt; i++) { + if (next->baseline_pos != NULL) /* default omitted */ +- lua_pushnumber(L, next->baseline_pos[i]); ++ lua_pushinteger(L, next->baseline_pos[i]); + else +- lua_pushnumber(L, 0); ++ lua_pushinteger(L, 0); + lua_rawseti(L, -2, (i + 1)); + } + lua_setfield(L, -2, "baseline"); +@@ -1823,13 +1823,13 @@ static void handle_axismap(lua_State * L, struct axismap *am) + lua_checkstack(L, 3); + lua_newtable(L); + for (i = 0; i < am->points; i++) { +- lua_pushnumber(L, am->blends[i]); ++ lua_pushinteger(L, am->blends[i]); + lua_rawseti(L, -2, (i + 1)); + } + lua_setfield(L, -2, "blends"); + lua_newtable(L); + for (i = 0; i < am->points; i++) { +- lua_pushnumber(L, am->designs[i]); ++ lua_pushinteger(L, am->designs[i]); + lua_rawseti(L, -2, (i + 1)); + } + lua_setfield(L, -2, "designs"); +@@ -1853,7 +1853,7 @@ static void handle_mmset(lua_State * L, struct mmset *mm) + if (mm->instance_count > 0) { + lua_newtable(L); + for (i = 0; i < mm->instance_count * mm->axis_count; i++) { +- lua_pushnumber(L, mm->positions[i]); ++ lua_pushinteger(L, mm->positions[i]); + lua_rawseti(L, -2, (i + 1)); + } + lua_setfield(L, -2, "positions"); +@@ -1878,7 +1878,7 @@ static void handle_mmset(lua_State * L, struct mmset *mm) + + lua_newtable(L); + for (i = 0; i < mm->instance_count; i++) { +- lua_pushnumber(L, mm->defweights[i]); ++ lua_pushinteger(L, mm->defweights[i]); + lua_rawseti(L, -2, (i + 1)); + } + lua_setfield(L, -2, "defweights"); +@@ -1980,14 +1980,14 @@ static void handle_splinefont(lua_State * L, struct splinefont *sf) + } + for (k = 0; k < l; k++) { + if (sf->glyphs[k]) { +- lua_pushnumber(L, (k + 1)); ++ lua_pushinteger(L, (k + 1)); + lua_createtable(L, 0, 12); + handle_splinechar(L, sf->glyphs[k], sf->hasvmetrics); + lua_rawset(L, -3); + } + } + if (sf->glyphs != NULL && l < sf->glyphcnt) { +- lua_pushnumber(L, 0); ++ lua_pushinteger(L, 0); + if (sf->glyphs[l]) { + lua_createtable(L, 0, 12); + handle_splinechar(L, sf->glyphs[l], sf->hasvmetrics); +@@ -2000,7 +2000,7 @@ static void handle_splinefont(lua_State * L, struct splinefont *sf) + if ((l + 1) < sf->glyphcnt) { + for (k = (l + 1); k < sf->glyphcnt; k++) { + if (sf->glyphs[k]) { +- lua_pushnumber(L, k); ++ lua_pushinteger(L, k); + lua_createtable(L, 0, 12); + handle_splinechar(L, sf->glyphs[k], sf->hasvmetrics); + lua_rawset(L, -3); +@@ -2098,8 +2098,8 @@ static void handle_splinefont(lua_State * L, struct splinefont *sf) + dump_enumfield(L, "type", sf->texdata.type, tex_type_enum); + lua_newtable(L); + for (k = 0; k < 22; k++) { +- lua_pushnumber(L, k); +- lua_pushnumber(L, sf->texdata.params[k]); ++ lua_pushinteger(L, k); ++ lua_pushinteger(L, sf->texdata.params[k]); + lua_rawset(L, -3); + } + lua_setfield(L, -2, "params"); +@@ -2590,7 +2590,7 @@ static int ff_glyph_index(lua_State * L) + lua_pushstring(L, glyph->name); + break; + case GK_unicode: +- lua_pushnumber(L, glyph->unicodeenc); ++ lua_pushinteger(L, glyph->unicodeenc); + break; + case GK_boundingbox: + if (glyph->xmax == 0 && glyph->ymax == 0 && glyph->xmin == 0 && glyph->ymin == 0) { +@@ -2602,27 +2602,27 @@ static int ff_glyph_index(lua_State * L) + glyph->ymax = bb.maxy; + } + lua_createtable(L, 4, 0); +- lua_pushnumber(L, 1); +- lua_pushnumber(L, glyph->xmin); ++ lua_pushinteger(L, 1); ++ lua_pushinteger(L, glyph->xmin); + lua_rawset(L, -3); +- lua_pushnumber(L, 2); +- lua_pushnumber(L, glyph->ymin); ++ lua_pushinteger(L, 2); ++ lua_pushinteger(L, glyph->ymin); + lua_rawset(L, -3); +- lua_pushnumber(L, 3); +- lua_pushnumber(L, glyph->xmax); ++ lua_pushinteger(L, 3); ++ lua_pushinteger(L, glyph->xmax); + lua_rawset(L, -3); +- lua_pushnumber(L, 4); +- lua_pushnumber(L, glyph->ymax); ++ lua_pushinteger(L, 4); ++ lua_pushinteger(L, glyph->ymax); + lua_rawset(L, -3); + break; + case GK_vwidth: +- lua_pushnumber(L, glyph->vwidth); ++ lua_pushinteger(L, glyph->vwidth); + break; + case GK_width: +- lua_pushnumber(L, glyph->width); ++ lua_pushinteger(L, glyph->width); + break; + case GK_lsidebearing: +- lua_pushnumber(L, glyph->lsidebearing); ++ lua_pushinteger(L, glyph->lsidebearing); + break; + case GK_class: + if (glyph->glyph_class > 0) { +@@ -2692,31 +2692,31 @@ static int ff_glyph_index(lua_State * L) + break; + case GK_tex_height: + if (glyph->tex_height != TEX_UNDEF) { +- lua_pushnumber(L, glyph->tex_height); ++ lua_pushinteger(L, glyph->tex_height); + } else { + lua_pushnil(L); + } + break; + case GK_tex_depth: + if (glyph->tex_height != TEX_UNDEF) { +- lua_pushnumber(L, glyph->tex_depth); ++ lua_pushinteger(L, glyph->tex_depth); + } else { + lua_pushnil(L); + } + break; + case GK_is_extended_shape: +- lua_pushnumber(L, glyph->is_extended_shape); ++ lua_pushinteger(L, glyph->is_extended_shape); + break; + case GK_italic_correction: + if (glyph->italic_correction != TEX_UNDEF) { +- lua_pushnumber(L, glyph->italic_correction); ++ lua_pushinteger(L, glyph->italic_correction); + } else { + lua_pushnil(L); + } + break; + case GK_top_accent: + if (glyph->top_accent_horiz != TEX_UNDEF) { +- lua_pushnumber(L, glyph->top_accent_horiz); ++ lua_pushinteger(L, glyph->top_accent_horiz); + } else { + lua_pushnil(L); + } +@@ -2794,38 +2794,38 @@ static int ff_index(lua_State * L) + lua_pushstring(L, sf->version); + break; + case FK_italicangle: +- lua_pushnumber(L, sf->italicangle); ++ lua_pushinteger(L, sf->italicangle); + break; + case FK_upos: +- lua_pushnumber(L, sf->upos); ++ lua_pushinteger(L, sf->upos); + break; + case FK_uwidth: +- lua_pushnumber(L, sf->uwidth); ++ lua_pushinteger(L, sf->uwidth); + break; + case FK_ascent: +- lua_pushnumber(L, sf->ascent); ++ lua_pushinteger(L, sf->ascent); + break; + case FK_descent: +- lua_pushnumber(L, sf->descent); ++ lua_pushinteger(L, sf->descent); + break; + case FK_uniqueid: +- lua_pushnumber(L, sf->uniqueid); ++ lua_pushinteger(L, sf->uniqueid); + break; + case FK_glyphcnt: + if (sf->glyphcnt > 0) { +- lua_pushnumber(L, sf->glyphmax - sf->glyphmin + 1); ++ lua_pushinteger(L, sf->glyphmax - sf->glyphmin + 1); + } else { +- lua_pushnumber(L, 0); ++ lua_pushinteger(L, 0); + } + break; + case FK_glyphmax: +- lua_pushnumber(L, sf->glyphmax - 1); ++ lua_pushinteger(L, sf->glyphmax - 1); + break; + case FK_glyphmin: +- lua_pushnumber(L, sf->glyphmin); ++ lua_pushinteger(L, sf->glyphmin); + break; + case FK_units_per_em: +- lua_pushnumber(L, sf->units_per_em); ++ lua_pushinteger(L, sf->units_per_em); + break; + case FK_lookups: + if (sf->possub != NULL) { +@@ -2844,34 +2844,34 @@ static int ff_index(lua_State * L) + lua_setmetatable(L, -2); /* assign the metatable */ + break; + case FK_hasvmetrics: +- lua_pushnumber(L, sf->hasvmetrics); ++ lua_pushinteger(L, sf->hasvmetrics); + break; + case FK_onlybitmaps: +- lua_pushnumber(L, sf->onlybitmaps); ++ lua_pushinteger(L, sf->onlybitmaps); + break; + case FK_serifcheck: +- lua_pushnumber(L, sf->serifcheck); ++ lua_pushinteger(L, sf->serifcheck); + break; + case FK_isserif: +- lua_pushnumber(L, sf->isserif); ++ lua_pushinteger(L, sf->isserif); + break; + case FK_issans: +- lua_pushnumber(L, sf->issans); ++ lua_pushinteger(L, sf->issans); + break; + case FK_encodingchanged: +- lua_pushnumber(L, sf->encodingchanged); ++ lua_pushinteger(L, sf->encodingchanged); + break; + case FK_strokedfont: +- lua_pushnumber(L, sf->strokedfont); ++ lua_pushinteger(L, sf->strokedfont); + break; + case FK_use_typo_metrics: +- lua_pushnumber(L, sf->use_typo_metrics); ++ lua_pushinteger(L, sf->use_typo_metrics); + break; + case FK_weight_width_slope_only: +- lua_pushnumber(L, sf->weight_width_slope_only); ++ lua_pushinteger(L, sf->weight_width_slope_only); + break; + case FK_head_optimized_for_cleartype: +- lua_pushnumber(L, sf->head_optimized_for_cleartype); ++ lua_pushinteger(L, sf->head_optimized_for_cleartype); + break; + case FK_uni_interp: + lua_pushstring(L, uni_interp_enum[(sf->uni_interp + 1)]); +@@ -2967,8 +2967,8 @@ static int ff_index(lua_State * L) + dump_enumfield(L, "type", sf->texdata.type, tex_type_enum); + lua_newtable(L); + for (k = 0; k < 22; k++) { +- lua_pushnumber(L, k); +- lua_pushnumber(L, sf->texdata.params[k]); ++ lua_pushinteger(L, k); ++ lua_pushinteger(L, sf->texdata.params[k]); + lua_rawset(L, -3); + } + lua_setfield(L, -2, "params"); +@@ -3028,16 +3028,16 @@ static int ff_index(lua_State * L) + lua_pushstring(L, sf->chosenname); + break; + case FK_macstyle: +- lua_pushnumber(L, sf->macstyle); ++ lua_pushinteger(L, sf->macstyle); + break; + case FK_fondname: + lua_pushstring(L, sf->fondname); + break; + case FK_design_size: +- lua_pushnumber(L, sf->design_size); ++ lua_pushinteger(L, sf->design_size); + break; + case FK_fontstyle_id: +- lua_pushnumber(L, sf->fontstyle_id); ++ lua_pushinteger(L, sf->fontstyle_id); + break; + case FK_fontstyle_name: + if (sf->fontstyle_name != NULL) { +@@ -3048,13 +3048,13 @@ static int ff_index(lua_State * L) + } + break; + case FK_design_range_bottom: +- lua_pushnumber(L, sf->design_range_bottom); ++ lua_pushinteger(L, sf->design_range_bottom); + break; + case FK_design_range_top: +- lua_pushnumber(L, sf->design_range_top); ++ lua_pushinteger(L, sf->design_range_top); + break; + case FK_strokewidth: +- lua_pushnumber(L, sf->strokewidth); ++ lua_pushinteger(L, sf->strokewidth); + break; + case FK_mark_classes: + if (sf->mark_class_cnt > 0) { +@@ -3071,16 +3071,16 @@ static int ff_index(lua_State * L) + } + break; + case FK_creationtime: +- lua_pushnumber(L, sf->creationtime); ++ lua_pushinteger(L, sf->creationtime); + break; + case FK_modificationtime: +- lua_pushnumber(L, sf->modificationtime); ++ lua_pushinteger(L, sf->modificationtime); + break; + case FK_os2_version: +- lua_pushnumber(L, sf->os2_version); ++ lua_pushinteger(L, sf->os2_version); + break; + case FK_sfd_version: +- lua_pushnumber(L, sf->sfd_version); ++ lua_pushinteger(L, sf->sfd_version); + break; + case FK_math: + if (sf->MATH != NULL) { +@@ -3157,7 +3157,7 @@ static int ff_index(lua_State * L) + } + break; + case FK_extrema_bound: +- lua_pushnumber(L, sf->extrema_bound); ++ lua_pushinteger(L, sf->extrema_bound); + break; + case FK_notdef_loc: + lua_pushinteger(L, notdef_loc(sf)); +diff --git a/texk/web2c/luatexdir/luamd5/md5lib.c b/texk/web2c/luatexdir/luamd5/md5lib.c +index 40fd2de38..dfbc0865a 100644 +--- a/texk/web2c/luatexdir/luamd5/md5lib.c ++++ b/texk/web2c/luatexdir/luamd5/md5lib.c +@@ -5,7 +5,6 @@ + * @author Roberto Ierusalimschy + */ + +- + #include + #include + #include +@@ -15,22 +14,21 @@ + + #include "luamd5.h" + +- + /** + * Hash function. Returns a hash for a given string. + * @param message: arbitrary binary string. + * @return A 128-bit hash string. + */ ++ + static int lmd5 (lua_State *L) { +- char buff[16]; +- size_t l; +- const char *message = luaL_checklstring(L, 1, &l); +- md5(message, l, buff); +- lua_pushlstring(L, buff, 16L); +- return 1; ++ char buff[16]; ++ size_t l; ++ const char *message = luaL_checklstring(L, 1, &l); ++ md5(message, l, buff); ++ lua_pushlstring(L, buff, 16L); ++ return 1; + } + +- + /** + * X-Or. Does a bit-a-bit exclusive-or of two strings. + * @param s1: arbitrary binary string. +@@ -38,78 +36,73 @@ static int lmd5 (lua_State *L) { + * @return a binary string with same length as s1 and s2, + * where each bit is the exclusive-or of the corresponding bits in s1-s2. + */ ++ + static int ex_or (lua_State *L) { +- size_t l1, l2; +- const char *s1 = luaL_checklstring(L, 1, &l1); +- const char *s2 = luaL_checklstring(L, 2, &l2); +- luaL_Buffer b; +- luaL_argcheck( L, l1 == l2, 2, "lengths must be equal" ); +- luaL_buffinit(L, &b); +- while (l1--) luaL_addchar(&b, (*s1++)^(*s2++)); +- luaL_pushresult(&b); +- return 1; ++ size_t l1, l2; ++ const char *s1 = luaL_checklstring(L, 1, &l1); ++ const char *s2 = luaL_checklstring(L, 2, &l2); ++ luaL_Buffer b; ++ luaL_argcheck( L, l1 == l2, 2, "lengths must be equal" ); ++ luaL_buffinit(L, &b); ++ while (l1--) ++ luaL_addchar(&b, (*s1++)^(*s2++)); ++ luaL_pushresult(&b); ++ return 1; + } + +- + static void checkseed (lua_State *L) { +- if (lua_isnone(L, 3)) { /* no seed? */ +- time_t tm = time(NULL); /* for `random' seed */ +- lua_pushlstring(L, (char *)&tm, sizeof(tm)); +- } ++ if (lua_isnone(L, 3)) { /* no seed? */ ++ time_t tm = time(NULL); /* for `random' seed */ ++ lua_pushlstring(L, (char *)&tm, sizeof(tm)); ++ } + } + +- +-#define MAXKEY 256 +-#define BLOCKSIZE 16 +- +- ++#define MAXKEY 256 ++#define BLOCKSIZE 16 + + static int initblock (lua_State *L, const char *seed, int lseed, char *block) { +- size_t lkey; +- const char *key = luaL_checklstring(L, 2, &lkey); +- if (lkey > MAXKEY) +- luaL_error(L, "key too long (> %d)", MAXKEY); +- memset(block, 0, BLOCKSIZE); +- memcpy(block, seed, lseed); +- memcpy(block+BLOCKSIZE, key, lkey); +- return (int)lkey+BLOCKSIZE; ++ size_t lkey; ++ const char *key = luaL_checklstring(L, 2, &lkey); ++ if (lkey > MAXKEY) ++ luaL_error(L, "key too long (> %d)", MAXKEY); ++ memset(block, 0, BLOCKSIZE); ++ memcpy(block, seed, lseed); ++ memcpy(block+BLOCKSIZE, key, lkey); ++ return (int)lkey+BLOCKSIZE; + } + +- + static void codestream (lua_State *L, const char *msg, size_t lmsg, + char *block, int lblock) { +- luaL_Buffer b; +- luaL_buffinit(L, &b); +- while (lmsg > 0) { +- char code[BLOCKSIZE]; +- int i; +- md5(block, lblock, code); +- for (i=0; i 0; i++, lmsg--) +- code[i] ^= *msg++; +- luaL_addlstring(&b, code, i); +- memcpy(block, code, i); /* update seed */ +- } +- luaL_pushresult(&b); ++ luaL_Buffer b; ++ luaL_buffinit(L, &b); ++ while (lmsg > 0) { ++ char code[BLOCKSIZE]; ++ int i; ++ md5(block, lblock, code); ++ for (i=0; i 0; i++, lmsg--) ++ code[i] ^= *msg++; ++ luaL_addlstring(&b, code, i); ++ memcpy(block, code, i); /* update seed */ ++ } ++ luaL_pushresult(&b); + } + +- + static void decodestream (lua_State *L, const char *cypher, size_t lcypher, + char *block, int lblock) { +- luaL_Buffer b; +- luaL_buffinit(L, &b); +- while (lcypher > 0) { +- char code[BLOCKSIZE]; +- int i; +- md5(block, lblock, code); /* update seed */ +- for (i=0; i 0; i++, lcypher--) +- code[i] ^= *cypher++; +- luaL_addlstring(&b, code, i); +- memcpy(block, cypher-i, i); +- } +- luaL_pushresult(&b); ++ luaL_Buffer b; ++ luaL_buffinit(L, &b); ++ while (lcypher > 0) { ++ char code[BLOCKSIZE]; ++ int i; ++ md5(block, lblock, code); /* update seed */ ++ for (i=0; i 0; i++, lcypher--) ++ code[i] ^= *cypher++; ++ luaL_addlstring(&b, code, i); ++ memcpy(block, cypher-i, i); ++ } ++ luaL_pushresult(&b); + } + +- + /** + * Encrypts a string. Uses the hash function md5 in CFB (Cipher-feedback + * mode). +@@ -117,28 +110,29 @@ static void decodestream (lua_State *L, const char *cypher, size_t lcypher, + * @param key: arbitrary binary string to be used as a key. + * @param [seed]: optional arbitrary binary string to be used as a seed. + * if no seed is provided, the function uses the result of +-* time() as a seed. ++* time() as a seed. + * @return The cyphertext (as a binary string). + */ ++ + static int crypt (lua_State *L) { +- size_t lmsg; +- const char *msg = luaL_checklstring(L, 1, &lmsg); +- size_t lseed; +- const char *seed; +- int lblock; +- char block[BLOCKSIZE+MAXKEY]; +- checkseed(L); +- seed = luaL_checklstring(L, 3, &lseed); +- if (lseed > BLOCKSIZE) +- luaL_error(L, "seed too long (> %d)", BLOCKSIZE); +- /* put seed and seed length at the beginning of result */ +- block[0] = (char)lseed; +- memcpy(block+1, seed, lseed); +- lua_pushlstring(L, block, lseed+1); /* to concat with result */ +- lblock = initblock(L, seed, lseed, block); +- codestream(L, msg, lmsg, block, lblock); +- lua_concat(L, 2); +- return 1; ++ size_t lmsg; ++ const char *msg = luaL_checklstring(L, 1, &lmsg); ++ size_t lseed; ++ const char *seed; ++ int lblock; ++ char block[BLOCKSIZE+MAXKEY]; ++ checkseed(L); ++ seed = luaL_checklstring(L, 3, &lseed); ++ if (lseed > BLOCKSIZE) ++ luaL_error(L, "seed too long (> %d)", BLOCKSIZE); ++ /* put seed and seed length at the beginning of result */ ++ block[0] = (char)lseed; ++ memcpy(block+1, seed, lseed); ++ lua_pushlstring(L, block, lseed+1); /* to concat with result */ ++ lblock = initblock(L, seed, lseed, block); ++ codestream(L, msg, lmsg, block, lblock); ++ lua_concat(L, 2); ++ return 1; + } + + +@@ -151,33 +145,104 @@ static int crypt (lua_State *L) { + * @return The plaintext. + */ + static int decrypt (lua_State *L) { +- size_t lcyphertext; +- const char *cyphertext = luaL_checklstring(L, 1, &lcyphertext); +- size_t lseed = cyphertext[0]; +- const char *seed = cyphertext+1; +- int lblock; +- char block[BLOCKSIZE+MAXKEY]; +- luaL_argcheck(L, lcyphertext >= lseed+1 && lseed <= BLOCKSIZE, 1, +- "invalid cyphered string"); +- cyphertext += lseed+1; +- lcyphertext -= lseed+1; +- lblock = initblock(L, seed, lseed, block); +- decodestream(L, cyphertext, lcyphertext, block, lblock); +- return 1; ++ size_t lcyphertext; ++ const char *cyphertext = luaL_checklstring(L, 1, &lcyphertext); ++ size_t lseed = cyphertext[0]; ++ const char *seed = cyphertext+1; ++ int lblock; ++ char block[BLOCKSIZE+MAXKEY]; ++ luaL_argcheck(L, lcyphertext >= lseed+1 && lseed <= BLOCKSIZE, 1, ++ "invalid cyphered string"); ++ cyphertext += lseed+1; ++ lcyphertext -= lseed+1; ++ lblock = initblock(L, seed, lseed, block); ++ decodestream(L, cyphertext, lcyphertext, block, lblock); ++ return 1; ++} ++ ++/* not now .. doesn't compile anyway ++ ++#include "../luapplib/util/utilmd5.h" ++ ++static int pdfelib_md_5(lua_State * L) ++{ ++ if (lua_type(L,1) == LUA_TSTRING) { ++ uint8_t result[16]; ++ size_t size = 0; ++ const char *data = lua_tolstring(L,1,&size); ++ md5(data,size,result); ++ lua_pushlstring(L,(const char *)result,16); ++ return 1; ++ } ++ return 0; + } + ++*/ + + static struct luaL_Reg md5lib[] = { +- {"sum", lmd5}, +- {"exor", ex_or}, +- {"crypt", crypt}, +- {"decrypt", decrypt}, +- {NULL, NULL} ++ { "sum", lmd5}, ++ { "exor", ex_or}, ++ { "crypt", crypt}, ++ { "decrypt", decrypt}, ++ { NULL, NULL} + }; + ++int luaopen_md5(lua_State *L) { ++ luaL_openlib(L, "md5", md5lib, 0); ++ return 1; ++} ++ ++/* We could use a different file but this is as easy. */ ++ ++#include "../luapplib/util/utilsha.h" ++ ++static int sha2_256(lua_State * L) ++{ ++ if (lua_type(L,1) == LUA_TSTRING) { ++ uint8_t result[SHA256_DIGEST_LENGTH]; ++ size_t size = 0; ++ const char *data = lua_tolstring(L,1,&size); ++ sha256(data,size,result); ++ lua_pushlstring(L,(const char *)result,SHA256_DIGEST_LENGTH); ++ return 1; ++ } ++ return 0; ++} ++ ++static int sha2_384(lua_State * L) ++{ ++ if (lua_type(L,1) == LUA_TSTRING) { ++ size_t size = 0; ++ uint8_t result[SHA384_DIGEST_LENGTH]; ++ const char *data = lua_tolstring(L,1,&size); ++ sha384(data,size,result); ++ lua_pushlstring(L,(const char *)result,SHA384_DIGEST_LENGTH); ++ return 1; ++ } ++ return 0; ++} + +-int luaopen_md5 (lua_State *L) { +- luaL_openlib(L, "md5", md5lib, 0); +- return 1; ++static int sha2_512(lua_State * L) ++{ ++ if (lua_type(L,1) == LUA_TSTRING) { ++ uint8_t result[SHA512_DIGEST_LENGTH]; ++ size_t size = 0; ++ const char *data = lua_tolstring(L,1,&size); ++ sha512(data,size,result); ++ lua_pushlstring(L,(const char *)result,SHA512_DIGEST_LENGTH); ++ return 1; ++ } ++ return 0; + } + ++static struct luaL_Reg sha2lib[] = { ++ { "digest256", sha2_256 }, ++ { "digest384", sha2_384 }, ++ { "digest512", sha2_512 }, ++ { NULL, NULL} ++}; ++ ++int luaopen_sha2(lua_State *L) { ++ luaL_openlib(L, "sha2", sha2lib, 0); ++ return 1; ++} +diff --git a/texk/web2c/luatexdir/luasocket/src/lua_preload.c b/texk/web2c/luatexdir/luasocket/src/lua_preload.c +index e9433dfdb..838871c1d 100644 +--- a/texk/web2c/luatexdir/luasocket/src/lua_preload.c ++++ b/texk/web2c/luatexdir/luasocket/src/lua_preload.c +@@ -15,6 +15,7 @@ int luatex_http_lua_open(lua_State*); + int luatex_ftp_lua_open(lua_State*); + + ++extern void luatex_socketlua_open (lua_State *) ; + #include "ftp_lua.c" + #include "headers_lua.c" + #include "http_lua.c" +diff --git a/texk/web2c/luatexdir/luasocket/src/options.c b/texk/web2c/luatexdir/luasocket/src/options.c +index 20f4c2802..fabfe8ce3 100644 +--- a/texk/web2c/luatexdir/luasocket/src/options.c ++++ b/texk/web2c/luatexdir/luasocket/src/options.c +@@ -37,7 +37,7 @@ int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps) + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { +- char msg[45]; ++ char msg[57]; + sprintf(msg, "unsupported option `%.35s'", name); + luaL_argerror(L, 2, msg); + } +@@ -50,7 +50,7 @@ int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps) + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { +- char msg[45]; ++ char msg[57]; + sprintf(msg, "unsupported option `%.35s'", name); + luaL_argerror(L, 2, msg); + } +diff --git a/texk/web2c/luatexdir/luasocket/src/serial.c b/texk/web2c/luatexdir/luasocket/src/serial.c +index f121bbf0a..b666e61f9 100644 +--- a/texk/web2c/luatexdir/luasocket/src/serial.c ++++ b/texk/web2c/luatexdir/luasocket/src/serial.c +@@ -31,44 +31,44 @@ have only one object type. + /*=========================================================================*\ + * Internal function prototypes + \*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_getstats(lua_State *L); +-static int meth_setstats(lua_State *L); ++/*static int global_create(lua_State *L);*/ ++/* static int meth_send(lua_State *L); */ ++/* static int meth_receive(lua_State *L); */ ++/* static int meth_close(lua_State *L); */ ++/* static int meth_settimeout(lua_State *L); */ ++/* static int meth_getfd(lua_State *L); */ ++/* static int meth_setfd(lua_State *L); */ ++/* static int meth_dirty(lua_State *L); */ ++/* static int meth_getstats(lua_State *L); */ ++/* static int meth_setstats(lua_State *L); */ + + /* serial object methods */ +-static luaL_Reg serial_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"close", meth_close}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"getstats", meth_getstats}, +- {"setstats", meth_setstats}, +- {"receive", meth_receive}, +- {"send", meth_send}, +- {"setfd", meth_setfd}, +- {"settimeout", meth_settimeout}, +- {NULL, NULL} +-}; ++/* static luaL_Reg serial_methods[] = { */ ++/* {"__gc", meth_close}, */ ++/* {"__tostring", auxiliar_tostring}, */ ++/* {"close", meth_close}, */ ++/* {"dirty", meth_dirty}, */ ++/* {"getfd", meth_getfd}, */ ++/* {"getstats", meth_getstats}, */ ++/* {"setstats", meth_setstats}, */ ++/* {"receive", meth_receive}, */ ++/* {"send", meth_send}, */ ++/* {"setfd", meth_setfd}, */ ++/* {"settimeout", meth_settimeout}, */ ++/* {NULL, NULL} */ ++/* }; */ + + /*-------------------------------------------------------------------------*\ +-* Initializes module ++* Initializes module (luatex extension, unused ) + \*-------------------------------------------------------------------------*/ +-LUASOCKET_API int luaopen_socket_serial(lua_State *L) { +- /* create classes */ +- auxiliar_newclass(L, "serial{client}", serial_methods); +- /* create class groups */ +- auxiliar_add2group(L, "serial{client}", "serial{any}"); +- lua_pushcfunction(L, global_create); +- return 1; +-} ++/* LUASOCKET_API int luaopen_socket_serial(lua_State *L) { */ ++/* /\* create classes *\/ */ ++/* auxiliar_newclass(L, "serial{client}", serial_methods); */ ++/* /\* create class groups *\/ */ ++/* auxiliar_add2group(L, "serial{client}", "serial{any}"); */ ++/* lua_pushcfunction(L, global_create); */ ++/* return 1; */ ++/* } */ + + /*=========================================================================*\ + * Lua methods +@@ -76,67 +76,67 @@ LUASOCKET_API int luaopen_socket_serial(lua_State *L) { + /*-------------------------------------------------------------------------*\ + * Just call buffered IO methods + \*-------------------------------------------------------------------------*/ +-static int meth_send(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_send(L, &un->buf); +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_receive(L, &un->buf); +-} +- +-static int meth_getstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_getstats(L, &un->buf); +-} +- +-static int meth_setstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_setstats(L, &un->buf); +-} ++/* static int meth_send(lua_State *L) { */ ++/* p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); */ ++/* return buffer_meth_send(L, &un->buf); */ ++/* } */ ++ ++/* static int meth_receive(lua_State *L) { */ ++/* p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); */ ++/* return buffer_meth_receive(L, &un->buf); */ ++/* } */ ++ ++/* static int meth_getstats(lua_State *L) { */ ++/* p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); */ ++/* return buffer_meth_getstats(L, &un->buf); */ ++/* } */ ++ ++/* static int meth_setstats(lua_State *L) { */ ++/* p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); */ ++/* return buffer_meth_setstats(L, &un->buf); */ ++/* } */ + + /*-------------------------------------------------------------------------*\ + * Select support methods + \*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- lua_pushboolean(L, !buffer_isempty(&un->buf)); +- return 1; +-} ++/* static int meth_getfd(lua_State *L) { */ ++/* p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); */ ++/* lua_pushnumber(L, (int) un->sock); */ ++/* return 1; */ ++/* } */ ++ ++/* /\* this is very dangerous, but can be handy for those that are brave enough *\/ */ ++/* static int meth_setfd(lua_State *L) { */ ++/* p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); */ ++/* un->sock = (t_socket) luaL_checknumber(L, 2); */ ++/* return 0; */ ++/* } */ ++ ++/* static int meth_dirty(lua_State *L) { */ ++/* p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); */ ++/* lua_pushboolean(L, !buffer_isempty(&un->buf)); */ ++/* return 1; */ ++/* } */ + + /*-------------------------------------------------------------------------*\ + * Closes socket used by object + \*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} ++/* static int meth_close(lua_State *L) */ ++/* { */ ++/* p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); */ ++/* socket_destroy(&un->sock); */ ++/* lua_pushnumber(L, 1); */ ++/* return 1; */ ++/* } */ + + + /*-------------------------------------------------------------------------*\ + * Just call tm methods + \*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} ++/* static int meth_settimeout(lua_State *L) { */ ++/* p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); */ ++/* return timeout_meth_settimeout(L, &un->tm); */ ++/* } */ + + /*=========================================================================*\ + * Library functions +@@ -146,35 +146,35 @@ static int meth_settimeout(lua_State *L) { + /*-------------------------------------------------------------------------*\ + * Creates a serial object + \*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) { +- const char* path = luaL_checkstring(L, 1); +- +- /* allocate unix object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- +- /* open serial device */ +-#if defined(_WIN32) +- t_socket sock = open(path, O_RDWR); +-#else +- t_socket sock = open(path, O_NOCTTY|O_RDWR); +-#endif +- +- /*printf("open %s on %d\n", path, sock);*/ +- +- if (sock < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- lua_pushnumber(L, errno); +- return 3; +- } +- /* set its type as client object */ +- auxiliar_setclass(L, "serial{client}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +-} ++/* static int global_create(lua_State *L) { */ ++/* const char* path = luaL_checkstring(L, 1); */ ++ ++/* /\* allocate unix object *\/ */ ++/* p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); */ ++ ++/* /\* open serial device *\/ */ ++/* #if defined(_WIN32) */ ++/* t_socket sock = open(path, O_RDWR); */ ++/* #else */ ++/* t_socket sock = open(path, O_NOCTTY|O_RDWR); */ ++/* #endif */ ++ ++/* /\*printf("open %s on %d\n", path, sock);*\/ */ ++ ++/* if (sock < 0) { */ ++/* lua_pushnil(L); */ ++/* lua_pushstring(L, socket_strerror(errno)); */ ++/* lua_pushnumber(L, errno); */ ++/* return 3; */ ++/* } */ ++/* /\* set its type as client object *\/ */ ++/* auxiliar_setclass(L, "serial{client}", -1); */ ++/* /\* initialize remaining structure fields *\/ */ ++/* socket_setnonblocking(&sock); */ ++/* un->sock = sock; */ ++/* io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, */ ++/* (p_error) socket_ioerror, &un->sock); */ ++/* timeout_init(&un->tm, -1, -1); */ ++/* buffer_init(&un->buf, &un->io, &un->tm); */ ++/* return 1; */ ++/* } */ +diff --git a/texk/web2c/luatexdir/luatex.c b/texk/web2c/luatexdir/luatex.c +index 1c8694987..014d8bfd2 100644 +--- a/texk/web2c/luatexdir/luatex.c ++++ b/texk/web2c/luatexdir/luatex.c +@@ -32,9 +32,9 @@ + stick to "0" upto "9" so users can expect a number represented as string. + */ + +-int luatex_version = 107; ++int luatex_version = 109; + int luatex_revision = '0'; +-const char *luatex_version_string = "1.07.0"; ++const char *luatex_version_string = "1.09.0"; + const char *engine_name = my_name; + + #include +diff --git a/texk/web2c/luatexdir/luatex_svnversion.h b/texk/web2c/luatexdir/luatex_svnversion.h +index 770aabbeb..90d51ef70 100644 +--- a/texk/web2c/luatexdir/luatex_svnversion.h ++++ b/texk/web2c/luatexdir/luatex_svnversion.h +@@ -1 +1 @@ +-#define luatex_svn_revision 6686 ++#define luatex_svn_revision 6924 +diff --git a/texk/web2c/luatexdir/luatexcallbackids.h b/texk/web2c/luatexdir/luatexcallbackids.h +index 9cdef26c5..a345edf64 100644 +--- a/texk/web2c/luatexdir/luatexcallbackids.h ++++ b/texk/web2c/luatexdir/luatexcallbackids.h +@@ -68,8 +68,13 @@ typedef enum { + call_edit_callback, + build_page_insert_callback, + glyph_stream_provider_callback, ++ font_descriptor_objnum_provider_callback, + finish_synctex_callback, +- total_callbacks ++ wrapup_run_callback, ++ new_graf_callback, ++ page_objnum_provider_callback, ++ make_extensible_callback, ++ total_callbacks, + } callback_callback_types; + + /* lcallbacklib.c */ +@@ -96,7 +101,6 @@ extern void get_lua_boolean(const char *table, const char *name, boolean * targe + extern void get_lua_number(const char *table, const char *name, int *target); + extern void get_lua_string(const char *table, const char *name, char **target); + +-extern int lua_reader_callback(int callback_id, pointer *buffloc); + + extern char *get_lua_name(int i); + +diff --git a/texk/web2c/luatexdir/pdf/pdfgen.h b/texk/web2c/luatexdir/pdf/pdfgen.h +index 01b0e74cd..00df7f36e 100644 +--- a/texk/web2c/luatexdir/pdf/pdfgen.h ++++ b/texk/web2c/luatexdir/pdf/pdfgen.h +@@ -114,6 +114,19 @@ printing ones but the output is going to PDF buffer. Subroutines with suffix + pdf_out(pdf, '\n'); \ + } 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; ++ + extern __attribute__ ((format(printf, 2, 3))) + void pdf_printf(PDF, const char *, ...); + +@@ -125,6 +138,7 @@ extern void pdf_print_str(PDF, const char *); + extern void pdf_add_null(PDF); + extern void pdf_add_bool(PDF, int i); + extern void pdf_add_int(PDF, int i); ++extern void pdf_add_real(PDF, double d); + extern void pdf_add_longint(PDF, longinteger n); + extern void pdf_add_ref(PDF, int num); + extern void pdf_add_string(PDF, const char *s); +@@ -140,6 +154,18 @@ extern void pdf_dict_add_streaminfo(PDF); + extern void pdf_begin_stream(PDF); + extern void pdf_end_stream(PDF); + ++typedef unsigned char BYTE; ++typedef unsigned long ULONG; ++ ++typedef struct { ++ ULONG length; ++ BYTE *data; ++} pdf_obj; ++ ++extern pdf_obj *pdf_new_stream(void); ++extern void pdf_add_stream(pdf_obj * stream, unsigned char *buf, long len); ++extern void pdf_release_obj(pdf_obj * stream); ++ + extern void pdf_add_bp(PDF, scaled); + + extern strbuf_s *new_strbuf(size_t size, size_t limit); +@@ -191,8 +217,6 @@ extern char *convertStringToPDFString(const char *in, int len); + extern void initialize_start_time(PDF); + extern char *getcreationdate(PDF); + +-extern void check_o_mode(PDF pdf, const char *s, int o_mode, boolean errorflag); +- + extern void set_job_id(PDF, int, int, int, int); + extern char *get_resname_prefix(PDF); + extern void pdf_begin_page(PDF pdf); +@@ -201,6 +225,10 @@ extern void print_pdf_table_string(PDF pdf, const char *s); + extern const char *get_pdf_table_string(const char *s); + extern int get_pdf_table_bool(PDF, const char *, int); + ++extern void pdf_open_file(PDF pdf); ++extern void pdf_write_header(PDF pdf); ++extern void pdf_finish_file(PDF pdf, int fatal_error); ++ + extern void ensure_output_state(PDF pdf, output_state s); + extern PDF init_pdf_struct(PDF pdf); + +@@ -210,8 +238,18 @@ extern halfword pdf_catalog_openaction; + extern halfword pdf_names_toks; /* additional keys of Names dictionary */ + extern halfword pdf_trailer_toks; /* additional keys of Trailer dictionary */ + extern void scan_pdfcatalog(PDF pdf); +-extern void finish_pdf_file(PDF pdf, int luatex_version, str_number luatex_revision); + + extern shipping_mode_e global_shipping_mode; + ++extern void pdf_push_list(PDF pdf, scaledpos *saved_pos, int *saved_loc); ++extern void pdf_pop_list(PDF pdf, scaledpos *saved_pos, int *saved_loc); ++ ++extern void pdf_set_reference_point(PDF pdf, posstructure *refpoint); ++ ++/* not pdf specific */ ++ ++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); ++ ++ + #endif +diff --git a/texk/web2c/luatexdir/pdf/pdflistout.h b/texk/web2c/luatexdir/pdf/pdflistout.h +index 76c1d552f..dd3775816 100644 +--- a/texk/web2c/luatexdir/pdf/pdflistout.h ++++ b/texk/web2c/luatexdir/pdf/pdflistout.h +@@ -21,28 +21,12 @@ + #ifndef PDFLISTOUT_H + # define PDFLISTOUT_H + +-# define pos_right(A) pdf->posstruct->pos.h = pdf->posstruct->pos.h + (A) +-# define pos_left(A) pdf->posstruct->pos.h = pdf->posstruct->pos.h - (A) +-# define pos_up(A) pdf->posstruct->pos.v = pdf->posstruct->pos.v + (A) +-# define pos_down(A) pdf->posstruct->pos.v = pdf->posstruct->pos.v - (A) +- +-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_struct; +- +-extern pos_info_structure pos_info; +- +-extern backend_function *backend_out; +-extern backend_function *backend_out_whatsit; +- +-extern void init_backend_functionpointers(output_mode o_mode); ++#define pos_right(A) pdf->posstruct->pos.h = pdf->posstruct->pos.h + (A) ++#define pos_left(A) pdf->posstruct->pos.h = pdf->posstruct->pos.h - (A) ++#define pos_up(A) pdf->posstruct->pos.v = pdf->posstruct->pos.v + (A) ++#define pos_down(A) pdf->posstruct->pos.v = pdf->posstruct->pos.v - (A) + + extern void hlist_out(PDF pdf, halfword this_box, int rule_callback_id); + extern void vlist_out(PDF pdf, halfword this_box, int rule_callback_id); +-extern void out_what(PDF pdf, halfword p); + + #endif +diff --git a/texk/web2c/luatexdir/pdf/pdfobj.h b/texk/web2c/luatexdir/pdf/pdfobj.h +index f8fac35ee..f6e49450b 100644 +--- a/texk/web2c/luatexdir/pdf/pdfobj.h ++++ b/texk/web2c/luatexdir/pdf/pdfobj.h +@@ -35,6 +35,7 @@ + + # define OBJ_FLAG_ISSTREAM (1 << 0) + # define OBJ_FLAG_ISFILE (1 << 1) ++# define OBJ_FLAG_NOLENGTH (1 << 2) + + # define obj_obj_is_stream(pdf,A) ((obj_obj_flags((pdf), (A)) & OBJ_FLAG_ISSTREAM) != 0) + # define set_obj_obj_is_stream(pdf,A) ((obj_obj_flags((pdf), (A)) |= OBJ_FLAG_ISSTREAM)) +@@ -44,6 +45,10 @@ + # define set_obj_obj_is_file(pdf,A) ((obj_obj_flags((pdf), (A)) |= OBJ_FLAG_ISFILE)) + # define unset_obj_obj_is_file(pdf,A) ((obj_obj_flags((pdf), (A)) &= ~OBJ_FLAG_ISFILE)) + ++# define obj_obj_no_length(pdf,A) ((obj_obj_flags((pdf), (A)) & OBJ_FLAG_NOLENGTH) != 0) ++# define set_obj_obj_no_length(pdf,A) ((obj_obj_flags((pdf), (A)) |= OBJ_FLAG_NOLENGTH)) ++# define unset_obj_obj_no_length(pdf,A) ((obj_obj_flags((pdf), (A)) &= ~OBJ_FLAG_NOLENGTH)) ++ + extern void init_obj_obj(PDF pdf, int k); + extern void pdf_write_obj(PDF pdf, int n); + extern void scan_obj(PDF pdf); +diff --git a/texk/web2c/luatexdir/pdf/pdftables.h b/texk/web2c/luatexdir/pdf/pdftables.h +index e29c5603d..8aa7403c1 100644 +--- a/texk/web2c/luatexdir/pdf/pdftables.h ++++ b/texk/web2c/luatexdir/pdf/pdftables.h +@@ -139,6 +139,8 @@ typedef enum { + c_pdf_pk_fixed_dpi, + c_pdf_suppress_optional_info, + c_pdf_omit_cidset, ++ c_pdf_recompress, ++ c_pdf_omit_charset, + } pdf_backend_counters ; + + typedef enum { +@@ -187,6 +189,8 @@ extern int pdf_cur_form; + # define pdf_pk_fixed_dpi get_tex_extension_count_register(c_pdf_pk_fixed_dpi) + # define pdf_suppress_optional_info get_tex_extension_count_register(c_pdf_suppress_optional_info) + # define pdf_omit_cidset get_tex_extension_count_register(c_pdf_omit_cidset) ++# define pdf_omit_charset get_tex_extension_count_register(c_pdf_omit_charset) ++# define pdf_recompress get_tex_extension_count_register(c_pdf_recompress) + + # define pdf_h_origin get_tex_extension_dimen_register(d_pdf_h_origin) + # define pdf_v_origin get_tex_extension_dimen_register(d_pdf_v_origin) +@@ -208,7 +212,9 @@ extern int pdf_cur_form; + # define set_pdf_compress_level(i) set_tex_extension_count_register(c_pdf_compress_level,i) + # define set_pdf_obj_compress_level(i) set_tex_extension_count_register(c_pdf_obj_compress_level,i) + # define set_pdf_omit_cidset(i) set_tex_extension_count_register(c_pdf_omit_cidset,i) ++# define set_pdf_omit_charset(i) set_tex_extension_count_register(c_pdf_omit_charset,i) + # define set_pdf_gen_tounicode(i) set_tex_extension_count_register(c_pdf_gen_tounicode,i) ++# define set_pdf_recompress(i) set_tex_extension_count_register(c_pdf_recompress,i) + + # define set_pdf_decimal_digits(i) set_tex_extension_count_register(c_pdf_decimal_digits,i) + # define set_pdf_pk_resolution(i) set_tex_extension_count_register(c_pdf_pk_resolution,i) +diff --git a/texk/web2c/luatexdir/pdf/pdftypes.h b/texk/web2c/luatexdir/pdf/pdftypes.h +index c5f0393cc..ef6a31e16 100644 +--- a/texk/web2c/luatexdir/pdf/pdftypes.h ++++ b/texk/web2c/luatexdir/pdf/pdftypes.h +@@ -90,10 +90,7 @@ typedef struct { + typedef struct scaledpos_ { + int64_t h; + int64_t v; +- } scaledpos; +- +- +- ++} scaledpos; + + typedef struct scaled_whd_ { + scaled wd; /* TeX width */ +@@ -106,11 +103,11 @@ typedef struct posstructure_ { + int dir; /* direction of stuff to be put onto the page */ + } posstructure; + +-typedef struct { +- scaledpos curpos; /* \pdflastpos position */ +- posstructure boxpos; /* box dir and position of the box origin on the page */ +- scaled_whd boxdim; /* box dimensions (in hlist/vlist coordinate system) */ +-} pos_info_structure; ++/* typedef struct { */ ++/* scaledpos curpos; */ /* \pdflastpos position */ ++/* posstructure boxpos; */ /* box dir and position of the box origin on the page */ ++/* scaled_whd boxdim; */ /* box dimensions (in hlist/vlist coordinate system) */ ++/* } pos_info_structure; */ + + typedef enum { + PMODE_NONE, +@@ -159,6 +156,10 @@ typedef struct { + int need_tf; /* flag whether Tf needs to be set */ + int need_tm; /* flag whether Tm needs to be set */ + int cur_ex; /* the current glyph ex factor */ ++ int need_width; ++ int need_mode; ++ int done_width; ++ int done_mode; + } pdfstructure; + + typedef struct obj_entry_ { +@@ -292,9 +293,11 @@ typedef struct pdf_output_file_ { + int decimal_digits; + int gen_tounicode; + int omit_cidset; ++ int omit_charset; + int inclusion_copy_font; + int major_version; /* fixed major part of the PDF version */ + int minor_version; /* fixed minor part of the PDF version */ ++ int recompress; + int compress_level; /* level for zlib object stream compression */ + int objcompresslevel; /* fixed level for activating PDF object streams */ + char *job_id_string; /* the full job string */ +@@ -351,6 +354,8 @@ typedef struct pdf_output_file_ { + int xform_count; + int ximage_count; + ++ int force_file; ++ + pdf_resource_struct *page_resources; + + scaledpos page_size; /* width and height of page being shipped */ +diff --git a/texk/web2c/luatexdir/ptexlib.h b/texk/web2c/luatexdir/ptexlib.h +index 21bb9348c..f63f480b0 100644 +--- a/texk/web2c/luatexdir/ptexlib.h ++++ b/texk/web2c/luatexdir/ptexlib.h +@@ -153,7 +153,8 @@ size_t T##_limit + # include "tex/expand.h" + # include "tex/conditional.h" + +-# include "pdf/pdftypes.h" /* the backend data structure, shared between dvi and pdf */ ++# include "pdf/pdftypes.h" /* the backend data structure, shared between dvi and pdf (might move to |tex/backend| */ ++# include "tex/backend.h" /* more backend data */ + + # include "synctex.h" + +@@ -254,7 +255,7 @@ int lua_appendtovlist_callback( + halfword box, int location, halfword prev_depth, boolean is_mirrored, + halfword * result, int * next_depth, boolean * prev_set); + +-void lua_pdf_literal(PDF pdf, int i); ++void lua_pdf_literal(PDF pdf, int i, int noline); + void copy_pdf_literal(pointer r, pointer p); + void free_pdf_literal(pointer p); + void show_pdf_literal(pointer p); +@@ -277,7 +278,7 @@ void undump_luac_registers(void); + void luacstring_start(int n); + void luacstring_close(int n); + int luacstring_cattable(void); +-int luacstring_input(void); ++int luacstring_input(halfword *n); + int luacstring_partial(void); + int luacstring_final_line(void); + +@@ -293,6 +294,7 @@ void flush_loggable_info(void); + + /* lua/luastuff.w */ + void luafunctioncall(int slot); ++void luabytecodecall(int slot); + + /* lua/luastuff.c */ + void luatokencall(int p, int nameptr); +diff --git a/texk/web2c/luatexdir/tex/commands.h b/texk/web2c/luatexdir/tex/commands.h +index 8eabaa4e7..c3a3ca304 100644 +--- a/texk/web2c/luatexdir/tex/commands.h ++++ b/texk/web2c/luatexdir/tex/commands.h +@@ -89,6 +89,7 @@ typedef enum { + char_num_cmd, /* character specified numerically ( \.{\\char} ) */ + math_char_num_cmd, /* explicit math code ( \.{\\mathchar} ) */ + mark_cmd, /* mark definition ( \.{\\mark} ) */ ++ node_cmd, + xray_cmd, /* peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~) */ + make_box_cmd, /* make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~) */ + hmove_cmd, /* horizontal motion ( \.{\\moveleft}, \.{\\moveright} ) */ +@@ -134,6 +135,9 @@ typedef enum { + normal_cmd, /* general extensions to \TeX\ that don't fit into a category */ + extension_cmd, /* extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~) */ + option_cmd, ++ lua_function_call_cmd, ++ lua_bytecode_call_cmd, ++ lua_call_cmd, + in_stream_cmd, /* files for reading ( \.{\\openin}, \.{\\closein} ) */ + begin_group_cmd, /* begin local grouping ( \.{\\begingroup} ) */ + end_group_cmd, /* end local grouping ( \.{\\endgroup} ) */ +@@ -179,7 +183,9 @@ typedef enum { + set_font_cmd, /* set current font ( font identifiers ) */ + def_font_cmd, /* define a font file ( \.{\\font} ) */ + register_cmd, /* internal register ( \.{\\count}, \.{\\dimen}, etc.~) */ ++ assign_box_direction_cmd, /* (\.{\\boxdirection}) */ + assign_box_dir_cmd, /* (\.{\\boxdir}) */ ++ assign_direction_cmd, /* (\.{\\pagedirection}, \.{\\textdirection}) */ + assign_dir_cmd, /* (\.{\\pagedir}, \.{\\textdir}) */ + # define max_internal_cmd assign_dir_cmd /* the largest code that can follow \.{\\the} */ + advance_cmd, /* advance a register or parameter ( \.{\\advance} ) */ +@@ -188,6 +194,7 @@ typedef enum { + prefix_cmd, /* qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} ) */ + let_cmd, /* assign a command code ( \.{\\let}, \.{\\futurelet} ) */ + shorthand_def_cmd, /* code definition ( \.{\\chardef}, \.{\\countdef}, etc.~) */ ++ def_lua_call_cmd, + read_to_cs_cmd, /* read into a control sequence ( \.{\\read} ) */ + def_cmd, /* macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} ) */ + set_box_cmd, /* set a box ( \.{\\setbox} ) */ +@@ -201,6 +208,8 @@ typedef enum { + expand_after_cmd, /* special expansion ( \.{\\expandafter} ) */ + no_expand_cmd, /* special nonexpansion ( \.{\\noexpand} ) */ + input_cmd, /* input a source file ( \.{\\input}, \.{\\endinput} or \.{\\scantokens} or \.{\\scantextokens} ) */ ++ lua_expandable_call_cmd, ++ lua_local_call_cmd, + if_test_cmd, /* conditional text ( \.{\\if}, \.{\\ifcase}, etc.~) */ + fi_or_else_cmd, /* delimiters for conditionals ( \.{\\else}, etc.~) */ + cs_name_cmd, /* make a control sequence from tokens ( \.{\\csname} ) */ +@@ -236,9 +245,12 @@ typedef enum { + + typedef enum { + number_code = 0, /* command code for \.{\\number} */ +- lua_function_code, /* command code for \.{\\luafunction} */ + lua_code, /* command code for \.{\\directlua} */ ++ lua_function_code, /* command code for \.{\\luafunction} */ ++ lua_bytecode_code, /* command code for \.{\\luabytecode} */ + expanded_code, /* command code for \.{\\expanded} */ ++ immediate_assignment_code, /* command code for \.{\\immediateassignment} */ ++ immediate_assigned_code, /* command code for \.{\\assigned} */ + math_style_code, /* command code for \.{\\mathstyle} */ + string_code, /* command code for \.{\\string} */ + cs_string_code, /* command code for \.{\\csstring} */ +@@ -324,6 +336,7 @@ typedef enum { + set_random_seed_code, + save_pos_code, + late_lua_code, ++ late_lua_call_code, + expand_font_code, + } normal_codes; + +diff --git a/texk/web2c/luatexdir/tex/conditional.h b/texk/web2c/luatexdir/tex/conditional.h +index b6abe7578..483496eec 100644 +--- a/texk/web2c/luatexdir/tex/conditional.h ++++ b/texk/web2c/luatexdir/tex/conditional.h +@@ -24,30 +24,31 @@ + # define unless_code 32 /* amount added for `\.{\\unless}' prefix */ + + typedef enum { +- if_char_code = 0, /* \.{\\if} */ +- if_cat_code = 1, /* \.{\\ifcat} */ +- if_int_code = 2, /* \.{\\ifnum} */ +- if_dim_code = 3, /* \.{\\ifdim} */ +- if_odd_code = 4, /* \.{\\ifodd} */ +- if_vmode_code = 5, /* \.{\\ifvmode} */ +- if_hmode_code = 6, /* \.{\\ifhmode} */ +- if_mmode_code = 7, /* \.{\\ifmmode} */ +- if_inner_code = 8, /* \.{\\ifinner} */ +- if_void_code = 9, /* \.{\\ifvoid} */ +- if_hbox_code = 10, /* \.{\\ifhbox} */ +- if_vbox_code = 11, /* \.{\\ifvbox} */ +- ifx_code = 12, /* \.{\\ifx} */ +- if_eof_code = 13, /* \.{\\ifeof} */ +- if_true_code = 14, /* \.{\\iftrue} */ +- if_false_code = 15, /* \.{\\iffalse} */ +- if_case_code = 16, /* \.{\\ifcase} */ +- if_def_code = 17, /* \.{\\ifdefined} */ +- if_cs_code = 18, /* \.{\\ifcsname} */ +- if_font_char_code = 19, /* \.{\\iffontchar} */ +- if_in_csname_code = 20, /* \.{\\ifincsname} */ +- if_primitive_code = 21, /* \.{\\ifprimitive} */ +- if_abs_num_code = 22, /* \.{\\ifabsnum} */ +- if_abs_dim_code = 23, /* \.{\\ifabsdim} */ ++ if_char_code = 0, /* \.{\\if} */ ++ if_cat_code = 1, /* \.{\\ifcat} */ ++ if_int_code = 2, /* \.{\\ifnum} */ ++ if_dim_code = 3, /* \.{\\ifdim} */ ++ if_odd_code = 4, /* \.{\\ifodd} */ ++ if_vmode_code = 5, /* \.{\\ifvmode} */ ++ if_hmode_code = 6, /* \.{\\ifhmode} */ ++ if_mmode_code = 7, /* \.{\\ifmmode} */ ++ if_inner_code = 8, /* \.{\\ifinner} */ ++ if_void_code = 9, /* \.{\\ifvoid} */ ++ if_hbox_code = 10, /* \.{\\ifhbox} */ ++ if_vbox_code = 11, /* \.{\\ifvbox} */ ++ if_x_code = 12, /* \.{\\ifx} */ ++ if_eof_code = 13, /* \.{\\ifeof} */ ++ if_true_code = 14, /* \.{\\iftrue} */ ++ if_false_code = 15, /* \.{\\iffalse} */ ++ if_case_code = 16, /* \.{\\ifcase} */ ++ if_def_code = 17, /* \.{\\ifdefined} */ ++ if_cs_code = 18, /* \.{\\ifcsname} */ ++ if_font_char_code = 19, /* \.{\\iffontchar} */ ++ if_in_csname_code = 20, /* \.{\\ifincsname} */ ++ if_primitive_code = 21, /* \.{\\ifprimitive} */ ++ if_abs_num_code = 22, /* \.{\\ifabsnum} */ ++ if_abs_dim_code = 23, /* \.{\\ifabsdim} */ ++ if_condition_code = 24, /* \.{\\ifcondition} */ + } if_type_codes; + + # define if_limit_subtype(A) subtype((A)+1) +diff --git a/texk/web2c/luatexdir/tex/directions.h b/texk/web2c/luatexdir/tex/directions.h +index 7ab524a1e..92d6cc18f 100644 +--- a/texk/web2c/luatexdir/tex/directions.h ++++ b/texk/web2c/luatexdir/tex/directions.h +@@ -22,43 +22,19 @@ + # define DIRECTIONS_H + + /* +- # define dir_TLT 0 +- # define dir_TRT 4 +- # define dir_LTL 9 +- # define dir_RTT 24 +- +- extern const char *dir_strings[128]; ++#define dir_swap 4 + */ + +-extern const char *dir_strings[8]; +- +-extern int dir_swap; +- +-/* +-# define RETURN_DIR_VALUES(a) \ +- if (s==luaS_##a##_ptr) { \ +- return (dir_##a); \ +- } else if (!absolute_only) { \ +- if (s==luaS_p##a##_ptr) \ +- return (dir_##a); \ +- else if (s==luaS_m##a##_ptr) \ +- return ((dir_##a)-4); \ +- } +-*/ ++#define dir_min_value 0 ++#define dir_max_value 3 + +-# define RETURN_DIR_VALUES(a) \ +- if (s==lua_key(a)) { \ +- return (dir_##a); \ +- } else if (!absolute_only) { \ +- if (s==lua_key_plus(a)) \ +- return (dir_##a); \ +- else if (s==lua_key_minus(a)) \ +- return ((dir_##a)-4); \ +- } ++#define check_dir_value(d) \ ++ if ((d < dir_min_value) || (d > dir_max_value)) \ ++ d = dir_min_value; + +-# define is_mirrored(a) 0 ++#define is_mirrored(a) 0 + +-# define is_rotated(a) (a == dir_RTT) ++#define is_rotated(a) (a == dir_RTT) + + /* + +@@ -90,10 +66,12 @@ extern int dir_swap; + (a == dir_RTT && b == dir_TRT) \ + ) + ++ # define dir_TLT_or_TRT(a) (a == dir_TLT || a == dir_TRT) ++ # define dir_LTL_or_RTT(a) (a == dir_LTL || a == dir_RTT) ++ + */ + +-/* # define dir_TLT_or_TRT(a) (a == dir_TLT || a == dir_TRT) */ +-/* # define dir_LTL_or_RTT(a) (a == dir_LTL || a == dir_RTT) */ ++/* TLT TRT LTL RTT */ + + # define dir_TLT_or_TRT(a) (a < 2) + # define dir_LTL_or_RTT(a) (a > 1) +@@ -174,7 +152,8 @@ extern void initialize_directions(void); + extern halfword new_dir(int s); + + extern const char *string_dir(int d); +-extern void print_dir(int d); ++extern void print_dir_par(int d); ++extern void print_dir_text(halfword d); + + extern void scan_direction(void); + +diff --git a/texk/web2c/luatexdir/tex/equivalents.h b/texk/web2c/luatexdir/tex/equivalents.h +index f478b4a41..3ff9ac16d 100644 +--- a/texk/web2c/luatexdir/tex/equivalents.h ++++ b/texk/web2c/luatexdir/tex/equivalents.h +@@ -38,10 +38,10 @@ distinction. + # define biggest_reg 65535 /* the largest allowed register number; must be |< max_quarterword| */ + # define number_regs 65536 /* |biggest_reg+1| */ + # define number_attrs 65536 /* total numbeer of attributes */ +-# define biggest_char 1114111 /* the largest allowed character number; must be |< max_halfword| */ +-# define too_big_char 1114112 /* |biggest_char+1| */ +-# define special_char 1114113 /* |biggest_char+2| */ +-# define number_chars 1114112 /* |biggest_char+1| */ ++# define biggest_char 1114111 /* 0x10FFFF, the largest allowed character number; must be |< max_halfword| */ ++# define too_big_char (biggest_char+1) /* 1114112, |biggest_char+1| */ ++# define special_char (biggest_char+2) /* 1114113, |biggest_char+2| */ ++# define number_chars (biggest_char+3) /* 1114112, |biggest_char+1| */ + # define number_fonts (5535-font_base+1) + # define biggest_lang 32767 + # define too_big_lang 32768 +@@ -292,15 +292,22 @@ the |number_regs| \.{\\dimen} registers. + # define automatic_hyphen_penalty_code 101 + # define explicit_hyphen_penalty_code 102 + # define automatic_hyphen_mode_code 103 +-# define break_after_dir_mode_code 104 +- +-# define pre_bin_op_penalty_code 105 +-# define pre_rel_penalty_code 106 +-# define math_penalties_mode_code 107 +-# define math_delimiters_mode_code 108 +-# define math_script_box_mode_code 109 +- +-# define suppress_primitive_error_code 110 ++# define compound_hyphen_mode_code 104 ++# define break_after_dir_mode_code 105 ++# define exception_penalty_code 106 ++ ++# define pre_bin_op_penalty_code 107 ++# define pre_rel_penalty_code 108 ++# define math_penalties_mode_code 109 ++# define math_delimiters_mode_code 110 ++# define math_script_box_mode_code 111 ++# define math_script_char_mode_code 112 ++# define math_rule_thickness_mode_code 113 ++# define math_flatten_mode_code 114 ++ ++# define copy_lua_input_nodes_code 115 ++# define suppress_primitive_error_code 116 ++# define fixup_boxes_code 117 + + # define math_option_code (suppress_primitive_error_code+1) + +@@ -441,7 +448,8 @@ We use the notation |saved(k)| to stand for an item that appears in location + # define saved_boxspec 14 + # define saved_boxdir 15 + # define saved_boxattr 16 +-# define saved_boxpack 18 ++# define saved_boxpack 17 ++# define saved_attrlist 18 + # define saved_eqtb 19 + + extern void print_save_stack(void); +@@ -453,10 +461,9 @@ extern void print_save_stack(void); + + typedef enum { + c_mathoption_old_code = 0, /* this one is stable */ +- c_mathoption_no_italic_compensation_code, /* just for tracing, can change */ +- c_mathoption_no_char_italic_code, /* just for tracing, can change */ +- c_mathoption_use_old_fraction_scaling_code, /* just for tracing, can change */ +- c_mathoption_umathcode_meaning_code, /* this one is stable */ ++ /* ++ c_mathoption_umathcode_meaning_code, ++ */ + } math_option_codes ; + + # define mathoption_int_par(A) eqtb[mathoption_int_base+(A)].cint +@@ -525,7 +532,7 @@ typedef enum { + cramped_script_script_style, /* |subtype| for \.{\\crampedscriptscriptstyle} */ + } math_style_subtypes; + +-typedef enum { ++typedef enum { /* this could move to directions.h */ + dir_TLT = 0, + dir_TRT, + dir_LTL, +@@ -663,6 +670,9 @@ extern halfword last_cs_name; + #define math_penalties_mode_par int_par(math_penalties_mode_code) + #define math_delimiters_mode_par int_par(math_delimiters_mode_code) + #define math_script_box_mode_par int_par(math_script_box_mode_code) ++#define math_script_char_mode_par int_par(math_script_char_mode_code) ++#define math_rule_thickness_mode_par int_par(math_rule_thickness_mode_code) ++#define math_flatten_mode_par int_par(math_flatten_mode_code) + #define null_delimiter_space_par dimen_par(null_delimiter_space_code) + #define disable_lig_par int_par(disable_lig_code) + #define disable_kern_par int_par(disable_kern_code) +@@ -757,12 +767,13 @@ extern halfword last_cs_name; + #define suppress_ifcsname_error_par int_par(suppress_ifcsname_error_code) + #define suppress_primitive_error_par int_par(suppress_primitive_error_code) + #define error_context_lines_par int_par(error_context_lines_code) ++#define copy_lua_input_nodes_par int_par(copy_lua_input_nodes_code) + + #define math_old_par mathoption_int_par(c_mathoption_old_code) +-#define math_no_italic_compensation_par mathoption_int_par(c_mathoption_no_italic_compensation_code) +-#define math_no_char_italic_par mathoption_int_par(c_mathoption_no_char_italic_code) +-#define math_use_old_fraction_scaling_par mathoption_int_par(c_mathoption_use_old_fraction_scaling_code) ++ ++/* + #define math_umathcode_meaning_par mathoption_int_par(c_mathoption_umathcode_meaning_code) ++*/ + + #define math_pre_display_gap_factor_par int_par(math_pre_display_gap_factor_code) + +@@ -790,11 +801,15 @@ extern halfword last_cs_name; + #define automatic_hyphen_penalty_par int_par(automatic_hyphen_penalty_code) + #define explicit_hyphen_penalty_par int_par(explicit_hyphen_penalty_code) + #define automatic_hyphen_mode_par int_par(automatic_hyphen_mode_code) ++#define compound_hyphen_mode_par int_par(compound_hyphen_mode_code) + #define break_after_dir_mode_par int_par(break_after_dir_mode_code) ++#define exception_penalty_par int_par(exception_penalty_code) + + #define cur_lang_par int_par(cur_lang_code) + #define cur_font_par equiv(cur_font_loc) + ++#define fixup_boxes_par int_par(fixup_boxes_code) ++ + /* */ + + #define math_use_current_family_code 7 +diff --git a/texk/web2c/luatexdir/tex/extensions.h b/texk/web2c/luatexdir/tex/extensions.h +index b68395344..5e22ddf26 100644 +--- a/texk/web2c/luatexdir/tex/extensions.h ++++ b/texk/web2c/luatexdir/tex/extensions.h +@@ -136,9 +136,12 @@ typedef enum { + use_box_resource_code, + save_image_resource_code, + use_image_resource_code, ++ end_local_code, + /* backend */ + dvi_extension_code, + pdf_extension_code, + } extension_codes ; + ++extern void wrapup_leader(halfword p); ++ + #endif +diff --git a/texk/web2c/luatexdir/tex/inputstack.h b/texk/web2c/luatexdir/tex/inputstack.h +index 378fabe24..8c139a833 100644 +--- a/texk/web2c/luatexdir/tex/inputstack.h ++++ b/texk/web2c/luatexdir/tex/inputstack.h +@@ -306,6 +306,7 @@ typedef enum { + mark_text = 14, /* |token_type| code for \.{\\topmark}, etc. */ + every_eof_text = 15, /* |token_type| code for \.{\\everyeof} */ + write_text = 16, /* |token_type| code for \.{\\write} */ ++ local_text = 17, /* |token_type| code for special purposed */ + } token_types; + + extern pointer *param_stack; +diff --git a/texk/web2c/luatexdir/tex/linebreak.h b/texk/web2c/luatexdir/tex/linebreak.h +index 1f4927831..d1e1768f7 100644 +--- a/texk/web2c/luatexdir/tex/linebreak.h ++++ b/texk/web2c/luatexdir/tex/linebreak.h +@@ -20,9 +20,6 @@ + #ifndef LINEBREAK_H + # define LINEBREAK_H + +-# define left_side 0 +-# define right_side 1 +- + extern halfword just_box; /* the |hlist_node| for the last line of the new paragraph */ + + extern void line_break(boolean d, int line_break_context); +diff --git a/texk/web2c/luatexdir/tex/mainbody.h b/texk/web2c/luatexdir/tex/mainbody.h +index 4a5cb93a1..245535120 100644 +--- a/texk/web2c/luatexdir/tex/mainbody.h ++++ b/texk/web2c/luatexdir/tex/mainbody.h +@@ -139,6 +139,9 @@ extern int filelineerrorstylep; + extern int haltonerrorp; + extern boolean quoted_filename; + ++extern int total_pages; ++extern int dead_cycles; ++ + /* + In order to make efficient use of storage space, \TeX\ bases its major data + structures on a |memory_word|, which contains either a (signed) integer, +diff --git a/texk/web2c/luatexdir/tex/maincontrol.h b/texk/web2c/luatexdir/tex/maincontrol.h +index 13e3c6ff3..04608c11e 100644 +--- a/texk/web2c/luatexdir/tex/maincontrol.h ++++ b/texk/web2c/luatexdir/tex/maincontrol.h +@@ -151,6 +151,7 @@ extern void do_endv(void); + extern void cs_error(void); + extern void prefixed_command(void); + extern void fixup_directions(void); ++extern void fixup_directions_only(void); + + + /* Assignments from Lua need helpers. */ +@@ -205,5 +206,12 @@ extern void show_whatever(void); + + extern void initialize(void); /* this procedure gets things started properly */ + ++/*extern int local_level;*/ ++ ++extern void local_control(void); ++extern halfword local_scan_box(void); ++extern int current_local_level(void); ++extern void end_local_control(void); ++extern void local_control_message(const char *s); + + #endif +diff --git a/texk/web2c/luatexdir/tex/mlist.h b/texk/web2c/luatexdir/tex/mlist.h +index bd1562122..1a15afe3c 100644 +--- a/texk/web2c/luatexdir/tex/mlist.h ++++ b/texk/web2c/luatexdir/tex/mlist.h +@@ -31,5 +31,6 @@ extern void fixup_math_parameters(int fam_id, int size_id, int f, int lvl); + extern scaled get_math_quad_style(int a); + extern scaled get_math_quad_size(int a); + ++extern pointer make_extensible(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att); + + #endif +diff --git a/texk/web2c/luatexdir/tex/packaging.h b/texk/web2c/luatexdir/tex/packaging.h +index 6a0802e46..9353afeaf 100644 +--- a/texk/web2c/luatexdir/tex/packaging.h ++++ b/texk/web2c/luatexdir/tex/packaging.h +@@ -143,7 +143,8 @@ latter two are used denote \.{\\vbox} and \.{\\hbox}, respectively. + # define global_box_flag (box_flag+number_regs) /* context code for `\.{\\global\\setbox0}' */ + # define max_global_box_flag (global_box_flag+number_regs) + # define ship_out_flag (max_global_box_flag+1) /* context code for `\.{\\shipout}' */ +-# define leader_flag ship_out_flag+1 /* context code for `\.{\\leaders}' */ ++# define lua_scan_flag (max_global_box_flag+2) /* context code for |scan_list| */ ++# define leader_flag (max_global_box_flag+3) /* context code for `\.{\\leaders}' */ + + extern void begin_box(int box_context); + +diff --git a/texk/web2c/luatexdir/tex/printing.h b/texk/web2c/luatexdir/tex/printing.h +index 1619c6072..79ca524ef 100644 +--- a/texk/web2c/luatexdir/tex/printing.h ++++ b/texk/web2c/luatexdir/tex/printing.h +@@ -77,7 +77,7 @@ extern void print_esc(str_number s); + extern void print_the_digs(eight_bits k); + extern void print_int(longinteger n); + extern void print_two(int n); +-extern void print_hex(int n); ++extern void print_qhex(int n); + extern void print_roman_int(int n); + extern void print_current_string(void); + +diff --git a/texk/web2c/luatexdir/tex/scanning.h b/texk/web2c/luatexdir/tex/scanning.h +index f5f620c67..e4189a0f6 100644 +--- a/texk/web2c/luatexdir/tex/scanning.h ++++ b/texk/web2c/luatexdir/tex/scanning.h +@@ -63,9 +63,13 @@ extern void scan_fifty_one_bit_int(void); + # define hex_token (other_token+'"') /* double quote, indicates a hex constant */ + # define alpha_token (other_token+'`') /* reverse apostrophe, precedes alpha constants */ + # define point_token (other_token+'.') /* decimal point */ ++# define comma_token (other_token+',') /* decimal comma */ ++# define plus_token (other_token + '+') ++# define minus_token (other_token + '-') + # define continental_point_token (other_token+',') /* decimal point, Eurostyle */ + # define infinity 017777777777 /* the largest positive value that \TeX\ knows */ + # define zero_token (other_token+'0') /* zero, the smallest digit */ ++# define nine_token (other_token+'9') /* zero, the smallest digit */ + # define A_token (letter_token+'A') /* the smallest special hex digit */ + # define other_A_token (other_token+'A') /* special hex digit of type |other_char| */ + extern int radix; +@@ -78,7 +82,6 @@ extern int cur_order; + + extern void scan_dimen(boolean mu, boolean inf, boolean shortcut); + extern void scan_glue(int level); +-extern void scan_scaled(void); + + extern halfword the_toks(void); + extern str_number the_scanned_result(void); +diff --git a/texk/web2c/luatexdir/tex/stringpool.h b/texk/web2c/luatexdir/tex/stringpool.h +index a969547e7..2ba156046 100644 +--- a/texk/web2c/luatexdir/tex/stringpool.h ++++ b/texk/web2c/luatexdir/tex/stringpool.h +@@ -1,5 +1,5 @@ + /* stringpool.h +- ++ + Copyright 2009 Taco Hoekwater + + This file is part of LuaTeX. +@@ -43,9 +43,10 @@ extern str_number init_str_ptr; + + # define get_nullstr() STRING_OFFSET + +-# define biggest_char 1114111 +-# define number_chars 1114112 +-# define special_char 1114113 /* |biggest_char+2| */ ++# define biggest_char 1114111 /* 0x10FFFF, the largest allowed character number; must be |< max_halfword| */ ++# define too_big_char (biggest_char+1) /* 1114112, |biggest_char+1| */ ++# define special_char (biggest_char+2) /* 1114113, |biggest_char+2| */ ++# define number_chars (biggest_char+3) /* 1114112, |biggest_char+1| */ + + /* + Several of the elementary string operations are performed using +diff --git a/texk/web2c/luatexdir/tex/texmath.h b/texk/web2c/luatexdir/tex/texmath.h +index 24bb34dc9..23fc5a0e4 100644 +--- a/texk/web2c/luatexdir/tex/texmath.h ++++ b/texk/web2c/luatexdir/tex/texmath.h +@@ -38,22 +38,6 @@ extern halfword new_sub_box(halfword); + + # define default_code 010000000000 /* denotes |default_rule_thickness| */ + +-typedef enum { +- ord_noad_type = 0, +- op_noad_type_normal, +- op_noad_type_limits, +- op_noad_type_no_limits, +- bin_noad_type, +- rel_noad_type, +- open_noad_type, +- close_noad_type, +- punct_noad_type, +- inner_noad_type, +- under_noad_type, +- over_noad_type, +- vcenter_noad_type, +-} noad_types; +- + extern void initialize_math(void); + extern void initialize_math_spacing(void); + extern halfword math_vcenter_group(halfword); +diff --git a/texk/web2c/luatexdir/tex/texnodes.h b/texk/web2c/luatexdir/tex/texnodes.h +index eb9b2da4a..e77e2916e 100644 +--- a/texk/web2c/luatexdir/tex/texnodes.h ++++ b/texk/web2c/luatexdir/tex/texnodes.h +@@ -97,8 +97,30 @@ extern halfword do_set_attribute(halfword p, int i, int val); + # define list_offset 6 + + typedef enum { ++ user_skip_glue, ++ line_skip_glue, ++ baseline_skip_glue, ++ par_skip_glue, ++ above_display_skip_glue, ++ below_display_skip_glue, ++ above_display_short_skip_glue, ++ below_display_short_skip_glue, ++ left_skip_glue, ++ right_skip_glue, ++ top_skip_glue, ++ split_top_skip_glue, ++ tab_skip_glue, ++ space_skip_glue, ++ xspace_skip_glue, ++ par_fill_skip_glue, ++ math_skip_glue, ++ thin_mu_skip_glue, ++ med_mu_skip_glue, ++ thick_mu_skip_glue, ++ /* math */ + cond_math_glue = 98, /* special |subtype| to suppress glue in the next node */ + mu_glue, /* |subtype| for math glue */ ++ /* leaders */ + a_leaders, /* |subtype| for aligned leaders */ + c_leaders, /* |subtype| for centered leaders */ + x_leaders, /* |subtype| for expanded leaders */ +@@ -199,7 +221,7 @@ typedef enum { + # define disc_penalty(a) vlink((a)+2) + # define pre_break(a) vinfo((a)+3) + # define post_break(a) vlink((a)+3) +-# define no_break(a) vlink((a)+4) ++# define no_break(a) vlink((a)+4) /* we have vinfo((a)+4) for later usage */ + + # define vlink_pre_break(a) vlink(pre_break_head(a)) + # define vlink_post_break(a) vlink(post_break_head(a)) +@@ -264,14 +286,17 @@ typedef enum { + math_under_rule, + math_fraction_rule, + math_radical_rule, ++ outline_rule, + } rule_subtypes; + +-# define rule_node_size 8 ++# define rule_node_size 9 + # define rule_dir(a) vlink((a)+5) + # define rule_index(a) vinfo((a)+6) + # define rule_transform(a) vlink((a)+6) +-# define synctex_tag_rule(a) vinfo((a)+7) +-# define synctex_line_rule(a) vlink((a)+7) ++# define rule_left(a) vinfo((a)+7) ++# define rule_right(a) vlink((a)+7) ++# define synctex_tag_rule(a) vinfo((a)+8) ++# define synctex_line_rule(a) vlink((a)+8) + + # define rule_math_size rule_index + # define rule_math_font rule_transform +@@ -292,7 +317,7 @@ typedef enum { + # define x_displace(a) vinfo((a)+4) + # define y_displace(a) vlink((a)+4) + # define ex_glyph(a) vinfo((a)+5) /* expansion factor (hz) */ +-# define x_advance(a) vlink((a)+5) /* obsolete, can become user field */ ++# define glyph_node_data(a) vlink((a)+5) + # define synctex_tag_glyph(a) vinfo((a)+6) + # define synctex_line_glyph(a) vlink((a)+6) + +@@ -318,11 +343,15 @@ typedef enum { + + /*@# {|subtype| of marginal kerns}*/ + +-# define left_side 0 +-# define right_side 1 ++typedef enum { ++ left_side = 0, ++ right_side ++} margin_kern_subtypes ; + +-# define before 0 /* |subtype| for math node that introduces a formula */ +-# define after 1 /* |subtype| for math node that winds up a formula */ ++typedef enum { ++ before = 0, ++ after ++} math_subtypes ; + + # define math_node_size 7 + /* define width(a) vinfo((a)+2) */ +@@ -487,6 +516,24 @@ typedef enum { + # define noadextra3(a) vlink((a)+7) /* see (!) below */ + # define noadextra4(a) vinfo((a)+7) /* used to store samesize */ + ++# define noad_fam(a) vlink((a)+6) /* noadextra1 */ ++ ++typedef enum { ++ ord_noad_type = 0, ++ op_noad_type_normal, ++ op_noad_type_limits, ++ op_noad_type_no_limits, ++ bin_noad_type, ++ rel_noad_type, ++ open_noad_type, ++ close_noad_type, ++ punct_noad_type, ++ inner_noad_type, ++ under_noad_type, ++ over_noad_type, ++ vcenter_noad_type, ++} noad_types; ++ + /* accent noads */ + + # define accent_noad_size 8 +@@ -495,6 +542,13 @@ typedef enum { + # define overlay_accent_chr(a) vinfo((a)+7) /* the |overlay_accent_chr| field of an accent noad */ + # define accentfraction(a) vlink((a)+7) + ++typedef enum { ++ bothflexible_accent, ++ fixedtop_accent, ++ fixedbottom_accent, ++ fixedboth_accent, ++} math_accent_subtypes ; ++ + /* left and right noads */ + + # define fence_noad_size 8 /* needs to match noad size */ +@@ -540,22 +594,36 @@ typedef enum { + noad_delimiter_mode_noshift = 0x01, + noad_delimiter_mode_italics = 0x02, + noad_delimiter_mode_ordinal = 0x04, ++ noad_delimiter_mode_samenos = 0x08, ++ noad_delimiter_mode_charnos = 0x10, + } delimiter_modes ; + + # define delimitermodenoshift ((math_delimiters_mode_par & noad_delimiter_mode_noshift) == noad_delimiter_mode_noshift) + # define delimitermodeitalics ((math_delimiters_mode_par & noad_delimiter_mode_italics) == noad_delimiter_mode_italics) + # define delimitermodeordinal ((math_delimiters_mode_par & noad_delimiter_mode_ordinal) == noad_delimiter_mode_ordinal) ++# define delimitermodesamenos ((math_delimiters_mode_par & noad_delimiter_mode_samenos) == noad_delimiter_mode_samenos) ++# define delimitermodecharnos ((math_delimiters_mode_par & noad_delimiter_mode_charnos) == noad_delimiter_mode_charnos) + + /* subtype of fence noads */ + ++/* + # define left_noad_side 1 + # define middle_noad_side 2 + # define right_noad_side 3 + # define no_noad_side 4 ++*/ ++ ++typedef enum { ++ unset_noad_side = 0, ++ left_noad_side = 1, ++ middle_noad_side = 2, ++ right_noad_side = 3, ++ no_noad_side = 4, ++} fence_subtypes ; + + /* fraction noads */ + +-# define fraction_noad_size 7 ++# define fraction_noad_size 8 + # define thickness(a) vlink((a)+2) /* |thickness| field in a fraction noad */ + # define numerator(a) vlink((a)+3) /* |numerator| field in a fraction noad */ + # define denominator(a) vinfo((a)+3) /* |denominator| field in a fraction noad */ +@@ -563,6 +631,7 @@ typedef enum { + # define right_delimiter(a) vinfo((a)+5) /* second delimiter field of a fraction noad */ + # define middle_delimiter(a) vlink((a)+6) + # define fractionoptions(a) vinfo((a)+6) ++# define fraction_fam(a) vlink((a)+7) + + # define fractionoptionset(a) ((fractionoptions(a) & noad_option_set ) == noad_option_set ) + # define fractionexact(a) ((fractionoptions(a) & noad_option_exact ) == noad_option_exact ) +@@ -582,6 +651,16 @@ typedef enum { + # define radicalmiddle(a) ((radicaloptions(a) & noad_option_middle) == noad_option_middle) + # define radicalright(a) ((radicaloptions(a) & noad_option_right ) == noad_option_right) + ++typedef enum { ++ radical_noad_type, ++ uradical_noad_type, ++ uroot_noad_type, ++ uunderdelimiter_noad_type, ++ uoverdelimiter_noad_type, ++ udelimiterunder_noad_type, ++ udelimiterover_noad_type, ++} radical_subtypes; ++ + /* accessors for the |nucleus|-style node fields */ + + # define math_kernel_node_size 3 +@@ -659,6 +738,15 @@ typedef enum { + # define GLYPH_LEFT (1 << 3) + # define GLYPH_RIGHT (1 << 4) + ++typedef enum { ++ glyph_unset = 0, ++ glyph_character = GLYPH_CHARACTER, ++ glyph_ligature = GLYPH_LIGATURE, ++ glyph_ghost = GLYPH_GHOST, ++ glyph_left = GLYPH_LEFT, ++ glyph_right = GLYPH_RIGHT, ++} glyph_subtypes; ++ + # define is_character(p) ((subtype(p)) & GLYPH_CHARACTER) + # define is_ligature(p) ((subtype(p)) & GLYPH_LIGATURE ) + # define is_ghost(p) ((subtype(p)) & GLYPH_GHOST ) +@@ -698,6 +786,11 @@ typedef enum { + + # define special_node_size 3 + ++typedef enum { ++ normal_dir = 0, ++ cancel_dir, ++} dir_subtypes ; ++ + # define dir_node_size 5 + # define dir_dir(a) vinfo((a)+2) + # define dir_level(a) vlink((a)+2) +@@ -737,6 +830,7 @@ typedef enum { + /* type of literal data */ + + # define lua_refid_literal 1 /* not a |normal| string */ ++# define lua_refid_call 2 /* not a |normal| string */ + + /* begin of pdf backend nodes */ + +@@ -899,40 +993,60 @@ extern void print_short_node_contents(halfword n); + extern void show_node_list(int i); + extern pointer actual_box_width(pointer r, scaled base_width); + +-/* from luanode.c */ ++typedef struct _subtype_info { ++ int id; ++ const char *name; ++ int lua; ++} subtype_info; ++ ++typedef struct _field_info { ++ const char *name; ++ int lua; ++} field_info; + + typedef struct _node_info { + int id; + int size; +- const char **fields; ++ subtype_info *subtypes; ++ field_info *fields; + const char *name; + int etex; ++ int lua; + } node_info; + + extern node_info node_data[]; + extern node_info whatsit_node_data[]; + +-extern const char *node_subtypes_glue[]; +-extern const char *node_subtypes_mathglue[]; +-extern const char *node_subtypes_leader[]; +-extern const char *node_subtypes_fill[]; +-extern const char *node_subtypes_boundary[]; +-extern const char *node_subtypes_penalty[]; +-extern const char *node_subtypes_kern[]; +-extern const char *node_subtypes_rule[]; +-extern const char *node_subtypes_glyph[]; +-extern const char *node_subtypes_disc[]; +-extern const char *node_subtypes_marginkern[]; +-extern const char *node_subtypes_list[]; +-extern const char *node_subtypes_adjust[]; +-extern const char *node_subtypes_math[]; +-extern const char *node_subtypes_noad[]; +-extern const char *node_subtypes_radical[]; +-extern const char *node_subtypes_accent[]; +-extern const char *node_subtypes_fence[]; +- +-extern const char *node_subtypes_pdf_destination[]; +-extern const char *node_subtypes_pdf_literal[]; ++extern subtype_info node_subtypes_dir[]; ++extern subtype_info node_subtypes_glue[]; ++extern subtype_info node_subtypes_mathglue[]; ++extern subtype_info node_subtypes_leader[]; ++extern subtype_info node_subtypes_boundary[]; ++extern subtype_info node_subtypes_penalty[]; ++extern subtype_info node_subtypes_kern[]; ++extern subtype_info node_subtypes_rule[]; ++extern subtype_info node_subtypes_glyph[]; ++extern subtype_info node_subtypes_disc[]; ++extern subtype_info node_subtypes_marginkern[]; ++extern subtype_info node_subtypes_list[]; ++extern subtype_info node_subtypes_adjust[]; ++extern subtype_info node_subtypes_math[]; ++extern subtype_info node_subtypes_noad[]; ++extern subtype_info node_subtypes_radical[]; ++extern subtype_info node_subtypes_accent[]; ++extern subtype_info node_subtypes_fence[]; ++ ++extern subtype_info node_values_pdf_destination[]; ++extern subtype_info node_values_pdf_literal[]; ++extern subtype_info node_values_pdf_literal[]; ++extern subtype_info node_values_pdf_action[]; ++extern subtype_info node_values_pdf_window[]; ++ ++extern subtype_info node_values_fill[]; ++extern subtype_info node_values_dir[]; ++extern subtype_info node_values_color_stack[]; ++ ++extern subtype_info other_values_page_states[]; + + extern halfword new_node(int i, int j); + extern void flush_node_list(halfword); +@@ -958,7 +1072,7 @@ extern void show_node_wrapup_dvi(halfword); + extern void show_node_wrapup_pdf(halfword); + + typedef enum { +- normal_g = 0, ++ normal_g = 0, /* normal */ + sfi, + fil, + fill, +@@ -1037,5 +1151,8 @@ extern void synctex_set_no_files(int flag); + extern int synctex_get_no_files(void); + extern int synctex_get_line(void); + ++extern void l_set_node_data(void) ; ++extern void l_set_whatsit_data(void) ; ++ + #endif + +diff --git a/texk/web2c/luatexdir/tex/textoken.h b/texk/web2c/luatexdir/tex/textoken.h +index 4f46a48c8..4b33b099a 100644 +--- a/texk/web2c/luatexdir/tex/textoken.h ++++ b/texk/web2c/luatexdir/tex/textoken.h +@@ -25,19 +25,19 @@ + # define null 0 + # define cs_token_flag 0x1FFFFFFF + +-# define left_brace_token 0x200000 /* $2^{21}\cdot|left_brace|$ */ +-# define right_brace_token 0x400000 /* $2^{21}\cdot|right_brace|$ */ +-# define left_brace_limit 0x400000 /* $2^{21}\cdot(|left_brace|+1)$ */ +-# define right_brace_limit 0x600000 /* $2^{21}\cdot(|right_brace|+1)$ */ +-# define math_shift_token 0x600000 /* $2^{21}\cdot|math_shift|$ */ +-# define tab_token 0x800000 /* $2^{21}\cdot|tab_mark|$ */ +-# define out_param_token 0xA00000 /* $2^{21}\cdot|out_param|$ */ +-# define space_token 0x1400020 /* $2^{21}\cdot|spacer|+|" "|$ */ +-# define letter_token 0x1600000 /* $2^{21}\cdot|letter|$ */ +-# define other_token 0x1800000 /* $2^{21}\cdot|other_char|$ */ +-# define match_token 0x1A00000 /* $2^{21}\cdot|match|$ */ +-# define end_match_token 0x1C00000 /* $2^{21}\cdot|end_match|$ */ +-# define protected_token 0x1C00001 /* $2^{21}\cdot|end_match|+1$ */ ++# define left_brace_token 0x0200000 /* $2^{21}\cdot|left_brace|$ */ ++# define right_brace_token 0x0400000 /* $2^{21}\cdot|right_brace|$ */ ++# define left_brace_limit 0x0400000 /* $2^{21}\cdot(|left_brace|+1)$ */ ++# define right_brace_limit 0x0600000 /* $2^{21}\cdot(|right_brace|+1)$ */ ++# define math_shift_token 0x0600000 /* $2^{21}\cdot|math_shift|$ */ ++# define tab_token 0x0800000 /* $2^{21}\cdot|tab_mark|$ */ ++# define out_param_token 0x0A00000 /* $2^{21}\cdot|out_param|$ */ ++# define space_token 0x1400020 /* $2^{21}\cdot|spacer|+|" "|$ */ ++# define letter_token 0x1600000 /* $2^{21}\cdot|letter|$ */ ++# define other_token 0x1800000 /* $2^{21}\cdot|other_char|$ */ ++# define match_token 0x1A00000 /* $2^{21}\cdot|match|$ */ ++# define end_match_token 0x1C00000 /* $2^{21}\cdot|end_match|$ */ ++# define protected_token 0x1C00001 /* $2^{21}\cdot|end_match|+1$ */ + + # include "tex/stringpool.h" + +@@ -126,6 +126,7 @@ extern void make_token_table(lua_State * L, int cmd, int chr, int cs); + extern void get_next(void); + extern void check_outer_validity(void); + extern boolean scan_keyword(const char *); ++extern boolean scan_keyword_case_sensitive(const char *); + extern halfword active_to_cs(int, int); + extern void get_token_lua(void); + halfword string_to_toks(const char *); +@@ -183,4 +184,6 @@ extern void free_lstring(lstring * ls); + # define token_chr(A) ((A) & (STRING_OFFSET - 1)) + # define token_val(A,B) (((A)< ++ * separation of mpmathbinary from the the core ++ ++ + 2018-02-19 Luigi Scarso + * Small cleanup of the code + * Bump to version 2.0rc2: the current version is 2.00 +diff --git a/texk/web2c/mplibdir/am/libmplib.am b/texk/web2c/mplibdir/am/libmplib.am +index 3d5723119..a4be39e68 100644 +--- a/texk/web2c/mplibdir/am/libmplib.am ++++ b/texk/web2c/mplibdir/am/libmplib.am +@@ -5,15 +5,18 @@ + + ## libmplib.a, used by MetaPost and luaTeX + ## +-EXTRA_LIBRARIES += libmplibcore.a libmplibbackends.a ++EXTRA_LIBRARIES += libmplibcore.a libmplibextramath.a libmplibbackends.a + +-libmplibcore_a_CPPFLAGS = $(MPFR_INCLUDES) $(GMP_INCLUDES) $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) $(AM_CPPFLAGS) -I$(srcdir)/mplibdir ++ ++libmplibcore_a_CPPFLAGS = $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) $(AM_CPPFLAGS) -I$(srcdir)/mplibdir ++libmplibextramath_a_CPPFLAGS = $(MPFR_INCLUDES) $(GMP_INCLUDES) -I${top_builddir}/../../libs $(AM_CPPFLAGS) -I$(srcdir)/mplibdir + libmplibbackends_a_CPPFLAGS = $(MPFR_INCLUDES) $(GMP_INCLUDES) $(CAIRO_INCLUDES) $(PIXMAN_INCLUDES) \ + $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) $(AM_CPPFLAGS) -I$(srcdir)/mplibdir + +-## libmplib C sources core + backends +-nodist_libmplibcore_a_SOURCES = tfmin.c $(mp_c_h) $(mpmath_c_h) $(mpmathbinary_c_h) $(mpmathdecimal_c_h) \ ++## libmplib C sources core + extramath + backends ++nodist_libmplibcore_a_SOURCES = tfmin.c $(mp_c_h) $(mpmath_c_h) $(mpmathdecimal_c_h) \ + $(mpmathdouble_c_h) $(mpstrings_c_h) $(psout_c_h) ++nodist_libmplibextramath_a_SOURCES = $(mpmathbinary_c_h) + nodist_libmplibbackends_a_SOURCES = $(pngout_c_h) $(svgout_c_h) + + +@@ -84,15 +87,16 @@ libmplib_web += mplibdir/mpmath.w mplibdir/mpmathbinary.w mplibdir/mpmathdecimal + libmplib_web += mplibdir/mpmathdouble.w mplibdir/mpstrings.w mplibdir/tfmin.w + + ## core need headers backends +-$(nodist_libmplibcore_a_SOURCES): $(svgout_c_h) $(pngout_c_h) ++$(nodist_libmplibcore_a_SOURCES): $(mpmathbinary_c_h) $(svgout_c_h) $(pngout_c_h) + +-$(libmplibcore_a_OBJECTS): $(nodist_libmplibcore_a_SOURCES) $(KPATHSEA_DEPEND) $(MPFR_DEPEND) ++$(libmplibcore_a_OBJECTS): $(nodist_libmplibcore_a_SOURCES) $(KPATHSEA_DEPEND) ++$(libmplibextramath_a_OBJECTS): $(nodist_libmplibextramath_a_SOURCES) $(KPATHSEA_DEPEND) $(MPFR_DEPEND) + $(libmplibbackends_a_OBJECTS): $(nodist_libmplibbackends_a_SOURCES) $(KPATHSEA_DEPEND) $(CAIRO_DEPEND) $(MPFR_DEPEND) + + + EXTRA_DIST += $(libmplib_web) + +-DISTCLEANFILES += $(nodist_libmplibcore_a_SOURCES) $(nodist_libmplibbackends_a_SOURCES) \ +- mp-tangle mpmath-tangle mpmathbinary-tangle mpmathdecimal-tangle mpmathdouble-tangle \ ++DISTCLEANFILES += $(nodist_libmplibcore_a_SOURCES) $(nodist_libmplibextramath_a_SOURCES) $(nodist_libmplibbackends_a_SOURCES) \ ++ mp-tangle mpmath-tangle mpmathdecimal-tangle mpmathdouble-tangle \ + mpstrings-tangle psout-tangle svgout-tangle pngout-tangle + +diff --git a/texk/web2c/mplibdir/am/mplib.am b/texk/web2c/mplibdir/am/mplib.am +index f58658de1..10b1fe4aa 100644 +--- a/texk/web2c/mplibdir/am/mplib.am ++++ b/texk/web2c/mplibdir/am/mplib.am +@@ -18,9 +18,7 @@ endif MP + EXTRA_PROGRAMS += mpost + + mpost_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(LIBPNG_INCLUDES) -I$(srcdir)/mplibdir +-#mpost_LDADD = libmplib.a $(KPATHSEA_LIBS) $(MPFR_LIBS) $(GMP_LIBS) \ +-# $(CAIRO_LIBS) $(PIXMAN_LIBS) $(LIBPNG_LIBS) $(ZLIB_LIBS) libmputil.a +-mpost_LDADD = libmplibcore.a libmplibbackends.a $(KPATHSEA_LIBS) $(MPFR_LIBS) $(GMP_LIBS) \ ++mpost_LDADD = libmplibcore.a libmplibextramath.a libmplibbackends.a $(KPATHSEA_LIBS) $(MPFR_LIBS) $(GMP_LIBS) \ + $(CAIRO_LIBS) $(PIXMAN_LIBS) $(LIBPNG_LIBS) $(ZLIB_LIBS) libmputil.a + + +@@ -60,7 +58,7 @@ mpxout-tangle: ctangle$(EXEEXT) mplibdir/mpxout.w tangle-sh + mpost_web = mplibdir/mpost.w mplibdir/mpxout.w + + #$(mpost_OBJECTS): $(nodist_mpost_SOURCES) libmplib.a $(LIBPNG_DEPEND) +-$(mpost_OBJECTS): $(nodist_mpost_SOURCES) libmplibcore.a libmplibbackends.a $(LIBPNG_DEPEND) ++$(mpost_OBJECTS): $(nodist_mpost_SOURCES) libmplibcore.a libmplibextramath.a libmplibbackends.a $(LIBPNG_DEPEND) + + EXTRA_DIST += mplibdir/ChangeLog $(mpost_web) + +diff --git a/texk/web2c/mplibdir/lmplib.c b/texk/web2c/mplibdir/lmplib.c +index d90482b88..eb8432bb5 100644 +--- a/texk/web2c/mplibdir/lmplib.c ++++ b/texk/web2c/mplibdir/lmplib.c +@@ -15,7 +15,9 @@ + License for more details. + + You should have received a copy of the GNU Lesser General Public License along +- with LuaTeX; if not, see . */ ++ with LuaTeX; if not, see . ++ ++*/ + + #include + #include +@@ -23,7 +25,7 @@ + #ifdef HAVE_UNISTD_H + #include + #endif +-#include /* temporary */ ++#include + + #ifndef pdfTeX + # include +@@ -41,20 +43,23 @@ + #define luaL_reg luaL_Reg + #endif + +- + #ifndef lua_objlen + #define lua_objlen lua_rawlen + #endif + +- + #include "mplib.h" + #include "mplibps.h" + #include "mplibsvg.h" + #include "mplibpng.h" + +-int luaopen_mplib(lua_State * L); /* forward */ ++int luaopen_mplib(lua_State * L); + +-/* metatable identifiers and tests */ ++/*tex ++ ++ We need a few metatable identifiers in order to access the metatables for the ++ main object and result userdata. ++ ++*/ + + #define MPLIB_METATABLE "MPlib.meta" + #define MPLIB_FIG_METATABLE "MPlib.fig" +@@ -64,25 +69,33 @@ int luaopen_mplib(lua_State * L); /* forward */ + #define is_fig(L,b) (struct mp_edge_object **)luaL_checkudata(L,b,MPLIB_FIG_METATABLE) + #define is_gr_object(L,b) (struct mp_graphic_object **)luaL_checkudata(L,b,MPLIB_GR_METATABLE) + +-/* Lua string pre-hashing */ ++/*tex + +-#define mplib_init_S(a) do { \ +- lua_pushliteral(L,#a); \ +- mplib_##a##_ptr = lua_tostring(L,-1); \ +- mplib_##a##_index = luaL_ref (L,LUA_REGISTRYINDEX); \ +- } while (0) ++ We pre-hash the \LUA\ strings which is much faster. The approach is similar to the one ++ used at the \TEX\ end. + +-#define mplib_push_S(a) do { \ +- lua_rawgeti(L,LUA_REGISTRYINDEX,mplib_##a##_index); \ +- } while (0) ++*/ ++ ++#define mplib_init_S(a) do { \ ++ lua_pushliteral(L,#a); \ ++ mplib_##a##_ptr = lua_tostring(L,-1); \ ++ mplib_##a##_index = luaL_ref (L,LUA_REGISTRYINDEX); \ ++} while (0) ++ ++#define mplib_push_S(a) do { \ ++ lua_rawgeti(L,LUA_REGISTRYINDEX,mplib_##a##_index); \ ++} while (0) + +-#define mplib_is_S(a,i) (mplib_##a##_ptr==lua_tostring(L,i)) ++#define mplib_is_S(a,i) \ ++ (mplib_##a##_ptr==lua_tostring(L,i)) + +-#define mplib_make_S(a) \ +- static int mplib_##a##_index = 0; \ +- static const char *mplib_##a##_ptr = NULL ++#define mplib_make_S(a) \ ++ static int mplib_##a##_index = 0; \ ++ static const char *mplib_##a##_ptr = NULL + +-static int mplib_type_Ses[mp_special_code + 1] = { 0 }; /* [0] is not used */ ++/*tex In the next array entry 0 is not used */ ++ ++static int mplib_type_Ses[mp_special_code + 1] = { 0 }; + + mplib_make_S(term); + mplib_make_S(error); +@@ -94,6 +107,7 @@ mplib_make_S(memory); + mplib_make_S(hash); + mplib_make_S(params); + mplib_make_S(open); ++mplib_make_S(cycle); + + mplib_make_S(offset); + mplib_make_S(dashes); +@@ -153,6 +167,7 @@ static void mplib_init_Ses(lua_State * L) + mplib_init_S(hash); + mplib_init_S(params); + mplib_init_S(open); ++ mplib_init_S(cycle); + + mplib_init_S(offset); + mplib_init_S(dashes); +@@ -210,11 +225,15 @@ static void mplib_init_Ses(lua_State * L) + mplib_init_S(elliptical); + } + ++/*tex ++ ++ Here are some enumeration arrays to map MPlib enums to \LUA\ strings. If needed ++ we can also predefine keys here, as we do with nodes. + +-/* Enumeration arrays to map MPlib enums to Lua strings */ ++*/ + + static const char *math_options[] = +- { "scaled", "double", "binary", "decimal", NULL }; ++ { "scaled", "double", "binary", "decimal", NULL }; + + static const char *interaction_options[] = + { "unknown", "batch", "nonstop", "scroll", "errorstop", NULL }; +@@ -255,18 +274,34 @@ static const char *stop_clip_fields[] = + static const char *no_fields[] = + { NULL }; + ++/*tex + +-/* The list of supported MPlib options (not all make sense) */ ++ The list of supported MPlib options (not all make sense). ++ ++*/ + + typedef enum { +- P_ERROR_LINE, P_MAX_LINE, P_RANDOM_SEED, P_MATH_MODE, +- P_INTERACTION, P_INI_VERSION, P_MEM_NAME, P_JOB_NAME, P_FIND_FILE, +- P_RUN_SCRIPT, P_MAKE_TEXT, P_SCRIPT_ERROR, P_EXTENSIONS, +- P__SENTINEL } mplib_parm_idx; ++ P_ERROR_LINE, ++ P_MAX_LINE, ++ P_RANDOM_SEED, ++ P_MATH_MODE, ++ P_INTERACTION, ++ P_INI_VERSION, ++ P_MEM_NAME, ++ P_JOB_NAME, ++ P_FIND_FILE, ++ P_RUN_SCRIPT, ++ P_MAKE_TEXT, ++ P_SCRIPT_ERROR, ++ P_EXTENSIONS, ++ P__SENTINEL ++} mplib_parm_idx; + + typedef struct { +- const char *name; /* parameter name */ +- mplib_parm_idx idx; /* parameter index */ ++ /*tex parameter name */ ++ const char *name; ++ /*tex parameter index */ ++ mplib_parm_idx idx; + } mplib_parm_struct; + + static mplib_parm_struct mplib_parms[] = { +@@ -284,10 +319,11 @@ static mplib_parm_struct mplib_parms[] = { + {NULL, P__SENTINEL } + }; + ++/*tex + +-/* Start by defining the needed callback routines for the library */ ++ We start by defining the needed callback routines for the library. + +-/* todo: make subtable in registry, beware, for all mp instances */ ++*/ + + static char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ftype) + { +@@ -300,7 +336,7 @@ static char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ft + lua_pushstring(L, fname); + lua_pushstring(L, fmode); + if (ftype >= mp_filetype_text) { +- lua_pushnumber(L, (lua_Number)(ftype - mp_filetype_text)); ++ lua_pushinteger(L, (ftype - mp_filetype_text)); + } else { + lua_pushstring(L, mplib_filetype_names[ftype]); + } +@@ -311,7 +347,8 @@ static char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ft + x = lua_tostring(L, -1); + if (x != NULL) + s = strdup(x); +- lua_pop(L, 1); /* pop the string */ ++ /*tex pop the string */ ++ lua_pop(L, 1); + return s; + } else { + lua_pop(L, 1); +@@ -325,7 +362,8 @@ static char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ft + static int mplib_find_file_function(lua_State * L) + { + if (!(lua_isfunction(L, -1) || lua_isnil(L, -1))) { +- return 1; /* error */ ++ /*tex An error. */ ++ return 1; + } + lua_pushstring(L, "mplib.file_finder"); + lua_pushvalue(L, -2); +@@ -333,6 +371,11 @@ static int mplib_find_file_function(lua_State * L) + return 0; + } + ++static void mplib_warning(const char *str) ++{ ++ fprintf(stdout,"mplib warning: %s\n",str); ++} ++ + static void mplib_script_error(MP mp, const char *str) + { + lua_State *L = (lua_State *)mp_userdata(mp); +@@ -340,9 +383,10 @@ static void mplib_script_error(MP mp, const char *str) + lua_getfield(L, LUA_REGISTRYINDEX, "mplib.script_error"); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, str); +- lua_pcall(L, 1, 0, 0); /* assume the function is ok */ ++ /*tex We assume that the function is okay. */ ++ lua_pcall(L, 1, 0, 0); + } else { +- fprintf(stdout,"Error in script: %s\n",str); ++ mplib_warning(str); + lua_pop(L, 1); + } + } +@@ -350,7 +394,8 @@ static void mplib_script_error(MP mp, const char *str) + static int mplib_script_error_function(lua_State * L) + { + if (!(lua_isfunction(L, -1) || lua_isnil(L, -1))) { +- return 1; /* error */ ++ /*tex An error. */ ++ return 1; + } + lua_pushstring(L, "mplib.script_error"); + lua_pushvalue(L, -2); +@@ -368,13 +413,14 @@ static char *mplib_run_script(MP mp, const char *str) + const char *x = NULL; + lua_pushstring(L, str); + if (lua_pcall(L, 1, 1, 0) != 0) { +- mplib_script_error(mp, lua_tostring(L, -1)); ++ fprintf(stdout,"mplib warning: error in script: %s\n",lua_tostring(L, -1)); + return NULL; + } + x = lua_tostring(L, -1); + if (x != NULL) + s = strdup(x); +- lua_pop(L, 1); /* pop the string */ ++ /*tex Pop the string. */ ++ lua_pop(L, 1); + return s; + } else { + lua_pop(L, 1); +@@ -385,7 +431,7 @@ static char *mplib_run_script(MP mp, const char *str) + static int mplib_run_script_function(lua_State * L) + { + if (!(lua_isfunction(L, -1) || lua_isnil(L, -1))) { +- return 1; /* error */ ++ return 1; /* error */ + } + lua_pushstring(L, "mplib.run_script"); + lua_pushvalue(L, -2); +@@ -410,7 +456,8 @@ static char *mplib_make_text(MP mp, const char *str, int mode) + x = lua_tostring(L, -1); + if (x != NULL) + s = strdup(x); +- lua_pop(L, 1); /* pop the string */ ++ /*tex Pop the string. */ ++ lua_pop(L, 1); + return s; + } else { + lua_pop(L, 1); +@@ -421,7 +468,8 @@ static char *mplib_make_text(MP mp, const char *str, int mode) + static int mplib_make_text_function(lua_State * L) + { + if (!(lua_isfunction(L, -1) || lua_isnil(L, -1))) { +- return 1; /* error */ ++ /*tex An error. */ ++ return 1; + } + lua_pushstring(L, "mplib.make_text"); + lua_pushvalue(L, -2); +@@ -487,7 +535,8 @@ static int mplib_new(lua_State * L) + int i; + struct MP_options *options = mp_options(); + options->userdata = (void *) L; +- options->noninteractive = 1; /* required ! */ ++ /*tex Required: */ ++ options->noninteractive = 1; + options->extensions = 0 ; + options->find_file = mplib_find_file; + options->run_script = mplib_run_script; +@@ -500,56 +549,59 @@ static int mplib_new(lua_State * L) + lua_getfield(L, 1, mplib_parms[i].name); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); +- continue; /* skip unset */ ++ continue; + } + switch (mplib_parms[i].idx) { +- case P_ERROR_LINE: +- options->error_line = (int)lua_tointeger(L, -1); +- if (options->error_line<60) options->error_line =60; +- if (options->error_line>250) options->error_line = 250; +- options->half_error_line = (options->error_line/2)+10; +- break; +- case P_MAX_LINE: +- options->max_print_line = (int)lua_tointeger(L, -1); +- if (options->max_print_line<60) options->max_print_line = 60; +- break; +- case P_RANDOM_SEED: +- options->random_seed = (int)lua_tointeger(L, -1); +- break; +- case P_INTERACTION: +- options->interaction = luaL_checkoption(L, -1, "errorstopmode", interaction_options); +- break; +- case P_MATH_MODE: +- options->math_mode = luaL_checkoption(L, -1, "scaled", math_options); +- break; +- case P_JOB_NAME: +- options->job_name = strdup(lua_tostring(L, -1)); +- break; +- case P_FIND_FILE: +- if (mplib_find_file_function(L)) { /* error here */ +- fprintf(stdout,"Invalid arguments to mp.new { find_file = ... }\n"); +- } +- break; +- case P_RUN_SCRIPT: +- if (mplib_run_script_function(L)) { /* error here */ +- fprintf(stdout,"Invalid arguments to mp.new { run_script = ... }\n"); +- } +- break; +- case P_MAKE_TEXT: +- if (mplib_make_text_function(L)) { /* error here */ +- fprintf(stdout,"Invalid arguments to mp.new { make_text = ... }\n"); +- } +- break; +- case P_SCRIPT_ERROR: +- if (mplib_script_error_function(L)) { /* error here */ +- fprintf(stdout,"Invalid arguments to mp.new { script_error = ... }\n"); +- } +- break; +- case P_EXTENSIONS: +- options->extensions = (int)lua_tointeger(L, -1); +- break; +- default: +- break; ++ case P_ERROR_LINE: ++ options->error_line = (int)lua_tointeger(L, -1); ++ if (options->error_line < 60) ++ options->error_line = 60; ++ if (options->error_line > 250) ++ options->error_line = 250; ++ options->half_error_line = (options->error_line/2)+10; ++ break; ++ case P_MAX_LINE: ++ options->max_print_line = (int)lua_tointeger(L, -1); ++ if (options->max_print_line < 60) ++ options->max_print_line = 60; ++ break; ++ case P_RANDOM_SEED: ++ options->random_seed = (int)lua_tointeger(L, -1); ++ break; ++ case P_INTERACTION: ++ options->interaction = luaL_checkoption(L, -1, "errorstopmode", interaction_options); ++ break; ++ case P_MATH_MODE: ++ options->math_mode = luaL_checkoption(L, -1, "scaled", math_options); ++ break; ++ case P_JOB_NAME: ++ options->job_name = strdup(lua_tostring(L, -1)); ++ break; ++ case P_FIND_FILE: ++ if (mplib_find_file_function(L)) { ++ mplib_warning("function expected for 'find_file'"); ++ } ++ break; ++ case P_RUN_SCRIPT: ++ if (mplib_run_script_function(L)) { ++ mplib_warning("function expected for 'run_script'"); ++ } ++ break; ++ case P_MAKE_TEXT: ++ if (mplib_make_text_function(L)) { ++ mplib_warning("function expected for 'make_text'"); ++ } ++ break; ++ case P_SCRIPT_ERROR: ++ if (mplib_script_error_function(L)) { ++ mplib_warning("function expected for 'script_error'"); ++ } ++ break; ++ case P_EXTENSIONS: ++ options->extensions = (int)lua_tointeger(L, -1); ++ break; ++ default: ++ break; + } + lua_pop(L, 1); + } +@@ -626,7 +678,7 @@ static int mplib_wrapresults(lua_State * L, mp_run_data *res, int status) + res->edges = NULL; + } + mplib_push_S(status); +- lua_pushnumber(L, (lua_Number)status); ++ lua_pushinteger(L, status); + lua_rawset(L,-3); + return 1; + } +@@ -716,16 +768,16 @@ static int mplib_statistics(lua_State * L) + if (*mp_ptr != NULL) { + lua_newtable(L); + mplib_push_S(memory); +- lua_pushnumber(L, (lua_Number)mp_memory_usage(*mp_ptr)); ++ lua_pushinteger(L, mp_memory_usage(*mp_ptr)); + lua_rawset(L,-3); + mplib_push_S(hash); +- lua_pushnumber(L, (lua_Number)mp_hash_usage(*mp_ptr)); ++ lua_pushinteger(L, mp_hash_usage(*mp_ptr)); + lua_rawset(L,-3); + mplib_push_S(params); +- lua_pushnumber(L, (lua_Number)mp_param_usage(*mp_ptr)); ++ lua_pushinteger(L, mp_param_usage(*mp_ptr)); + lua_rawset(L,-3); + mplib_push_S(open); +- lua_pushnumber(L, (lua_Number)mp_open_usage(*mp_ptr)); ++ lua_pushinteger(L, mp_open_usage(*mp_ptr)); + lua_rawset(L,-3); + } else { + lua_pushnil(L); +@@ -733,7 +785,6 @@ static int mplib_statistics(lua_State * L) + return 1; + } + +- + static int set_direction (lua_State * L, MP mp, mp_knot p) { + double direction_x = 0, direction_y = 0; + direction_x = (double)lua_tonumber(L,-1); +@@ -821,8 +872,8 @@ static int set_right_control (lua_State * L, MP mp, mp_knot p) { + return 1; + } + +- + #if 0 ++ + #define ROUNDED_ZERO(v) (fabs((v))<0.00001 ? 0 : (v)) + #define PI 3.1415926535897932384626433832795028841971 + #define RADIANS(a) (mp_number_as_double(mp,(a)) / 16.0) * PI/180.0 +@@ -915,6 +966,7 @@ void mp_dump_path (MP mp, mp_knot h) { + printf("cycle"); + printf (";\n"); + } ++ + #endif + + static int mplib_solve_path(lua_State * L) +@@ -938,10 +990,8 @@ static int mplib_solve_path(lua_State * L) + mp = *mp_ptr; + cyclic = lua_toboolean(L,3); + lua_pop(L,1); +- +- /* build up the path */ ++ /*tex We build up the path. */ + numpoints = lua_objlen(L,2); +- + first = p = NULL; + for (i=1;i<=numpoints;i++) { + int left_set = 0, right_set = 0; +@@ -951,7 +1001,6 @@ static int mplib_solve_path(lua_State * L) + errormsg = "Wrong argument types"; + goto BAD; + } +- + mplib_push_S(x_coord); + lua_rawget(L,-2); + if (!lua_isnumber(L,-1)) { +@@ -960,7 +1009,6 @@ static int mplib_solve_path(lua_State * L) + } + x_coord = (double)lua_tonumber(L,-1); + lua_pop(L,1); +- + mplib_push_S(y_coord); + lua_rawget(L,-2); + if (!lua_isnumber(L,-1)) { +@@ -969,11 +1017,14 @@ static int mplib_solve_path(lua_State * L) + } + y_coord = (double)lua_tonumber(L,-1); + lua_pop(L,1); +- + q = p; + if (q!=NULL) { +- /* we have to save the right_tension because |mp_append_knot| trashes +- it, believing that it is as yet uninitialized */ ++ /*tex ++ ++ We have to save the right_tension because |mp_append_knot| ++ trashes it, believing that it is as yet uninitialized. ++ ++ */ + double saved_tension = mp_number_as_double(mp, mp_knot_right_tension(mp,p)); + p = mp_append_knot(mp, p, x_coord, y_coord); + if ( ! p ) { +@@ -988,10 +1039,8 @@ static int mplib_solve_path(lua_State * L) + goto BAD; + } + } +- + if (first == NULL) + first = p; +- + mplib_push_S(left_curl); + lua_rawget(L,-2); + if (lua_isnumber(L,-1)) { +@@ -1001,9 +1050,9 @@ static int mplib_solve_path(lua_State * L) + } + left_set = 1; + } else { +- lua_pop(L,1); /* a nil value */ ++ /*tex A |nil| value. */ ++ lua_pop(L,1); + } +- + mplib_push_S(left_tension); + lua_rawget(L,-2); + if (lua_isnumber(L,-1)) { +@@ -1018,9 +1067,9 @@ static int mplib_solve_path(lua_State * L) + left_set = 1; + } + } else { +- lua_pop(L,1); /* a nil value */ ++ /*tex A |nil| value. */ ++ lua_pop(L,1); + } +- + mplib_push_S(left_x); + lua_rawget(L,-2); + if (lua_isnumber(L,-1)) { +@@ -1036,7 +1085,6 @@ static int mplib_solve_path(lua_State * L) + } else { + lua_pop(L,1); + } +- + mplib_push_S(right_curl); + lua_rawget(L,-2); + if (lua_isnumber(L,-1)) { +@@ -1046,9 +1094,9 @@ static int mplib_solve_path(lua_State * L) + } + right_set = 1; + } else { +- lua_pop(L,1); /* a nil value */ ++ /*tex A |nil| value. */ ++ lua_pop(L,1); + } +- + mplib_push_S(right_tension); + lua_rawget(L,-2); + if (lua_isnumber(L,-1)) { +@@ -1065,7 +1113,6 @@ static int mplib_solve_path(lua_State * L) + } else { + lua_pop(L,1); + } +- + mplib_push_S(right_x); + lua_rawget(L,-2); + if (lua_isnumber(L,-1)) { +@@ -1081,7 +1128,6 @@ static int mplib_solve_path(lua_State * L) + } else { + lua_pop(L,1); + } +- + mplib_push_S(direction_x); + lua_rawget(L,-2); + if (lua_isnumber(L,-1)) { +@@ -1090,12 +1136,12 @@ static int mplib_solve_path(lua_State * L) + goto BAD; + } + } else { +- lua_pop(L,1); /* a nil value */ ++ /*tex A |nil| value. */ ++ lua_pop(L,1); + } +- +- lua_pop(L,1); /* done with this item */ ++ /*tex Up the next item */ ++ lua_pop(L,1); + } +- + if (cyclic) { + mp_close_path_cycle (mp, p, first); + } else { +@@ -1104,12 +1150,12 @@ static int mplib_solve_path(lua_State * L) + #if 0 + mp_dump_path(mp,first); + #endif +- /* finished reading arguments */ ++ /*tex We're finished reading arguments. */ + if (!mp_solve_path(mp,first)) { + errormsg = "Failed to solve the path"; + goto BAD; + } +- /* squeeze the new values back into the table */ ++ /*tex Squeeze the new values back into the table. */ + p = first; + for (i=1;i<=numpoints;i++) { + lua_rawgeti(L,-1, i); +@@ -1117,14 +1163,14 @@ static int mplib_solve_path(lua_State * L) + mplib_push_S(left_y); lua_pushnumber(L, mp_number_as_double(mp, mp_knot_left_y(mp, p))); lua_rawset(L,-3); + mplib_push_S(right_x); lua_pushnumber(L, mp_number_as_double(mp, mp_knot_right_x(mp, p))); lua_rawset(L,-3); + mplib_push_S(right_y); lua_pushnumber(L, mp_number_as_double(mp, mp_knot_right_y(mp, p))); lua_rawset(L,-3); +- /* is this really needed */ +- mplib_push_S(left_tension); lua_pushnil(L); lua_rawset(L,-3); +- mplib_push_S(right_tension); lua_pushnil(L); lua_rawset(L,-3); +- mplib_push_S(left_curl); lua_pushnil(L); lua_rawset(L,-3); +- mplib_push_S(right_curl); lua_pushnil(L); lua_rawset(L,-3); +- mplib_push_S(direction_x); lua_pushnil(L); lua_rawset(L,-3); +- mplib_push_S(direction_y); lua_pushnil(L); lua_rawset(L,-3); +- /* till here */ ++ /*tex This is a bit overkill \unknown */ ++ mplib_push_S(left_tension); lua_pushnil(L); lua_rawset(L,-3); ++ mplib_push_S(right_tension); lua_pushnil(L); lua_rawset(L,-3); ++ mplib_push_S(left_curl); lua_pushnil(L); lua_rawset(L,-3); ++ mplib_push_S(right_curl); lua_pushnil(L); lua_rawset(L,-3); ++ mplib_push_S(direction_x); lua_pushnil(L); lua_rawset(L,-3); ++ mplib_push_S(direction_y); lua_pushnil(L); lua_rawset(L,-3); ++ /*tex \unknown\ till here. */ + mplib_push_S(left_type); lua_pushstring(L, knot_type_enum[mp_knot_left_type(mp, p)]); lua_rawset(L, -3); + mplib_push_S(right_type); lua_pushstring(L, knot_type_enum[mp_knot_right_type(mp, p)]); lua_rawset(L, -3); + lua_pop(L,1); +@@ -1132,7 +1178,7 @@ static int mplib_solve_path(lua_State * L) + } + lua_pushboolean(L, 1); + return 1; +-BAD: ++ BAD: + if (p != NULL) { + mp_close_path (mp, p, first); + mp_free_path (mp, p); +@@ -1142,7 +1188,11 @@ BAD: + return 2; + } + +-/* figure methods */ ++/*tex ++ ++ The next methods are for collecting the results from |fig|. ++ ++*/ + + static int mplib_fig_collect(lua_State * L) + { +@@ -1171,7 +1221,8 @@ static int mplib_fig_body(lua_State * L) + i++; + p = p->next; + } +- (*hh)->body = NULL; /* prevent double free */ ++ /*tex Prevent a double free: */ ++ (*hh)->body = NULL; + return 1; + } + +@@ -1233,7 +1284,6 @@ static int mplib_fig_svg(lua_State * L) + return 1; + } + +- + static int mplib_fig_png(lua_State * L) + { + mp_run_data *res; +@@ -1316,8 +1366,6 @@ static int mplib_fig_charcode(lua_State * L) + return 1; + } + +- +- + static int mplib_fig_bb(lua_State * L) + { + struct mp_edge_object **hh = is_fig(L, 1); +@@ -1333,7 +1381,11 @@ static int mplib_fig_bb(lua_State * L) + return 1; + } + +-/* object methods */ ++/*tex ++ ++ The methods for the figure objects plus a few helpers. ++ ++*/ + + static int mplib_gr_collect(lua_State * L) + { +@@ -1413,7 +1465,6 @@ static double coord_range_y (mp_gr_knot h, double dz) { + return (zhi - zlo <= dz ? aspect_bound : aspect_default); + } + +- + static int mplib_gr_peninfo(lua_State * L) { + double x_coord, y_coord, left_x, left_y, right_x, right_y; + double wx, wy; +@@ -1481,6 +1532,13 @@ static int mplib_gr_peninfo(lua_State * L) { + return 1; + } + ++/*tex ++ ++ Here is a helper that reports the valid field names of the possible ++ objects. ++ ++*/ ++ + static int mplib_gr_fields(lua_State * L) + { + const char **fields; +@@ -1526,7 +1584,6 @@ static int mplib_gr_fields(lua_State * L) + return 1; + } + +- + #define mplib_push_number(L,x) lua_pushnumber(L,(lua_Number)(x)) + + #define MPLIB_PATH 0 +@@ -1534,7 +1591,7 @@ static int mplib_gr_fields(lua_State * L) + + static void mplib_push_path(lua_State * L, mp_gr_knot h, int is_pen) + { +- mp_gr_knot p; /* for scanning the path */ ++ mp_gr_knot p; + int i = 1; + p = h; + if (p != NULL) { +@@ -1583,16 +1640,63 @@ static void mplib_push_path(lua_State * L, mp_gr_knot h, int is_pen) + } + } + +-/* this assumes that the top of the stack is a table +- or nil already in the case ++static int mplib_get_path(lua_State * L) ++{ ++ MP *mp = is_mp(L, 1); ++ if (*mp != NULL) { ++ size_t l; ++ const char *s = lua_tolstring(L, 2, &l); ++ if (s != NULL) { ++ mp_knot p = mp_get_path_value(*mp,s,l) ; ++ if (p != NULL) { ++ int i = 1; ++ mp_knot h = p; ++ lua_newtable(L); ++ do { ++ lua_createtable(L, 6, 1); ++ mplib_push_number(L, mp_number_as_double(*mp,p->x_coord)); ++ lua_rawseti(L,-2,1); ++ mplib_push_number(L, mp_number_as_double(*mp,p->y_coord)); ++ lua_rawseti(L,-2,2); ++ mplib_push_number(L, mp_number_as_double(*mp,p->left_x)); ++ lua_rawseti(L,-2,3); ++ mplib_push_number(L, mp_number_as_double(*mp,p->left_y)); ++ lua_rawseti(L,-2,4); ++ mplib_push_number(L, mp_number_as_double(*mp,p->right_x)); ++ lua_rawseti(L,-2,5); ++ mplib_push_number(L, mp_number_as_double(*mp,p->right_y)); ++ lua_rawseti(L,-2,6); ++ lua_rawseti(L,-2, i); ++ i++; ++ if (p->data.types.right_type == mp_endpoint) { ++ mplib_push_S(cycle); ++ lua_pushboolean(L,0); ++ lua_rawset(L,-3); ++ return 1; ++ } ++ p = p->next; ++ } while (p != h); ++ mplib_push_S(cycle); ++ lua_pushboolean(L,1); ++ lua_rawset(L,-3); ++ return 1; ++ } ++ } ++ } ++ return 0; ++} ++ ++/*tex ++ ++ This assumes that the top of the stack is a table or nil already in the case. + */ + + static void mplib_push_pentype(lua_State * L, mp_gr_knot h) + { +- mp_gr_knot p; /* for scanning the path */ ++ mp_gr_knot p; + p = h; + if (p == NULL) { +- /* do nothing */ ++ /*tex Do nothing. */ + } else if (p == p->next) { + mplib_push_S(type); + mplib_push_S(elliptical); +@@ -1643,7 +1747,11 @@ static void mplib_push_color(lua_State * L, struct mp_graphic_object *p) + } + } + +-/* the dash scale is not exported, the field has no external value */ ++/*tex ++ ++ The dash scale is not exported, the field has no external value. ++ ++*/ + + static void mplib_push_dash(lua_State * L, struct mp_stroked_object *h) + { +@@ -1811,7 +1919,6 @@ static int mplib_gr_index(lua_State * L) + struct mp_graphic_object **hh = is_gr_object(L, 1); + if (*hh) { + struct mp_graphic_object *h = *hh; +- + if (mplib_is_S(type, 2)) { + lua_rawgeti(L, LUA_REGISTRYINDEX, mplib_type_Ses[h->type]); + } else { +@@ -1847,61 +1954,77 @@ static int mplib_gr_index(lua_State * L) + } + + static const struct luaL_reg mplib_meta[] = { +- {"__gc", mplib_collect}, +- {"__tostring", mplib_tostring}, +- {NULL, NULL} /* sentinel */ ++ { "__gc", mplib_collect }, ++ { "__tostring", mplib_tostring }, ++ /*tex sentinel */ ++ { NULL, NULL} + }; + + static const struct luaL_reg mplib_fig_meta[] = { +- {"__gc", mplib_fig_collect}, +- {"__tostring", mplib_fig_tostring}, +- {"objects", mplib_fig_body}, +- {"copy_objects", mplib_fig_copy_body}, +- {"filename", mplib_fig_filename}, +- {"postscript", mplib_fig_postscript}, +- {"png", mplib_fig_png}, +- {"svg", mplib_fig_svg}, +- {"boundingbox", mplib_fig_bb}, +- {"width", mplib_fig_width}, +- {"height", mplib_fig_height}, +- {"depth", mplib_fig_depth}, +- {"italcorr", mplib_fig_italcorr}, +- {"charcode", mplib_fig_charcode}, +- {NULL, NULL} /* sentinel */ ++ { "__gc", mplib_fig_collect }, ++ { "__tostring", mplib_fig_tostring }, ++ { "objects", mplib_fig_body }, ++ { "copy_objects", mplib_fig_copy_body }, ++ { "filename", mplib_fig_filename }, ++ { "postscript", mplib_fig_postscript }, ++ { "png", mplib_fig_png }, ++ { "svg", mplib_fig_svg }, ++ { "boundingbox", mplib_fig_bb }, ++ { "width", mplib_fig_width }, ++ { "height", mplib_fig_height }, ++ { "depth", mplib_fig_depth }, ++ { "italcorr", mplib_fig_italcorr }, ++ { "charcode", mplib_fig_charcode }, ++ /*tex sentinel */ ++ { NULL, NULL} + }; + + static const struct luaL_reg mplib_gr_meta[] = { +- {"__gc", mplib_gr_collect}, +- {"__tostring", mplib_gr_tostring}, +- {"__index", mplib_gr_index}, +- {NULL, NULL} /* sentinel */ ++ { "__gc", mplib_gr_collect}, ++ { "__tostring", mplib_gr_tostring}, ++ { "__index", mplib_gr_index}, ++ /*tex sentinel */ ++ { NULL, NULL} + }; + + static const struct luaL_reg mplib_d[] = { +- {"execute", mplib_execute}, +- {"finish", mplib_finish}, +- {"char_width", mplib_charwidth}, +- {"char_height", mplib_charheight}, +- {"char_depth", mplib_chardepth}, +- {"statistics", mplib_statistics}, +- {"solve_path", mplib_solve_path}, +- {"get_numeric", mplib_get_numeric}, +- {"get_number", mplib_get_numeric}, +- {"get_boolean", mplib_get_boolean}, +- {"get_string", mplib_get_string}, +- {NULL, NULL} /* sentinel */ ++ { "execute", mplib_execute }, ++ { "finish", mplib_finish }, ++ { "char_width", mplib_charwidth }, ++ { "char_height", mplib_charheight }, ++ { "char_depth", mplib_chardepth }, ++ { "statistics", mplib_statistics }, ++ { "solve_path", mplib_solve_path }, ++ { "get_numeric", mplib_get_numeric }, ++ { "get_number", mplib_get_numeric }, ++ { "get_boolean", mplib_get_boolean }, ++ { "get_string", mplib_get_string }, ++ { "get_path", mplib_get_path }, ++ /*tex sentinel */ ++ {NULL, NULL } + }; + + static const struct luaL_reg mplib_m[] = { +- {"new", mplib_new}, +- {"version", mplib_version}, +- {"fields", mplib_gr_fields}, +- {"pen_info", mplib_gr_peninfo}, +- {"get_numeric", mplib_get_numeric}, +- {"get_number", mplib_get_numeric}, +- {"get_boolean", mplib_get_boolean}, +- {"get_string", mplib_get_string}, +- {NULL, NULL} /* sentinel */ ++ { "new", mplib_new }, ++ { "version", mplib_version }, ++ { "fields", mplib_gr_fields }, ++ /* indirect */ ++ { "execute", mplib_execute }, ++ { "finish", mplib_finish }, ++ { "char_width", mplib_charwidth }, ++ { "char_height", mplib_charheight }, ++ { "char_depth", mplib_chardepth }, ++ { "statistics", mplib_statistics }, ++ { "solve_path", mplib_solve_path }, ++ /* helpers */ ++ { "pen_info", mplib_gr_peninfo }, ++ { "get_numeric", mplib_get_numeric }, ++ { "get_number", mplib_get_numeric }, ++ { "get_boolean", mplib_get_boolean }, ++ { "get_string", mplib_get_string }, ++ { "get_path", mplib_get_path }, ++ /*tex sentinel */ ++ { NULL, NULL} + }; + + int luaopen_mplib(lua_State * L) +@@ -1909,22 +2032,23 @@ int luaopen_mplib(lua_State * L) + mplib_init_Ses(L); + + luaL_newmetatable(L, MPLIB_GR_METATABLE); +- lua_pushvalue(L, -1); /* push metatable */ +- lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ +- luaL_register(L, NULL, mplib_gr_meta); /* object meta methods */ ++ lua_pushvalue(L, -1); ++ lua_setfield(L, -2, "__index"); ++ luaL_register(L, NULL, mplib_gr_meta); + lua_pop(L, 1); + + luaL_newmetatable(L, MPLIB_FIG_METATABLE); +- lua_pushvalue(L, -1); /* push metatable */ +- lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ +- luaL_register(L, NULL, mplib_fig_meta); /* figure meta methods */ ++ lua_pushvalue(L, -1); ++ lua_setfield(L, -2, "__index"); ++ luaL_register(L, NULL, mplib_fig_meta); + lua_pop(L, 1); + + luaL_newmetatable(L, MPLIB_METATABLE); +- lua_pushvalue(L, -1); /* push metatable */ +- lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ +- luaL_register(L, NULL, mplib_meta); /* meta methods */ +- luaL_register(L, NULL, mplib_d); /* dict methods */ +- luaL_register(L, "mplib", mplib_m); /* module functions */ ++ lua_pushvalue(L, -1); ++ lua_setfield(L, -2, "__index"); ++ luaL_register(L, NULL, mplib_meta); ++ luaL_register(L, NULL, mplib_d); ++ luaL_register(L, "mplib", mplib_m); ++ + return 1; + } +diff --git a/texk/web2c/mplibdir/mp.w b/texk/web2c/mplibdir/mp.w +index 2c956ea5a..d52ad37ce 100644 +--- a/texk/web2c/mplibdir/mp.w ++++ b/texk/web2c/mplibdir/mp.w +@@ -139,7 +139,7 @@ typedef struct MP_instance { + + @ @c + /*\#define DEBUGENVELOPE */ +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + static int DEBUGENVELOPECOUNTER=0; + #define dbg_str(A) printf("\n--[==[%03d DEBUGENVELOPE ]==] %s", DEBUGENVELOPECOUNTER++, #A) + #define dbg_n(A) printf("\n--[==[%03d DEBUGENVELOPE ]==] ['%s']=%s, ", DEBUGENVELOPECOUNTER++, #A, number_tostring(A)) +@@ -176,8 +176,8 @@ static int DEBUGENVELOPECOUNTER=0; + #include /* for |PNG_LIBPNG_VER_STRING|, |png_libpng_ver| */ + /*\#include */ /* for |PIXMAN_VERSION_STRING|, |pixman_version_string()| */ + /*\#include */ /* for |CAIRO_VERSION_STRING|, |cairo_version_string()| */ +-#include /* for |gmp_version| */ +-#include /* for |MPFR_VERSION_STRING|, |mpfr_get_version()| */ ++/*\#include */ /* for |gmp_version| */ ++/*\#include */ /* for |MPFR_VERSION_STRING|, |mpfr_get_version()| */ + #include "mplib.h" + #include "mplibps.h" /* external header */ + /*\#include "mplibsvg.h" */ /* external header */ +@@ -189,7 +189,7 @@ static int DEBUGENVELOPECOUNTER=0; + #include "mpmath.h" /* internal header */ + #include "mpmathdouble.h" /* internal header */ + #include "mpmathdecimal.h" /* internal header */ +-#include "mpmathbinary.h" /* internal header */ ++/*#include "mpmathbinary.h"*/ /* internal header */ + #include "mpstrings.h" /* internal header */ + /* BEGIN PATCH */ + mp_number dx_ap; /* approximation of dx */ +@@ -200,12 +200,19 @@ mp_number ueps_ap; /* epsilon for above approximations */ + boolean is_dxdy, is_dxindyin; + /* END PATCH */ + +-@ We move the {\tt cairo} and {\tt pixman} libraries outside {\tt mp.w}, ++@ We move the {\tt cairo} and {\tt pixman} libraries outside {\tt mp.w}, + to minimize dependencies. + + @c + extern const char *COMPILED_CAIRO_VERSION_STRING; + extern const char* cairo_version_string (void); ++extern const char *COMPILED_MPFR_VERSION_STRING; ++extern const char* mpfr_get_version (void); ++extern void * mp_initialize_binary_math (MP mp) ; ++extern int COMPILED__GNU_MP_VERSION; ++extern int COMPILED__GNU_MP_VERSION_MINOR; ++extern int COMPILED__GNU_MP_VERSION_PATCHLEVEL; ++extern const char * const COMPILED_gmp_version; + extern const char *COMPILED_PIXMAN_VERSION_STRING; + extern const char* pixman_version_string (void); + extern void mp_png_backend_initialize (MP mp); +@@ -2628,7 +2635,7 @@ void mp_new_randoms (MP mp) { + mp->j_random = 54; + } + +-@ To consume a random fraction, the program below will say `|next_random|'. ++@ To consume a random fraction, the program below will say `|next_random|'. + Now each number system has its own implementation, + true to the original as much as possibile. + +@@ -2657,7 +2664,7 @@ As said before, now each number system has its own implementation. + @c + /*Unused. + static void mp\_unif\_rand (MP mp, mp\_number *ret, mp\_number x\_orig) { +- mp\_number y; // trial value ++ mp\_number y; // trial value + mp\_number x, abs\_x; + mp\_number u; + new\_fraction (y); +@@ -2896,7 +2903,7 @@ void *do_alloc_node (MP mp, size_t size) { + + @c + void mp_xfree (void *x) { +- if (x != NULL) ++ if (x != NULL) + free (x); + } + void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size) { +@@ -3235,8 +3242,7 @@ mp_random_seed, /* initialize random number generator (\&{randomseed}) */ + mp_message_command, /* communicate to user (\&{message}, \&{errmessage}) */ + mp_every_job_command, /* designate a starting token (\&{everyjob}) */ + mp_delimiters, /* define a pair of delimiters (\&{delimiters}) */ +-mp_special_command, /* output special info (\&{special}) +- or font map info (\&{fontmapfile}, \&{fontmapline}) */ ++mp_special_command, /* output special info (\&{special}) or font map info (\&{fontmapfile}, \&{fontmapline}) */ + mp_write_command, /* write text to a file (\&{write}) */ + mp_type_name, /* declare a type (\&{numeric}, \&{pair}, etc.) */ + mp_left_delimiter, /* the left delimiter of a matching pair */ +@@ -3275,15 +3281,13 @@ mp_left_bracket, /* the operator `\.[' */ + mp_right_bracket, /* the operator `\.]' */ + mp_right_brace, /* the operator `\.{\char`\}}' */ + mp_with_option, /* option for filling (\&{withpen}, \&{withweight}, etc.) */ +-mp_thing_to_add, +- /* variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also}) */ ++mp_thing_to_add, /* variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also}) */ + mp_of_token, /* the operator `\&{of}' */ + mp_to_token, /* the operator `\&{to}' */ + mp_step_token, /* the operator `\&{step}' */ + mp_until_token, /* the operator `\&{until}' */ + mp_within_token, /* the operator `\&{within}' */ +-mp_lig_kern_token, +- /* the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}', etc. */ ++mp_lig_kern_token, /* the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}', etc. */ + mp_assignment, /* the operator `\.{:=}' */ + mp_skip_to, /* the operation `\&{skipto}' */ + mp_bchar_label, /* the operator `\.{\char'174\char'174:}' */ +@@ -4046,6 +4050,7 @@ enum mp_given_internal { + mp_pausing, /* positive to display lines on the terminal before they are read */ + mp_showstopping, /* positive to stop after each \&{show} command */ + mp_fontmaking, /* positive if font metric output is to be produced */ ++ mp_texscriptmode, /* controls spacing in texmode */ + mp_linejoin, /* as in \ps: 0 for mitered, 1 for round, 2 for beveled */ + mp_linecap, /* as in \ps: 0 for butt, 1 for round, 2 for square */ + mp_miterlimit, /* controls miter length as in \ps */ +@@ -4196,6 +4201,8 @@ mp_primitive (mp, "showstopping", mp_internal_quantity, mp_showstopping); + @:mp_showstopping_}{\&{showstopping} primitive@>; + mp_primitive (mp, "fontmaking", mp_internal_quantity, mp_fontmaking); + @:mp_fontmaking_}{\&{fontmaking} primitive@>; ++mp_primitive (mp, "texscriptmode", mp_internal_quantity, mp_texscriptmode); ++@:mp_texscriptmode_}{\&{texscriptmode} primitive@>; + mp_primitive (mp, "linejoin", mp_internal_quantity, mp_linejoin); + @:mp_linejoin_}{\&{linejoin} primitive@>; + mp_primitive (mp, "linecap", mp_internal_quantity, mp_linecap); +@@ -4214,8 +4221,7 @@ mp_primitive (mp, "mpprocset", mp_internal_quantity, mp_procset); + @:mp_procset_}{\&{mpprocset} primitive@>; + mp_primitive (mp, "troffmode", mp_internal_quantity, mp_gtroffmode); + @:troffmode_}{\&{troffmode} primitive@>; +-mp_primitive (mp, "defaultcolormodel", mp_internal_quantity, +- mp_default_color_model); ++mp_primitive (mp, "defaultcolormodel", mp_internal_quantity, mp_default_color_model); + @:mp_default_color_model_}{\&{defaultcolormodel} primitive@>; + mp_primitive (mp, "restoreclipcolor", mp_internal_quantity, mp_restore_clip_color); + @:mp_restore_clip_color_}{\&{restoreclipcolor} primitive@>; +@@ -4273,6 +4279,7 @@ set_internal_string (mp_output_format, mp_intern (mp, "eps")); + set_internal_string (mp_output_format_options, mp_intern (mp, "")); + set_internal_string (mp_number_system, mp_intern (mp, "scaled")); + set_internal_from_number (mp_number_precision, precision_default); ++set_internal_from_number (mp_texscriptmode, unity_t); + #if DEBUG + number_clone (internal_value (mp_tracing_titles), three_t); + number_clone (internal_value (mp_tracing_equations), three_t); +@@ -4320,6 +4327,7 @@ set_internal_name (mp_design_size, xstrdup ("designsize")); + set_internal_name (mp_pausing, xstrdup ("pausing")); + set_internal_name (mp_showstopping, xstrdup ("showstopping")); + set_internal_name (mp_fontmaking, xstrdup ("fontmaking")); ++set_internal_name (mp_texscriptmode, xstrdup ("texscriptmode")); + set_internal_name (mp_linejoin, xstrdup ("linejoin")); + set_internal_name (mp_linecap, xstrdup ("linecap")); + set_internal_name (mp_miterlimit, xstrdup ("miterlimit")); +@@ -4443,6 +4451,8 @@ class numbers in nonstandard extensions of \MP. + @d invalid_class 20 /* bad character in the input */ + @d max_class 20 /* the largest class number */ + ++@d semicolon_class 6 /* the ; */ ++ + @= + #define digit_class 0 /* the class number of \.{0123456789} */ + int char_class[256]; /* the class numbers */ +@@ -4843,10 +4853,26 @@ char *mp_get_string_value (MP mp, const char *s, size_t l) { + return NULL; + } + ++mp_knot mp_get_path_value (MP mp, const char *s, size_t l) { ++ char *ss = mp_xstrdup(mp,s); ++ if (ss) { ++ mp_sym sym = mp_id_lookup(mp,ss,l,false); ++ if (sym != NULL) { ++ if (mp_type(sym->v.data.node) == mp_path_type) { ++ mp_xfree (ss); ++ return (mp_knot) sym->v.data.node->data.p; ++ } ++ } ++ } ++ mp_xfree (ss); ++ return NULL; ++} ++ + @ @= + double mp_get_numeric_value(MP mp,const char *s,size_t l); + int mp_get_boolean_value(MP mp,const char *s,size_t l); + char *mp_get_string_value(MP mp,const char *s,size_t l); ++mp_knot mp_get_path_value(MP mp,const char *s,size_t l); + + @ We need to put \MP's ``primitive'' symbolic tokens into the hash + table, together with their command code (which will be the |eq_type|) +@@ -13270,26 +13296,26 @@ static mp_knot mp_offset_prep (MP mp, mp_knot c, mp_knot h) { + c0 = c; + k_needed = 0; + #ifdef DEBUGENVELOPE +-dbg_nl;dbg_str(--[==[BEGIN]==]);dbg_nl; +-dbg_str(return {);dbg_nl; ++dbg_nl;dbg_str(--[==[BEGIN]==]);dbg_nl; ++dbg_str(return {);dbg_nl; + dbg_n(w0->x_coord); + dbg_n(w0->y_coord); + #endif + do { + q = mp_next_knot (p); + #ifdef DEBUGENVELOPE +-dbg_nl;dbg_open_t;dbg_str(--[==[begin loop]==]);dbg_nl; ++dbg_nl;dbg_open_t;dbg_str(--[==[begin loop]==]);dbg_nl; + dbg_n(p->x_coord);dbg_n(p->y_coord); + dbg_n(p->right_x);dbg_n(p->right_y); + dbg_n(q->left_x);dbg_n(q->left_y); + dbg_n(q->x_coord);dbg_n(q->y_coord); + dbg_n(w0->x_coord); + dbg_n(w0->y_coord); +-#endif ++#endif + @; +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key(end Split the cubic between |p| and |q|);dbg_open_t;dbg_nl; + dbg_n(w->x_coord);dbg_n(w->y_coord); + dbg_n(w0->x_coord);dbg_n(w0->y_coord); +@@ -13301,7 +13327,7 @@ dbg_close_t; dbg_comma;dbg_nl; + #ifdef DEBUGENVELOPE + dbg_n(w0->x_coord);dbg_n(w0->y_coord); + dbg_str(--[==[end loop]==]);dbg_nl; dbg_close_t;dbg_comma;dbg_nl; +-#endif ++#endif + } while (q != c); + #ifdef DEBUGENVELOPE + dbg_key(Fix the offset change);dbg_open_t;dbg_nl; +@@ -13312,7 +13338,7 @@ dbg_str(--[==[end loop]==]);dbg_nl; dbg_close_t;dbg_comma;dbg_nl; + #endif + @; +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_n(p->x_coord);dbg_n(p->y_coord); + dbg_key_ival(info post,mp_knot_info(p));dbg_comma;dbg_nl; + dbg_n(c->x_coord);dbg_n(c->y_coord); +@@ -13412,7 +13438,7 @@ the testcase reported by Bogus\l{}aw Jackowski in tracker id 267, case 52c + on Sarovar.) + + @= +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_comment(Advance |p| to node |q|);dbg_nl; + #endif + q0 = q; +@@ -13437,11 +13463,11 @@ if ((q != q0) && (q != c || c == c0)) + + @ @= + { +- #ifdef DEBUGENVELOPE ++ #ifdef DEBUGENVELOPE + dbg_key(Remove the cubic following p);dbg_open_t;dbg_nl; + dbg_n(p->x_coord);dbg_n(p->y_coord); + dbg_key_ival(pre info p,mp_knot_info(p)); dbg_close_t;dbg_comma;dbg_nl; +- #endif ++ #endif + k_needed = mp_knot_info (p) - zero_off; + if (r == q) { + q = p; +@@ -13459,11 +13485,11 @@ if ((q != q0) && (q != c || c == c0)) + mp->spec_p2 = p; + r = p; + mp_remove_cubic (mp, p); +- #ifdef DEBUGENVELOPE ++ #ifdef DEBUGENVELOPE + dbg_key(Remove the cubic following p);dbg_open_t;dbg_nl; + dbg_n(p->x_coord);dbg_n(p->y_coord); + dbg_key_ival(post info p,mp_knot_info (p)); dbg_close_t;dbg_comma;dbg_nl; +- #endif ++ #endif + } + + +@@ -13532,16 +13558,16 @@ We may have to split a cubic into many pieces before each + piece corresponds to a unique offset. + + @= +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_comment(Split the cubic between |p| and |q|);dbg_nl; + dbg_key(Split the cubic);dbg_open_t;dbg_nl; + dbg_key_ival(pre info p,mp_knot_info(p));dbg_comma; + dbg_n(w0->x_coord);dbg_n(w0->y_coord); +-#endif ++#endif + mp_knot_info (p) = zero_off + k_needed; +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key_ival(post info p,mp_knot_info(p));dbg_close_t;dbg_comma; dbg_nl; +-#endif ++#endif + k_needed = 0; + @; +@@ -13708,7 +13734,7 @@ void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, mp_number + new_number(t2); + new_fraction(s); + new_fraction(t); +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key(mp_fin_offset_prep);dbg_open_t;dbg_nl; + #endif + while (1) { +@@ -13716,7 +13742,7 @@ dbg_key(mp_fin_offset_prep);dbg_open_t;dbg_nl; + ww = mp_next_knot (w); /* a pointer to $w\k$ */ + else + ww = mp_prev_knot (w); /* a pointer to $w_{k-1}$ */ +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_comment(begin iteration); + dbg_open_t;dbg_nl; + dbg_n(w->x_coord);dbg_n(w->y_coord); +@@ -13727,11 +13753,11 @@ dbg_in(rise); + #endif + @; +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_comment(crossing_point); + #endif + crossing_point (t, t0, t1, t2); +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_n(t);dbg_n(t0);dbg_n(t1);dbg_n(t2); + dbg_in(number_greaterequal(t, fraction_one_t)); + dbg_in(turn_amt); +@@ -13743,18 +13769,18 @@ dbg_close_t; dbg_comma;dbg_nl; + else + goto RETURN; + } +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_comment(Split the cubic at $t$ and split off another cubic if the derivative crosses back); + #endif + @; + w = ww; +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_comment(end iteration); + #endif + } + RETURN: +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_comment(RETURN); + dbg_n(t); + #endif +@@ -13766,7 +13792,7 @@ dbg_n(t); + free_number (t0); + free_number (t1); + free_number (t2); +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_close_t; dbg_comma;dbg_nl; + #endif + } +@@ -13782,7 +13808,7 @@ begins to fail. + mp_number abs_du, abs_dv; + new_number (abs_du); + new_number (abs_dv); +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key(Compute test coefficients |(t0,t1,t2)| for $d(t)$ versus...);dbg_open_t;dbg_nl; + #endif + set_number_from_substraction(du, ww->x_coord, w->x_coord); +@@ -13791,7 +13817,7 @@ dbg_key(Compute test coefficients |(t0,t1,t2)| for $d(t)$ versus...);dbg_open_t; + number_abs(abs_du); + number_clone(abs_dv, dv); + number_abs(abs_dv); +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_CUBIC; + dbg_n(w->x_coord);dbg_n(w->y_coord); + dbg_n(ww->x_coord);dbg_n(ww->y_coord); +@@ -13838,7 +13864,7 @@ dbg_in(number_greaterequal(abs_du, abs_dv)); + free_number (abs_dv); + if (number_negative(t0)) + set_number_to_zero(t0); /* should be positive without rounding error */ +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_n(t0);dbg_n(t1);dbg_n(t2); + dbg_close_t; dbg_comma;dbg_nl; + #endif +@@ -13981,19 +14007,19 @@ if (number_zero(dxin) && number_zero(dyin)) { + #ifdef DEBUGENVELOPE + dbg_key(dxin dyin before);dbg_open_t;dbg_nl; + dbg_n(dxin);dbg_n(dyin); +-dbg_close_t;dbg_comma; ++dbg_close_t;dbg_comma; + #endif + #ifdef DEBUGENVELOPE + dbg_key(dxin dyin after);dbg_open_t;dbg_nl; + dbg_n(dxin);dbg_n(dyin); +-dbg_close_t;dbg_comma; ++dbg_close_t;dbg_comma; + #endif + /* BEGIN PATCH */ + #ifdef DEBUGENVELOPE + dbg_key(dx dy dxin dyin after patch);dbg_open_t;dbg_nl; + dbg_n(dx);dbg_n(dy);dbg_n(dx_ap);dbg_n(dy_ap); + dbg_n(dxin);dbg_n(dyin);dbg_n(dxin_ap);dbg_n(dyin_ap); +-dbg_close_t;dbg_comma; ++dbg_close_t;dbg_comma; + #endif + /* END PATCH ****/ + +@@ -14012,13 +14038,13 @@ right.) This code depends on |w0| being the offset for |(dxin,dyin)|. + #ifdef DEBUGENVELOPE + dbg_nl; + dbg_comment(Update |mp_knot_info(p)|);dbg_nl; +-dbg_key(mp_get_turn_amt_dx_dy);dbg_open_t;dbg_str(--[==[call mp_get_turn_amt]==]);dbg_nl; ++dbg_key(mp_get_turn_amt_dx_dy);dbg_open_t;dbg_str(--[==[call mp_get_turn_amt]==]);dbg_nl; + dbg_n(w0->x_coord);dbg_n(w0->y_coord);dbg_n(dx);dbg_n(dy);dbg_in(number_nonnegative(ab_vs_cd)); + dbg_n(ab_vs_cd); + #endif + is_dxdy=true; + turn_amt = mp_get_turn_amt (mp, w0, dx, dy, number_nonnegative(ab_vs_cd)); +- is_dxdy=false; ++ is_dxdy=false; + #ifdef DEBUGENVELOPE + dbg_dn(turn_amt); + dbg_close_t;dbg_comma; +@@ -14078,20 +14104,20 @@ integer mp_get_turn_amt (MP mp, mp_knot w, mp_number dx, mp_number dy, boolean c + ab_vs_cd (t, dy, arg1, dx, arg2); + #ifdef DEBUGENVELOPE + dbg_sp; +- dbg_open_t;dbg_str(--[==[inside mp_get_turn_amt do loop ]==]);dbg_nl; ++ dbg_open_t;dbg_str(--[==[inside mp_get_turn_amt do loop ]==]);dbg_nl; + dbg_n(w->x_coord);dbg_n(w->y_coord);dbg_n(ww->x_coord);dbg_n(ww->y_coord); + dbg_n(t);dbg_n(dy);dbg_n(arg1);dbg_n(dx);dbg_n(arg2); + dbg_n(t_ap);dbg_n(dy_ap);dbg_n(dx_ap);dbg_n(dyin_ap);dbg_n(dxin_ap); + dbg_close_t;dbg_comma; + dbg_in(number_zero(dx) && number_zero(arg1) && number_positive(dy) && number_positive(arg2) && is_dxdy); +- dbg_in(is_dxdy && number_zero(dx) && number_zero(arg1) && number_negative(dy) && number_negative(arg2) && number_positive(dyin_ap)); ++ dbg_in(is_dxdy && number_zero(dx) && number_zero(arg1) && number_negative(dy) && number_negative(arg2) && number_positive(dyin_ap)); + dbg_in(is_dxindyin && number_zero(dx) && number_zero(arg1) && number_positive(dy) && number_positive(arg2) && number_negative(dyin_ap)); + dbg_in(number_zero(dy) && number_zero(arg2) && number_negative(dx) && number_negative(arg1)); + dbg_in(number_zero(dx) && number_zero(arg1) && number_negative(dy) && number_positive(arg2)); + dbg_in(number_zero(dy) && number_zero(arg2) && number_positive(dx) && number_negative(arg1)); + dbg_nl; + #endif +- if (number_negative(t)) ++ if (number_negative(t)) + break; + incr (s); + w = ww; +@@ -14104,7 +14130,7 @@ integer mp_get_turn_amt (MP mp, mp_knot w, mp_number dx, mp_number dy, boolean c + ab_vs_cd (t, dy, arg1, dx, arg2); + #ifdef DEBUGENVELOPE + dbg_sp; +- dbg_open_t;dbg_str(--[==[outside mp_get_turn_amt do loop ]==]);dbg_nl; ++ dbg_open_t;dbg_str(--[==[outside mp_get_turn_amt do loop ]==]);dbg_nl; + dbg_n(w->x_coord);dbg_n(w->y_coord);dbg_n(ww->x_coord);dbg_n(ww->y_coord); + dbg_n(t);dbg_n(dy);dbg_n(arg1);dbg_n(dx);dbg_n(arg2); + dbg_n(t_ap);dbg_n(dy_ap);dbg_n(dx_ap);dbg_n(dyin_ap);dbg_n(dxin_ap); +@@ -14120,7 +14146,7 @@ integer mp_get_turn_amt (MP mp, mp_knot w, mp_number dx, mp_number dy, boolean c + ab_vs_cd (t, dy, arg1, dx, arg2); + #ifdef DEBUGENVELOPE + dbg_sp; +- dbg_open_t;dbg_str(--[==[inside mp_get_turn_amt do loop for t<0 ]==]);dbg_nl; ++ dbg_open_t;dbg_str(--[==[inside mp_get_turn_amt do loop for t<0 ]==]);dbg_nl; + dbg_n(w->x_coord);dbg_n(w->y_coord);dbg_n(ww->x_coord);dbg_n(ww->y_coord); + dbg_n(t);dbg_n(dy);dbg_n(arg1);dbg_n(dx);dbg_n(arg2); + dbg_n(t_ap);dbg_n(dy_ap);dbg_n(dx_ap); +@@ -14173,14 +14199,14 @@ with respect to $d_{k-1}$, and apply |fin_offset_prep| to each part. + + @= + ww = mp_prev_knot (w); +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key(Complete the offset splitting process);dbg_open_t;dbg_nl; + dbg_n(w->x_coord);dbg_n(w->y_coord); + dbg_n(ww->x_coord);dbg_n(ww->y_coord); + dbg_close_t; dbg_comma;dbg_nl; + #endif + @; +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key(after Compute test coeff);dbg_open_t;dbg_nl; + dbg_n(w->x_coord);dbg_n(w->y_coord); + dbg_n(ww->x_coord);dbg_n(ww->y_coord); +@@ -14189,7 +14215,7 @@ dbg_close_t; dbg_comma;dbg_nl; + @; + if (number_greater(t, fraction_one_t)) { +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key(t > fraction_one_t);dbg_open_t;dbg_nl; + dbg_n(p->x_coord);dbg_n(p->y_coord); + dbg_n(w->x_coord);dbg_n(w->y_coord); +@@ -14207,7 +14233,7 @@ dbg_close_t; dbg_comma;dbg_nl; + set_number_from_of_the_way(y1a, t, y0, y1); + set_number_from_of_the_way(y1, t, y1, y2); + set_number_from_of_the_way(y2a, t, y1a, y1); +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key(t <= fraction_one_t);dbg_open_t;dbg_nl; + dbg_n(p->x_coord);dbg_n(p->y_coord); + dbg_n(t); +@@ -14245,7 +14271,7 @@ dbg_close_t; dbg_comma;dbg_nl; + mp_fin_offset_prep (mp, r, ww, x0, x1, x2, y0, y1, y2, -1, (-1 - turn_amt)); + } + } +-#ifdef DEBUGENVELOPE ++#ifdef DEBUGENVELOPE + dbg_key(end Complete the offset splitting process);dbg_open_t;dbg_nl; + dbg_n(w->x_coord);dbg_n(w->y_coord); + dbg_n(w0->x_coord);dbg_n(w0->y_coord); +@@ -14278,7 +14304,7 @@ crossing and the first crossing cannot be antiparallel. + @= + #ifdef DEBUGENVELOPE + dbg_key(Find the first |t| where);dbg_open_t;dbg_nl; +-#endif ++#endif + crossing_point (t, t0, t1, t2); + if (turn_amt >= 0) { + if (number_negative(t2)) { +@@ -14316,7 +14342,7 @@ if (turn_amt >= 0) { + #ifdef DEBUGENVELOPE + dbg_n(t); + dbg_close_t; dbg_comma;dbg_nl; +-#endif ++#endif + + + @ @= +@@ -19845,6 +19871,7 @@ is less than |loop_text|. + @ @= + if (s != NULL) { + int k ; ++ mp_value new_expr; + size_t size = strlen(s); + memset(&new_expr,0,sizeof(mp_value)); + new_number(new_expr.data.n); +@@ -19860,7 +19887,6 @@ if (s != NULL) { + } + limit = (halfword) k; + (void) memcpy ((mp->buffer + mp->first), s, size); +- free(s); + mp->buffer[limit] = xord ('%'); + mp->first = (size_t) (limit + 1); + loc = start; +@@ -19891,26 +19917,42 @@ if (s != NULL) { + } else { + mp_back_input (mp); + if (cur_exp_str ()->len > 0) { +- mp_value new_expr; + char *s = mp->run_script(mp,(const char*) cur_exp_str()->str) ; + @ ++ free(s); + } + } + } + +-@ @= ++@ The |texscriptmode| parameter controls how spaces and newlines get honoured in ++|btex| or |verbatimtex| ... |etex|. The default value is~1. Possible values are: ++0: no newlines, 1: newlines in |verbatimtex|, 2: newlines in |verbatimtex| and ++|etex|, 3: no leading and trailing strip in |verbatimtex|, 4: no leading and ++trailing strip in |verbatimtex| and |btex|. That way the Lua handler can do what ++it likes. An |etex| has to be followed by a space or |;| or be at the end of a ++line and preceded by a space or at the beginning of a line. ++ ++@= + { +- int first ; +- while ((loc < limit - 4) && (mp->buffer[loc] == ' ')) { ++ char *txt = NULL; ++ char *ptr = NULL; ++ int slin = line; ++ int size = 0; ++ int done = 0; ++ int mode = round_unscaled(internal_value(mp_texscriptmode)) ; /* default: 1 */ ++ int verb = cur_mod() == verbatim_code; ++ int first; ++ /* we had a (mandate) trailing space */ ++ if (loc <= limit && mp->char_class[mp->buffer[loc]] == space_class) { + incr(loc); ++ } else { ++ /* maybe issue an error message and quit */ + } +- first = loc ; +- if (mp->buffer[loc-1] == ' ') { +- decr(loc); +- } +- while (loc < limit - 5) { +- if (mp->buffer[loc] == ' ') { +- incr(loc); ++ /* we loop over lines */ ++ first = loc; ++ while (1) { ++ /* we don't need to check when we have less than 4 characters left */ ++ if (loc < limit - 4) { + if (mp->buffer[loc] == 'e') { + incr(loc); + if (mp->buffer[loc] == 't') { +@@ -19918,39 +19960,135 @@ if (s != NULL) { + if (mp->buffer[loc] == 'e') { + incr(loc) ; + if (mp->buffer[loc] == 'x') { +- /* start action */ +- char *s, *txt ; +- int size ; +- mp_value new_expr; +- size = loc - first + 1 - 4 ; +- if (size < 0) { +- size = 0 ; +- } else { +- while ((size > 1) && (mp->buffer[first+size-1] == ' ')) { +- decr(size); +- } ++ /* let's see if we have the right boundary */ ++ if (first == (loc - 3)) { ++ /* when we're at the start of a line no leading space is required */ ++ done = 1; ++ } else if (mp->char_class[mp->buffer[loc - 4]] == space_class) { ++ /* when we're beyond the start of a line a leading space is required */ ++ done = 2; + } +- txt = malloc(size+1); +- if (size > 0) { +- (void) memcpy (txt, mp->buffer + first, size); ++ if (done) { ++ if ((loc + 1) <= limit) { ++ quarterword c = mp->char_class[mp->buffer[loc + 1]] ; ++ if (c != letter_class) { ++ incr(loc) ; ++ /* we're past the 'x' */ ++ break; ++ } else { ++ /* this is no valid etex */ ++ done = 0; ++ } ++ } else { ++ /* when we're at the end of a line we're ok */ ++ incr(loc) ; ++ /* we're past the 'x' */ ++ break; ++ } + } +- txt[size] = '\0'; +- incr(loc); +- s = mp->make_text(mp,txt,(cur_mod() == verbatim_code)) ; /* we could pass the size */ +- @ +- /* done */ +- free(txt); +- break ; +- } else { +- // decr(loc) ; + } + } + } + } ++ } ++ /* no etex seen (yet) */ ++ if (loc >= limit) { ++ if (size) { ++ txt = realloc(txt, size + limit - first + 1); ++ } else { ++ txt = malloc(limit - first + 1); ++ } ++ (void) memcpy (txt + size, mp->buffer + first, limit - first); ++ size += limit - first + 1; ++ if (mode <= 0) { ++ txt[size - 1] = ' '; ++ } else if (verb) { ++ /* modes >= 1 permit a newline in verbatimtex */ ++ txt[size - 1] = '\n'; ++ } else if (mode >= 2) { ++ /* modes >= 2 permit a newline in btex */ ++ txt[size - 1] = '\n'; ++ } else { ++ txt[size - 1] = ' '; ++ } ++ if (move_to_next_line(mp)) { ++ /* we abort the scanning */ ++ goto FATAL_ERROR; ++ } ++ first = loc; + } else { + incr(loc); + } + } ++ if (done) { ++ /* we're past the 'x' */ ++ int l = loc - 5 ; // 4 ++ int n = l - first + 1 ; ++ /* we're before the 'etex' */ ++ if (done == 2) { ++ /* we had ' etex' */ ++ l -= 1; ++ n -= 1; ++ /* we're before the ' etex' */ ++ } ++ if (size) { ++ txt = realloc(txt, size + n + 1); ++ } else { ++ txt = malloc(n + 1); ++ } ++ (void) memcpy (txt + size, mp->buffer + first, n); /* 0 */ ++ size += n; ++ if (verb && mode >= 3) { ++ /* don't strip verbatimtex */ ++ txt[size] = '\0'; ++ ptr = txt; ++ } else if (mode >= 4) { ++ /* don't strip btex */ ++ txt[size] = '\0'; ++ ptr = txt; ++ } else { ++ /* strip trailing whitespace, we have a \0 so we're one off */ ++ /* while ((size > 1) && (mp->char_class[(ASCII_code) txt[size-2]] == space_class || txt[size-2] == '\n')) { */ ++ while ((size > 1) && (mp->char_class[(ASCII_code) txt[size-1]] == space_class || txt[size-1] == '\n')) { ++ decr(size); ++ } ++ /* prune the string */ ++ txt[size] = '\0'; ++ /* strip leading whitespace */ ++ ptr = txt; ++ while ((size > 1) && (mp->char_class[(ASCII_code) ptr[0]] == space_class || ptr[0] == '\n')) { ++ incr(ptr); ++ decr(size); ++ } ++ } ++ /* action */ ++ { ++ char *s = mp->make_text(mp,ptr,verb) ; ++ @ ++ free(s); ++ } ++ free(txt); ++ /* really needed */ ++ mp_get_next(mp); ++ return; ++ } ++ /* ++ we don't recover because in practice the graphic will be broken anyway and ++ we're not really interacting in mplib .. just fix the input ++ */ ++ FATAL_ERROR: ++ { ++ /* line numbers are not always meaningfull so we can get a 0 reported */ ++ char msg[256]; ++ const char *hlp[] = { "An 'etex' is missing at this input level, nothing gets done.", NULL }; ++ if (slin > 0) { ++ mp_snprintf(msg, 256, "No matching 'etex' for '%stex'.", verb ? "verbatim" : "b"); ++ } else { ++ mp_snprintf(msg, 256, "No matching 'etex' for '%stex' in line %d.", verb ? "verbatim" : "b",slin); ++ } ++ mp_error (mp, msg, hlp, false); ++ free(txt); ++ } + } + + @ @= +@@ -19964,7 +20102,7 @@ if (s != NULL) { + mp_value new_expr; + const char *hlp[] = { + "I'm going to flush this expression, since", +- "makete should be followed by a known string.", ++ "maketext should be followed by a known string.", + NULL }; + memset(&new_expr,0,sizeof(mp_value)); + new_number(new_expr.data.n); +@@ -19976,9 +20114,9 @@ if (s != NULL) { + } else { + mp_back_input (mp); + if (cur_exp_str ()->len > 0) { +- mp_value new_expr; + char *s = mp->make_text(mp,(const char*) cur_exp_str()->str,0) ; + @ ++ free(s); + } + } + } +@@ -23295,7 +23433,7 @@ RESTART: + + q = mp_get_value_node (mp); + mp_name_type (q) = mp_capsule; +- if (cur_cmd() == mp_comma) { ++ if (cur_cmd() == mp_comma) { + mp_init_color_node (mp, q); + r = value_node (q); + mp_stash_in (mp, y_part (r)); +@@ -23321,7 +23459,7 @@ RESTART: + } + mp_stash_in (mp, blue_part (r)); + +- if (cur_cmd() == mp_comma) { ++ if (cur_cmd() == mp_comma) { + mp_node t; /* a token */ + mp_init_cmykcolor_node (mp, q); + t = value_node (q); +@@ -23337,13 +23475,13 @@ RESTART: + set_dep_list (cyan_part(t),dep_list ((mp_value_node) red_part(r))); + set_prev_dep (cyan_part(t),prev_dep ((mp_value_node) red_part(r))); + set_mp_link (prev_dep (cyan_part(t)), (mp_node) cyan_part(t)); +- } ++ } + if ( ((mp_type (magenta_part (t))) != mp_independent) && ((mp_type (magenta_part (t))) != mp_known) ) { + /* Copy the dep list */ + set_dep_list (magenta_part(t),dep_list ((mp_value_node) green_part(r))); + set_prev_dep (magenta_part(t),prev_dep ((mp_value_node) green_part(r))); + set_mp_link (prev_dep (magenta_part(t)), (mp_node) magenta_part(t)); +- } ++ } + if ( ((mp_type (yellow_part (t))) != mp_independent) && ((mp_type (yellow_part (t))) != mp_known)) { + /* Copy the dep list */ + set_dep_list (yellow_part(t),dep_list ((mp_value_node) blue_part(r))); +@@ -28985,7 +29123,7 @@ static void mp_set_up_boundingpath (MP mp, mp_node p) { + unsigned char ljoin, lcap; + mp_number miterlim; + mp_knot q = mp_copy_path (mp, cur_exp_knot ()); /* the original path */ +- mp_knot pen; ++ mp_knot pen; + mp_knot qq; + + new_number(miterlim); +@@ -30671,8 +30809,8 @@ void mp_show_library_versions (void) { + fprintf(stdout, "Compiled with pixman %s; using %s\n",COMPILED_PIXMAN_VERSION_STRING, pixman_version_string()); + fprintf(stdout, "Compiled with libpng %s; using %s\n", PNG_LIBPNG_VER_STRING, png_libpng_ver); + fprintf(stdout, "Compiled with zlib %s; using %s\n", ZLIB_VERSION, zlibVersion()); +- fprintf(stdout, "Compiled with mpfr %s; using %s\n", MPFR_VERSION_STRING, mpfr_get_version()); +- fprintf(stdout, "Compiled with gmp %d.%d.%d; using %s\n\n", __GNU_MP_VERSION, __GNU_MP_VERSION_MINOR, __GNU_MP_VERSION_PATCHLEVEL, gmp_version); ++ fprintf(stdout, "Compiled with mpfr %s; using %s\n", COMPILED_MPFR_VERSION_STRING, mpfr_get_version()); ++ fprintf(stdout, "Compiled with gmp %d.%d.%d; using %s\n\n", COMPILED__GNU_MP_VERSION, COMPILED__GNU_MP_VERSION_MINOR, COMPILED__GNU_MP_VERSION_PATCHLEVEL, COMPILED_gmp_version); + } + + @ @= +@@ -33230,7 +33368,7 @@ We may need to cancel skips that span more than 127 lig/kern steps. + + + @ The header could contain ASCII zeroes, so can't use |strdup|. +-The index |j| can be beyond the index |header_last|, hence we ++The index |j| can be beyond the index |header_last|, hence we + have to sure to update the end of stream marker to reflect the + actual position. + +@@ -34555,8 +34693,9 @@ extreme cases so it may have to be shortened on some systems. + + @= + { +- s = xmalloc (7, 1); +- mp_snprintf (s, 7, ".%i", (int) c); ++ s = xmalloc (12, 1); ++ mp_snprintf (s, 12, ".%i", (int) c); ++ s[7]='\0'; + } + + +diff --git a/texk/web2c/mplibdir/mpmath.w b/texk/web2c/mplibdir/mpmath.w +index 772f8042b..6c0ee6d00 100644 +--- a/texk/web2c/mplibdir/mpmath.w ++++ b/texk/web2c/mplibdir/mpmath.w +@@ -1,4 +1,4 @@ +-% $Id$ ++% $Id: mpmath.w 2118 2017-02-15 17:49:54Z luigi $ + % + % This file is part of MetaPost; + % the MetaPost program is in the public domain. +diff --git a/texk/web2c/mplibdir/mpmathbinary.w b/texk/web2c/mplibdir/mpmathbinary.w +index c6df9ced4..f3d9ed947 100644 +--- a/texk/web2c/mplibdir/mpmathbinary.w ++++ b/texk/web2c/mplibdir/mpmathbinary.w +@@ -39,6 +39,19 @@ + #include "mpmp.h" /* internal header */ + #include + #include ++ ++#ifdef HAVE_CONFIG_H ++#include ++const char * const COMPILED_gmp_version = VERSION; ++#else ++const char * const COMPILED_gmp_version = "unknown"; ++#endif ++ ++const char *COMPILED_MPFR_VERSION_STRING = MPFR_VERSION_STRING; ++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 ; ++ + @; + #endif + +diff --git a/texk/web2c/mplibdir/mpmathdouble.w b/texk/web2c/mplibdir/mpmathdouble.w +index 4834876e5..cb1496936 100644 +--- a/texk/web2c/mplibdir/mpmathdouble.w ++++ b/texk/web2c/mplibdir/mpmathdouble.w +@@ -1,4 +1,4 @@ +-% $Id$ ++% $Id: mpmathdouble.w 2118 2017-02-15 17:49:54Z luigi $ + % + % This file is part of MetaPost; + % the MetaPost program is in the public domain. +--- source/texk/web2c/pdftexdir/pdftoepdf.cc.orig 2020-11-07 13:23:37.163328041 +0100 ++++ source/texk/web2c/pdftexdir/pdftoepdf.cc 2020-11-07 13:39:34.880565627 +0100 +@@ -120,7 +120,7 @@ + + static InObj *inObjList; + static UsedEncoding *encodingList; +-static GBool isInit = gFalse; ++static bool isInit = false; + + // -------------------------------------------------------------------- + // Maintain list of open embedded PDF files +@@ -275,7 +275,7 @@ + + static void copyObject(Object *); + +-static void copyName(char *s) ++static void copyName(const char *s) + { + pdf_puts("/"); + for (; *s != 0; s++) { +@@ -292,7 +292,7 @@ + Object obj1; + copyName(obj->dictGetKey(i)); + pdf_puts(" "); +- obj1 = obj->dictGetValNF(i); ++ obj1 = obj->dictGetValNF(i).copy(); + copyObject(&obj1); + pdf_puts("\n"); + } +@@ -310,7 +310,7 @@ + static void copyFontDict(Object * obj, InObj * r) + { + int i, l; +- char *key; ++ const char *key; + if (!obj->isDict()) + pdftex_fail("PDF inclusion: invalid dict type <%s>", + obj->getTypeName()); +@@ -351,7 +351,7 @@ + obj->getTypeName()); + pdf_puts("/ProcSet [ "); + for (i = 0, l = obj->arrayGetLength(); i < l; ++i) { +- procset = obj->arrayGetNF(i); ++ procset = obj->arrayGetNF(i).copy(); + if (!procset.isName()) + pdftex_fail("PDF inclusion: invalid ProcSet entry type <%s>", + procset.getTypeName()); +@@ -382,7 +382,7 @@ + return false; + } + +-static void copyFont(char *tag, Object * fontRef) ++static void copyFont(const char *tag, Object * fontRef) + { + Object fontdict, subtype, basefont, fontdescRef, fontdesc, charset, + stemV; +@@ -406,7 +406,7 @@ + if (fontdict.isDict()) { + subtype = fontdict.dictLookup("Subtype"); + basefont = fontdict.dictLookup("BaseFont"); +- fontdescRef = fontdict.dictLookupNF("FontDescriptor"); ++ fontdescRef = fontdict.dictLookupNF("FontDescriptor").copy(); + if (fontdescRef.isRef()) { + fontdesc = fontdescRef.fetch(xref); + } +@@ -427,7 +427,7 @@ + charset = fontdesc.dictLookup("CharSet"); + if (!charset.isNull() && + charset.isString() && is_subsetable(fontmap)) +- epdf_mark_glyphs(fd, charset.getString()->getCString()); ++ epdf_mark_glyphs(fd, charset.getString()->c_str()); + else + embed_whole_font(fd); + addFontDesc(fontdescRef.getRef(), fd); +@@ -452,7 +452,7 @@ + obj->getTypeName()); + pdf_puts("/Font << "); + for (i = 0, l = obj->dictGetLength(); i < l; ++i) { +- fontRef = obj->dictGetValNF(i); ++ fontRef = obj->dictGetValNF(i).copy(); + if (fontRef.isRef()) + copyFont(obj->dictGetKey(i), &fontRef); + else if (fontRef.isDict()) { // some programs generate pdf with embedded font object +@@ -467,7 +467,7 @@ + pdf_puts(">>\n"); + } + +-static void copyOtherResources(Object * obj, char *key) ++static void copyOtherResources(Object * obj, const char *key) + { + // copies all other resources (write_epdf handles Fonts and ProcSets), + +@@ -554,8 +554,8 @@ + Object obj1; + int i, l, c; + Ref ref; +- char *p; +- GString *s; ++ const char *p; ++ const GString *s; + if (obj->isBool()) { + pdf_printf("%s", obj->getBool()? "true" : "false"); + } else if (obj->isInt()) { +@@ -566,7 +566,7 @@ + pdf_printf("%s", convertNumToPDF(obj->getNum())); + } else if (obj->isString()) { + s = obj->getString(); +- p = s->getCString(); ++ p = s->c_str(); + l = s->getLength(); + if (strlen(p) == (unsigned int) l) { + pdf_puts("("); +@@ -595,7 +595,7 @@ + } else if (obj->isArray()) { + pdf_puts("["); + for (i = 0, l = obj->arrayGetLength(); i < l; ++i) { +- obj1 = obj->arrayGetNF(i); ++ obj1 = obj->arrayGetNF(i).copy(); + if (!obj1.isName()) + pdf_puts(" "); + copyObject(&obj1); +@@ -664,7 +664,7 @@ + ("PDF inclusion: CID fonts are not supported" + " (try to disable font replacement to fix this)"); + } +- if ((s = ((Gfx8BitFont *) r->font)->getCharName(i)) != 0) ++ if ((s = (char *) ((Gfx8BitFont *) r->font)->getCharName(i)) != 0) + glyphNames[i] = s; + else + glyphNames[i] = notdef; +@@ -683,7 +683,7 @@ + } + + // get the pagebox according to the pagebox_spec +-static PDFRectangle *get_pagebox(Page * page, int pagebox_spec) ++static const PDFRectangle *get_pagebox(Page * page, int pagebox_spec) + { + if (pagebox_spec == pdfboxspecmedia) + return page->getMediaBox(); +@@ -715,7 +715,7 @@ + { + PdfDocument *pdf_doc; + Page *page; +- PDFRectangle *pagebox; ++ const PDFRectangle *pagebox; + #ifdef POPPLER_VERSION + int pdf_major_version_found, pdf_minor_version_found; + #else +@@ -723,9 +723,9 @@ + #endif + // initialize + if (!isInit) { +- globalParams = new GlobalParams(); +- globalParams->setErrQuiet(gFalse); +- isInit = gTrue; ++ globalParams = std::unique_ptr(new GlobalParams()); ++ globalParams->setErrQuiet(false); ++ isInit = true; + } + // open PDF file + pdf_doc = find_add_document(image_name); +@@ -757,15 +757,14 @@ + if (page_name) { + // get page by name + GString name(page_name); +- LinkDest *link = pdf_doc->doc->findDest(&name); ++ std::unique_ptr link = pdf_doc->doc->findDest(&name); + if (link == 0 || !link->isOk()) + pdftex_fail("PDF inclusion: invalid destination <%s>", page_name); + Ref ref = link->getPageRef(); +- page_num = pdf_doc->doc->getCatalog()->findPage(ref.num, ref.gen); ++ page_num = pdf_doc->doc->getCatalog()->findPage(ref); + if (page_num == 0) + pdftex_fail("PDF inclusion: destination is not a page <%s>", + page_name); +- delete link; + } else { + // get page by number + if (page_num <= 0 || page_num > epdf_num_pages) +@@ -822,7 +821,7 @@ + Object groupDict; + bool writeSepGroup = false; + Object info; +- char *key; ++ const char *key; + char s[256]; + int i, l; + int rotate; +@@ -849,7 +848,7 @@ + pageObj = xref->fetch(pageRef->num, pageRef->gen); + pageDict = pageObj.getDict(); + rotate = page->getRotate(); +- PDFRectangle *pagebox; ++ const PDFRectangle *pagebox; + // write the Page header + pdf_puts("/Type /XObject\n"); + pdf_puts("/Subtype /Form\n"); +@@ -921,13 +920,13 @@ + pdf_puts(stripzeros(s)); + + // Metadata validity check (as a stream it must be indirect) +- dictObj = pageDict->lookupNF("Metadata"); ++ dictObj = pageDict->lookupNF("Metadata").copy(); + if (!dictObj.isNull() && !dictObj.isRef()) + pdftex_warn("PDF inclusion: /Metadata must be indirect object"); + + // copy selected items in Page dictionary except Resources & Group + for (i = 0; pageDictKeys[i] != NULL; i++) { +- dictObj = pageDict->lookupNF(pageDictKeys[i]); ++ dictObj = pageDict->lookupNF(pageDictKeys[i]).copy(); + if (!dictObj.isNull()) { + pdf_newline(); + pdf_printf("/%s ", pageDictKeys[i]); +@@ -936,7 +935,7 @@ + } + + // handle page group +- dictObj = pageDict->lookupNF("Group"); ++ dictObj = pageDict->lookupNF("Group").copy(); + if (!dictObj.isNull()) { + if (pdfpagegroupval == 0) { + // another pdf with page group was included earlier on the +@@ -977,8 +976,8 @@ + } + l = dic1.getLength(); + for (i = 0; i < l; i++) { +- groupDict.dictAdd(copyString(dic1.getKey(i)), +- dic1.getValNF(i)); ++ groupDict.dictAdd(dic1.getKey(i), ++ dic1.getValNF(i).copy()); + } + // end modification + pdf_printf("/Group %ld 0 R\n", (long)pdfpagegroupval); +@@ -1108,6 +1107,6 @@ + delete_document(p); + } + // see above for globalParams +- delete globalParams; ++ globalParams.reset(); + } + } +--- source/texk/web2c/pdftexdir/pdftosrc.cc.orig 2020-11-07 13:23:37.163328041 +0100 ++++ source/texk/web2c/pdftexdir/pdftosrc.cc 2020-11-07 13:40:55.764601960 +0100 +@@ -24,6 +24,15 @@ + POPPLER_VERSION should be defined. + */ + ++#ifdef POPPLER_VERSION ++#include ++#define xpdfVersion POPPLER_VERSION ++#define xpdfString "poppler" ++#else ++#include /* just to get the xpdf version */ ++#define xpdfString "xpdf" ++#endif ++ + #include + + #include +@@ -79,7 +88,7 @@ + exit(1); + } + fileName = new GString(argv[1]); +- globalParams = new GlobalParams(); ++ globalParams = std::unique_ptr(new GlobalParams()); + doc = new PDFDoc(fileName); + if (!doc->isOk()) { + fprintf(stderr, "Invalid PDF file\n"); +@@ -100,7 +109,7 @@ + if (objnum == 0) { + srcStream = catalogDict.dictLookup("SourceObject"); + static char const_SourceFile[] = "SourceFile"; +- if (!srcStream.isStream(const_SourceFile)) { ++ if (!(srcStream.isStream() && srcStream.getDict()->is(const_SourceFile))) { + fprintf(stderr, "No SourceObject found\n"); + exit(1); + } +@@ -109,7 +118,7 @@ + fprintf(stderr, "No SourceName found\n"); + exit(1); + } +- outname = srcName.getString()->getCString(); ++ outname = srcName.getString()->c_str(); + // We cannot free srcName, as objname shares its string. + // srcName.free(); + } else if (objnum > 0) { +@@ -118,7 +127,7 @@ + fprintf(stderr, "Not a Stream object\n"); + exit(1); + } +- sprintf(buf, "%s", fileName->getCString()); ++ sprintf(buf, "%s", fileName->c_str()); + if ((p = strrchr(buf, '.')) == 0) + p = strchr(buf, 0); + if (objgen == 0) +@@ -128,7 +137,7 @@ + outname = buf; + } else { // objnum < 0 means we are extracting the XRef table + extract_xref_table = true; +- sprintf(buf, "%s", fileName->getCString()); ++ sprintf(buf, "%s", fileName->c_str()); + if ((p = strrchr(buf, '.')) == 0) + p = strchr(buf, 0); + sprintf(p, ".xref"); +@@ -156,12 +165,11 @@ + (e->type == xrefEntryFree ? "f" : "n")); + else { // e->offset is the object number of the object stream + Stream *str; +- Lexer *lexer; + Parser *parser; + Object objStr, obj1, obj2; + int nObjects, first, n; + int localOffset = 0; +- Guint firstOffset; ++ unsigned int firstOffset; + + objStr = xref->fetch(e->offset, 0); + assert(objStr.isStream()); +@@ -173,9 +181,8 @@ + + // parse the header: object numbers and offsets + objStr.streamReset(); +- str = new EmbedStream(objStr.getStream(), Object(objNull), gTrue, first); +- lexer = new Lexer(xref, str); +- parser = new Parser(xref, lexer, gFalse); ++ str = new EmbedStream(objStr.getStream(), Object(objNull), true, first); ++ parser = new Parser(xref, str, false); + for (n = 0; n < nObjects; ++n) { + obj1 = parser->getObj(); + obj2 = parser->getObj(); +@@ -207,5 +214,5 @@ + fprintf(stderr, "Cross-reference table extracted to %s\n", outname); + fclose(outfile); + delete doc; +- delete globalParams; ++ globalParams.reset(); + } +diff -up source/texk/web2c/pdftexdir/utils.c.me source/texk/web2c/pdftexdir/utils.c +--- source/texk/web2c/pdftexdir/utils.c.me 2020-11-06 17:34:13.775699638 +0100 ++++ source/texk/web2c/pdftexdir/utils.c 2020-11-06 17:37:29.022637825 +0100 +@@ -32,14 +32,6 @@ with this program. If not, see + #include "ptexlib.h" + #include +-#ifdef POPPLER_VERSION +-#include +-#define xpdfVersion POPPLER_VERSION +-#define xpdfString "poppler" +-#else +-#include /* just to get the xpdf version */ +-#define xpdfString "xpdf" +-#endif + + #define check_nprintf(size_get, size_want) \ + if ((unsigned)(size_get) >= (unsigned)(size_want)) \ +@@ -977,12 +969,10 @@ void initversionstring(char **versions) + { + const_string fmt = + "Compiled with libpng %s; using libpng %s\n" +- "Compiled with zlib %s; using zlib %s\n" +- "Compiled with %s version %s\n"; ++ "Compiled with zlib %s; using zlib %s\n"; + size_t len = strlen(fmt) + + strlen(PNG_LIBPNG_VER_STRING) + strlen(png_libpng_ver) + + strlen(ZLIB_VERSION) + strlen(zlib_version) +- + strlen(xpdfString) + strlen(xpdfVersion) + + 1; + + /* len will be more than enough, because of the placeholder chars in fmt +@@ -990,7 +980,7 @@ void initversionstring(char **versions) + *versions = xmalloc(len); + sprintf(*versions, fmt, + PNG_LIBPNG_VER_STRING, png_libpng_ver, +- ZLIB_VERSION, zlib_version, xpdfString, xpdfVersion); ++ ZLIB_VERSION, zlib_version); + } + + +diff -up source/texk/web2c/luatexdir/luamd5/md5lib.c.me source/texk/web2c/luatexdir/luamd5/md5lib.c +diff -up source/texk/web2c/mplibdir/mpmathbinary.w.me source/texk/web2c/mplibdir/mpmathbinary.w +--- source/texk/web2c/mplibdir/mpmathbinary.w.me 2020-11-07 13:48:29.165195061 +0100 ++++ source/texk/web2c/mplibdir/mpmathbinary.w 2020-11-07 13:50:00.711463667 +0100 +@@ -41,8 +41,9 @@ + #include + + #ifdef HAVE_CONFIG_H +-#include +-const char * const COMPILED_gmp_version = VERSION; ++#define MP_STR_HELPER(x) #x ++#define MP_STR(x) MP_STR_HELPER(x) ++const char * const COMPILED_gmp_version = MP_STR(__GNU_MP_VERSION) "." MP_STR( __GNU_MP_VERSION_MINOR) "." MP_STR(__GNU_MP_VERSION_PATCHLEVEL); + #else + const char * const COMPILED_gmp_version = "unknown"; + #endif +diff -up source/texk/web2c/pdftexdir/pdftoepdf.cc.me source/texk/web2c/pdftexdir/pdftoepdf.cc +--- source/texk/web2c/pdftexdir/pdftoepdf.cc.me 2020-11-07 13:52:29.288114063 +0100 ++++ source/texk/web2c/pdftexdir/pdftoepdf.cc 2020-11-07 13:54:11.092270259 +0100 +@@ -418,7 +418,7 @@ static void copyFont(const char *tag, Ob + && fontdescRef.isRef() + && fontdesc.isDict() + && embeddableFont(&fontdesc) +- && (fontmap = lookup_fontmap(basefont.getName())) != NULL) { ++ && (fontmap = lookup_fontmap((char *)basefont.getName())) != NULL) { + // round /StemV value, since the PDF input is a float + // (see Font Descriptors in PDF reference), but we only store an + // integer, since we don't want to change the struct. +@@ -427,7 +427,7 @@ static void copyFont(const char *tag, Ob + charset = fontdesc.dictLookup("CharSet"); + if (!charset.isNull() && + charset.isString() && is_subsetable(fontmap)) +- epdf_mark_glyphs(fd, charset.getString()->c_str()); ++ epdf_mark_glyphs(fd, (char *)charset.getString()->c_str()); + else + embed_whole_font(fd); + addFontDesc(fontdescRef.getRef(), fd); +diff -up source/texk/web2c/pdftexdir/pdftosrc.cc.me source/texk/web2c/pdftexdir/pdftosrc.cc +--- source/texk/web2c/pdftexdir/pdftosrc.cc.me 2020-11-07 13:54:51.983680256 +0100 ++++ source/texk/web2c/pdftexdir/pdftosrc.cc 2020-11-07 13:55:20.247155069 +0100 +@@ -118,7 +118,7 @@ int main(int argc, char *argv[]) + fprintf(stderr, "No SourceName found\n"); + exit(1); + } +- outname = srcName.getString()->c_str(); ++ outname = (char *)srcName.getString()->c_str(); + // We cannot free srcName, as objname shares its string. + // srcName.free(); + } else if (objnum > 0) { +diff -up source/texk/web2c/xetexdir/pdfimage.cpp.me source/texk/web2c/xetexdir/pdfimage.cpp +--- source/texk/web2c/xetexdir/pdfimage.cpp.me 2020-11-07 19:05:23.805273334 +0100 ++++ source/texk/web2c/xetexdir/pdfimage.cpp 2020-11-07 19:06:44.778390280 +0100 +@@ -79,24 +79,26 @@ pdf_get_rect(char* filename, int page_nu + Page* page = doc->getCatalog()->getPage(page_num); + + PDFRectangle* r; ++ const PDFRectangle* cr; + switch (pdf_box) { + default: + case pdfbox_crop: +- r = page->getCropBox(); ++ cr = page->getCropBox(); + break; + case pdfbox_media: +- r = page->getMediaBox(); ++ cr = page->getMediaBox(); + break; + case pdfbox_bleed: +- r = page->getBleedBox(); ++ cr = page->getBleedBox(); + break; + case pdfbox_trim: +- r = page->getTrimBox(); ++ cr = page->getTrimBox(); + break; + case pdfbox_art: +- r = page->getArtBox(); ++ cr = page->getArtBox(); + break; + } ++ r = new PDFRectangle (cr->x1, cr->y1, cr->x2, cr->y2); + + int RotAngle = 0; + RotAngle = (int)page->getRotate() % 360; +diff -up source/texk/web2c/xetexdir/XeTeX_ext.c.me source/texk/web2c/xetexdir/XeTeX_ext.c +--- source/texk/web2c/xetexdir/XeTeX_ext.c.me 2020-11-07 19:03:59.638390812 +0100 ++++ source/texk/web2c/xetexdir/XeTeX_ext.c 2020-11-07 19:05:05.683581239 +0100 +@@ -38,7 +38,6 @@ authorization from the copyright holders + + #include + +-#include + #include + #include + #include +commit 91d642115acc57be0abfabf36567fb905dd67f30 +Author: Luigi Scarso +Date: Wed Sep 5 21:32:42 2018 +0000 + + pplib for luatex + + git-svn-id: svn://tug.org/texlive/trunk/Build/source@48592 c570f23f-e606-0410-a88d-b1316a301751 + +diff --git a/texk/web2c/luatexdir/luapplib/ppapi.h b/texk/web2c/luatexdir/luapplib/ppapi.h +new file mode 100644 +index 000000000..6d591ca05 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppapi.h +@@ -0,0 +1,391 @@ ++ ++#ifndef PP_API_H ++#define PP_API_H ++ ++#include ++#include ++#include ++ ++#include "ppconf.h" ++ ++#define pplib_version "v0.97" ++#define pplib_author "p.jackowski@gust.org.pl" ++ ++/* types */ ++ ++typedef int64_t ppint; ++typedef size_t ppuint; // machine word ++ ++typedef double ppnum; ++typedef char * ppname; ++typedef char * ppstring; ++ ++typedef struct { ++ size_t size; ++ int flags; ++} _ppname; ++ ++typedef struct { ++ size_t size; ++ int flags; ++} _ppstring; ++ ++typedef struct ppobj ppobj; ++typedef struct ppref ppref; ++ ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++typedef struct { ++ ppobj *data; ++ size_t size; ++ ppnum PPARRAY_ALIGNMENT; ++} pparray; ++#else ++typedef struct { ++ ppobj *data; ++ size_t size; ++} pparray; ++#endif ++ ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++typedef struct { ++ ppobj *data; ++ ppname *keys; ++ size_t size; ++ ppnum PPDICT_ALIGNMENT; ++} ppdict; ++ ++#else ++typedef struct { ++ ppobj *data; ++ ppname *keys; ++ size_t size; ++} ppdict; ++#endif ++ ++ ++typedef struct { ++ ppdict *dict; ++ void *input, *I; ++ size_t offset; ++ size_t length; ++ ppstring cryptkey; ++ int flags; ++} ppstream; ++ ++#define PPSTREAM_COMPRESSED (1<<0) ++#define PPSTREAM_ENCRYPTED_AES (1<<1) ++#define PPSTREAM_ENCRYPTED_RC4 (1<<2) ++#define PPSTREAM_ENCRYPTED (PPSTREAM_ENCRYPTED_AES|PPSTREAM_ENCRYPTED_RC4) ++#define PPSTREAM_ENCRYPTED_OWN (1<<3) ++ ++#define ppstream_compressed(stream) ((stream)->flags & PPSTREAM_COMPRESSED) ++#define ppstream_encrypted(stream) ((stream)->flags & PPSTREAM_ENCRYPTED) ++ ++typedef enum { ++ PPNONE = 0, ++ PPNULL, ++ PPBOOL, ++ PPINT, ++ PPNUM, ++ PPNAME, ++ PPSTRING, ++ PPARRAY, ++ PPDICT, ++ PPSTREAM, ++ PPREF ++} ppobjtp; ++ ++PPDEF extern const char * ppobj_kind[]; ++ ++struct ppobj { ++ ppobjtp type; ++ union { ++ ppint integer; ++ ppnum number; ++ ppname name; ++ ppstring string; ++ pparray *array; ++ ppdict *dict; ++ ppstream *stream; ++ ppref *ref; ++ void *any; ++ }; ++}; ++ ++typedef struct ppxref ppxref; ++ ++struct ppref { ++ ppobj object; ++ ppuint number, version; ++ size_t offset; ++ size_t length; ++ ppxref *xref; ++}; ++ ++typedef struct ppdoc ppdoc; ++ ++/* object */ ++ ++#define ppobj_get_null(o) ((o)->type == PPNULL ? 1 : 0) ++#define ppobj_get_bool(o, v) ((o)->type == PPBOOL ? ((v = ((o)->integer != 0)), 1) : 0) ++#define ppobj_get_int(o, v) ((o)->type == PPINT ? ((v = (o)->integer), 1) : 0) ++#define ppobj_get_uint(o, v) ((o)->type == PPINT && (o)->integer >= 0 ? ((v = (ppuint)((o)->integer)), 1) : 0) ++#define ppobj_get_num(o, v) ((o)->type == PPNUM ? ((v = (o)->number), 1) : (((o)->type == PPINT ? ((v = (ppnum)((o)->integer)), 1) : 0))) ++#define ppobj_get_name(o) ((o)->type == PPNAME ? (o)->name : NULL) ++#define ppobj_get_string(o) ((o)->type == PPSTRING ? (o)->string : NULL) ++#define ppobj_get_array(o) ((o)->type == PPARRAY ? (o)->array : NULL) ++#define ppobj_get_dict(o) ((o)->type == PPDICT ? (o)->dict : NULL) ++#define ppobj_get_stream(o) ((o)->type == PPSTREAM ? (o)->stream : NULL) ++#define ppobj_get_ref(o) ((o)->type == PPREF ? (o)->ref : NULL) ++ ++#define ppobj_rget_obj(o) ((o)->type == PPREF ? ppref_obj((o)->ref) : o) ++#define ppobj_rget_null(o) ((o)->type == PPNULL ? 1 : ((o)->type == PPREF ? ppobj_get_null(ppref_obj((o)->ref)) : 0)) ++#define ppobj_rget_bool(o, v) ((o)->type == PPBOOL ? ((v = ((o)->integer != 0)), 1) : ((o)->type == PPREF ? ppobj_get_bool(ppref_obj((o)->ref), v) : 0)) ++#define ppobj_rget_int(o, v) ((o)->type == PPINT ? ((v = (o)->integer), 1) : ((o)->type == PPREF ? ppobj_get_int(ppref_obj((o)->ref), v) : 0)) ++#define ppobj_rget_uint(o, v) ((o)->type == PPINT && (o)->integer >= 0 ? ((v = (ppuint)((o)->integer)), 1) : ((o)->type == PPREF ? ppobj_get_uint(ppref_obj((o)->ref), v) : 0)) ++#define ppobj_rget_num(o, v) ((o)->type == PPNUM ? ((v = (o)->number), 1) : (((o)->type == PPINT ? ((v = (ppnum)((o)->integer)), 1) : ((o)->type == PPREF ? ppobj_get_num(ppref_obj((o)->ref), v) : 0)))) ++#define ppobj_rget_name(o) ((o)->type == PPNAME ? (o)->name : ((o)->type == PPREF ? ppobj_get_name(ppref_obj((o)->ref)) : NULL)) ++#define ppobj_rget_string(o) ((o)->type == PPSTRING ? (o)->string : ((o)->type == PPREF ? ppobj_get_string(ppref_obj((o)->ref)) : NULL)) ++#define ppobj_rget_array(o) ((o)->type == PPARRAY ? (o)->array : ((o)->type == PPREF ? ppobj_get_array(ppref_obj((o)->ref)) : NULL)) ++#define ppobj_rget_dict(o) ((o)->type == PPDICT ? (o)->dict : ((o)->type == PPREF ? ppobj_get_dict(ppref_obj((o)->ref)) : NULL)) ++#define ppobj_rget_stream(o) ((o)->type == PPSTREAM ? (o)->stream : ((o)->type == PPREF ? ppobj_get_stream(ppref_obj((o)->ref)) : NULL)) ++#define ppobj_rget_ref(o) ((o)->type == PPREF ? (o)->ref : ((o)->type == PPREF ? ppobj_get_ref(ppref_obj((o)->ref)) : NULL)) ++ ++#define ppobj_get_bool_value(o) ((o)->type == PPBOOL ? ((o)->integer != 0) : 0) ++#define ppobj_get_int_value(o) ((o)->type == PPINT ? (o)->integer : 0) ++#define ppobj_get_num_value(o) ((o)->type == PPNUM ? (o)->number : ((o)->type == PPINT ? (ppnum)((o)->integer) : 0.0)) ++ ++/* name */ ++ ++#define ppname_is(name, s) (memcmp(name, s, sizeof("" s) - 1) == 0) ++#define ppname_eq(name, n) (memcmp(name, s, ppname_size(name)) == 0) ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#define _ppname_ghost(name) (((const _ppname *)((void *)name)) - 1) ++#else ++#define _ppname_ghost(name) (((const _ppname *)(name)) - 1) ++#endif ++ ++#define ppname_size(name) (_ppname_ghost(name)->size) ++#define ppname_exec(name) (_ppname_ghost(name)->flags & PPNAME_EXEC) ++ ++#define PPNAME_ENCODED (1 << 0) ++#define PPNAME_DECODED (1 << 1) ++#define PPNAME_EXEC (1 << 1) ++ ++PPAPI ppname ppname_decoded (ppname name); ++PPAPI ppname ppname_encoded (ppname name); ++ ++/* string */ ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#define _ppstring_ghost(string) (((const _ppstring *)((void *)string)) - 1) ++#else ++#define _ppstring_ghost(string) (((const _ppstring *)(string)) - 1) ++#endif ++ ++#define ppstring_size(string) (_ppstring_ghost(string)->size) ++ ++#define PPSTRING_ENCODED (1 << 0) ++#define PPSTRING_DECODED (1 << 1) ++//#define PPSTRING_EXEC (1 << 2) // postscript only ++#define PPSTRING_PLAIN 0 ++#define PPSTRING_BASE16 (1 << 3) ++#define PPSTRING_BASE85 (1 << 4) ++#define PPSTRING_UTF16BE (1 << 5) ++#define PPSTRING_UTF16LE (1 << 6) ++ ++#define ppstring_type(string) (_ppstring_ghost(string)->flags & (PPSTRING_BASE16|PPSTRING_BASE85)) ++#define ppstring_hex(string) (_ppstring_ghost(string)->flags & PPSTRING_BASE16) ++#define ppstring_utf(string) (_ppstring_ghost(string)->flags & (PPSTRING_UTF16BE|PPSTRING_UTF16LE)) ++ ++PPAPI ppstring ppstring_decoded (ppstring string); ++PPAPI ppstring ppstring_encoded (ppstring string); ++ ++/* array */ ++ ++#define pparray_size(array) ((array)->size) ++#define pparray_at(array, index) ((array)->data + index) ++ ++#define pparray_first(array, index, obj) ((index) = 0, (obj) = pparray_at(array, 0)) ++#define pparray_next(index, obj) (++(index), ++(obj)) ++ ++#define pparray_get(array, index) (index < (array)->size ? pparray_at(array, index) : NULL) ++ ++PPAPI ppobj * pparray_get_obj (pparray *array, size_t index); ++PPAPI int pparray_get_bool (pparray *array, size_t index, int *v); ++PPAPI int pparray_get_int (pparray *array, size_t index, ppint *v); ++PPAPI int pparray_get_uint (pparray *array, size_t index, ppuint *v); ++PPAPI int pparray_get_num (pparray *array, size_t index, ppnum *v); ++PPAPI ppname pparray_get_name (pparray *array, size_t index); ++PPAPI ppstring pparray_get_string (pparray *array, size_t index); ++PPAPI pparray * pparray_get_array (pparray *array, size_t index); ++PPAPI ppdict * pparray_get_dict (pparray *array, size_t index); ++//PPAPI ppstream * pparray_get_stream (pparray *array, size_t index); ++PPAPI ppref * pparray_get_ref (pparray *array, size_t index); ++ ++PPAPI ppobj * pparray_rget_obj (pparray *array, size_t index); ++PPAPI int pparray_rget_bool (pparray *array, size_t index, int *v); ++PPAPI int pparray_rget_int (pparray *array, size_t index, ppint *v); ++PPAPI int pparray_rget_uint (pparray *array, size_t index, ppuint *v); ++PPAPI int pparray_rget_num (pparray *array, size_t index, ppnum *v); ++PPAPI ppname pparray_rget_name (pparray *array, size_t index); ++PPAPI ppstring pparray_rget_string (pparray *array, size_t index); ++PPAPI pparray * pparray_rget_array (pparray *array, size_t index); ++PPAPI ppdict * pparray_rget_dict (pparray *array, size_t index); ++PPAPI ppstream * pparray_rget_stream (pparray *array, size_t index); ++PPAPI ppref * pparray_rget_ref (pparray *array, size_t index); ++ ++/* dict */ ++ ++#define ppdict_size(dict) ((dict)->size) ++#define ppdict_at(dict, index) ((dict)->data + index) ++#define ppdict_key(dict, index) ((dict)->keys[index]) ++ ++PPAPI ppobj * ppdict_get_obj (ppdict *dict, const char *name); ++PPAPI int ppdict_get_bool (ppdict *dict, const char *name, int *v); ++PPAPI int ppdict_get_int (ppdict *dict, const char *name, ppint *v); ++PPAPI int ppdict_get_uint (ppdict *dict, const char *name, ppuint *v); ++PPAPI int ppdict_get_num (ppdict *dict, const char *name, ppnum *v); ++PPAPI ppname ppdict_get_name (ppdict *dict, const char *name); ++PPAPI ppstring ppdict_get_string (ppdict *dict, const char *name); ++PPAPI pparray * ppdict_get_array (ppdict *dict, const char *name); ++PPAPI ppdict * ppdict_get_dict (ppdict *dict, const char *name); ++//PPAPI ppstream * ppdict_get_stream (ppdict *dict, const char *name); ++PPAPI ppref * ppdict_get_ref (ppdict *dict, const char *name); ++ ++PPAPI ppobj * ppdict_rget_obj (ppdict *dict, const char *name); ++PPAPI int ppdict_rget_bool (ppdict *dict, const char *name, int *v); ++PPAPI int ppdict_rget_int (ppdict *dict, const char *name, ppint *v); ++PPAPI int ppdict_rget_uint (ppdict *dict, const char *name, ppuint *v); ++PPAPI int ppdict_rget_num (ppdict *dict, const char *name, ppnum *v); ++PPAPI ppname ppdict_rget_name (ppdict *dict, const char *name); ++PPAPI ppstring ppdict_rget_string (ppdict *dict, const char *name); ++PPAPI pparray * ppdict_rget_array (ppdict *dict, const char *name); ++PPAPI ppdict * ppdict_rget_dict (ppdict *dict, const char *name); ++PPAPI ppstream * ppdict_rget_stream (ppdict *dict, const char *name); ++PPAPI ppref * ppdict_rget_ref (ppdict *dict, const char *name); ++ ++#define ppdict_first(dict, pkey, obj) (pkey = (dict)->keys, obj = (dict)->data) ++#define ppdict_next(pkey, obj) (++(pkey), ++(obj)) ++ ++/* stream */ ++ ++#define ppstream_dict(stream) ((stream)->dict) ++ ++PPAPI uint8_t * ppstream_first (ppstream *stream, size_t *size, int decode); ++PPAPI uint8_t * ppstream_next (ppstream *stream, size_t *size); ++PPAPI uint8_t * ppstream_all (ppstream *stream, size_t *size, int decode); ++PPAPI void ppstream_done (ppstream *stream); ++ ++PPAPI void ppstream_init_buffers (void); ++PPAPI void ppstream_free_buffers (void); ++ ++/* ref */ ++ ++#define ppref_obj(ref) (&(ref)->object) ++ ++/* xref */ ++ ++PPAPI ppxref * ppdoc_xref (ppdoc *pdf); ++PPAPI ppxref * ppxref_prev (ppxref *xref); ++PPAPI ppdict * ppxref_trailer (ppxref *xref); ++PPAPI ppdict * ppxref_catalog (ppxref *xref); ++PPAPI ppdict * ppxref_info (ppxref *xref); ++PPAPI ppref * ppxref_pages (ppxref *xref); ++PPAPI ppref * ppxref_find (ppxref *xref, ppuint refnumber); ++ ++/* doc */ ++ ++PPAPI ppdoc * ppdoc_load (const char *filename); ++PPAPI ppdoc * ppdoc_mem (const void *data, size_t size); ++PPAPI void ppdoc_free (ppdoc *pdf); ++ ++#define ppdoc_trailer(pdf) ppxref_trailer(ppdoc_xref(pdf)) ++#define ppdoc_catalog(pdf) ppxref_catalog(ppdoc_xref(pdf)) ++#define ppdoc_info(pdf) ppxref_info(ppdoc_xref(pdf)) ++#define ppdoc_pages(pdf) ppxref_pages(ppdoc_xref(pdf)) ++ ++PPAPI ppuint ppdoc_page_count (ppdoc *pdf); ++PPAPI ppref * ppdoc_page (ppdoc *pdf, ppuint index); ++PPAPI ppref * ppdoc_first_page (ppdoc *pdf); ++PPAPI ppref * ppdoc_next_page (ppdoc *pdf); ++ ++PPAPI ppstream * ppcontents_first (ppdict *dict); ++PPAPI ppstream * ppcontents_next (ppdict *dict, ppstream *stream); ++ ++/* crypt */ ++ ++typedef enum { ++ PPCRYPT_NONE = 0, ++ PPCRYPT_DONE = 1, ++ PPCRYPT_FAIL = -1, ++ PPCRYPT_PASS = -2 ++} ppcrypt_status; ++ ++PPAPI ppcrypt_status ppdoc_crypt_status (ppdoc *pdf); ++PPAPI ppcrypt_status ppdoc_crypt_pass (ppdoc *pdf, const void *userpass, size_t userpasslength, const void *ownerpass, size_t ownerpasslength); ++ ++/* permission flags, effect in Acrobat File -> Properties -> Security tab */ ++ ++PPAPI ppint ppdoc_permissions (ppdoc *pdf); ++ ++#define PPDOC_ALLOW_PRINT (1<<2) // printing ++#define PPDOC_ALLOW_MODIFY (1<<3) // filling form fields, signing, creating template pages ++#define PPDOC_ALLOW_COPY (1<<4) // copying, copying for accessibility ++#define PPDOC_ALLOW_ANNOTS (1<<5) // filling form fields, copying, signing ++#define PPDOC_ALLOW_EXTRACT (1<<9) // contents copying for accessibility ++#define PPDOC_ALLOW_ASSEMBLY (1<<10) // (no effect) ++#define PPDOC_ALLOW_PRINT_HIRES (1<<11) // (no effect) ++ ++/* context */ ++ ++typedef struct ppcontext ppcontext; ++ ++PPAPI ppcontext * ppcontext_new (void); ++PPAPI void ppcontext_done (ppcontext *context); ++PPAPI void ppcontext_free (ppcontext *context); ++ ++/* contents parser */ ++ ++PPAPI ppobj * ppcontents_first_op (ppcontext *context, ppstream *stream, size_t *psize, ppname *pname); ++PPAPI ppobj * ppcontents_next_op (ppcontext *context, ppstream *stream, size_t *psize, ppname *pname); ++PPAPI ppobj * ppcontents_parse (ppcontext *context, ppstream *stream, size_t *psize); ++ ++/* boxes and transforms */ ++ ++typedef struct { ++ ppnum lx, ly, rx, ry; ++} pprect; ++ ++PPAPI pprect * pparray_to_rect (pparray *array, pprect *rect); ++PPAPI pprect * ppdict_get_rect (ppdict *dict, const char *name, pprect *rect); ++PPAPI pprect * ppdict_get_box (ppdict *dict, const char *name, pprect *rect); ++ ++typedef struct { ++ ppnum xx, xy, yx, yy, x, y; ++} ppmatrix; ++ ++PPAPI ppmatrix * pparray_to_matrix (pparray *array, ppmatrix *matrix); ++PPAPI ppmatrix * ppdict_get_matrix (ppdict *dict, const char *name, ppmatrix *matrix); ++ ++/* logger */ ++ ++typedef void (*pplogger_callback) (const char *message, void *alien); ++PPAPI void pplog_callback (pplogger_callback logger, void *alien); ++PPAPI int pplog_prefix (const char *prefix); ++ ++/* version */ ++ ++PPAPI const char * ppdoc_version_string (ppdoc *pdf); ++PPAPI int ppdoc_version_number (ppdoc *pdf, int *minor); ++ ++/* doc info */ ++ ++PPAPI size_t ppdoc_file_size (ppdoc *pdf); ++PPAPI ppuint ppdoc_objects (ppdoc *pdf); ++PPAPI size_t ppdoc_memory (ppdoc *pdf, size_t *waste); ++ ++#endif +diff --git a/texk/web2c/luatexdir/luapplib/pparray.c b/texk/web2c/luatexdir/luapplib/pparray.c +new file mode 100644 +index 000000000..25e8c4950 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/pparray.c +@@ -0,0 +1,146 @@ ++ ++#include "pplib.h" ++ ++pparray * pparray_create (const ppobj *stackpos, size_t size, ppheap **pheap) ++{ ++ pparray *array; ++ ppobj *data; ++ array = (pparray *)ppheap_take(pheap, sizeof(pparray) + size * sizeof(ppobj)); ++ array->size = size; ++ array->data = data = (ppobj *)(array + 1); ++ memcpy(data, stackpos, size * sizeof(ppobj)); ++ return array; ++} ++ ++ppobj * pparray_get_obj (pparray *array, size_t index) ++{ ++ return pparray_get(array, index); ++} ++ ++ppobj * pparray_rget_obj (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_obj(obj) : NULL; ++} ++ ++int pparray_get_bool (pparray *array, size_t index, int *v) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_bool(obj, *v) : 0; ++} ++ ++int pparray_rget_bool (pparray *array, size_t index, int *v) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_bool(obj, *v) : 0; ++} ++ ++int pparray_get_int (pparray *array, size_t index, ppint *v) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_int(obj, *v) : 0; ++} ++ ++int pparray_rget_int (pparray *array, size_t index, ppint *v) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_int(obj, *v) : 0; ++} ++ ++int pparray_get_uint (pparray *array, size_t index, ppuint *v) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_uint(obj, *v) : 0; ++} ++ ++int pparray_rget_uint (pparray *array, size_t index, ppuint *v) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_uint(obj, *v) : 0; ++} ++ ++int pparray_get_num (pparray *array, size_t index, ppnum *v) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_num(obj, *v) : 0; ++} ++ ++int pparray_rget_num (pparray *array, size_t index, ppnum *v) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_num(obj, *v) : 0; ++} ++ ++ppname pparray_get_name (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_name(obj) : NULL; ++} ++ ++ppname pparray_rget_name (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_name(obj) : NULL; ++} ++ ++ppstring pparray_get_string (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_string(obj) : NULL; ++} ++ ++ppstring pparray_rget_string (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_string(obj) : NULL; ++} ++ ++pparray * pparray_get_array (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_array(obj) : NULL; ++} ++ ++pparray * pparray_rget_array (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_array(obj) : NULL; ++} ++ ++ppdict * pparray_get_dict (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_dict(obj) : NULL; ++} ++ ++ppdict * pparray_rget_dict (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_dict(obj) : NULL; ++} ++ ++/* ++ppstream * pparray_get_stream (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_stream(obj) : NULL; ++} ++*/ ++ ++ppstream * pparray_rget_stream (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_stream(obj) : NULL; ++} ++ ++ppref * pparray_get_ref (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_get_ref(obj) : NULL; ++} ++ ++ppref * pparray_rget_ref (pparray *array, size_t index) ++{ ++ ppobj *obj; ++ return (obj = pparray_get(array, index)) != NULL ? ppobj_rget_ref(obj) : NULL; ++} +diff --git a/texk/web2c/luatexdir/luapplib/pparray.h b/texk/web2c/luatexdir/luapplib/pparray.h +new file mode 100644 +index 000000000..6fdd8b814 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/pparray.h +@@ -0,0 +1,7 @@ ++ ++#ifndef PP_ARRAY_H ++#define PP_ARRAY_H ++ ++pparray * pparray_create (const ppobj *stack, size_t size, ppheap **pheap); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/ppconf.h b/texk/web2c/luatexdir/luapplib/ppconf.h +new file mode 100644 +index 000000000..57edc205a +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppconf.h +@@ -0,0 +1,76 @@ ++ ++#ifndef PP_CONF_H ++#define PP_CONF_H ++ ++/* ++Aux flags: ++ PPDLL -- indicates a part of a shared library ++ PPEXE -- indicates a host program using shared library functions ++*/ ++ ++#if defined(_WIN32) || defined(_WIN64) ++# ifdef PPDLL ++# define PPAPI __declspec(dllexport) ++# define PPDEF __declspec(dllexport) ++# else ++# ifdef PPEXE ++# define PPAPI __declspec(dllimport) ++# define PPDEF ++# else ++# define PPAPI ++# define PPDEF ++# endif ++# endif ++#else ++# define PPAPI ++# define PPDEF ++#endif ++ ++/* platform vs integers */ ++ ++#if defined(_WIN32) || defined(WIN32) ++# ifdef _MSC_VER ++# if defined(_M_64) || defined(_WIN64) ++# define MSVC64 ++# else ++# define MSVC32 ++# endif ++# else ++# if defined(__MINGW64__) ++# define MINGW64 ++# else ++# if defined(__MINGW32__) ++# define MINGW32 ++# endif ++# endif ++# endif ++#endif ++ ++#if defined(_WIN64) || defined(__MINGW32__) ++# define PPINT64F "%I64d" ++# define PPUINT64F "%I64u" ++#else ++# define PPINT64F "%lld" ++# define PPUINT64F "%llu" ++#endif ++ ++#if defined(MSVC64) ++# define PPINT(N) N##I64 ++# define PPUINT(N) N##UI64 ++# define PPINTF PPINT64F ++# define PPUINTF PPUINT64F ++#elif defined(MINGW64) ++# define PPINT(N) N##LL ++# define PPUINT(N) N##ULL ++# define PPINTF PPINT64F ++# define PPUINTF PPUINT64F ++#else // 32bit or sane 64bit (LP64, where long is long indeed) ++# define PPINT(N) N##L ++# define PPUINT(N) N##UL ++# define PPINTF "%ld" ++# define PPUINTF "%lu" ++#endif ++ ++#define PPSIZEF PPUINTF ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/ppcrypt.c b/texk/web2c/luatexdir/luapplib/ppcrypt.c +new file mode 100644 +index 000000000..780ec78be +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppcrypt.c +@@ -0,0 +1,628 @@ ++ ++#include "utilmd5.h" ++#include "utilsha.h" ++ ++#include "pplib.h" ++ ++/* crypt struct */ ++ ++#define CRYPT_AES (1<<0) ++#define CRYPT_RC4 (1<<1) ++#define CRYPT_MD (1<<2) ++#define CRYPT_NOMD (1<<3) ++ ++static ppcrypt * ppcrypt_create (ppheap **pheap) ++{ ++ ppcrypt *crypt; ++ crypt = (ppcrypt *)ppheap_take(pheap, sizeof(ppcrypt)); ++ memset(crypt, 0, sizeof(ppcrypt)); ++ return crypt; ++} ++ ++static int ppcrypt_type (ppcrypt *crypt, ppname cryptname, ppuint *length, int *cryptflags) ++{ ++ ppdict *filterdict; ++ ppname filtertype; ++ int cryptmd = 0, default256 = 0; ++ ++ if (crypt->map == NULL || (filterdict = ppdict_rget_dict(crypt->map, cryptname)) == NULL) ++ return 0; ++ if ((filtertype = ppdict_get_name(filterdict, "CFM")) == NULL) ++ return 0; ++ *cryptflags = 0; ++ if (ppname_is(filtertype, "V2")) ++ *cryptflags |= CRYPT_RC4; ++ else if (ppname_is(filtertype, "AESV2")) ++ *cryptflags |= CRYPT_AES; ++ else if (ppname_is(filtertype, "AESV3")) ++ *cryptflags |= CRYPT_AES, default256 = 1; ++ else ++ return 0; ++ /* pdf spec page. 134: /Length is said to be optional bit-length of the key, but it seems to be a mistake, as Acrobat ++ produces /Length key with bytes lengths, opposite to /Length key of the main encrypt dict. */ ++ if (length != NULL) ++ if (!ppdict_get_uint(filterdict, "Length", length)) ++ *length = (*cryptflags & CRYPT_RC4) ? 5 : (default256 ? 32 : 16); ++ /* one of metadata flags is set iff there is an explicit EncryptMetadata key */ ++ if (ppdict_get_bool(filterdict, "EncryptMetadata", &cryptmd)) ++ *cryptflags |= (cryptmd ? CRYPT_MD : CRYPT_NOMD); ++ return 1; ++} ++ ++static const uint8_t padding_string[] = { ++ 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, ++ 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A ++}; ++ ++static void ppcrypt_set_userpass (ppcrypt *crypt, const void *userpass, size_t userpasslength) ++{ ++ crypt->userpasslength = userpasslength > 32 ? 32 : userpasslength; ++ memcpy(crypt->userpass, userpass, crypt->userpasslength); ++ memcpy(crypt->userpass + crypt->userpasslength, padding_string, 32 - crypt->userpasslength); ++ crypt->flags |= PPCRYPT_USER_PASSWORD; ++} ++ ++static void ppcrypt_set_ownerpass (ppcrypt *crypt, const void *ownerpass, size_t ownerpasslength) ++{ ++ crypt->ownerpasslength = ownerpasslength > 32 ? 32 : ownerpasslength; ++ memcpy(crypt->ownerpass, ownerpass, crypt->ownerpasslength); ++ memcpy(crypt->ownerpass + crypt->ownerpasslength, padding_string, 32 - crypt->ownerpasslength); ++ crypt->flags |= PPCRYPT_OWNER_PASSWORD; ++} ++ ++/* retrieving user password from owner password and owner key (variant < 5) */ ++ ++static void ppcrypt_retrieve_userpass (ppcrypt *crypt, const void *ownerkey, size_t ownerkeysize) ++{ ++ uint8_t temp[16], rc4key[32], rc4key2[32]; ++ uint8_t i; ++ ppuint k; ++ ++ md5init(); ++ md5add(crypt->ownerpass, 32); ++ md5put(rc4key); ++ if (crypt->algorithm_revision >= 3) ++ { ++ for (i = 0; i < 50; ++i) ++ { ++ pplib_md5(rc4key, 16, temp); ++ memcpy(rc4key, temp, 16); ++ } ++ } ++ rc4_decode_data(ownerkey, ownerkeysize, crypt->userpass, rc4key, crypt->filekeylength); ++ if (crypt->algorithm_revision >= 3) ++ { ++ for (i = 1; i <= 19; ++i) ++ { ++ for (k = 0; k < crypt->filekeylength; ++k) ++ rc4key2[k] = rc4key[k] ^ i; ++ rc4_decode_data(crypt->userpass, 32, crypt->userpass, rc4key2, crypt->filekeylength); ++ } ++ } ++ //crypt->userpasslength = 32; ++ for (crypt->userpasslength = 0; crypt->userpasslength < 32; ++crypt->userpasslength) ++ if (memcmp(&crypt->userpass[crypt->userpasslength], padding_string, 32 - crypt->userpasslength) == 0) ++ break; ++ crypt->flags |= PPCRYPT_USER_PASSWORD; ++} ++ ++/* generating file key; pdf spec p. 125 */ ++ ++static void ppcrypt_filekey (ppcrypt *crypt, const void *ownerkey, size_t ownerkeysize, const void *id, size_t idsize) ++{ ++ uint32_t p; ++ uint8_t permissions[4], temp[16]; ++ int i; ++ ++ md5init(); ++ md5add(crypt->userpass, 32); ++ md5add(ownerkey, ownerkeysize); ++ p = (uint32_t)crypt->permissions; ++ permissions[0] = get_byte1(p); ++ permissions[1] = get_byte2(p); ++ permissions[2] = get_byte3(p); ++ permissions[3] = get_byte4(p); ++ md5add(permissions, 4); ++ md5add(id, idsize); ++ if (crypt->algorithm_revision >= 4 && (crypt->flags & PPCRYPT_NO_METADATA)) ++ md5add("\xFF\xFF\xFF\xFF", 4); ++ md5put(crypt->filekey); ++ if (crypt->algorithm_revision >= 3) ++ { ++ for (i = 0; i < 50; ++i) ++ { ++ pplib_md5(crypt->filekey, (size_t)crypt->filekeylength, temp); ++ memcpy(crypt->filekey, temp, 16); ++ } ++ } ++} ++ ++/* generating userkey for comparison with /U; requires a general file key and id; pdf spec page 126-127 */ ++ ++static void ppcrypt_userkey (ppcrypt *crypt, const void *id, size_t idsize, uint8_t *password_hash) ++{ ++ uint8_t rc4key2[32]; ++ uint8_t i; ++ ppuint k; ++ ++ if (crypt->algorithm_revision <= 2) ++ { ++ rc4_encode_data(padding_string, 32, password_hash, crypt->filekey, crypt->filekeylength); ++ } ++ else ++ { ++ md5init(); ++ md5add(padding_string, 32); ++ md5add(id, idsize); ++ md5put(password_hash); ++ rc4_encode_data(password_hash, 16, password_hash, crypt->filekey, crypt->filekeylength); ++ for (i = 1; i <= 19; ++i) ++ { ++ for (k = 0; k < crypt->filekeylength; ++k) ++ rc4key2[k] = crypt->filekey[k] ^ i; ++ rc4_encode_data(password_hash, 16, password_hash, rc4key2, crypt->filekeylength); ++ } ++ for (i = 16; i < 32; ++i) ++ password_hash[i] = password_hash[i - 16] ^ i; /* arbitrary 16-bytes padding */ ++ } ++} ++ ++/* validating /Perms key (pdf 1.7, /V 5 /R 5 crypt) */ ++ ++static const uint8_t nulliv[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* AES-256 initialization vector */ ++ ++static ppcrypt_status ppcrypt_authenticate_perms (ppcrypt *crypt, ppstring perms) ++{ /* decode /Perms string overriding crypt setup (should match anyway) */ ++ uint8_t permsdata[16]; ++ //int64_t p; ++ //int i; ++ ++ aes_decode_data(perms, ppstring_size(perms), permsdata, crypt->filekey, crypt->filekeylength, nulliv, AES_NULL_PADDING); ++ ++ if (permsdata[9] != 'a' || permsdata[10] != 'd' || permsdata[11] != 'b') ++ return PPCRYPT_FAIL; ++ ++ // do not update permissions flags; they seem to be different inside crypt string ++ //for (p = 0, i = 0; i < 8; ++i) ++ // p = p + (permsdata[i] << (i << 3)); /* low order bytes first */ ++ //crypt->permissions = (ppint)(int32_t)(p & 0x00000000FFFFFFFFLL); /* unset bits 33..64, treat as 32-bit signed int */ ++ ++ if (permsdata[8] == 'T') ++ crypt->flags &= ~PPCRYPT_NO_METADATA; ++ else if (permsdata[8] == 'F') ++ crypt->flags |= PPCRYPT_NO_METADATA; ++ ++ return PPCRYPT_DONE; ++} ++ ++ppcrypt_status ppdoc_crypt_init (ppdoc *pdf, const void *userpass, size_t userpasslength, const void *ownerpass, size_t ownerpasslength) ++{ ++ ppcrypt *crypt; ++ ppdict *trailer, *encrypt; ++ ppobj *obj; ++ ppname name, *pkey; ++ ppstring userkey, ownerkey, userkey_e = NULL, ownerkey_e = NULL; ++ size_t hashlength; ++ pparray *idarray; ++ ppstring id = NULL, perms = NULL; ++ int cryptflags, encryptmd; ++ size_t strkeylength, stmkeylength; ++ ++ uint8_t password_hash[32]; /* /U and /O are 48 bytes strings for AES-256, but here we use only 32 */ ++ uint8_t *validation_salt, *key_salt; ++ ++ /* Every xref could theoretically have a separate encryption info. Not clarified in pdf spec but it seems that the top ++ level xref encryption info is the one to be applied to all objects in all xrefs, including older. */ ++ trailer = ppxref_trailer(pdf->xref); ++ if ((obj = ppdict_get_obj(trailer, "Encrypt")) == NULL) ++ return PPCRYPT_NONE; ++ /* Typically this is all done early, before loading body, so if /Encrypt is indirect reference, it points nothing. We have to load it here. */ ++ obj = ppobj_preloaded(pdf, obj); ++ if (obj->type != PPDICT) ++ return PPCRYPT_FAIL; ++ encrypt = obj->dict; ++ for (ppdict_first(encrypt, pkey, obj); *pkey != NULL; ppdict_next(pkey, obj)) ++ (void)ppobj_preloaded(pdf, obj); ++ ++ if ((name = ppdict_get_name(encrypt, "Filter")) != NULL && !ppname_is(name, "Standard")) ++ return PPCRYPT_FAIL; ++ ++ if ((crypt = pdf->crypt) == NULL) ++ crypt = pdf->crypt = ppcrypt_create(&pdf->heap); ++ if (!ppdict_get_uint(encrypt, "V", &crypt->algorithm_variant)) ++ crypt->algorithm_variant = 0; ++ if (crypt->algorithm_variant < 1 || crypt->algorithm_variant > 5) ++ return PPCRYPT_FAIL; ++ if (!ppdict_get_uint(encrypt, "R", &crypt->algorithm_revision)) ++ return PPCRYPT_FAIL; ++ if (crypt->algorithm_revision >= 3) ++ crypt->flags |= PPCRYPT_OBSCURITY; ++ if (!ppdict_get_int(encrypt, "P", &crypt->permissions)) ++ return PPCRYPT_FAIL; ++ if ((userkey = ppdict_get_string(encrypt, "U")) == NULL || (ownerkey = ppdict_get_string(encrypt, "O")) == NULL) ++ return PPCRYPT_FAIL; ++ userkey = ppstring_decoded(userkey); ++ ownerkey = ppstring_decoded(ownerkey); ++ /* for some reason acrobat pads /O and /U to 127 bytes with NULL, so we don't check the exact length but ensure the minimal */ ++ hashlength = crypt->algorithm_variant < 5 ? 32 : 48; ++ if (ppstring_size(userkey) < hashlength || ppstring_size(ownerkey) < hashlength) ++ return PPCRYPT_FAIL; ++ if (crypt->algorithm_variant < 5) ++ { // get first string from /ID (must not be ref) ++ if ((idarray = ppdict_get_array(trailer, "ID")) == NULL || (id = pparray_get_string(idarray, 0)) == NULL) ++ return PPCRYPT_FAIL; ++ id = ppstring_decoded(id); ++ } ++ else ++ { ++ if ((userkey_e = ppdict_get_string(encrypt, "UE")) == NULL || (ownerkey_e = ppdict_get_string(encrypt, "OE")) == NULL) ++ return PPCRYPT_FAIL; ++ userkey_e = ppstring_decoded(userkey_e); ++ ownerkey_e = ppstring_decoded(ownerkey_e); ++ if (ppstring_size(userkey_e) < 32 || ppstring_size(ownerkey_e) < 32) ++ return PPCRYPT_FAIL; ++ if ((perms = ppdict_get_string(encrypt, "Perms")) == NULL) ++ return PPCRYPT_FAIL; ++ perms = ppstring_decoded(perms); ++ if (ppstring_size(perms) != 16) ++ return PPCRYPT_FAIL; ++ } ++ ++ switch (crypt->algorithm_revision) ++ { ++ case 1: ++ crypt->filekeylength = 5; ++ crypt->flags |= PPCRYPT_RC4; ++ break; ++ case 2: case 3: ++ if (ppdict_get_uint(encrypt, "Length", &crypt->filekeylength)) ++ crypt->filekeylength >>= 3; /* 40..256 bits, 5..32 bytes*/ ++ else ++ crypt->filekeylength = 5; /* 40 bits, 5 bytes */ ++ crypt->flags |= PPCRYPT_RC4; ++ break; ++ case 4: case 5: ++ if ((crypt->map = ppdict_rget_dict(encrypt, "CF")) == NULL) ++ return PPCRYPT_FAIL; ++ for (ppdict_first(crypt->map, pkey, obj); *pkey != NULL; ppdict_next(pkey, obj)) ++ (void)ppobj_preloaded(pdf, obj); ++ /* /EncryptMetadata relevant only for version >=4, may be also provided in crypt filter dictionary; which takes a precedence then? ++ we assume that if there is an explicit EncryptMetadata key, it overrides main encrypt dict flag or default flag (the default is true, ++ meaning that Metadata stream is encrypted as others) */ ++ if (ppdict_get_bool(encrypt, "EncryptMetadata", &encryptmd) && !encryptmd) ++ crypt->flags |= PPCRYPT_NO_METADATA; ++ ++ strkeylength = stmkeylength = 0; ++ /* streams filter */ ++ if ((name = ppdict_get_name(encrypt, "StmF")) != NULL && ppcrypt_type(crypt, name, &stmkeylength, &cryptflags)) ++ { ++ if (cryptflags & CRYPT_AES) ++ crypt->flags |= PPCRYPT_STREAM_AES; ++ else if (cryptflags & CRYPT_RC4) ++ crypt->flags |= PPCRYPT_STREAM_RC4; ++ if (cryptflags & CRYPT_NOMD) ++ crypt->flags |= PPCRYPT_NO_METADATA; ++ else if (cryptflags & CRYPT_MD) ++ crypt->flags &= ~PPCRYPT_NO_METADATA; ++ } /* else identity */ ++ /* strings filter */ ++ if ((name = ppdict_get_name(encrypt, "StrF")) != NULL && ppcrypt_type(crypt, name, &strkeylength, &cryptflags)) ++ { ++ if (cryptflags & CRYPT_AES) ++ crypt->flags |= PPCRYPT_STRING_AES; ++ else if (cryptflags & CRYPT_RC4) ++ crypt->flags |= PPCRYPT_STRING_RC4; ++ } /* else identity */ ++ ++ /* /Length of encrypt dict is irrelevant here, theoretically every crypt filter may have own length... It means that we should ++ actually keep a different file key for streams and strings. But it leads to nonsense, as /U and /O entries refers to a single ++ keylength, without a distinction for strings/streams. So we have to assume /Length is consistent. To expose the limitation: */ ++ if ((crypt->flags & PPCRYPT_STREAM) && (crypt->flags & PPCRYPT_STRING)) ++ if (strkeylength != stmkeylength) ++ return PPCRYPT_FAIL; ++ crypt->filekeylength = stmkeylength ? stmkeylength : strkeylength; ++ if ((crypt->flags & PPCRYPT_STREAM) || (crypt->flags & PPCRYPT_STRING)) ++ if (crypt->filekeylength == 0) ++ return PPCRYPT_FAIL; ++ break; ++ default: ++ return PPCRYPT_FAIL; ++ } ++ ++ /* password */ ++ ++ if (userpass != NULL) ++ { ++ ppcrypt_set_userpass(crypt, userpass, userpasslength); ++ } ++ else if (ownerpass != NULL) ++ { ++ if (crypt->algorithm_variant < 5) // fetch user password from owner password ++ ppcrypt_retrieve_userpass(crypt, ownerkey, ppstring_size(ownerkey)); ++ else // open the document using owner password ++ ppcrypt_set_ownerpass(crypt, ownerpass, ownerpasslength); ++ } ++ else ++ { ++ return PPCRYPT_FAIL; ++ } ++ ++ if (crypt->algorithm_variant < 5) ++ { /* authenticate by comparing a generated vs present /U entry; depending on variant 16 or 32 bytes to compare */ ++ ppcrypt_filekey(crypt, ownerkey, ppstring_size(ownerkey), id, ppstring_size(id)); ++ ppcrypt_userkey(crypt, id, ppstring_size(id), password_hash); /* needs file key so comes after key generation */ ++ if (memcmp(userkey, password_hash, (crypt->algorithm_revision >= 3 ? 16 : 32)) == 0) ++ return PPCRYPT_DONE; ++ return PPCRYPT_PASS; ++ } ++ if (crypt->flags & PPCRYPT_USER_PASSWORD) ++ { ++ validation_salt = (uint8_t *)userkey + 32; ++ key_salt = validation_salt + 8; ++ sha256init(); ++ sha256add(crypt->userpass, crypt->userpasslength); ++ sha256add(validation_salt, 8); ++ sha256put(password_hash); ++ if (memcmp(userkey, password_hash, 32) != 0) ++ return PPCRYPT_PASS; ++ sha256init(); ++ sha256add(crypt->userpass, crypt->userpasslength); ++ sha256add(key_salt, 8); ++ sha256put(password_hash); ++ aes_decode_data(userkey_e, 32, crypt->filekey, password_hash, 32, nulliv, AES_NULL_PADDING); ++ return ppcrypt_authenticate_perms(crypt, perms); ++ } ++ if (crypt->flags & PPCRYPT_OWNER_PASSWORD) ++ { ++ validation_salt = (uint8_t *)ownerkey + 32; ++ key_salt = validation_salt + 8; ++ sha256init(); ++ sha256add(crypt->ownerpass, crypt->ownerpasslength); ++ sha256add(validation_salt, 8); ++ sha256add(userkey, 48); ++ sha256put(password_hash); ++ if (memcmp(ownerkey, password_hash, 32) != 0) ++ return PPCRYPT_PASS; ++ sha256init(); ++ sha256add(crypt->ownerpass, crypt->ownerpasslength); ++ sha256add(key_salt, 8); ++ sha256add(userkey, 48); ++ sha256put(password_hash); ++ aes_decode_data(ownerkey_e, 32, crypt->filekey, password_hash, 32, nulliv, AES_NULL_PADDING); ++ return ppcrypt_authenticate_perms(crypt, perms); ++ } ++ return PPCRYPT_FAIL; // should never get here ++} ++ ++/* decrypting strings */ ++ ++/* ++Since strings are generally rare, but might occur in mass (name trees). We generate decryption key when needed. ++All strings within the same reference are crypted with the same key. Both RC4 and AES algorithms expands ++the crypt key in some way and the result of expansion is the same for the same crypt key. Instead of recreating ++the ky for every string, we backup the initial decryption state. ++*/ ++ ++static void ppcrypt_strkey (ppcrypt *crypt, ppref *ref, int aes) ++{ ++ if (crypt->cryptkeylength > 0) ++ { /* crypt key already generated, just reinitialize crypt states */ ++ if (aes) ++ { /* aes codecs that works on c-strings do not modify aes_state flags at all, so we actually don't need to revitalize the state, ++ we only rewrite an initialization vector, which is modified during crypt procedure */ ++ } ++ else ++ { /* rc4 crypt map is modified during crypt procedure, so here we reinitialize rc4 bytes map */ ++ rc4_map_restore(&crypt->rc4state, &crypt->rc4copy); ++ } ++ return; ++ } ++ ++ if (crypt->algorithm_variant < 5) ++ { ++ crypt->filekey[crypt->filekeylength + 0] = get_byte1(ref->number); ++ crypt->filekey[crypt->filekeylength + 1] = get_byte2(ref->number); ++ crypt->filekey[crypt->filekeylength + 2] = get_byte3(ref->number); ++ crypt->filekey[crypt->filekeylength + 3] = get_byte1(ref->version); ++ crypt->filekey[crypt->filekeylength + 4] = get_byte2(ref->version); ++ ++ if (aes) ++ { ++ crypt->filekey[crypt->filekeylength + 5] = 0x73; ++ crypt->filekey[crypt->filekeylength + 6] = 0x41; ++ crypt->filekey[crypt->filekeylength + 7] = 0x6C; ++ crypt->filekey[crypt->filekeylength + 8] = 0x54; ++ } ++ ++ pplib_md5(crypt->filekey, crypt->filekeylength + (aes ? 9 : 5), crypt->cryptkey); ++ crypt->cryptkeylength = crypt->filekeylength + 5 >= 16 ? 16 : crypt->filekeylength + 5; ++ } ++ else ++ { ++ memcpy(crypt->cryptkey, crypt->filekey, 32); ++ crypt->cryptkeylength = 32; ++ } ++ ++ if (aes) ++ { ++ aes_decode_initialize(&crypt->aesstate, &crypt->aeskeyblock, crypt->cryptkey, crypt->cryptkeylength, NULL); ++ aes_pdf_mode(&crypt->aesstate); ++ } ++ else ++ { ++ rc4_state_initialize(&crypt->rc4state, &crypt->rc4map, crypt->cryptkey, crypt->cryptkeylength); ++ rc4_map_save(&crypt->rc4state, &crypt->rc4copy); ++ } ++} ++ ++int ppstring_decrypt (ppcrypt *crypt, const void *input, size_t size, void *output, size_t *newsize) ++{ ++ int aes, rc4; ++ aes = crypt->flags & PPCRYPT_STRING_AES; ++ rc4 = crypt->flags & PPCRYPT_STRING_RC4; ++ if (aes || rc4) ++ { ++ ppcrypt_strkey(crypt, crypt->ref, aes); ++ if (aes) ++ *newsize = aes_decode_state_data(&crypt->aesstate, input, size, output); ++ else // if (rc4) ++ *newsize = rc4_decode_state_data(&crypt->rc4state, input, size, output); ++ return 1; ++ } ++ return 0; // identity crypt ++} ++ ++/* decrypting streams */ ++ ++/* ++Streams are decrypted everytime when accessing the stream data. We need to be able to get or make ++the key for decryption as long as the stream is alive. And to get the key we need the reference ++number and version, plus document crypt info. First thought was to keep the reference to which ++the stream belongs; stream->ref and accessing the crypt info stream->ref->xref->pdf->crypt. ++It would be ok as long as absolutelly nothing happens with ref and crypt. At some point pplib ++may drift into rewriting support, which would imply ref/xref/crypt/pdf structures modifications. ++So I feel better with generating a crypt key for every stream in encrypted document, paying a cost ++of pplib_md5() for all streams, not necessarily those actually read. ++ ++Key generation is the same as for strings, but different for distinct encryption methods (rc4 vs aes). ++Since streams and strings might theoretically be encrypted with different filters. No reason to cacche ++decryption state here. ++*/ ++ ++static ppstring ppcrypt_stmkey (ppcrypt *crypt, ppref *ref, int aes, ppheap **pheap) ++{ ++ ppstring cryptkeystring; ++ //if (crypt->cryptkeylength > 0) ++ // return; ++ ++ if (crypt->algorithm_variant < 5) ++ { ++ crypt->filekey[crypt->filekeylength + 0] = get_byte1(ref->number); ++ crypt->filekey[crypt->filekeylength + 1] = get_byte2(ref->number); ++ crypt->filekey[crypt->filekeylength + 2] = get_byte3(ref->number); ++ crypt->filekey[crypt->filekeylength + 3] = get_byte1(ref->version); ++ crypt->filekey[crypt->filekeylength + 4] = get_byte2(ref->version); ++ ++ if (aes) ++ { ++ crypt->filekey[crypt->filekeylength + 5] = 0x73; ++ crypt->filekey[crypt->filekeylength + 6] = 0x41; ++ crypt->filekey[crypt->filekeylength + 7] = 0x6C; ++ crypt->filekey[crypt->filekeylength + 8] = 0x54; ++ } ++ ++ pplib_md5(crypt->filekey, crypt->filekeylength + (aes ? 9 : 5), crypt->cryptkey); ++ crypt->cryptkeylength = crypt->filekeylength + 5 >= 16 ? 16 : crypt->filekeylength + 5; // how about 256bits AES?? ++ } ++ else ++ { // we could actually generate this string once, but.. aes itself is way more expensive that we can earn here ++ memcpy(crypt->cryptkey, crypt->filekey, 32); // just for the record ++ crypt->cryptkeylength = 32; ++ } ++ cryptkeystring = ppstring_internal(crypt->cryptkey, crypt->cryptkeylength, pheap); ++ return ppstring_decoded(cryptkeystring); ++} ++ ++void ppstream_info (ppstream *stream, ppdoc *pdf) ++{ // just after the stream creation ++ ppdict *dict; ++ ppobj *fobj, *pobj; ++ pparray *farray; ++ ppname fname, owncryptfilter, tname; ++ ppcrypt *crypt; ++ ppref *ref; ++ size_t i; ++ int owncrypt, cflags; ++ ++ dict = stream->dict; ++ ppdict_rget_uint(dict, "Length", &stream->length); ++ ++ if ((fobj = ppdict_get_obj(dict, "Filter")) != NULL) ++ { ++ fobj = ppobj_preloaded(pdf, fobj); ++ switch (fobj->type) ++ { ++ case PPNAME: ++ stream->flags |= PPSTREAM_COMPRESSED; ++ break; ++ case PPARRAY: ++ if (fobj->array->size > 0) ++ stream->flags |= PPSTREAM_COMPRESSED; ++ break; ++ default: ++ break; ++ } ++ } ++ if ((crypt = pdf->crypt) == NULL || (ref = crypt->ref) == NULL) ++ return; ++ owncrypt = 0; ++ owncryptfilter = NULL; ++ if (fobj != NULL) ++ { ++ if ((pobj = ppdict_get_obj(dict, "DecodeParms")) != NULL) ++ pobj = ppobj_preloaded(pdf, pobj); ++ switch (fobj->type) ++ { ++ case PPNAME: ++ if (ppname_is(fobj->name, "Crypt")) ++ { ++ owncrypt = 1; ++ if (pobj != NULL && pobj->type == PPDICT) // /Type /CryptFilterDecodeParms ++ owncryptfilter = ppdict_get_name(pobj->dict, "Name"); ++ } ++ break; ++ case PPARRAY: ++ farray = fobj->array; ++ for (i = 0; i < farray->size; ++i) ++ { ++ if ((fname = pparray_get_name(farray, i)) != NULL && ppname_is(fname, "Crypt")) ++ { ++ owncrypt = 1; ++ if (pobj != NULL && pobj->type == PPARRAY && (pobj = pparray_get_obj(pobj->array, i)) != NULL) ++ { ++ pobj = ppobj_preloaded(pdf, pobj); ++ if (pobj != NULL && pobj->type == PPDICT) // /Type /CryptFilterDecodeParms ++ owncryptfilter = ppdict_get_name(pobj->dict, "Name"); ++ } ++ break; ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ ++ if (owncrypt) ++ { ++ stream->flags |= PPSTREAM_ENCRYPTED_OWN; ++ /* Seems a common habit to use just /Crypt filter name with no params, which defaults to /Identity. ++ A real example with uncompressed metadata: <> */ ++ if (owncryptfilter != NULL && !ppname_is(owncryptfilter, "Identity")) ++ { ++ if (crypt->map != NULL && ppcrypt_type(crypt, owncryptfilter, NULL, &cflags)) ++ { ++ if (cflags & CRYPT_AES) ++ stream->flags |= PPSTREAM_ENCRYPTED_AES; ++ else if (cflags & CRYPT_RC4) ++ stream->flags |= PPSTREAM_ENCRYPTED_RC4; ++ } ++ } ++ } ++ else ++ { ++ if ((crypt->flags & PPCRYPT_NO_METADATA) && (tname = ppdict_get_name(dict, "Type")) != NULL && ppname_is(tname, "Metadata")) ++ ; /* special treatment of metadata stream; we assume that explicit /Filter /Crypt setup overrides document level setup of EncryptMetadata. */ ++ else ++ { ++ if (crypt->flags & PPCRYPT_STREAM_RC4) ++ stream->flags |= PPSTREAM_ENCRYPTED_RC4; ++ else if (crypt->flags & PPCRYPT_STREAM_AES) ++ stream->flags |= PPSTREAM_ENCRYPTED_AES; ++ } ++ } ++ ++ /* finally, if the stream is encrypted with non-identity crypt (implicit or explicit), make and save the crypt key */ ++ if (stream->flags & PPSTREAM_ENCRYPTED) ++ stream->cryptkey = ppcrypt_stmkey(crypt, ref, ((stream->flags & PPSTREAM_ENCRYPTED_AES) != 0), &pdf->heap); ++} +diff --git a/texk/web2c/luatexdir/luapplib/ppcrypt.h b/texk/web2c/luatexdir/luapplib/ppcrypt.h +new file mode 100644 +index 000000000..fc74cfe37 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppcrypt.h +@@ -0,0 +1,61 @@ ++ ++#ifndef PP_CRYPT_H ++#define PP_CRYPT_H ++ ++#include "ppfilter.h" ++#include "utilcrypt.h" ++#include "utilcryptdef.h" ++ ++typedef struct { ++ ppuint algorithm_variant; /* /V entry of encrypt dict */ ++ ppuint algorithm_revision; /* /R entry of encrypt dict */ ++ ppint permissions; /* /P entry of encrypt dict */ ++ ppdict *map; /* /CF filters map of encrypt dict */ ++ uint8_t userpass[32]; /* padded user password */ ++ size_t userpasslength; /* the length of unpadded user password */ ++ uint8_t ownerpass[32]; /* padded owner password */ ++ size_t ownerpasslength; /* the length of unpadded owner password */ ++ uint8_t filekey[32+5+4]; /* generated file key with extra space of 5..9 bytes for salt */ ++ size_t filekeylength; /* key length; usually 5, 16 or 32 bytes */ ++ uint8_t cryptkey[32]; /* final crypt key for a given reference */ ++ size_t cryptkeylength; /* final crypt key length; usually keylength + 5 */ ++ ppref *ref; /* currently loaded ref (each ref may have a different key) */ ++ union { /* cached crypt states for strings encrypted/decrypted with the same key */ ++ struct { ++ rc4_state rc4state; ++ rc4_map rc4map; ++ rc4_map rc4copy; ++ }; ++ struct { ++ aes_state aesstate; ++ aes_keyblock aeskeyblock; ++ uint8_t ivcopy[16]; ++ }; ++ }; ++ int flags; ++} ppcrypt; ++ ++#define PPCRYPT_NO_METADATA (1<<0) ++#define PPCRYPT_USER_PASSWORD (1<<1) ++#define PPCRYPT_OWNER_PASSWORD (1<<2) ++#define PPCRYPT_STREAM_RC4 (1<<3) ++#define PPCRYPT_STRING_RC4 (1<<4) ++#define PPCRYPT_STREAM_AES (1<<5) ++#define PPCRYPT_STRING_AES (1<<6) ++#define PPCRYPT_OBSCURITY (1<<7) ++ ++#define PPCRYPT_STREAM (PPCRYPT_STREAM_AES|PPCRYPT_STREAM_RC4) ++#define PPCRYPT_STRING (PPCRYPT_STRING_AES|PPCRYPT_STRING_RC4) ++#define PPCRYPT_RC4 (PPCRYPT_STREAM_RC4|PPCRYPT_STRING_RC4) ++#define PPCRYPT_AES (PPCRYPT_STREAM_AES|PPCRYPT_STRING_AES) ++ ++ppcrypt_status ppdoc_crypt_init (ppdoc *pdf, const void *userpass, size_t userpasslength, const void *ownerpass, size_t ownerpasslength); ++int ppstring_decrypt (ppcrypt *crypt, const void *input, size_t size, void *output, size_t *newsize); ++ ++#define ppcrypt_start_ref(crypt, r) ((crypt)->ref = r, (crypt)->cryptkeylength = 0) ++#define ppcrypt_end_ref(crypt) ((crypt)->ref = NULL, (crypt)->cryptkeylength = 0) ++#define ppcrypt_ref(pdf, crypt) ((crypt = (pdf)->crypt) != NULL && crypt->ref != NULL) ++ ++void ppstream_info (ppstream *stream, ppdoc *pdf); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/ppdict.c b/texk/web2c/luatexdir/luapplib/ppdict.c +new file mode 100644 +index 000000000..f04403d83 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppdict.c +@@ -0,0 +1,163 @@ ++ ++#include "pplib.h" ++ ++ppdict * ppdict_create (const ppobj *stackpos, size_t size, ppheap **pheap) ++{ ++ ppdict *dict; ++ ppobj *data; ++ ppname *pkey; ++ size_t i; ++ size >>= 1; ++ dict = (ppdict *)ppheap_take(pheap, sizeof(ppdict) + size * sizeof(ppobj) + (size + 1) * sizeof(ppname *)); // ? + size * sizeof(ppdict_map_node) ++ dict->size = 0; ++ dict->data = data = (ppobj *)(dict + 1); ++ dict->keys = pkey = (ppname *)(dict->data + size); ++ for (i = 0; i < size; ++i, stackpos += 2) ++ { ++ if (stackpos->type != PPNAME) // we need this check at lest for trailer hack ++ continue; ++ *pkey = stackpos->name; ++ *data = *(stackpos + 1); ++ ++pkey, ++data, ++dict->size; ++ } ++ *pkey = NULL; // sentinel for convinient iteration ++ return dict; ++} ++ ++ppobj * ppdict_get_obj (ppdict *dict, const char *name) ++{ // some lookup? will see ++ ppname *pkey; ++ ppobj *obj; ++ for (ppdict_first(dict, pkey, obj); *pkey != NULL; ppdict_next(pkey, obj)) ++ if (strcmp(*pkey, name) == 0) // not ppname_eq() or ppname_is()!! ++ return obj; ++ return NULL; ++} ++ ++ppobj * ppdict_rget_obj (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_obj(obj) : NULL; ++} ++ ++int ppdict_get_bool (ppdict *dict, const char *name, int *v) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_bool(obj, *v) : 0; ++} ++ ++int ppdict_rget_bool (ppdict *dict, const char *name, int *v) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_bool(obj, *v) : 0; ++} ++ ++int ppdict_get_int (ppdict *dict, const char *name, ppint *v) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_int(obj, *v) : 0; ++} ++ ++int ppdict_rget_int (ppdict *dict, const char *name, ppint *v) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_int(obj, *v) : 0; ++} ++ ++int ppdict_get_uint (ppdict *dict, const char *name, ppuint *v) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_uint(obj, *v) : 0; ++} ++ ++int ppdict_rget_uint (ppdict *dict, const char *name, ppuint *v) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_uint(obj, *v) : 0; ++} ++ ++int ppdict_get_num (ppdict *dict, const char *name, ppnum *v) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_num(obj, *v) : 0; ++} ++ ++int ppdict_rget_num (ppdict *dict, const char *name, ppnum *v) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_num(obj, *v) : 0; ++} ++ ++ppname ppdict_get_name (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_name(obj) : NULL; ++} ++ ++ppname ppdict_rget_name (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_name(obj) : NULL; ++} ++ ++ppstring ppdict_get_string (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_string(obj) : NULL; ++} ++ ++ppstring ppdict_rget_string (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_string(obj) : NULL; ++} ++ ++pparray * ppdict_get_array (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_array(obj) : NULL; ++} ++ ++pparray * ppdict_rget_array (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_array(obj) : NULL; ++} ++ ++ppdict * ppdict_get_dict (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_dict(obj) : NULL; ++} ++ ++ppdict * ppdict_rget_dict (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_dict(obj) : NULL; ++} ++ ++/* ++ppstream * ppdict_get_stream (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_stream(obj) : NULL; ++} ++*/ ++ ++ppstream * ppdict_rget_stream (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_stream(obj) : NULL; ++} ++ ++ppref * ppdict_get_ref (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_get_ref(obj) : NULL; ++} ++ ++ppref * ppdict_rget_ref (ppdict *dict, const char *name) ++{ ++ ppobj *obj; ++ return (obj = ppdict_get_obj(dict, name)) != NULL ? ppobj_rget_ref(obj) : NULL; ++} +diff --git a/texk/web2c/luatexdir/luapplib/ppdict.h b/texk/web2c/luatexdir/luapplib/ppdict.h +new file mode 100644 +index 000000000..5a808b772 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppdict.h +@@ -0,0 +1,7 @@ ++ ++#ifndef PP_DICT_H ++#define PP_DICT_H ++ ++ppdict * ppdict_create (const ppobj *stack, size_t size, ppheap **pheap); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/ppfilter.h b/texk/web2c/luatexdir/luapplib/ppfilter.h +new file mode 100644 +index 000000000..583aa8cf4 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppfilter.h +@@ -0,0 +1,10 @@ ++ ++#ifndef PP_FILTER_H ++#define PP_FILTER_H ++ ++#include "utilbasexx.h" ++#include "utilflate.h" ++#include "utillzw.h" ++#include "utilfpred.h" ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/ppheap.c b/texk/web2c/luatexdir/luapplib/ppheap.c +new file mode 100644 +index 000000000..f05e1b7a5 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppheap.c +@@ -0,0 +1,424 @@ ++ ++#include "pplib.h" ++ ++#define PPHEAP_BUFFER 0xFFFF ++#define PPHEAP_WASTE 0x00FF ++ ++#define ppheap_head(heap) ((uint8_t *)((heap) + 1)) ++ ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++# define PPHEAP_ARCH_ARM ++# define PPHEAP_NEED_ALIGNMENT ++#endif ++ ++ ++ ++#ifdef PPHEAP_NEED_ALIGNMENT ++/* Tests has shown that long double seems to work: */ ++/* for 32bit aligned_data has algn: 64 as ppxref and ppref, */ ++/* the other algns divide algn: 64, so it's ok.*/ ++/* Hopefully it's ok for aarch64 too */ ++ ++/* These data are stored in ppheap.c.001t.tu */ ++/* made with gcc -fdump-tree-all-fdump-tree-all */ ++ ++ ++/* @2586 identifier_node strg: ppxref lngt: 6 */ ++/* @2565 record_type name: @2586 size: @679 algn: 64 */ ++/* tag : struct flds: @2587 */ ++ ++ ++/* @2672 identifier_node strg: ppxsec lngt: 6 */ ++/* @2648 type_decl name: @2672 type: @2623 scpe: @221 */ ++/* srcp: ppxref.h:16 chain: @2673 */ ++/* @2623 record_type name: @2648 unql: @2649 size: @593 */ ++/* algn: 32 tag : struct flds: @2650 */ ++ ++ ++/* @2642 identifier_node strg: ppstream lngt: 8 */ ++/* @2615 type_decl name: @2642 type: @2643 scpe: @221 */ ++/* srcp: ppapi.h:54 chain: @2644 */ ++/* @2643 record_type name: @2615 unql: @2614 size: @634 */ ++/* algn: 32 tag : struct flds: @2641 */ ++ ++ ++/* @2765 identifier_node strg: ppkids lngt: 6 */ ++/* @2743 type_decl name: @2765 type: @2766 scpe: @221 */ ++/* srcp: ppload.h:16 chain: @2767 */ ++/* @2766 record_type name: @2743 unql: @2742 size: @19 */ ++/* algn: 32 tag : struct flds: @2764 */ ++ ++ ++/* @2625 identifier_node strg: ppdoc lngt: 5 */ ++/* @2605 record_type name: @2625 size: @2626 algn: 32 */ ++/* tag : struct flds: @2627 */ ++ ++/* @2526 identifier_node strg: ppref lngt: 5 */ ++/* @2513 record_type name: @2526 size: @662 algn: 64 */ ++/* tag : struct flds: @2527 */ ++ ++ ++/* @2595 identifier_node strg: ppdict lngt: 6 */ ++/* @2576 type_decl name: @2595 type: @2596 scpe: @221 */ ++/* srcp: ppapi.h:45 chain: @2597 */ ++/* @2596 record_type name: @2576 unql: @2575 size: @593 */ ++/* algn: 32 tag : struct flds: @2594 */ ++ ++ ++/* @2769 identifier_node strg: ppcrypt_status lngt: 14 */ ++/* @2752 type_decl name: @2769 type: @2770 scpe: @221 */ ++/* srcp: ppapi.h:295 chain: @2771 */ ++/* @2770 enumeral_type name: @2752 unql: @2655 size: @5 */ ++/* algn: 32 prec: 32 sign: signed */ ++/* min : @6 max : @7 csts: @2681 */ ++ ++ ++/* @2558 identifier_node strg: pparray lngt: 7 */ ++/* @2541 type_decl name: @2558 type: @2559 scpe: @221 */ ++/* srcp: ppapi.h:39 chain: @2560 */ ++/* @2559 record_type name: @2541 unql: @2540 size: @19 */ ++/* algn: 32 tag : struct flds: @2557 */ ++ ++ ++/* @2817 identifier_node strg: aligned_data */ ++/* @2801 type_decl name: @2817 type: @2818 scpe: @221 */ ++/* srcp: ppheap.c:22 chain: @2819 */ ++/* @2818 real_type name: @2801 unql: @99 size: @19 */ ++/* algn: 64 prec: 64 */ ++ ++ ++typedef long double aligned_data; ++ ++ ++#define ALIGN_BUFF_BUCKET_SIZE 0x3000 /* heuristic value, found by running few tests */ ++#ifdef __SIZEOF_POINTER__ ++#define SIZE_OF_POINTER __SIZEOF_POINTER__ ++#else ++#define SIZE_OF_POINTER (sizeof(void *)) ++#endif ++ ++typedef struct _simplereg { ++ size_t bucket_pos; ++ size_t bucket_size; ++ size_t heap_instance; ++ aligned_data **align_data_set ; ++} simplereg; ++ ++/* By default static vars are initialized to NULL, but to be clear.. */ ++static simplereg *align_set = NULL ; ++ ++static void align_init_set(void){ ++ size_t size ; ++ ++ if (align_set) { ++ align_set->heap_instance++; ++ return ; ++ } ++ ++ align_set = malloc(sizeof(simplereg)); ++ if (!align_set) { ++ fprintf(stderr,"! fatal error: unable to setup master register for aligned pointers\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ size = SIZE_OF_POINTER*ALIGN_BUFF_BUCKET_SIZE; ++ align_set->align_data_set = malloc(size); ++ if (!align_set->align_data_set) { ++ fprintf(stderr,"! fatal error: unable to setup register for aligned pointers\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ align_set->bucket_pos = 0; ++ align_set->bucket_size = ALIGN_BUFF_BUCKET_SIZE; ++ align_set->heap_instance = 1; ++ memset(align_set->align_data_set, 0UL,size); ++} ++ ++static void align_save_into_set(aligned_data *p){ ++ if (align_set->bucket_pos >= align_set->bucket_size) { ++ size_t new_size; ++ aligned_data **align_data_set_new; ++ ++ if(!align_set->align_data_set){ ++ fprintf(stderr,"! fatal error: unable to save aligned pointer,corrupted set\n"); ++ exit(EXIT_FAILURE); ++ } ++ new_size = (ALIGN_BUFF_BUCKET_SIZE+align_set->bucket_size)*SIZE_OF_POINTER; ++ align_data_set_new = malloc(new_size); ++ if (!align_data_set_new) { ++ fprintf(stderr,"! fatal error: unable to save aligned pointer\n"); ++ exit(EXIT_FAILURE); ++ } ++ memset(align_data_set_new,0,new_size); ++ memcpy(align_data_set_new, align_set->align_data_set, align_set->bucket_size*SIZE_OF_POINTER); ++ free(align_set->align_data_set); ++ align_set->align_data_set = align_data_set_new ; ++ align_set->bucket_size += ALIGN_BUFF_BUCKET_SIZE; ++ } ++ if (align_set->bucket_pos>align_set->bucket_size){ ++ fprintf(stderr,"! fatal error: unable to save aligned pointer, wrong position\n"); ++ exit(EXIT_FAILURE); ++ } ++ align_set->align_data_set[align_set->bucket_pos] = p ; ++ align_set->bucket_pos++; ++ ++} ++ ++static void align_free_set(void){ ++ /* We don't know what heap does with its data, so free here is not secure */ ++ ++ if(align_set){ ++ if (align_set->heap_instance>1) { ++ align_set->heap_instance--; ++ } else if (align_set->heap_instance ==1) { ++ if (align_set->align_data_set){ ++ size_t p; ++ for(p=1;pbucket_pos;p++){ ++ if (align_set->align_data_set[p]) { ++ free(align_set->align_data_set[p]); ++ } ++ } ++ free(align_set->align_data_set); ++ align_set->align_data_set = NULL; ++ } ++ align_set->heap_instance=0; ++ free(align_set); ++ align_set = NULL ; ++ } ++ } ++} ++ ++#endif /* PPHEAP_NEED_ALIGNMENT */ ++ ++ ++static ppheap * ppheap_create (size_t size) ++{ ++ ppheap *heap; ++ heap = (ppheap *)pp_malloc(sizeof(ppheap) + size * sizeof(uint8_t)); ++ heap->size = size; ++ heap->space = size; ++ heap->data = ppheap_head(heap); ++ heap->prev = NULL; ++ return heap; ++} ++ ++ppheap * ppheap_new (void) ++{ ++#ifdef PPHEAP_NEED_ALIGNMENT ++ align_init_set(); ++#endif ++ return ppheap_create(PPHEAP_BUFFER); ++} ++ ++void ppheap_free (ppheap *heap) ++{ ++ ppheap *prev; ++ do ++ { ++ prev = heap->prev; ++ pp_free(heap); ++ heap = prev; ++ } while (heap != NULL); ++#ifdef PPHEAP_NEED_ALIGNMENT ++ align_free_set(); ++#endif ++ ++} ++ ++void ppheap_renew (ppheap *heap) ++{ // free all but first ++ ppheap *prev; ++ if ((prev = heap->prev) != NULL) ++ { ++ heap->prev = NULL; ++ ppheap_free(prev); ++ } ++ heap->size = heap->space; ++ heap->data = ppheap_head(heap); ++} ++ ++static ppheap * ppheap_insert_top (ppheap **pheap, size_t size) ++{ ++ ppheap *heap; ++ heap = ppheap_create(size); ++ heap->prev = (*pheap); ++ *pheap = heap; ++ return heap; ++} ++ ++static ppheap * ppheap_insert_sub (ppheap **pheap, size_t size) ++{ ++ ppheap *heap; ++ heap = ppheap_create(size); ++ heap->prev = (*pheap)->prev; ++ (*pheap)->prev = heap; ++ return heap; ++} ++ ++void * ppheap_take (ppheap **pheap, size_t size) ++{ ++ ppheap *heap; ++ uint8_t *data; ++#ifdef PPHEAP_NEED_ALIGNMENT ++ aligned_data *p_aligned_data; ++#endif ++ heap = *pheap; ++ if (size <= heap->size) ++ ; ++ else if (heap->size <= PPHEAP_WASTE && size <= (PPHEAP_BUFFER >> 1)) ++ heap = ppheap_insert_top(pheap, PPHEAP_BUFFER); ++ else ++ heap = ppheap_insert_sub(pheap, size); ++ data = heap->data; ++ heap->data += size; ++ heap->size -= size; ++#ifdef PPHEAP_NEED_ALIGNMENT ++ /* Todo: only if data%sizeof(aligned_data) != 0 */ ++ p_aligned_data = malloc(size); ++ memcpy(p_aligned_data,data,size); ++ align_save_into_set(p_aligned_data); ++ return (void *)p_aligned_data; ++#else ++ return (void *)data; ++#endif ++ ++} ++ ++ ++/* iof buffer tied to a heap */ ++ ++static ppheap * ppheap_resize (ppheap **pheap, size_t size) ++{ ++ ppheap *heap; ++ heap = ppheap_create(size); ++ heap->prev = (*pheap)->prev; ++ memcpy(heap->data, (*pheap)->data, (*pheap)->space); // (*pheap)->size is irrelevant ++ pp_free(*pheap); ++ *pheap = heap; ++ return heap; ++} ++ ++static size_t ppheap_buffer_handler (iof *O, iof_mode mode) ++{ ++ ppheap **pheap, *heap; ++ size_t objectsize, buffersize, neededsize; ++ uint8_t *copyfrom; ++ switch (mode) ++ { ++ case IOFWRITE: ++ /* apparently more space needed then assumed initsize */ ++ pheap = (ppheap **)O->link; ++ heap = *pheap; ++ objectsize = (size_t)(O->buf - heap->data); ++ buffersize = (size_t)(O->pos - O->buf); ++ neededsize = objectsize + (buffersize << 1); ++ if (ppheap_head(heap) < heap->data) ++ { ++ if (heap->size <= PPHEAP_WASTE && neededsize <= (PPHEAP_BUFFER >> 1)) ++ { ++ heap = ppheap_insert_top(pheap, PPHEAP_BUFFER); ++ copyfrom = heap->prev->data; ++ } ++ else ++ { ++ heap = ppheap_insert_sub(pheap, neededsize); ++ copyfrom = (*pheap)->data; ++ O->link = (void *)(&(*pheap)->prev); ++ } ++ memcpy(heap->data, copyfrom, objectsize + buffersize); ++ } ++ else ++ { /* the buffer was (re)initialized from a new empty heap and occupies its entire space */ ++ // ASSERT(ppheap_head(heap) == heap->data); ++ heap = ppheap_resize(pheap, neededsize); ++ } ++ O->buf = heap->data + objectsize; ++ O->pos = O->buf + buffersize; ++ O->end = heap->data + heap->size; ++ return (size_t)(O->end - O->pos); ++ case IOFFLUSH: ++ return 0; ++ case IOFCLOSE: ++ // O->buf = O->pos = O->end = NULL; ++ // O->link = NULL; ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++iof * ppheap_buffer (ppheap **pheap, size_t objectsize, size_t initsize) ++{ ++ static iof ppheap_buffer_iof = IOF_WRITER_STRUCT(ppheap_buffer_handler, NULL, NULL, 0, 0); ++ ppheap *heap; ++ size_t size; ++ size = objectsize + initsize; ++ heap = *pheap; ++ if (size <= heap->size) ++ ; ++ else if (heap->size <= PPHEAP_WASTE && size <= (PPHEAP_BUFFER >> 1)) ++ { ++ heap = ppheap_create(PPHEAP_BUFFER); ++ heap->prev = (*pheap); ++ *pheap = heap; ++ } ++ else ++ { ++ heap = ppheap_create(size); ++ heap->prev = (*pheap)->prev; ++ (*pheap)->prev = heap; ++ pheap = &(*pheap)->prev; // passed to ppheap_buffer_iof.link ++ } ++ ppheap_buffer_iof.buf = ppheap_buffer_iof.pos = heap->data + objectsize; ++ ppheap_buffer_iof.end = heap->data + heap->size; ++ ppheap_buffer_iof.link = pheap; // ASSERT(*pheap == heap) ++ return &ppheap_buffer_iof; ++} ++ ++/* ++void * ppheap_buffer_data (iof *O, size_t *psize) ++{ ++ ppheap *heap; ++ heap = *((ppheap **)(O->link)); ++ *psize = ppheap_buffer_size(O, heap); ++ return heap->data; ++} ++*/ ++ ++void * ppheap_flush (iof *O, size_t *psize) // not from explicit ppheap ** !!! ++{ ++ ppheap *heap; ++ uint8_t *data; ++ size_t size; ++#ifdef PPHEAP_NEED_ALIGNMENT ++ aligned_data *p_aligned_data; ++#endif ++ heap = *((ppheap **)(O->link)); ++ *psize = ppheap_buffer_size(O, heap); ++ size = *psize; ++ data = heap->data; ++/* heap->data += *psize; ++ heap->size -= *psize; ++*/ ++ heap->data += size; ++ heap->size -= size; ++ // O->buf = O->pos = O->end = NULL; ++ // O->link = NULL; ++ // iof_close(O); ++#ifdef PPHEAP_NEED_ALIGNMENT ++ /* Todo: only if data%sizeof(aligned_data) != 0 */ ++ p_aligned_data = malloc(size); ++ memcpy(p_aligned_data,data,size); ++ align_save_into_set(p_aligned_data); ++ return (void *)p_aligned_data; ++#else ++ return data; ++ ++#endif ++ ++ ++} ++ ++ +diff --git a/texk/web2c/luatexdir/luapplib/ppheap.h b/texk/web2c/luatexdir/luapplib/ppheap.h +new file mode 100644 +index 000000000..128f9c56d +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppheap.h +@@ -0,0 +1,29 @@ ++ ++#ifndef PP_HEAP_H ++#define PP_HEAP_H ++ ++#include "utilmem.h" ++ ++#define pp_malloc util_malloc ++#define pp_callic util_calloc ++#define pprealloc util_realloc ++#define pp_free util_free ++ ++typedef struct ppheap ppheap; ++struct ppheap { ++ size_t size; ++ size_t space; ++ uint8_t *data; ++ ppheap *prev; ++}; ++ ++ppheap * ppheap_new (void); ++void ppheap_free (ppheap *heap); ++void ppheap_renew (ppheap *heap); ++void * ppheap_take (ppheap **pheap, size_t size); ++iof * ppheap_buffer (ppheap **pheap, size_t objectsize, size_t initsize); ++#define ppheap_buffer_size(O, heap) (size_t)((O)->pos - (heap)->data) // == (size_t)(O->buf - heap->data) + (size_t)(O->pos - O->buf); ++//void * ppheap_buffer_data (iof *O, size_t *psize); ++void * ppheap_flush (iof *O, size_t *psize); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/pplib.h b/texk/web2c/luatexdir/luapplib/pplib.h +new file mode 100644 +index 000000000..e753cfa05 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/pplib.h +@@ -0,0 +1,22 @@ ++ ++#ifndef PP_LIB_H ++#define PP_LIB_H ++ ++#include ++#include ++#include ++#include ++ ++#include "utiliof.h" ++#include "utillog.h" ++ ++#include "ppapi.h" ++#include "ppheap.h" ++#include "ppdict.h" ++#include "ppstream.h" ++#include "pparray.h" ++#include "ppcrypt.h" ++#include "ppxref.h" ++#include "ppload.h" ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/ppload.c b/texk/web2c/luatexdir/luapplib/ppload.c +new file mode 100644 +index 000000000..0ad286e57 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppload.c +@@ -0,0 +1,2590 @@ ++ ++#include ++ ++#include "pplib.h" ++ ++const char * ppobj_kind[] = { "none", "null", "bool", "integer", "number", "name", "string", "array", "dict", "stream", "ref" }; ++ ++#define ignored_char(c) (c == 0x20 || c == 0x0A || c == 0x0D || c == 0x09 || c == 0x00) ++#define newline_char(c) (c == 0x0A || c == 0x0D) ++#define IGNORED_CHAR_CASE 0x20: case 0x0A: case 0x0D: case 0x09: case 0x00 ++#define NEWLINE_CHAR_CASE 0x0A: case 0x0D ++#define DIGIT_CHAR_CASE '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9' ++#define OCTAL_CHAR_CASE '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7' ++ ++#define MAX_INT_DIGITS 32 ++ ++#define PP_LENGTH_UNKNOWN ((size_t)-1) ++ ++static const char * ppref_str (ppuint refnumber, ppuint refversion) ++{ ++ static char buffer[MAX_INT_DIGITS + 1 + MAX_INT_DIGITS + 1 + 1 + 1]; ++#if defined(MSVC64)|| defined(MINGW64) ++ sprintf(buffer, PPUINTF " " PPUINTF " R", refnumber, refversion); ++#else ++ sprintf(buffer, PPUINTF " " PPUINTF " R", (unsigned long)(refnumber), (unsigned long)(refversion)); ++#endif ++ return buffer; ++} ++ ++/* name */ ++ ++// pdf name delimiters: 0..32, ()<>[]{}/% ++// # treated specially ++// .+- are valid part of name; keep in mind names such as -| | |- .notdef ABCDEF+Font etc. ++const char ppname_byte_lookup[] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 1, 1, '#', 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ++}; ++ ++#define PPNAME_INIT (8+1) ++ ++#define ppname_flush(O, ghost, siz, flgs) \ ++ (iof_put(O, '\0'), \ ++ ghost = (_ppname *)ppheap_flush(O, &siz), \ ++ ghost->flags = flgs, \ ++ ghost->size = siz - sizeof(_ppname) - 1, \ ++ (ppname)(ghost + 1)) ++ ++#define ppname_flush_with_ego(O, ghost, siz, flgs) \ ++ (iof_put(O, '\0'), \ ++ iof_ensure(O, sizeof(ppname *)), \ ++ O->pos += sizeof(ppname *), \ ++ ghost = (_ppname *)ppheap_flush(O, &siz), \ ++ ghost->flags = flgs, \ ++ ghost->size = siz - sizeof(_ppname) - 1 - sizeof(ppname *), \ ++ (ppname)(ghost + 1)) ++ ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#define ppname_set_alter_ego(name, ghost, ego) do {\ ++ ppname temp;\ ++ ppname *temp1;\ ++ temp = (name + (ghost)->size + 1) ; \ ++ temp1 = (ppname *)((void*)temp); \ ++ *temp1= ego; \ ++ }while(0) ++#else ++#define ppname_set_alter_ego(name, ghost, ego) (*((ppname *)(name + (ghost)->size + 1)) = ego) ++#endif ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#define ppname_get_alter_ego(name) (*((ppname *)( (void*)(name + ppname_size(name) + 1)))) ++#else ++#define ppname_get_alter_ego(name) (*((ppname *)(name + ppname_size(name) + 1))) ++#endif ++ ++ ++ ++ ++ ++static ppname ppscan_name (iof *I, ppheap **pheap) ++{ ++ int c, decode; ++ iof *O; ++ _ppname *ghost1, *ghost2; ++ ppname encoded, decoded; ++ size_t size; ++ const char *p, *e; ++ uint8_t v1, v2; ++ O = ppheap_buffer(pheap, sizeof(_ppname), PPNAME_INIT); ++ for (decode = 0, c = iof_char(I); c >= 0 && ppname_byte_lookup[c]; c = iof_next(I)) ++ { ++ if (c == '#') ++ decode = 1; ++ iof_put(O, c); ++ } ++ if (!decode) ++ return ppname_flush(O, ghost1, size, 0); ++ encoded = ppname_flush_with_ego(O, ghost1, size, 0|PPNAME_ENCODED); ++ O = ppheap_buffer(pheap, sizeof(_ppname), PPNAME_INIT); ++ for (p = encoded, e = encoded + ghost1->size; p < e; ++p) ++ { ++ if (*p == '#' && p + 2 < e ){ ++ v1 = base16_value(p[1]); ++ v2 = base16_value(p[2]); ++ iof_put(O, ((v1<<4)+v2)); ++ }else ++ iof_put(O, *p); ++ } ++ decoded = ppname_flush_with_ego(O, ghost2, size, 0|PPNAME_DECODED); ++ ppname_set_alter_ego(encoded, ghost1, decoded); ++ ppname_set_alter_ego(decoded, ghost2, encoded); ++ return encoded; ++} ++ ++static ppname ppscan_exec (iof *I, ppheap **pheap, int first) ++{ ++ int c, decode; ++ iof *O; ++ _ppname *ghost1, *ghost2; ++ ppname encoded, decoded; ++ size_t size; ++ const char *p, *e; ++ uint8_t v1, v2; ++ ++ O = ppheap_buffer(pheap, sizeof(_ppname), PPNAME_INIT); ++ iof_put(O, first); ++ for (decode = 0, c = iof_char(I); c >= 0 && ppname_byte_lookup[c]; c = iof_next(I)) ++ { ++ if (c == '#') ++ decode = 1; ++ iof_put(O, c); ++ } ++ if (!decode) ++ return ppname_flush(O, ghost1, size, PPNAME_EXEC); ++ encoded = ppname_flush_with_ego(O, ghost1, size, PPNAME_EXEC|PPNAME_ENCODED); ++ O = ppheap_buffer(pheap, sizeof(_ppname), PPNAME_INIT); ++ for (p = encoded, e = encoded + ghost1->size; p < e; ++p) ++ { ++ if (*p == '#' && p + 2 < e ){ ++ v1 = base16_value(p[1]); ++ v2 = base16_value(p[2]); ++ iof_put(O, ((v1<<4)+v2)); ++ }else ++ iof_put(O, *p); ++ } ++ decoded = ppname_flush_with_ego(O, ghost2, size, PPNAME_EXEC|PPNAME_DECODED); ++ ppname_set_alter_ego(encoded, ghost1, decoded); ++ ppname_set_alter_ego(decoded, ghost2, encoded); ++ return encoded; ++} ++ ++static ppname ppexec_internal (const void *data, size_t size, ppheap **pheap) ++{ // used only for artificial 'EI' operator name ++ iof *O; ++ _ppname *ghost1; ++ ++ O = ppheap_buffer(pheap, sizeof(_ppname), size); ++ iof_write(O, data, size); ++ return ppname_flush(O, ghost1, size, PPNAME_EXEC); ++} ++ ++ppname ppname_decoded (ppname name) ++{ ++ const _ppname *ghost; ++ ghost = _ppname_ghost(name); ++ return (ghost->flags & PPNAME_ENCODED) ? ppname_get_alter_ego(name) : name; ++} ++ ++ppname ppname_encoded (ppname name) ++{ ++ const _ppname *ghost; ++ ghost = _ppname_ghost(name); ++ return (ghost->flags & PPNAME_DECODED) ? ppname_get_alter_ego(name) : name; ++} ++ ++/* string */ ++ ++#define PPSTRING_INIT (16+1) ++ ++#define ppstring_flush(O, ghost, siz, flgs) \ ++ (iof_put(O, '\0'), \ ++ ghost = (_ppstring *)ppheap_flush(O, &siz), \ ++ ghost->flags = flgs, \ ++ ghost->size = siz - sizeof(_ppstring) - 1, \ ++ (ppstring)(ghost + 1)) ++ ++#define ppstring_flush_with_ego(O, ghost, siz, flgs) \ ++ (iof_put(O, '\0'), \ ++ iof_ensure(O, sizeof(ppstring *)), \ ++ O->pos += sizeof(ppstring *), \ ++ ghost = (_ppstring *)ppheap_flush(O, &siz), \ ++ ghost->flags = flgs, \ ++ ghost->size = siz - sizeof(_ppstring) - 1 - sizeof(ppstring *), \ ++ (ppstring)(ghost + 1)) ++ ++#define ppstring_utf16be_bom(decoded) (decoded[0] == ((char)0xFE) && decoded[1] == ((char)0xFF) ) ++#define ppstring_utf16le_bom(decoded) (decoded[0] == ((char)0xFF) && decoded[1] == ((char)0xFE)) ++ ++#define ppstring_check_bom(decoded, ghost) ((void)\ ++ (ghost->size >= 2 ? (ppstring_utf16be_bom(decoded) ? (ghost->flags |= PPSTRING_UTF16BE) : \ ++ (ppstring_utf16le_bom(decoded) ? (ghost->flags |= PPSTRING_UTF16LE) : 0)) : 0)) ++ ++#define ppstring_check_bom2(decoded, ghost1, ghost2) ((void)\ ++ (ghost2->size >= 2 ? (ppstring_utf16be_bom(decoded) ? ((ghost1->flags |= PPSTRING_UTF16BE), (ghost2->flags |= PPSTRING_UTF16BE)) : \ ++ (ppstring_utf16le_bom(decoded) ? ((ghost1->flags |= PPSTRING_UTF16LE), (ghost2->flags |= PPSTRING_UTF16LE)) : 0)) : 0)) ++ ++ ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#define ppstring_set_alter_ego(string, ghost, ego) (*((ppstring *)((void *)(string + (ghost)->size + 1))) = ego) ++#else ++#define ppstring_set_alter_ego(string, ghost, ego) (*((ppstring *)(string + (ghost)->size + 1)) = ego) ++#endif ++ ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#define ppstring_get_alter_ego(string) (*((ppstring *)((void *)(string + ppstring_size(string) + 1)))) ++#else ++#define ppstring_get_alter_ego(string) (*((ppstring *)(string + ppstring_size(string) + 1))) ++#endif ++ ++ ++ ++ ++static ppstring ppscan_string (iof *I, ppheap **pheap) ++{ ++ int c, decode, balance; ++ iof *O; ++ _ppstring *ghost1, *ghost2; ++ uint8_t *p, *e; ++ ppstring encoded, decoded; ++ size_t size; ++ O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT); ++ for (decode = 0, balance = 0, c = iof_char(I); c >= 0; ) ++ { ++ switch (c) ++ { ++ case '\\': ++ decode = 1; // unescaping later ++ iof_put(O, '\\'); ++ if ((c = iof_next(I)) >= 0) ++ { ++ iof_put(O, c); ++ c = iof_next(I); ++ } ++ break; ++ case '(': // may be unescaped if balanced ++ ++balance; ++ iof_put(O, '('); ++ c = iof_next(I); ++ break; ++ case ')': ++ if (balance == 0) ++ { ++ c = IOFEOF; ++ ++I->pos; ++ break; ++ } ++ --balance; ++ iof_put(O, ')'); ++ c = iof_next(I); ++ break; ++ default: ++ iof_put(O, c); ++ c = iof_next(I); ++ } ++ } ++ if (!decode) ++ { ++ encoded = ppstring_flush(O, ghost1, size, 0); ++ ppstring_check_bom(encoded, ghost1); // any bytes can be there ++ return encoded; ++ } ++ encoded = ppstring_flush_with_ego(O, ghost1, size, 0|PPSTRING_ENCODED); ++ O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT); ++ for (p = (uint8_t *)encoded, e = (uint8_t *)encoded + ghost1->size; p < e; ++p) ++ { ++ if (*p == '\\') ++ { ++ if (++p >= e) ++ break; ++ switch (*p) ++ { ++ case OCTAL_CHAR_CASE: ++ c = *p - '0'; ++ if (++p < e && *p >= '0' && *p <= '7') ++ { ++ c = (c << 3) + *p - '0'; ++ if (++p < e && *p >= '0' && *p <= '7') ++ c = (c << 3) + *p - '0'; ++ } ++ iof_put(O, c); ++ break; ++ case 'n': ++ iof_put(O, '\n'); ++ break; ++ case 'r': ++ iof_put(O, '\r'); ++ break; ++ case 't': ++ iof_put(O, '\t'); ++ break; ++ case 'b': ++ iof_put(O, '\b'); ++ break; ++ case 'f': ++ iof_put(O, '\f'); ++ break; ++ case NEWLINE_CHAR_CASE: // not a part of the string, ignore (pdf spec page 55) ++ break; ++ case '(': case ')': case '\\': ++ default: // for enything else backslash is ignored (pdf spec page 54) ++ iof_put(O, *p); ++ break; ++ } ++ } ++ else ++ iof_put(O, *p); ++ } ++ decoded = ppstring_flush_with_ego(O, ghost2, size, 0|PPSTRING_DECODED); ++ ppstring_check_bom2(decoded, ghost1, ghost2); ++ ppstring_set_alter_ego(encoded, ghost1, decoded); ++ ppstring_set_alter_ego(decoded, ghost2, encoded); ++ return encoded; ++} ++ ++/* Hex string may contain white characters. If odd number of digits, the last assumed to be '0' */ ++ ++static ppstring ppscan_base16 (iof *I, ppheap **pheap) ++{ ++ int c, v1, v2; ++ iof *O; ++ _ppstring *ghost1, *ghost2; ++ size_t size; ++ ppstring encoded, decoded; ++ uint8_t *p, *e; ++ ++ O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT); ++ for (c = iof_char(I); c >= 0 && (base16_digit(c) || ignored_char(c)); c = iof_next(I)) ++ iof_put(O, c); ++ if (c == '>') ++ ++I->pos; ++ encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_BASE16|PPSTRING_ENCODED); ++ O = ppheap_buffer(pheap, sizeof(_ppstring), (ghost1->size >> 1) + 1); ++ for (p = (uint8_t *)encoded, e = (uint8_t *)encoded + ghost1->size; p < e; ++p) ++ { ++ if ((v1 = base16_value(*p)) < 0) // ignored ++ continue; ++ for (v2 = 0, ++p; p < e && (v2 = base16_value(*p)) < 0; ++p); ++ iof_put(O, (v1<<4)|v2); ++ } ++ decoded = ppstring_flush_with_ego(O, ghost2, size, 0|PPSTRING_DECODED); ++ ppstring_check_bom2(decoded, ghost1, ghost2); ++ ppstring_set_alter_ego(encoded, ghost1, decoded); ++ ppstring_set_alter_ego(decoded, ghost2, encoded); ++ return encoded; ++} ++ ++/* internal use only; binary string */ ++ ++static ppstring ppstring_buffer (iof *O, ppheap **pheap) ++{ ++ _ppstring *ghost1, *ghost2; ++ ppstring encoded, decoded; ++ uint8_t *p, *e; ++ size_t size; ++ ++ decoded = ppstring_flush_with_ego(O, ghost2, size, 0|PPSTRING_DECODED); ++ O = ppheap_buffer(pheap, sizeof(_ppstring), (ghost2->size << 1) + 1); ++ for (p = (uint8_t *)decoded, e = (uint8_t *)decoded + ghost2->size; p < e; ++p) ++ iof_set2(O, base16_uc_alphabet[(*p)>>4], base16_uc_alphabet[(*p)&15]); ++ encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_BASE16|PPSTRING_ENCODED); ++ ppstring_set_alter_ego(encoded, ghost1, decoded); ++ ppstring_set_alter_ego(decoded, ghost2, encoded); ++ return encoded; ++} ++ ++ppstring ppstring_internal (const void *data, size_t size, ppheap **pheap) ++{ // so far used only for crypt key ++ iof *O; ++ ++ O = ppheap_buffer(pheap, sizeof(_ppstring), size); ++ iof_write(O, data, size); ++ return ppstring_buffer(O, pheap); ++} ++ ++/* PDF spec says nothing about base85 strings, but streams might be (afair no leading '<~' but present trailing '~>') */ ++ ++static ppstring ppscan_base85 (iof *I, ppheap **pheap) ++{ // bawse85 alphabet is 33.117, adobe also hires 'z' and 'y' for compression ++ int c; ++ iof *O, B; ++ _ppstring *ghost1, *ghost2; ++ size_t size; ++ ppstring encoded, decoded; ++ O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT); ++ for (c = iof_char(I); (c >= '!' && c <= 'u') || c == 'z' || c == 'y'; c = iof_next(I)) ++ iof_put(O, c); ++ if (c == '~') ++ if ((c = iof_next(I)) == '>') ++ ++I->pos; ++ encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_BASE85|PPSTRING_ENCODED); ++ iof_string_reader(&B, encoded, ghost1->size); ++ O = ppheap_buffer(pheap, sizeof(_ppstring), (ghost1->size * 5 / 4) + 1); ++ base85_decode(&B, O); ++ decoded = ppstring_flush_with_ego(O, ghost2, size, 0|PPSTRING_DECODED); ++ ppstring_check_bom2(decoded, ghost1, ghost2); ++ ppstring_set_alter_ego(encoded, ghost1, decoded); ++ ppstring_set_alter_ego(decoded, ghost2, encoded); ++ return encoded; ++} ++ ++/* ++Encrypted strings. In case of encrypted strings, we first need to decode the string (saving original form hardly makes sense), ++then decrypt the string, and encode it again. ++*/ ++ ++const char ppstring_byte_escape[] = { /* -1 escaped with octal, >0 escaped with \\, 0 left intact*/ ++ -1,-1,-1,-1,-1,-1,-1,-1,'b','t','n',-1,'f','r',-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ 0, 0, 0, 0, 0, 0, 0, 0,'(',')', 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++}; ++ ++ ++static ppstring ppscan_crypt_string (iof *I, ppcrypt *crypt, ppheap **pheap) ++{ ++ int c, b, balance, encode; ++ iof *O; ++ _ppstring *ghost1, *ghost2; ++ ppstring encoded, decoded; ++ uint8_t *p, *e; ++ size_t size; ++ ++ O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT); ++ for (balance = 0, encode = 0, c = iof_char(I); c >= 0; ) ++ { ++ switch (c) ++ { ++ case '\\': ++ if ((c = iof_next(I)) < 0) ++ break; ++ encode = 1; ++ switch (c) ++ { ++ case OCTAL_CHAR_CASE: ++ b = c - '0'; ++ if ((c = iof_next(I)) >= 0 && c >= '0' && c <= '7') ++ { ++ b = (b << 3) + c - '0'; ++ if ((c = iof_next(I)) >= 0 && c >= '0' && c <= '7') ++ { ++ b = (b << 3) + c - '0'; ++ c = iof_next(I); ++ } ++ } ++ iof_put(O, b); ++ // c is set to the next char ++ break; ++ case 'n': ++ iof_put(O, '\n'); ++ c = iof_next(I); ++ break; ++ case 'r': ++ iof_put(O, '\r'); ++ c = iof_next(I); ++ break; ++ case 't': ++ iof_put(O, '\t'); ++ c = iof_next(I); ++ break; ++ case 'b': ++ iof_put(O, '\b'); ++ c = iof_next(I); ++ break; ++ case 'f': ++ iof_put(O, '\f'); ++ c = iof_next(I); ++ break; ++ case NEWLINE_CHAR_CASE: // not a part of the string, ignore (pdf spec page 55) ++ c = iof_next(I); ++ break; ++ case '(': case ')': case '\\': ++ default: // for enything else backslash is ignored (pdf spec page 54) ++ iof_put(O, c); ++ c = iof_next(I); ++ break; ++ } ++ break; ++ case '(': ++ ++balance; ++ encode = 1; ++ iof_put(O, '('); ++ c = iof_next(I); ++ break; ++ case ')': ++ if (balance == 0) ++ { ++ c = IOFEOF; ++ ++I->pos; ++ } ++ else ++ { ++ --balance; ++ //encode = 1; ++ iof_put(O, ')'); ++ c = iof_next(I); ++ } ++ break; ++ default: ++ if (ppstring_byte_escape[c] != 0) ++ encode = 1; ++ iof_put(O, c); ++ c = iof_next(I); ++ } ++ } ++ /* decrypt the buffer in place, update size */ ++ if (ppstring_decrypt(crypt, O->buf, iof_size(O), O->buf, &size)) ++ O->pos = O->buf + size; ++ /* make encoded counterpart */ ++ if (!encode) ++ { ++ decoded = ppstring_flush(O, ghost2, size, 0); ++ ppstring_check_bom(decoded, ghost2); ++ return decoded; ++ } ++ decoded = ppstring_flush_with_ego(O, ghost2, size, PPSTRING_DECODED); ++ O = ppheap_buffer(pheap, sizeof(_ppstring), ghost2->size); ++ for (p = (uint8_t *)decoded, e = (uint8_t *)decoded + ghost2->size; p < e; ++p) ++ { ++ switch ((b = ppstring_byte_escape[*p])) ++ { ++ case 0: ++ iof_put(O, *p); ++ break; ++ case -1: ++ iof_put4(O, '\\', (c >> 6) + '0', ((c >> 3) & 7) + '0', (c & 7) + '0'); ++ break; ++ default: ++ iof_put2(O, '\\', b); ++ break; ++ } ++ } ++ encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_ENCODED); ++ ppstring_check_bom2(decoded, ghost1, ghost2); ++ ppstring_set_alter_ego(encoded, ghost1, decoded); ++ ppstring_set_alter_ego(decoded, ghost2, encoded); ++ return encoded; ++} ++ ++ ++static ppstring ppscan_crypt_base16 (iof *I, ppcrypt *crypt, ppheap **pheap) ++{ ++ int c, v1, v2; ++ iof *O; ++ _ppstring *ghost1, *ghost2; ++ ppstring encoded, decoded; ++ uint8_t *p, *e; ++ size_t size; ++ ++ O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT); ++ // base16_decode(I, O); // no info about the last char.. ++ for (c = iof_char(I); c != '>' && c >= 0; ) ++ { ++ if ((v1 = base16_value(c)) < 0) ++ { ++ if (ignored_char(c)) ++ { ++ c = iof_next(I); ++ continue; ++ } ++ break; ++ } ++ do { ++ c = iof_next(I); ++ if ((v2 = base16_value(c)) >= 0) ++ { ++ c = iof_next(I); ++ break; ++ } ++ if (!ignored_char(c)) // c == '>' || c < 0 or some crap ++ { ++ v2 = 0; ++ break; ++ } ++ } while (1); ++ iof_put(O, (v1 << 4)|v2); ++ } ++ if (c == '>') ++ ++I->pos; ++ /* decrypt the buffer in place, update size */ ++ if (ppstring_decrypt(crypt, O->buf, iof_size(O), O->buf, &size)) ++ O->pos = O->buf + size; ++ decoded = ppstring_flush_with_ego(O, ghost2, size, PPSTRING_DECODED); ++ /* recreate an encoded form */ ++ O = ppheap_buffer(pheap, sizeof(_ppstring), (ghost2->size << 1) + 1); ++ for (p = (uint8_t *)decoded, e = (uint8_t *)decoded + ghost2->size; p < e; ++p) ++ iof_set2(O, base16_uc_alphabet[(*p)>>4], base16_uc_alphabet[(*p)&15]); ++ encoded = ppstring_flush_with_ego(O, ghost1, size, PPSTRING_BASE16|PPSTRING_ENCODED); ++ ppstring_check_bom2(decoded, ghost1, ghost2); ++ ppstring_set_alter_ego(encoded, ghost1, decoded); ++ ppstring_set_alter_ego(decoded, ghost2, encoded); ++ return encoded; ++} ++ ++/* ppstring alter ego switcher */ ++ ++ppstring ppstring_decoded (ppstring string) ++{ ++ const _ppstring *ghost; ++ ghost = _ppstring_ghost(string); ++ return (ghost->flags & PPSTRING_ENCODED) ? ppstring_get_alter_ego(string) : string; ++} ++ ++ppstring ppstring_encoded (ppstring string) ++{ ++ const _ppstring *ghost; ++ ghost = _ppstring_ghost(string); ++ return (ghost->flags & PPSTRING_DECODED) ? ppstring_get_alter_ego(string) : string; ++} ++ ++/* scanner stack */ ++ ++#define PPSTACK_BUFFER 512 ++ ++static void ppstack_init (ppstack *stack, ppheap **pheap) ++{ ++ stack->buf = stack->pos = (ppobj *)pp_malloc(PPSTACK_BUFFER * sizeof(ppobj)); ++ stack->size = 0; ++ stack->space = PPSTACK_BUFFER; ++ stack->pheap = pheap; ++} ++ ++#define ppstack_free_buffer(stack) (pp_free((stack)->buf)) ++ ++static void ppstack_resize (ppstack *stack) ++{ ++ ppobj *newbuffer; ++ stack->space <<= 1; ++ newbuffer = (ppobj *)pp_malloc(stack->space * sizeof(ppobj)); ++ memcpy(newbuffer, stack->buf, stack->size * sizeof(ppobj)); ++ ppstack_free_buffer(stack); ++ stack->buf = newbuffer; ++ stack->pos = newbuffer + stack->size; ++} ++ ++#define ppstack_push(stack) ((void)((stack)->size < (stack)->space || (ppstack_resize(stack), 0)), ++(stack)->size, (stack)->pos++) ++#define ppstack_pop(stack, n) ((stack)->size -= (n), (stack)->pos -= (n)) ++#define ppstack_at(stack, i) ((stack)->buf + i) ++#define ppstack_clear(stack) ((stack)->pos = (stack)->buf, (stack)->size = 0) ++ ++/* scanner commons */ ++ ++#define ppscan_uint(I, u) iof_get_uintlw(I, u) ++#define ppread_uint(s, u) string_to_uintlw((const char *)(s), u) ++ ++static ppobj * ppscan_numobj (iof *I, ppobj *obj, int negative) ++{ ++ ppint integer; ++ ppnum number; ++ int exponent; ++ int c; ++ c = iof_char(I); ++ iof_scan_integer(I, c, integer); ++ switch(c) ++ { ++ case '.': ++ { ++ number = (ppnum)integer; ++ c = iof_next(I); ++ iof_scan_fraction(I, c, number, exponent); ++ double_negative_exp10(number, exponent); ++ obj->type = PPNUM, obj->number = negative ? -number : number; ++ break; ++ } ++ default: ++ obj->type = PPINT, obj->integer = negative ? -integer : integer; ++ break; ++ } ++ return obj; ++} ++ ++static ppobj * ppscan_numobj_frac (iof *I, ppobj *obj, int negative) ++{ ++ ppnum number; ++ int c, exponent; ++ ++ number = 0.0; ++ c = iof_next(I); ++ iof_scan_fraction(I, c, number, exponent); ++ double_negative_exp10(number, exponent); ++ obj->type = PPNUM, obj->number = negative ? -number : number; ++ return obj; ++} ++ ++static int ppscan_find (iof *I) ++{ // skips whitechars and comments ++ int c; ++ for (c = iof_char(I); ; c = iof_next(I)) ++ { ++ switch (c) ++ { ++ case IGNORED_CHAR_CASE: ++ break; ++ case '%': { ++ do { ++ if ((c = iof_next(I)) < 0) ++ return c; ++ } while (!newline_char(c)); ++ break; ++ } ++ default: ++ return c; ++ } ++ } ++ return c; // never reached ++} ++ ++static int ppscan_keyword (iof *I, const char *keyword, size_t size) ++{ ++ size_t i; ++ int c; ++ if (iof_left(I) >= size) ++ { ++ if (memcmp(I->pos, keyword, size) != 0) ++ return 0; ++ I->pos += size; ++ return 1; ++ } ++ // sticky case, we can't go back ++ for (i = 0, c = iof_char(I); i < size; ++i, ++keyword, c = iof_next(I)) ++ if (i != c) ++ return 0; ++ return 1; ++} ++ ++#define ppscan_key(I, literal) ppscan_keyword(I, "" literal, sizeof(literal) - 1) ++ ++/* objects parser */ ++ ++static ppref * ppref_unresolved (ppheap **pheap, ppuint refnumber, ppuint refversion) ++{ ++ ppref *ref = (ppref *)ppheap_take(pheap, sizeof(ppref)); ++ memset(ref, 0, sizeof(ppref)); ++ ref->object.type = PPNONE; ++ ref->number = refnumber; ++ ref->version = refversion; ++ return ref; ++} ++ ++#define PPMARK PPNONE ++ ++static ppobj * ppscan_obj (iof *I, ppdoc *pdf, ppxref *xref) ++{ ++ int c; ++ ppobj *obj; ++ size_t mark, size; ++ ppuint refnumber, refversion; ++ ppref *ref; ++ ppstack *stack; ++ ppcrypt *crypt; ++ ++ stack = &pdf->stack; ++ c = iof_char(I); ++ switch (c) ++ { ++ case DIGIT_CHAR_CASE: ++ return ppscan_numobj(I, ppstack_push(stack), 0); ++ case '.': ++ return ppscan_numobj_frac(I, ppstack_push(stack), 0); ++ case '+': ++ ++I->pos; ++ return ppscan_numobj(I, ppstack_push(stack), 0); ++ case '-': ++ ++I->pos; ++ return ppscan_numobj(I, ppstack_push(stack), 1); ++ case '/': ++ ++I->pos; ++ obj = ppstack_push(stack); ++ obj->type = PPNAME; ++ obj->name = ppscan_name(I, &pdf->heap); ++ return obj; ++ case '(': ++ ++I->pos; ++ obj = ppstack_push(stack); ++ obj->type = PPSTRING; ++ if (ppcrypt_ref(pdf, crypt)) ++ obj->string = ppscan_crypt_string(I, crypt, &pdf->heap); ++ else ++ obj->string = ppscan_string(I, &pdf->heap); ++ return obj; ++ case '[': ++ mark = stack->size; ++ obj = ppstack_push(stack); ++ obj->type = PPMARK; // ppscan_obj() checks types backward for 'R', so set the type immediatelly (reserved for PPARRAY) ++ obj->any = NULL; ++ ++I->pos; ++ for (c = ppscan_find(I); c != ']'; c = ppscan_find(I)) ++ { ++ if (ppscan_obj(I, pdf, xref) == NULL) ++ { // callers assume that NULL returns means nothing pushed ++ size = stack->size - mark; // pop items AND the obj reserved for array ++ ppstack_pop(stack, size); ++ return NULL; ++ } ++ } ++ ++I->pos; ++ size = stack->size - mark - 1; ++ obj = ppstack_at(stack, mark); // stack might have been realocated ++ obj->type = PPARRAY; ++ obj->array = pparray_create(ppstack_at(stack, mark + 1), size, &pdf->heap); ++ ppstack_pop(stack, size); // pop array items, leave the array on top ++ return obj; ++ case '<': ++ if ((c = iof_next(I)) == '<') ++ { ++ mark = stack->size; ++ obj = ppstack_push(stack); ++ obj->type = PPMARK; ++ obj->any = NULL; ++ ++I->pos; ++ for (c = ppscan_find(I); c != '>'; c = ppscan_find(I)) ++ { ++ if (ppscan_obj(I, pdf, xref) == NULL) ++ { ++ size = stack->size - mark; ++ ppstack_pop(stack, size); ++ return NULL; ++ } ++ } ++ if (iof_next(I) == '>') ++ ++I->pos; ++ size = stack->size - mark - 1; ++ obj = ppstack_at(stack, mark); ++ obj->type = PPDICT; ++ obj->dict = ppdict_create(ppstack_at(stack, mark + 1), size, &pdf->heap); ++ ppstack_pop(stack, size); ++ return obj; ++ } ++ obj = ppstack_push(stack); ++ obj->type = PPSTRING; ++ if (ppcrypt_ref(pdf, crypt)) ++ obj->string = ppscan_crypt_base16(I, crypt, &pdf->heap); ++ else ++ obj->string = ppscan_base16(I, &pdf->heap); ++ return obj; ++ case 'R': ++ if (stack->size >= 2 && stack->pos[-1].type == PPINT && stack->pos[-2].type == PPINT) ++ { ++ ++I->pos; ++ obj = &stack->pos[-2]; ++ refnumber = (ppuint)obj->integer; ++ ppstack_pop(stack, 1); // pop version number, retype obj to a reference ++ if (xref == NULL || (ref = ppxref_find(xref, refnumber)) == NULL) ++ { /* pdf spec page 64: unresolvable reference is not an error, should just be treated as a reference to null. ++ we also need this to read trailer, where refs can't be resolved yet */ ++ refversion = (obj + 1)->integer; ++ //if (xref != NULL) ++ // loggerf("unresolved reference %s", ppref_str(refnumber, refversion)); ++ ref = ppref_unresolved(stack->pheap, refnumber, refversion); ++ } ++ obj->type = PPREF; ++ obj->ref = ref; ++ return obj; ++ } ++ break; ++ case 't': ++ if (iof_next(I) == 'r' && iof_next(I) == 'u' && iof_next(I) == 'e') ++ { ++ ++I->pos; ++ obj = ppstack_push(stack); ++ obj->type = PPBOOL; ++ obj->integer = 1; ++ return obj; ++ } ++ break; ++ case 'f': ++ if (iof_next(I) == 'a' && iof_next(I) == 'l' && iof_next(I) == 's' && iof_next(I) == 'e') ++ { ++ ++I->pos; ++ obj = ppstack_push(stack); ++ obj->type = PPBOOL; ++ obj->integer = 0; ++ return obj; ++ } ++ break; ++ case 'n': ++ if (iof_next(I) == 'u' && iof_next(I) == 'l' && iof_next(I) == 'l') ++ { ++ ++I->pos; ++ obj = ppstack_push(stack); ++ obj->type = PPNULL; ++ obj->any = NULL; ++ return obj; ++ } ++ break; ++ } ++ return NULL; ++} ++ ++/* ++A variant for contents streams (aka postscript); wise of operators, blind to references. ++We are still PDF, so we don't care about postscript specific stuff such as radix numbers ++and scientific numbers notation. It takes ppstack * as context (no ppdoc *) to be able ++to run contents parser beyond the scope of ppdoc heap. ++*/ ++ ++static ppstring ppstring_inline (iof *I, ppdict *imagedict, ppheap **pheap); ++ ++static ppobj * ppscan_psobj (iof *I, ppstack *stack) ++{ ++ int c; ++ ppobj *obj, *op; ++ size_t size, mark; ++ ppname exec; ++ ++ c = iof_char(I); ++ switch (c) ++ { ++ case DIGIT_CHAR_CASE: ++ return ppscan_numobj(I, ppstack_push(stack), 0); ++ case '.': ++ return ppscan_numobj_frac(I, ppstack_push(stack), 0); ++ case '+': ++ c = iof_next(I); ++ if (base10_digit(c)) // '+.abc' is probably an executable name, but we are not in postscript ++ return ppscan_numobj(I, ppstack_push(stack), 0); ++ else if (c == '.') ++ return ppscan_numobj_frac(I, ppstack_push(stack), 0); ++ obj = ppstack_push(stack); ++ obj->type = PPNAME; ++ obj->name = ppscan_exec(I, stack->pheap, '+'); ++ return obj; ++ case '-': ++ c = iof_next(I); ++ if (base10_digit(c)) // ditto, we would handle type1 '-|' '|-' operators though ++ return ppscan_numobj(I, ppstack_push(stack), 1); ++ else if (c == '.') ++ return ppscan_numobj_frac(I, ppstack_push(stack), 1); ++ obj = ppstack_push(stack); ++ obj->type = PPNAME; ++ obj->name = ppscan_exec(I, stack->pheap, '-'); ++ return obj; ++ case '/': ++ ++I->pos; ++ obj = ppstack_push(stack); ++ obj->type = PPNAME; ++ obj->name = ppscan_name(I, stack->pheap); ++ return obj; ++ case '(': ++ ++I->pos; ++ obj = ppstack_push(stack); ++ obj->type = PPSTRING; ++ obj->string = ppscan_string(I, stack->pheap); ++ return obj; ++ case '[': ++ mark = stack->size; ++ obj = ppstack_push(stack); ++ obj->type = PPMARK; ++ obj->any = NULL; ++ ++I->pos; ++ for (c = ppscan_find(I); c != ']'; c = ppscan_find(I)) ++ { ++ if (ppscan_psobj(I, stack) == NULL) ++ { ++ size = stack->size - mark; ++ ppstack_pop(stack, size); ++ return NULL; ++ } ++ } ++ ++I->pos; ++ size = stack->size - mark - 1; ++ obj = ppstack_at(stack, mark); ++ obj->type = PPARRAY; ++ obj->array = pparray_create(ppstack_at(stack, mark + 1), size, stack->pheap); ++ ppstack_pop(stack, size); ++ return obj; ++ case '<': ++ if ((c = iof_next(I)) == '<') ++ { ++ mark = stack->size; ++ obj = ppstack_push(stack); ++ obj->type = PPMARK; ++ obj->any = NULL; ++ ++I->pos; ++ for (c = ppscan_find(I); c != '>'; c = ppscan_find(I)) ++ { ++ if (ppscan_psobj(I, stack) == NULL) ++ { ++ size = stack->size - mark; ++ ppstack_pop(stack, size); ++ return NULL; ++ } ++ } ++ if (iof_next(I) == '>') ++ ++I->pos; ++ size = stack->size - mark - 1; ++ obj = ppstack_at(stack, mark); ++ obj->type = PPDICT; ++ obj->dict = ppdict_create(ppstack_at(stack, mark + 1), size, stack->pheap); ++ ppstack_pop(stack, size); ++ return obj; ++ } ++ obj = ppstack_push(stack); ++ obj->type = PPSTRING; ++ if (c == '~') ++ ++I->pos, obj->string = ppscan_base85(I, stack->pheap); ++ else ++ obj->string = ppscan_base16(I, stack->pheap); ++ return obj; ++ default: ++ if (c < 0 || !ppname_byte_lookup[c]) ++ break; // forbid empty names; dead loop otherwise ++ ++I->pos; ++ /* true false null practically don't occur in streams so it makes sense to assume that we get an operator name here. ++ If it happen to be a keyword we could give back those several bytes to the heap but.. heap buffer is tricky enough. */ ++ exec = ppscan_exec(I, stack->pheap, c); ++ obj = ppstack_push(stack); ++ switch (exec[0]) ++ { ++ case 't': ++ if (exec[1] == 'r' && exec[2] == 'u' && exec[3] == 'e' && exec[4] == '\0') ++ { ++ obj->type = PPBOOL; ++ obj->integer = 1; ++ // todo: drop exec ++ return obj; ++ } ++ break; ++ case 'f': ++ if (exec[1] == 'a' && exec[2] == 'l' && exec[3] == 's' && exec[4] == 'e' && exec[5] == '\0') ++ { ++ obj->type = PPBOOL; ++ obj->integer = 0; ++ // todo: drop exec ++ return obj; ++ } ++ break; ++ case 'n': ++ if (exec[1] == 'u' && exec[2] == 'l' && exec[3] == 'l' && exec[4] == '\0') ++ { ++ obj->type = PPNULL; ++ obj->any = NULL; ++ // todo: drop exec ++ return obj; ++ } ++ break; ++ case 'B': ++ /* ++ Inline images break rules of operand/operator syntax, so 'BI/ID' operators need to be treated as special syntactic keywords. ++ ++ BI IDEI ++ ++ We treat the image as a single syntactic token; BI starts collecting a dict, ID is the beginning of the data. Effectively EI ++ operator obtains two operands - dict and string. It is ok to put three items onto the stack, callers dont't assume there is just one. ++ */ ++ if (exec[1] == 'I' && exec[2] == '\0') ++ { ++ ppdict *imagedict; ++ /* key val pairs -> dict */ ++ mark = stack->size - 1; ++ obj->type = PPMARK; ++ obj->any = NULL; ++ for (c = ppscan_find(I); ; c = ppscan_find(I)) ++ { ++ if ((op = ppscan_psobj(I, stack)) == NULL) ++ { ++ size = stack->size - mark; ++ ppstack_pop(stack, size); ++ return NULL; ++ } ++ if (op->type == PPNAME && ppname_exec(op->name)) ++ { ++ if (!ppname_is(op->name, "ID")) ++ { // weird ++ size = stack->size - mark; ++ ppstack_pop(stack, size); ++ return NULL; ++ } ++ break; ++ } ++ } ++ size = stack->size - mark - 1; ++ obj = ppstack_at(stack, mark); ++ obj->type = PPDICT; ++ obj->dict = imagedict = ppdict_create(ppstack_at(stack, mark + 1), size, stack->pheap); ++ ppstack_pop(stack, size); ++ /* put image data string */ ++ obj = ppstack_push(stack); ++ obj->type = PPSTRING; ++ obj->string = ppstring_inline(I, imagedict, stack->pheap);; ++ /* put EI operator name */ ++ obj = ppstack_push(stack); ++ obj->type = PPNAME; ++ obj->name = ppexec_internal("EI", 2, stack->pheap); ++ return obj; ++ } ++ } ++ obj->type = PPNAME; ++ obj->name = exec; ++ return obj; ++ } ++ return NULL; ++} ++ ++/* ++We try to get the exact inline image length from its dict params. If cannot predict the length, we have to scan the input until 'EI'. ++I've checked on may examples that it gives the same results but one can never be sure, as 'EI' might happen to be a part of the data. ++Stripping white char is also very heuristic; \0 is a white char in PDF and very likely to be a data byte.. weak method. ++*/ ++ ++static size_t inline_image_length (ppdict *dict) ++{ ++ ppuint w, h, bpc, colors; ++ ppname cs; ++ ++ if (ppdict_get_uint(dict, "W", &w) && ppdict_get_uint(dict, "H", &h) && ppdict_get_uint(dict, "BPC", &bpc) && (cs = ppdict_get_name(dict, "CS")) != NULL) ++ { ++ if (ppname_is(cs, "DeviceGray")) ++ colors = 1; ++ else if (ppname_is(cs, "DeviceRGB")) ++ colors = 3; ++ else if (ppname_is(cs, "DeviceCMYK")) ++ colors = 4; ++ else ++ return PP_LENGTH_UNKNOWN; ++ return (w * h * bpc * colors + 7) >> 3; ++ } ++ return PP_LENGTH_UNKNOWN; ++} ++ ++static ppstring ppstring_inline (iof *I, ppdict *imagedict, ppheap **pheap) ++{ ++ iof *O; ++ int c, d, e; ++ size_t length, leftin, leftout, bytes; ++ ++ O = ppheap_buffer(pheap, sizeof(_ppstring), PPSTRING_INIT); ++ c = iof_char(I); ++ if (ignored_char(c)) ++ c = iof_next(I); ++ ++ length = inline_image_length(imagedict); ++ if (length != PP_LENGTH_UNKNOWN) ++ { ++ while (length > 0 && iof_readable(I) && iof_writable(O)) ++ { ++ leftin = iof_left(I); ++ leftout = iof_left(O); ++ bytes = length; ++ if (bytes > leftin) bytes = leftin; ++ if (bytes > leftout) bytes = leftout; ++ memcpy(O->pos, I->pos, bytes); ++ I->pos += bytes; ++ O->pos += bytes; ++ length -= bytes; ++ } ++ // gobble EI ++ if (ppscan_find(I) == 'E') ++ if (iof_next(I) == 'I') ++ ++I->pos; ++ } ++ else ++ { ++ while (c >= 0) ++ { ++ if (c == 'E') ++ { ++ d = iof_next(I); ++ if (d == 'I') ++ { ++ e = iof_next(I); ++ if (!ppname_byte_lookup[e]) ++ { /* strip one newline from the end and stop */ ++ if (O->pos - 2 >= O->buf) // sanity ++ { ++ c = *(O->pos - 1); ++ if (ignored_char(c)) ++ { ++ if (c == 0x0A && *(O->pos - 2) == 0x0D) ++ O->pos -= 2; ++ else ++ O->pos -= 1; ++ } ++ } ++ break; ++ } ++ iof_put2(O, c, d); ++ c = e; ++ } ++ else ++ { ++ iof_put(O, c); ++ c = d; ++ } ++ } ++ else ++ { ++ iof_put(O, c); ++ c = iof_next(I); ++ } ++ } ++ } ++ return ppstring_buffer(O, pheap); ++} ++ ++/* input reader */ ++ ++/* ++PDF input is a pseudo file that either keeps FILE * or data. Reader iof * is a proxy to input ++that provides byte-by-byte interface. Our iof structure is capable to link iof_file *input, ++but t avoid redundant checks on IOF_DATA flag, here we link iof *I directly to FILE * or mem buffer. ++When reading from file we need an internal buffer, which should be kept rather small, as it is ++only used to parse xrefs and objects (no streams). We allocate the buffer from a private heap ++(not static) to avoid conflicts when processing >1 pdfs at once. Besides, the input buffer may be ++needed after loading the document, eg. to access references raw data. ++*/ ++ ++#define PPDOC_BUFFER 0xFFF // keep that small, it is only used to parse body objects ++ ++static void ppdoc_reader_init (ppdoc *pdf, iof_file *input) ++{ ++ iof *I; ++ pdf->input = *input; ++ input = &pdf->input; ++ input->refcount = 1; ++ I = &pdf->reader; ++ if (input->flags & IOF_DATA) ++ { ++ pdf->buffer = NULL; // input iof_file is the buffer ++ iof_string_reader(I, NULL, 0); // gets IOF_DATA flag ++ } ++ else ++ { ++ pdf->buffer = (uint8_t *)ppheap_take(&pdf->heap, PPDOC_BUFFER); ++ iof_setup_file_handle_reader(I, NULL, 0, iof_file_get_fh(input)); // gets IOF_FILE_HANDLE flag and FILE * ++ I->space = PPDOC_BUFFER; // used on refill ++ } ++} ++ ++/* ++Whenever we need to read the input file, we fseek the to the given offset and fread to to the private buffer. ++The length we need is not always predictable, in which case PPDOC_BUFFER bytes are read (keep it small). ++I->buf = I->pos is set to the beginning, I->end set to the end (end is the first byte one shouldn't read). ++*/ ++ ++static iof * ppdoc_reader (ppdoc *pdf, size_t offset, size_t length) ++{ ++ iof_file *input; ++ iof *I; ++ input = &pdf->input; ++ I = &pdf->reader; ++ if (iof_file_seek(input, offset, SEEK_SET) != 0) ++ return NULL; ++ I->flags &= ~IOF_STOPPED; ++ if (input->flags & IOF_DATA) ++ { ++ I->buf = I->pos = input->pos; ++ I->end = (length == PP_LENGTH_UNKNOWN || I->pos + length >= input->end) ? input->end : (I->pos + length); ++ } ++ else ++ { ++ I->buf = I->pos = pdf->buffer; // ->buf is actually permanently equal pdf->buffer but we might need some tricks ++ if (length == PP_LENGTH_UNKNOWN || length > PPDOC_BUFFER) ++ length = PPDOC_BUFFER; ++ length = fread(I->buf, 1, length, I->file); ++ I->end = I->buf + length; ++ } ++ return I; ++} ++ ++/* The position from the beginning of input ++- for data buffer: (pdf->input.pos - pdf->input.buf) + (I->pos - I->buf) ++ I->buf == pdf->input.pos, so this resolves to (I->pos - pdf->input.buf), independent from I->buf ++- for file buffer: ftell(pdf->input.file) - (I->end - I->pos) ++*/ ++ ++#define ppdoc_reader_tell(pdf, I) ((size_t)(((pdf)->input.flags & IOF_DATA) ? ((I)->pos - (pdf)->input.buf) : (ftell(iof_file_get_fh(&(pdf)->input)) - ((I)->end - (I)->pos)))) ++ ++/* pdf */ ++ ++#define PPDOC_HEADER 10 // "%PDF-?.??\n" ++ ++static int ppdoc_header (ppdoc *pdf, uint8_t header[PPDOC_HEADER]) ++{ ++ size_t i; ++ if (memcmp(header, "%PDF-", 5) != 0) ++ return 0; ++ for (i = 5; i < PPDOC_HEADER - 1 && !ignored_char(header[i]); ++i) ++ pdf->version[i - 5] = header[i]; ++ pdf->version[i - 5] = '\0'; ++ return 1; ++} ++ ++static int ppdoc_tail (ppdoc *pdf, iof_file *input, size_t *pxrefoffset) ++{ ++ int c; ++ uint8_t tail[4*10], *p, back, tailbytes; ++ ++ if (iof_file_seek(input, 0, SEEK_END) != 0) ++ return 0; ++ pdf->filesize = (size_t)iof_file_tell(input); ++ // simple heuristic to avoif fgetc() / fseek(-2) hiccup: keep seeking back by len(startxref) + 1 == 10 ++ // until a letter found (assuming liberal white characters and tail length) ++ for (back = 1, tailbytes = 0; ; ++back) ++ { ++ if (iof_file_seek(input, -10, SEEK_CUR) != 0) ++ return 0; ++ tailbytes += 10; ++ c = iof_file_getc(input); ++ tailbytes -= 1; ++ switch (c) ++ { ++ case IGNORED_CHAR_CASE: ++ case DIGIT_CHAR_CASE: ++ case '%': case 'E': case 'O': case 'F': ++ if (back > 4) // 2 should be enough ++ return 0; ++ continue; ++ case 's': case 't': case 'a': case 'r': case 'x': case 'e': case 'f': ++ if (iof_file_read(tail, 1, tailbytes, input) != tailbytes) ++ return 0; ++ tail[tailbytes] = '\0'; ++ for (p = &tail[0]; ; ++p) ++ { ++ if (*p == '\0') ++ return 0; ++ if ((c = base10_value(*p)) >= 0) ++ break; ++ } ++ ppread_uint(p, pxrefoffset); ++ return 1; ++ default: ++ return 0; ++ } ++ } ++ return 0; ++} ++ ++/* xref/body */ ++ ++static int ppscan_start_entry (iof *I, ppref *ref) ++{ ++ ppuint u; ++ ppscan_find(I); if (!ppscan_uint(I, &u) || u != ref->number) return 0; ++ ppscan_find(I); if (!ppscan_uint(I, &u) || u != ref->version) return 0; ++ ppscan_find(I); if (!ppscan_key(I, "obj")) return 0; ++ ppscan_find(I); ++ return 1; ++} ++ ++static int ppscan_skip_entry (iof *I) ++{ ++ size_t u; ++ ppscan_find(I); if (!ppscan_uint(I, &u)) return 0; ++ ppscan_find(I); if (!ppscan_uint(I, &u)) return 0; ++ ppscan_find(I); if (!ppscan_key(I, "obj")) return 0; ++ ppscan_find(I); ++ return 1; ++} ++ ++static int ppscan_start_stream (iof *I, ppdoc *pdf, size_t *streamoffset) ++{ ++ int c; ++ ppscan_find(I); ++ if (ppscan_key(I, "stream")) ++ { // skip 1 or 2 whites (here we shouldn't just gobble all blanks) ++ c = iof_char(I); ++ if (ignored_char(c)) ++ { ++ c = iof_next(I); ++ if (ignored_char(c)) ++ ++I->pos; ++ } ++ *streamoffset = ppdoc_reader_tell(pdf, I); ++ return 1; ++ } ++ return 0; ++} ++ ++static ppxref * ppxref_load (ppdoc *pdf, size_t xrefoffset); ++static ppxref * ppxref_load_chain (ppdoc *pdf, ppxref *xref); ++ ++/* Parsing xref table ++ ++ 1 10 // first ref number and refs count ++ 0000000000 00000 n // 10-digits offset, 5 digits version, type identifier ++ 0000000000 00000 n // n states for normal I guess ++ 0000000000 00000 f // f states for free (not used) ++ ... ++ ++Free entries seem to be a relic of ancient times, completelly useless for us. To avoid parsing xref table twice, ++we waste some space on free entries by allocating one plane of refs for each section. Later on we slice sections, ++so that effectively free entries are not involved in map. ++ ++Subsequent refs gets number, version and offset. Other fields initialized when parsing PDF body. ++ ++Having xref table loaded, we sort sections for future binary search (xref with objects count == 0 is considered invalid). ++ ++Then we have to deal with the trailer dict. In general, to load objects and resolve references we need a complete chain ++of xrefs (not only the top). To load the previous xref, we need its offset, which is given in trailer. So we have to ++parse the trailer ignoring references, which might be unresolvable at this point (objects parser makes a dummy check ++for xref != NULL on refs resolving ppscan_obj(), which irritates me but I don't want a separate parser for trailer..). ++The same applies to xref streams, in which we have parse the trailer not having xref map at all. So the procedure is: ++ ++ - load xref map, initialize references, make it ready to search ++ - parse trailer ignoring references ++ - get /Prev xref offset and load older xref (linked list via ->prev) ++ - sort all refs in all xrefs by offset ++ - parse refs in order resolving references in contained objects ++ - fix trailer references ++ ++First created xref becomes a pdf->xref (top xref). We link that early to control offsets already read (insane loops?). ++*/ ++ ++// Every xref table item "0000000000 00000 n" is said to be terminated with 2-byte EOL but we don't like relying on whites. ++#define xref_item_length (10+1+5+1+1) ++ ++static ppxref * ppxref_load_table (iof *I, ppdoc *pdf, size_t xrefoffset) ++{ ++ ppxref *xref; ++ ppxsec *xrefsection; ++ ppref *ref; ++ ppuint first, count, refindex; ++ uint8_t buffer[xref_item_length + 1]; ++ const char *p; ++ const ppobj *obj; ++ ++ buffer[xref_item_length] = '\0'; ++ xref = ppxref_create(pdf, 0, xrefoffset); ++ if (pdf->xref == NULL) pdf->xref = xref; ++ for (ppscan_find(I); ppscan_uint(I, &first); ppscan_find(I)) ++ { ++ ppscan_find(I); ++ if (!ppscan_uint(I, &count)) ++ return NULL; ++ if (count == 0) // weird ++ continue; ++ xref->count += count; ++ xrefsection = NULL; ++ ref = (ppref *)ppheap_take(&pdf->heap, count * sizeof(ppref)); ++ for (refindex = 0; refindex < count; ++refindex, ++ref) ++ { ++ ref->xref = xref; ++ ref->number = first + refindex; ++ ppscan_find(I); ++ iof_read(I, buffer, xref_item_length); ++ switch (buffer[xref_item_length - 1]) ++ { ++ case 'n': ++ if (xrefsection == NULL) ++ { ++ xrefsection = ppxref_push_section(xref, &pdf->heap); ++ xrefsection->first = ref->number; ++ xrefsection->refs = ref; ++ } ++ xrefsection->last = ref->number; ++ for (p = (const char *)buffer; *p == '0'; ++p); ++ p = ppread_uint(p, &ref->offset); ++ for ( ; *p == ' ' || *p == '0'; ++p); ++ p = ppread_uint(p, &ref->version); ++ ref->object.type = PPNONE; // init for sanity ++ ref->object.any = NULL; ++ ref->length = 0; ++ break; ++ case 'f': ++ default: ++ --ref; ++ xrefsection = NULL; ++ --xref->count; ++ } ++ } ++ } ++ /* sort section */ ++ if (!ppxref_sort(xref)) ++ ; // case of xref->size == 0 handled by ppxref_load_chain() ++ /* get trailer ignoring refs */ ++ if (!ppscan_key(I, "trailer")) ++ return NULL; ++ ppscan_find(I); ++ if ((obj = ppscan_obj(I, pdf, NULL)) == NULL) ++ return NULL; ++ ppstack_pop(&pdf->stack, 1); ++ if (obj->type != PPDICT) ++ return NULL; ++ xref->trailer = *obj; ++ return ppxref_load_chain(pdf, xref); ++} ++ ++/* Parsing xref stream ++First we load the trailer, ignoring references. Dict defines sections and fields lengths: ++ ++ /Size % max ref number plus 1 ++ /Index [ first count first count ... ] % a pair of numbers for every section, defaults to [0 Size] ++ /W [w1 w2 w3] % fields lengths, 0 states for omitted field ++ ++xref stream data is a continuous stream of binary number triplets. First number is a type: ++ ++ 0 - free entry (as 'f' in xref table) ++ 1 - normal entry, followed by offset an version (as 'n' in xref table) ++ 2 - compressed entry, followed by parent object stream number and entry index ++ ++0 and 1 are handled as 'n' and 'f' entries in xref table. For type 2 we normally initialize ++ref->number and ref->version (the later is implicitly 0). ref->offset is set to 0 (invalid offset), ++which is recognized by objects loader. ++*/ ++ ++#define XREF_STREAM_MAX_FIELD 4 ++ ++static ppxref * ppxref_load_stream (iof *I, ppdoc *pdf, size_t xrefoffset) ++{ ++ ppxref *xref; ++ ppxsec *xrefsection; ++ ppref *ref; ++ ppobj *obj; ++ ppstream *xrefstream; ++ size_t streamoffset; ++ ppuint w1, w2, w3, w, bufferbytes; ++ uint8_t buffer[3 * XREF_STREAM_MAX_FIELD], *b; ++ ppuint first, count, f1, f2, f3; ++ pparray *fieldwidths, *sectionindices; ++ ppobj sectionmock[2], *sectionfirst, *sectioncount; ++ size_t sections, sectionindex, refindex; ++ ++ if (!ppscan_skip_entry(I)) ++ return NULL; ++ if ((obj = ppscan_obj(I, pdf, NULL)) == NULL) ++ return NULL; ++ ppstack_pop(&pdf->stack, 1); ++ if (obj->type != PPDICT || !ppscan_start_stream(I, pdf, &streamoffset)) ++ return NULL; ++ xrefstream = ppstream_create(pdf, obj->dict, streamoffset); ++ /* All normal streams go through ppstream_info(), but it makes no sense for trailer stream (no crypt allowed, no refs yet). ++ So we just record the length and informative flag. Here we have to expect that /Length and /Filter are not indirects. */ ++ if (!ppdict_get_uint(obj->dict, "Length", &xrefstream->length)) ++ return NULL; ++ if (ppdict_get_obj(obj->dict, "Filter") != NULL) ++ xrefstream->flags |= PPSTREAM_COMPRESSED; ++ if ((fieldwidths = ppdict_get_array(xrefstream->dict, "W")) != NULL) ++ { ++ if (!pparray_get_uint(fieldwidths, 0, &w1)) w1 = 0; ++ if (!pparray_get_uint(fieldwidths, 1, &w2)) w2 = 0; ++ if (!pparray_get_uint(fieldwidths, 2, &w3)) w3 = 0; ++ } ++ else ++ w1 = w2 = w3 = 0; ++ if (w1 > XREF_STREAM_MAX_FIELD || w2 > XREF_STREAM_MAX_FIELD || w3 > XREF_STREAM_MAX_FIELD) ++ return NULL; ++ bufferbytes = w1 + w2 + w3; ++ if ((sectionindices = ppdict_get_array(xrefstream->dict, "Index")) != NULL) ++ { ++ sections = sectionindices->size >> 1; ++ sectionfirst = sectionindices->data; ++ } ++ else ++ { ++ sections = 1; ++ sectionmock[0].type = PPINT; ++ sectionmock[0].integer = 0; ++ sectionmock[1].type = PPINT; ++ if (!ppdict_get_int(xrefstream->dict, "Size", §ionmock[1].integer)) ++ sectionmock[1].integer = 0; ++ sectionfirst = §ionmock[0]; ++ } ++ if ((I = ppstream_read(xrefstream, 1, 0)) == NULL) ++ return NULL; // we fseek() so original I is useless anyway ++ xref = ppxref_create(pdf, sections, xrefoffset); ++ if (pdf->xref == NULL) pdf->xref = xref; ++ xref->trailer.type = PPSTREAM; ++ xref->trailer.stream = xrefstream; ++ for (sectionindex = 0; sectionindex < sections; ++sectionindex, sectionfirst += 2) ++ { ++ sectioncount = sectionfirst + 1; ++ if (!ppobj_get_uint(sectionfirst, first) || !ppobj_get_uint(sectioncount, count)) ++ goto xref_stream_error; ++ if (count == 0) ++ continue; ++ xref->count += count; ++ xrefsection = NULL; ++ ref = (ppref *)ppheap_take(&pdf->heap, count * sizeof(ppref)); ++ for (refindex = 0; refindex < count; ++refindex, ++ref) ++ { ++ ref->xref = xref; ++ ref->number = first + refindex; ++ if (iof_read(I, buffer, bufferbytes) != bufferbytes) ++ goto xref_stream_error; ++ b = buffer; ++ if (w1 == 0) ++ f1 = 1; // default type is 1 ++ else ++ for (f1 = 0, w = 0; w < w1; f1 = (f1 << 8)|(*b), ++w, ++b); ++ for (f2 = 0, w = 0; w < w2; f2 = (f2 << 8)|(*b), ++w, ++b); ++ for (f3 = 0, w = 0; w < w3; f3 = (f3 << 8)|(*b), ++w, ++b); ++ switch (f1) ++ { ++ case 0: ++ //--ref; ++ xrefsection = NULL; ++ --xref->count; ++ break; ++ case 1: ++ if (xrefsection == NULL) ++ { ++ xrefsection = ppxref_push_section(xref, &pdf->heap); ++ xrefsection->first = ref->number; ++ xrefsection->refs = ref; ++ } ++ xrefsection->last = ref->number; ++ ref->offset = f2; ++ ref->version = f3; ++ ref->object.type = PPNONE; ++ ref->object.any = NULL; ++ ref->length = 0; ++ break; ++ case 2: ++ if (xrefsection == NULL) ++ { ++ xrefsection = ppxref_push_section(xref, &pdf->heap); ++ xrefsection->first = ref->number; ++ xrefsection->refs = ref; ++ } ++ xrefsection->last = ref->number; ++ ref->offset = 0; // f2 is parent objstm, f3 is index in parent, both useless ++ ref->version = 0; // compressed objects has implicit version == 0 ++ ref->object.type = PPNONE; ++ ref->object.any = NULL; ++ ref->length = 0; ++ break; ++ default: ++ goto xref_stream_error; ++ } ++ } ++ } ++ /* sort sections */ ++ if (!ppxref_sort(xref)) ++ ; // case of xref->size == 0 handled by ppxref_load_chain() ++ /* close the stream _before_ loading prev xref */ ++ ppstream_done(xrefstream); ++ /* load prev and return */ ++ return ppxref_load_chain(pdf, xref); ++xref_stream_error: ++ ppstream_done(xrefstream); ++ return NULL; ++} ++ ++/* ++The following procedure loads xref /Prev, links xref->prev and typically returns xref. ++Some docs contain empty xref (one section with zero objects) that is actually a proxy ++to xref stream referred as /XRefStm (genuine concept of xrefs old/new style xrefs in ++the same doc). In case of 0-length xref we ignore the proxy and return the target xref ++(otherwise we would need annoying sanity check for xref->size > 0 on every ref search). ++*/ ++ ++static ppxref * ppxref_load_chain (ppdoc *pdf, ppxref *xref) ++{ ++ ppdict *trailer; ++ ppuint xrefoffset; ++ ppxref *prevxref, *nextxref; ++ ++ trailer = ppxref_trailer(xref); ++ if (!ppdict_get_uint(trailer, "Prev", &xrefoffset)) // XRefStm is useless ++ return xref; // missing /Prev is obviously ok ++ for (nextxref = pdf->xref; nextxref != NULL; nextxref = nextxref->prev) ++ if (nextxref->offset == xrefoffset) // insane ++ return NULL; ++ if ((prevxref = ppxref_load(pdf, (size_t)xrefoffset)) == NULL) ++ return NULL; ++ if (xref->size > 0) ++ { ++ xref->prev = prevxref; ++ return xref; ++ } ++ if (pdf->xref == xref) ++ pdf->xref = prevxref; ++ return prevxref; ++} ++ ++static ppxref * ppxref_load (ppdoc *pdf, size_t xrefoffset) ++{ ++ iof *I; ++ if ((I = ppdoc_reader(pdf, xrefoffset, PP_LENGTH_UNKNOWN)) == NULL) ++ return NULL; ++ ppscan_find(I); ++ if (ppscan_key(I, "xref")) ++ return ppxref_load_table(I, pdf, xrefoffset); ++ return ppxref_load_stream(I, pdf, xrefoffset); ++ // iof_close(I) does nothing here ++} ++ ++static void ppoffmap_sort (ppref **left, ppref **right) ++{ ++ ppref **l, **r, *t; ++ ppuint pivot; ++ l = left, r = right; ++ pivot = (*(l + ((r - l) / 2)))->offset; ++ do ++ { // don't read from pointer! ++ while ((*l)->offset < pivot) ++l; ++ while ((*r)->offset > pivot) --r; ++ if (l <= r) ++ { ++ t = *l; ++ *l = *r; ++ *r = t; ++ ++l, --r; ++ } ++ } while (l <= r); ++ if (left < r) ++ ppoffmap_sort(left, r); ++ if (l < right) ++ ppoffmap_sort(l, right); ++} ++ ++ ++static void fix_trailer_references (ppdoc *pdf) ++{ ++ ppxref *xref; ++ ppdict *trailer; ++ ppname *pkey; ++ ppobj *obj; ++ ppref *ref; ++ for (xref = pdf->xref; xref != NULL; xref = xref->prev) ++ { ++ if ((trailer = ppxref_trailer(xref)) == NULL) ++ continue; ++ for (ppdict_first(trailer, pkey, obj); *pkey != NULL; ppdict_next(pkey, obj)) ++ { // no need to go deeper in structs, all items in trailer except info and root must be direct refs ++ if (obj->type != PPREF) ++ continue; ++ ref = obj->ref; ++ if (ref->offset == 0) // unresolved? ++ if ((ref = ppxref_find(xref, ref->number)) != NULL) ++ obj->ref = ref; // at this moment the reference still points nothing, but should be the one with the proper offset ++ } ++ } ++} ++ ++/* ++Here comes a procedure that loads all entries from all document bodies. We resolve references while ++parsing objects and to make resolving correct, we need a complete chain of xref maps, and a knowledge ++about possible linearized dict (first offset). So loading refs sorted by offsets makes sense (not sure ++if it matters nowadays but we also avoid fseek() by large offsets). ++ ++Here is the proc: ++ ++ - create a list of all refs in all bodies ++ - sort the list by offsets ++ - for every ref from the sorted list: ++ - estimate object length to avoid fread-ing more than necessary (not perfect but enough) ++ - fseek() to the proper offset, fread() entry data or its part ++ - parse the object with ppscan_obj(I, pdf, xref), where xref is not necessarily top pdf->xref ++ - save the actual ref->length (not sure if we need that?) ++ - make a stream if a dict is followed by "stream" keyword, also save the stream offset ++ - free the list ++*/ ++ ++static int ppdoc_load_objstm (ppstream *stream, ppdoc *pdf, ppxref *xref); ++ ++static void ppdoc_load_entries (ppdoc *pdf) ++{ ++ size_t objects, sectionindex, refnumber, offindex; ++ ppnum linearized; ++ ppref **offmap, **pref, *ref; ++ ppxref *xref; ++ ppxsec *xsec; ++ ppobj *obj; ++ ppname type; ++ int redundant_indirection = 0; ++ ppcrypt *crypt; ++ ppstream *stream; ++ ++ if ((objects = (size_t)ppdoc_objects(pdf)) == 0) // can't happen ++ return; ++ pref = offmap = (ppref **)pp_malloc(objects * sizeof(ppref *)); ++ objects = 0; // recount refs with offset > 0 ++ for (xref = pdf->xref; xref != NULL; xref = xref->prev) ++ for (sectionindex = 0, xsec = xref->sects; sectionindex < xref->size; ++sectionindex, ++xsec) ++ for (refnumber = xsec->first, ref = xsec->refs; refnumber <= xsec->last; ++refnumber, ++ref) ++ if (ref->offset > 0) // 0 means compressed or insane ++ *pref++ = ref, ++objects; ++ ppoffmap_sort(offmap, offmap + objects - 1); ++ ++ crypt = pdf->crypt; ++ for (offindex = 0, pref = offmap; offindex < objects; ) ++ { ++ ref = *pref; ++ ++pref; ++ ++offindex; ++ if (ref->object.type != PPNONE) // might be preloaded already (/Encrypt dict, stream filter dicts, stream /Length..) ++ continue; ++ if (offindex < objects) ++ ref->length = (*pref)->offset - ref->offset; ++ else ++ ref->length = pdf->filesize > ref->offset ? pdf->filesize - ref->offset : 0; ++ if (crypt != NULL) ++ { ++ ppcrypt_start_ref(crypt, ref); ++ obj = ppdoc_load_entry(pdf, ref); ++ ppcrypt_end_ref(crypt); ++ } ++ else ++ { ++ obj = ppdoc_load_entry(pdf, ref); ++ } ++ switch (obj->type) ++ { ++ case PPDICT: /* Check if the object at first offset is linearized dict. We need that to resolve all references properly. */ ++ if (offindex == 1 && ppdict_get_num(obj->dict, "Linearized", &linearized)) // /Linearized value is a version number, default 1.0 ++ pdf->flags |= PPDOC_LINEARIZED; ++ break; ++ case PPREF: ++ redundant_indirection = 1; ++ break; ++ default: ++ break; ++ } ++ // if pdf->crypt crypt->ref = NULL ++ } ++ ++ /* refs pointngs refs? cut. */ ++ if (redundant_indirection) ++ { ++ for (offindex = 0, pref = offmap; offindex < objects; ++offindex) ++ { ++ ref = *pref++; ++ if (ref->object.type == PPREF) ++ ref->object = ref->object.ref->object; // doing for all effectively cuts all insane chains ++ } ++ } ++ ++ /* now handle streams; update stream info (eg. /Length), load pdf 1.5 object streams ++ we could do earlier but then we would need to struggle with indirects */ ++ for (offindex = 0, pref = offmap; offindex < objects; ++offindex) ++ { ++ ref = *pref++; ++ obj = &ref->object; ++ if (obj->type != PPSTREAM) ++ continue; ++ stream = obj->stream; ++ if (crypt != NULL) ++ { ++ ppcrypt_start_ref(crypt, ref); ++ ppstream_info(stream, pdf); ++ ppcrypt_end_ref(crypt); ++ } ++ else ++ { ++ ppstream_info(stream, pdf); ++ } ++ if (ref->xref->trailer.type == PPSTREAM && (type = ppdict_get_name(stream->dict, "Type")) != NULL && ppname_is(type, "ObjStm")) // somewhat dummy.. ++ if (!ppdoc_load_objstm(stream, pdf, ref->xref)) ++ loggerf("invalid objects stream %s at offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset); ++ } ++ pp_free(offmap); ++} ++ ++ppobj * ppdoc_load_entry (ppdoc *pdf, ppref *ref) ++{ ++ iof *I; ++ size_t length; ++ ppxref *xref; ++ ppobj *obj; ++ ppstack *stack; ++ size_t streamoffset; ++ ppref *refref; ++ ppuint refnumber, refversion; ++ ++ length = ref->length > 0 ? ref->length : PP_LENGTH_UNKNOWN; // estimated or unknown ++ if ((I = ppdoc_reader(pdf, ref->offset, length)) == NULL || !ppscan_start_entry(I, ref)) ++ { ++ loggerf("invalid %s offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset); ++ return &ref->object; // PPNONE ++ } ++ stack = &pdf->stack; ++ xref = ref->xref; // to resolve indirects properly ++ if ((obj = ppscan_obj(I, pdf, xref)) == NULL) ++ { ++ loggerf("invalid %s object at offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset); ++ return &ref->object; // PPNONE ++ } ++ ref->object = *obj; ++ ppstack_pop(stack, 1); ++ obj = &ref->object; ++ ref->length = ppdoc_reader_tell(pdf, I) - ref->offset; ++ if (obj->type == PPDICT) ++ { ++ if (ppscan_start_stream(I, pdf, &streamoffset)) ++ { ++ obj->type = PPSTREAM; ++ obj->stream = ppstream_create(pdf, obj->dict, streamoffset); ++ } ++ } ++ else if (obj->type == PPINT) ++ { ++ ppscan_find(I); ++ if (ppscan_uint(I, &refversion) && ppscan_find(I) == 'R') ++ { ++ refnumber = (ppuint)obj->integer; ++ if ((refref = ppxref_find(xref, refnumber)) != NULL) ++ { ++ obj->type = PPREF; ++ obj->ref = refref; ++ } ++ else ++ { ++ obj->type = PPNONE; // as ppref_unresolved() ++ obj->any = NULL; ++ } ++ } ++ } ++ return obj; ++} ++ ++/* Loading entries from object stream ++ ++ /N is the number of contained entries ++ /First is the offset of the first item ++ ++The stream consists of N pairs of numbers ... ++Offsets are ascending (relative to the first), but ref numbers order is arbitrary. ++PDF spec says there might be some additional data between objects, so we should obey offsets. ++Which means we should basically load the stream at once (may be needed anyway to grab the stream [...]). ++*/ ++ ++static int ppdoc_load_objstm (ppstream *stream, ppdoc *pdf, ppxref *xref) ++{ ++ ppdict *dict; // stream dict, actually still on stack ++ ppref *ref; ++ ppobj *obj; ++ ppuint items, firstoffset, offset, objnum, i, invalid = 0; ++ iof *I; ++ uint8_t *firstdata, *indexdata; ++ ppstack *stack; ++ ++ dict = stream->dict; ++ if (!ppdict_rget_uint(dict, "N", &items) || !ppdict_rget_uint(dict, "First", &firstoffset)) ++ return 0; ++ if ((I = ppstream_read(stream, 1, 1)) == NULL) ++ return 0; ++ firstdata = I->pos + firstoffset; ++ if (firstdata >= I->end) ++ goto invalid_objstm; ++ stack = &pdf->stack; ++ //if (pdf->crypt != NULL) ++ // ppcrypt_end_ref(pdf->crypt); // objects are not encrypted, pdf->crypt->ref ensured NULL ++ for (i = 0; i < items; ++i) ++ { ++ ppscan_find(I); ++ if (!ppscan_uint(I, &objnum)) ++ goto invalid_objstm; ++ ppscan_find(I); ++ if (!ppscan_uint(I, &offset)) ++ goto invalid_objstm; ++ if ((ref = ppxref_find_local(xref, objnum)) == NULL || ref->object.type != PPNONE) ++ { ++ loggerf("invalid compressed object number " PPUINTF " at position " PPUINTF, objnum, i); ++ ++invalid; ++ continue; ++ } ++ if (firstdata + offset >= I->end) ++ { ++ loggerf("invalid compressed object offset " PPUINTF " at position " PPUINTF, offset, i); ++ ++invalid; ++ continue; ++ } ++ indexdata = I->pos; // save position ++ I->pos = firstdata + offset; // go to the object ++ ppscan_find(I); ++ if ((obj = ppscan_obj(I, pdf, xref)) != NULL) ++ { ++ ref->object = *obj; ++ ppstack_pop(stack, 1); ++ // nothing more needed, as obj can never be indirect ref or stream ++ } ++ else ++ { ++ ++invalid; ++ loggerf("invalid compressed object %s at stream offset " PPUINTF, ppref_str(objnum, 0), offset); ++ } ++ I->pos = indexdata; // restore position and read next from index ++ } ++ ppstream_done(stream); ++ return invalid == 0; ++invalid_objstm: ++ ppstream_done(stream); ++ return 0; ++} ++ ++/* main PDF loader proc */ ++ ++ppcrypt_status ppdoc_crypt_pass (ppdoc *pdf, const void *userpass, size_t userpasslength, const void *ownerpass, size_t ownerpasslength) ++{ ++ switch (pdf->cryptstatus) ++ { ++ case PPCRYPT_NONE: ++ case PPCRYPT_DONE: ++ case PPCRYPT_FAIL: ++ break; ++ case PPCRYPT_PASS: // initial status or really needs password ++ pdf->cryptstatus = ppdoc_crypt_init(pdf, userpass, userpasslength, ownerpass, ownerpasslength); ++ switch (pdf->cryptstatus) ++ { ++ case PPCRYPT_NONE: ++ case PPCRYPT_DONE: ++ ppdoc_load_entries(pdf); ++ break; ++ case PPCRYPT_PASS: // user needs to check ppdoc_crypt_status() and recall ppdoc_crypt_pass() with the proper password ++ case PPCRYPT_FAIL: // hopeless.. ++ break; ++ } ++ break; ++ } ++ return pdf->cryptstatus; ++} ++ ++static ppdoc * ppdoc_read (ppdoc *pdf, iof_file *input) ++{ ++ uint8_t header[PPDOC_HEADER]; ++ size_t xrefoffset; ++ ++ input = &pdf->input; ++ if (iof_file_read(header, 1, PPDOC_HEADER, input) != PPDOC_HEADER || !ppdoc_header(pdf, header)) ++ return NULL; ++ if (!ppdoc_tail(pdf, input, &xrefoffset)) ++ return NULL; ++ if (ppxref_load(pdf, xrefoffset) == NULL) ++ return NULL; ++ fix_trailer_references(pdf); // after loading xrefs but before accessing trailer refs (/Encrypt might be a reference) ++ // check encryption, if any, try empty password ++ switch (ppdoc_crypt_pass(pdf, "", 0, NULL, 0)) ++ { ++ case PPCRYPT_NONE: // no encryption ++ case PPCRYPT_DONE: // encryption with an empty password ++ case PPCRYPT_PASS: // the user needs to check ppdoc_crypt_status() and call ppdoc_crypt_pass() ++ break; ++ case PPCRYPT_FAIL: // hopeless ++ //loggerf("decryption failed"); ++ //return NULL; ++ break; ++ } ++ return pdf; ++} ++ ++static void ppdoc_pages_init (ppdoc *pdf); ++ ++static ppdoc * ppdoc_create (iof_file *input) ++{ ++ ppdoc *pdf; ++ ppheap *heap; ++ ++ heap = ppheap_new(); ++ pdf = (ppdoc *)ppheap_take(&heap, sizeof(ppdoc)); ++ pdf->flags = 0; ++ pdf->heap = heap; ++ pdf->xref = NULL; ++ pdf->version[0] = '\0'; ++ pdf->crypt = NULL; ++ pdf->cryptstatus = PPCRYPT_PASS; // force encryption check on ppdoc_read() -> ppdoc_crypt_pass() ++ ppstack_init(&pdf->stack, &pdf->heap); ++ ppdoc_reader_init(pdf, input); ++ ppdoc_pages_init(pdf); ++ if (ppdoc_read(pdf, &pdf->input) != NULL) ++ return pdf; ++ ppdoc_free(pdf); ++ return NULL; ++} ++ ++ppdoc * ppdoc_load (const char *filename) ++{ ++ FILE *file; ++ iof_file input; ++ if ((file = fopen(filename, "rb")) == NULL) ++ return NULL; ++ iof_file_init(&input, file); ++ input.flags |= IOF_CLOSE_FILE; ++ return ppdoc_create(&input); ++} ++ ++ppdoc * ppdoc_mem (const void *data, size_t size) ++{ ++ iof_file input; ++ iof_file_rdata_init(&input, data, size); ++ input.flags |= IOF_BUFFER_ALLOC; // todo: 3 modes: borrow, take over, copy? ++ return ppdoc_create(&input); ++} ++ ++void ppdoc_free (ppdoc *pdf) ++{ ++ //iof_file_free(&pdf->input); ++ iof_file_decref(&pdf->input); ++ ppstack_free_buffer(&pdf->stack); ++ ppheap_free(pdf->heap); // last! ++} ++ ++ppcrypt_status ppdoc_crypt_status (ppdoc *pdf) ++{ ++ return pdf->cryptstatus; ++} ++ ++ppint ppdoc_permissions (ppdoc *pdf) ++{ ++ return pdf->crypt != NULL ? pdf->crypt->permissions : (ppint)0xFFFFFFFFFFFFFFFF; ++} ++ ++/* pages access */ ++ ++static pparray * pppage_node (ppdict *dict, ppuint *count, ppname *type) ++{ ++ ppname *pkey, key; ++ ppobj *obj; ++ pparray *kids = NULL; ++ *count = 0; ++ *type = NULL; ++ for (ppdict_first(dict, pkey, obj); (key = *pkey) != NULL; ppdict_next(pkey, obj)) ++ { ++ switch (key[0]) ++ { ++ case 'T': ++ if (ppname_is(key, "Type")) ++ *type = ppobj_get_name(obj); ++ break; ++ case 'C': ++ if (ppname_is(key, "Count")) ++ ppobj_get_uint(obj, *count); ++ break; ++ case 'K': ++ if (ppname_is(key, "Kids")) ++ kids = ppobj_rget_array(obj); ++ break; ++ } ++ } ++ return kids; ++} ++ ++#define ppname_is_page(type) (type != NULL && ppname_is(type, "Page")) ++ ++ppuint ppdoc_page_count (ppdoc *pdf) ++{ ++ ppref *ref; ++ ppname type; ++ ppuint count; ++ if ((ref = ppxref_pages(pdf->xref)) == NULL) ++ return 0; ++ if (pppage_node(ref->object.dict, &count, &type) == NULL) ++ return ppname_is_page(type) ? 1 : 0; // acrobat and ghostscript accept documents with root /Pages entry being a reference to a sole /Page object ++ return count; ++} ++ ++ppref * ppdoc_page (ppdoc *pdf, ppuint index) ++{ ++ ppdict *dict; ++ ppuint count; ++ pparray *kids; ++ size_t size, i; ++ ppobj *r, *o; ++ ppref *ref; ++ ppname type; ++ ++ ++ if ((ref = ppxref_pages(pdf->xref)) == NULL) ++ return NULL; ++ dict = ref->object.dict; ++ if ((kids = pppage_node(dict, &count, &type)) != NULL) ++ { ++ if (index < 1 || index > count) ++ return NULL; ++ } ++ else ++ { ++ return index == 1 && ppname_is_page(type) ? ref : NULL; ++ } ++scan_array: ++ if (index <= count / 2) ++ { // probably shorter way from the beginning ++ for (i = 0, size = kids->size, r = pparray_at(kids, 0); i < size; ++i, ++r) ++ { ++ if (r->type != PPREF) ++ return NULL; ++ o = &r->ref->object; ++ if (o->type != PPDICT) ++ return NULL; ++ dict = o->dict; ++ if ((kids = pppage_node(dict, &count, &type)) != NULL) ++ { ++ if (index <= count) ++ goto scan_array; ++ index -= count; ++ continue; ++ } ++ if (index == 1 && ppname_is_page(type)) ++ return r->ref; ++ --index; ++ } ++ } ++ else if ((size = kids->size) > 0) // for safe (size-1) ++ { // probably shorter way from the end ++ index = count - index + 1; ++ for (i = 0, r = pparray_at(kids, size - 1); i < size; ++i, --r) ++ { ++ if (r->type != PPREF) ++ return NULL; ++ o = &r->ref->object; ++ if (o->type != PPDICT) ++ return NULL; ++ dict = o->dict; ++ if ((kids = pppage_node(dict, &count, &type)) != NULL) ++ { ++ if (index <= count) ++ goto scan_array; ++ index -= count; ++ continue; ++ } ++ if (index == 1 && ppname_is_page(type)) ++ return r->ref; ++ --index; ++ } ++ } ++ return NULL; ++} ++ ++/* ++Through pages iterator. Iterating over pages tree just on the base of /Kids and /Parent keys ++is ineffective, as to get next pageref we need to take parent, find the pageref in /Kids, ++take next (or go upper).. Annoying. We use a dedicated stack for pages iterator. This could ++actually be done with pdf->stack, but some operations may clear it, so safer to keep it independent ++Besides, its depth is constant (set on first use), so no need for allocs. ++*/ ++ ++static void ppdoc_pages_init (ppdoc *pdf) ++{ ++ pppages *pages; ++ pages = &pdf->pages; ++ pages->root = pages->parent = pages->buffer; ++ pages->depth = 0; ++ pages->space = PPPAGES_STACK_DEPTH; ++} ++ ++static ppkids * pppages_push (ppdoc *pdf, pparray *kids) ++{ ++ ppkids *newroot, *bounds; ++ pppages *pages; ++ pages = &pdf->pages; ++ if (pages->depth == pages->space) ++ { ++ pages->space <<= 1; ++ newroot = (ppkids *)ppheap_take(&pdf->heap, pages->space * sizeof(ppkids)); ++ memcpy(newroot, pages->root, pages->depth * sizeof(ppkids)); ++ pages->root = newroot; ++ } ++ bounds = pages->parent = &pages->root[pages->depth++]; ++ bounds->current = pparray_at(kids, 0); ++ bounds->sentinel = pparray_at(kids, kids->size); ++ return bounds; ++} ++ ++#define pppages_pop(pages) (--((pages)->parent), --((pages)->depth)) ++ ++static ppref * ppdoc_pages_group_first (ppdoc *pdf, ppref *ref) ++{ ++ ppdict *dict; ++ pparray *kids; ++ ppuint count; ++ ppname type; ++ ++ dict = ref->object.dict; // typecheck made by callers ++ while ((kids = pppage_node(dict, &count, &type)) != NULL) ++ { ++ if ((ref = pparray_get_ref(kids, 0)) == NULL || ref->object.type != PPDICT) ++ return NULL; ++ pppages_push(pdf, kids); ++ dict = ref->object.dict; ++ } ++ return ppname_is_page(type) ? ref : NULL; ++} ++ ++ppref * ppdoc_first_page (ppdoc *pdf) ++{ ++ ppref *ref; ++ pppages *pages; ++ if ((ref = ppdoc_pages(pdf)) == NULL) ++ return NULL; ++ pages = &pdf->pages; ++ pages->parent = pages->root; ++ pages->depth = 0; ++ return ppdoc_pages_group_first(pdf, ref); ++} ++ ++ppref * ppdoc_next_page (ppdoc *pdf) ++{ ++ pppages *pages; ++ ppkids *bounds; ++ ppref *ref; ++ ppobj *obj; ++ pages = &pdf->pages; ++ while (pages->depth > 0) ++ { ++ bounds = pages->parent; ++ obj = ++bounds->current; ++ if (obj < bounds->sentinel) ++ { ++ if (obj->type != PPREF) ++ return NULL; ++ ref = obj->ref; ++ if (ref->object.type != PPDICT) ++ return NULL; ++ return ppdoc_pages_group_first(pdf, ref); ++ } ++ else ++ { // no next node, go upper ++ pppages_pop(pages); ++ } ++ } ++ return NULL; ++} ++ ++/* context */ ++ ++ppcontext * ppcontext_new (void) ++{ ++ ppheap *heap; ++ ppcontext *context; ++ heap = ppheap_new(); ++ context = (ppcontext *)pp_malloc(sizeof(ppcontext)); // not from priv heap, as we delete it on renew ++ context->heap = heap; ++ ppstack_init(&context->stack, &context->heap); ++ return context; ++} ++ ++void ppcontext_done (ppcontext *context) ++{ ++ ppheap_renew(context->heap); ++ ppstack_clear(&context->stack); ++} ++ ++void ppcontext_free (ppcontext *context) ++{ ++ ppstack_free_buffer(&context->stack); ++ ppheap_free(context->heap); ++ pp_free(context); ++} ++ ++/* page contents streams */ ++ ++//#define ppcontents_first_stream(array) pparray_rget_stream(array, 0) ++ ++static ppstream * ppcontents_first_stream (pparray *array) ++{ ++ size_t i; ++ ppobj *obj; ++ ppref *ref; ++ for (pparray_first(array, i, obj); i < array->size; pparray_next(i, obj)) ++ if ((ref = ppobj_get_ref(obj)) != NULL && ref->object.type == PPSTREAM) ++ return ref->object.stream; ++ return NULL; ++} ++ ++static ppstream * ppcontents_next_stream (pparray *array, ppstream *stream) ++{ ++ size_t i; ++ ppobj *obj; ++ ppref *ref; ++ for (pparray_first(array, i, obj); i < array->size; pparray_next(i, obj)) ++ if ((ref = ppobj_get_ref(obj)) != NULL && ref->object.type == PPSTREAM && ref->object.stream == stream) ++ if (++i < array->size && (ref = ppobj_get_ref(obj + 1)) != NULL && ref->object.type == PPSTREAM) ++ return ref->object.stream; ++ return NULL; ++} ++ ++ppstream * ppcontents_first (ppdict *dict) ++{ ++ ppobj *contentsobj; ++ if ((contentsobj = ppdict_rget_obj(dict, "Contents")) == NULL) ++ return NULL; ++ switch (contentsobj->type) ++ { ++ case PPARRAY: ++ return ppcontents_first_stream(contentsobj->array); ++ case PPSTREAM: ++ return contentsobj->stream; ++ default: ++ break; ++ } ++ return NULL; ++} ++ ++ppstream * ppcontents_next (ppdict *dict, ppstream *stream) ++{ ++ ppobj *contentsobj; ++ if ((contentsobj = ppdict_rget_obj(dict, "Contents")) == NULL) ++ return NULL; ++ switch (contentsobj->type) ++ { ++ case PPARRAY: ++ return ppcontents_next_stream(contentsobj->array, stream); ++ case PPSTREAM: ++ break; ++ default: ++ break; ++ } ++ return NULL; ++} ++ ++static ppobj * ppcontents_op (iof *I, ppstack *stack, size_t *psize, ppname *pname) ++{ ++ ppobj *obj; ++ ppstack_clear(stack); ++ do { ++ if (ppscan_find(I) < 0) ++ return NULL; ++ if ((obj = ppscan_psobj(I, stack)) == NULL) ++ return NULL; ++ } while (obj->type != PPNAME || !ppname_exec(obj->name)); ++ *pname = obj->name; ++ *psize = stack->size - 1; ++ return stack->buf; ++} ++ ++ppobj * ppcontents_first_op (ppcontext *context, ppstream *stream, size_t *psize, ppname *pname) ++{ ++ iof *I; ++ if ((I = ppstream_read(stream, 1, 0)) == NULL) ++ return NULL; ++ return ppcontents_op(I, &context->stack, psize, pname); ++} ++ ++ppobj * ppcontents_next_op (ppcontext *context, ppstream *stream, size_t *psize, ppname *pname) ++{ ++ return ppcontents_op(ppstream_iof(stream), &context->stack, psize, pname); ++} ++ ++ppobj * ppcontents_parse (ppcontext *context, ppstream *stream, size_t *psize) ++{ ++ iof *I; ++ ppstack *stack; ++ ppobj *obj; ++ stack = &context->stack; ++ ppstack_clear(stack); ++ if ((I = ppstream_read(stream, 1, 0)) == NULL) ++ return NULL; ++ while (ppscan_find(I) >= 0) ++ if ((obj = ppscan_psobj(I, stack)) == NULL) ++ goto error; ++ *psize = stack->size; ++ ppstream_done(stream); ++ return stack->buf; ++error: ++ ppstream_done(stream); ++ return NULL; ++} ++ ++/* boxes */ ++ ++pprect * pparray_to_rect (pparray *array, pprect *rect) ++{ ++ ppobj *obj; ++ if (array->size != 4) ++ return NULL; ++ obj = pparray_at(array, 0); ++ if (!ppobj_get_num(obj, rect->lx)) return NULL; ++ obj = pparray_at(array, 1); ++ if (!ppobj_get_num(obj, rect->ly)) return NULL; ++ obj = pparray_at(array, 2); ++ if (!ppobj_get_num(obj, rect->rx)) return NULL; ++ obj = pparray_at(array, 3); ++ if (!ppobj_get_num(obj, rect->ry)) return NULL; ++ return rect; ++} ++ ++pprect * ppdict_get_rect (ppdict *dict, const char *name, pprect *rect) ++{ ++ pparray *array; ++ return (array = ppdict_rget_array(dict, name)) != NULL ? pparray_to_rect(array, rect) : NULL; ++} ++ ++pprect * ppdict_get_box (ppdict *dict, const char *name, pprect *rect) ++{ ++ do { ++ if (ppdict_get_rect(dict, name, rect) != NULL) ++ return rect; ++ dict = ppdict_rget_dict(dict, "Parent"); ++ } while (dict != NULL); ++ return NULL; ++} ++ ++ppmatrix * pparray_to_matrix (pparray *array, ppmatrix *matrix) ++{ ++ ppobj *obj; ++ if (array->size != 6) ++ return NULL; ++ obj = pparray_at(array, 0); ++ if (!ppobj_get_num(obj, matrix->xx)) return NULL; ++ obj = pparray_at(array, 1); ++ if (!ppobj_get_num(obj, matrix->xy)) return NULL; ++ obj = pparray_at(array, 2); ++ if (!ppobj_get_num(obj, matrix->yx)) return NULL; ++ obj = pparray_at(array, 3); ++ if (!ppobj_get_num(obj, matrix->yy)) return NULL; ++ obj = pparray_at(array, 4); ++ if (!ppobj_get_num(obj, matrix->x)) return NULL; ++ obj = pparray_at(array, 5); ++ if (!ppobj_get_num(obj, matrix->y)) return NULL; ++ return matrix; ++} ++ ++ppmatrix * ppdict_get_matrix (ppdict *dict, const char *name, ppmatrix *matrix) ++{ ++ pparray *array; ++ return (array = ppdict_rget_array(dict, name)) != NULL ? pparray_to_matrix(array, matrix) : NULL; ++} ++ ++/* logger */ ++ ++void pplog_callback (pplogger_callback logger, void *alien) ++{ ++ logger_callback((logger_function)logger, alien); ++} ++ ++int pplog_prefix (const char *prefix) ++{ ++ return logger_prefix(prefix); ++} ++ ++/* version */ ++ ++const char * ppdoc_version_string (ppdoc *pdf) ++{ ++ return pdf->version; ++} ++ ++int ppdoc_version_number (ppdoc *pdf, int *minor) ++{ ++ *minor = pdf->version[2] - '0'; ++ return pdf->version[0] - '0'; ++} ++ ++/* doc info */ ++ ++size_t ppdoc_file_size (ppdoc *pdf) ++{ ++ return pdf->filesize; ++} ++ ++ppuint ppdoc_objects (ppdoc *pdf) ++{ ++ ppuint count; ++ ppxref *xref; ++ for (count = 0, xref = pdf->xref; xref != NULL; xref = xref->prev) ++ count += xref->count; ++ return count; ++} ++ ++size_t ppdoc_memory (ppdoc *pdf, size_t *waste) ++{ ++ size_t used; ++ ppheap *heap; ++ used = 0, *waste = 0; ++ for (heap = pdf->heap; heap != NULL; heap = heap->prev) ++ { ++ used += heap->space; ++ *waste += heap->size; ++ } ++ return used; ++} +diff --git a/texk/web2c/luatexdir/luapplib/ppload.h b/texk/web2c/luatexdir/luapplib/ppload.h +new file mode 100644 +index 000000000..163928398 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppload.h +@@ -0,0 +1,54 @@ ++ ++#ifndef PP_LOAD_H ++#define PP_LOAD_H ++ ++typedef struct { ++ ppobj *buf; // ppobjects buffer (allocated, not from our heap) ++ ppobj *pos; // current ppobj * ++ size_t size; // stack size ++ size_t space; // available space ++ ppheap **pheap; // allocator (parent pdf->stack->pheap or own) ++} ppstack; ++ ++typedef struct { ++ ppobj *current; ++ ppobj *sentinel; ++} ppkids; ++ ++#define PPPAGES_STACK_DEPTH 4 ++ ++typedef struct { ++ ppkids buffer[PPPAGES_STACK_DEPTH]; ++ ppkids *root; ++ ppkids *parent; ++ ppuint depth; ++ ppuint space; ++} pppages; ++ ++struct ppdoc { ++ char version[5]; ++ iof_file input; ++ iof reader; ++ uint8_t *buffer; ++ size_t filesize; ++ ppxref *xref; ++ ppheap *heap; ++ ppstack stack; ++ pppages pages; ++ int flags; ++ ppcrypt *crypt; ++ ppcrypt_status cryptstatus; ++}; ++ ++#define PPDOC_LINEARIZED (1 << 0) ++ ++ppobj * ppdoc_load_entry (ppdoc *pdf, ppref *ref); ++#define ppobj_preloaded(pdf, obj) ((obj)->type != PPREF ? (obj) : ((obj)->ref->object.type == PPNONE ? ppdoc_load_entry(pdf, (obj)->ref) : &(obj)->ref->object)) ++ppstring ppstring_internal (const void *data, size_t size, ppheap **pheap); ++ ++struct ppcontext { ++ ppheap *heap; ++ ppstack stack; ++}; ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/ppstream.c b/texk/web2c/luatexdir/luapplib/ppstream.c +new file mode 100644 +index 000000000..0e4be7b8e +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppstream.c +@@ -0,0 +1,310 @@ ++ ++#include "ppfilter.h" ++#include "pplib.h" ++ ++ppstream * ppstream_create (ppdoc *pdf, ppdict *dict, size_t offset) ++{ ++ ppstream *stream; ++ stream = (ppstream *)ppheap_take(&pdf->heap, sizeof(ppstream)); ++ stream->dict = dict; ++ stream->offset = offset; ++ //if (!ppdict_rget_uint(dict, "Length", &stream->length)) // may be indirect pointing PPNONE at this moment ++ // stream->length = 0; ++ stream->length = 0; ++ stream->input = &pdf->input; ++ stream->I = NULL; ++ stream->cryptkey = NULL; ++ stream->flags = 0; ++ return stream; ++} ++ ++/* codecs */ ++ ++enum { ++ PPSTREAM_UNKNOWN = -1, ++ PPSTREAM_BASE16 = 0, ++ PPSTREAM_BASE85, ++ PPSTREAM_RUNLENGTH, ++ PPSTREAM_FLATE, ++ PPSTREAM_LZW, ++ PPSTREAM_CCITT, ++ PPSTREAM_DCT, ++ PPSTREAM_JBIG2, ++ PPSTREAM_JPX, ++ PPSTREAM_CRYPT ++}; ++ ++static int ppstream_codec_type (ppname name) ++{ // one of those places where some hash wuld be nice.. ++ switch (name[0]) ++ { ++ case 'A': ++ if (ppname_is(name, "ASCIIHexDecode")) return PPSTREAM_BASE16; ++ if (ppname_is(name, "ASCII85Decode")) return PPSTREAM_BASE85; ++ break; ++ case 'R': ++ if (ppname_is(name, "RunLengthDecode")) return PPSTREAM_RUNLENGTH; ++ break; ++ case 'F': ++ if (ppname_is(name, "FlateDecode")) return PPSTREAM_FLATE; ++ break; ++ case 'L': ++ if (ppname_is(name, "LZWDecode")) return PPSTREAM_LZW; ++ break; ++ case 'D': ++ if (ppname_is(name, "DCTDecode")) return PPSTREAM_DCT; ++ break; ++ case 'C': ++ if (ppname_is(name, "CCITTFaxDecode")) return PPSTREAM_CCITT; ++ if (ppname_is(name, "Crypt")) return PPSTREAM_CRYPT; ++ break; ++ case 'J': ++ if (ppname_is(name, "JPXDecode")) return PPSTREAM_JPX; ++ if (ppname_is(name, "JBIG2Decode")) return PPSTREAM_JBIG2; ++ break; ++ } ++ return PPSTREAM_UNKNOWN; ++} ++ ++static iof * ppstream_predictor (ppdict *params, iof *N) ++{ ++ ppint predictor, rowsamples, components, samplebits; ++ ++ if (!ppdict_get_int(params, "Predictor", &predictor) || predictor <= 1) ++ return N; ++ if (!ppdict_get_int(params, "Columns", &rowsamples) || rowsamples == 0) // sanity, filter probably expects >0 ++ rowsamples = 1;; ++ if (!ppdict_get_int(params, "Colors", &components) || components == 0) // ditto ++ components = 1; ++ if (!ppdict_get_int(params, "BitsPerComponent", &samplebits) || samplebits == 0) ++ samplebits = 8; ++ return iof_filter_predictor_decoder(N, (int)predictor, (int)rowsamples, (int)components, (int)samplebits); ++} ++ ++static iof * ppstream_decoder (ppstream *stream, int codectype, ppdict *params, iof *N) ++{ ++ int flags; ++ iof *F, *P; ++ ppint earlychange; ++ ppstring cryptkey; ++ ++ switch (codectype) ++ { ++ case PPSTREAM_BASE16: ++ return iof_filter_base16_decoder(N); ++ case PPSTREAM_BASE85: ++ return iof_filter_base85_decoder(N); ++ case PPSTREAM_RUNLENGTH: ++ return iof_filter_runlength_decoder(N); ++ case PPSTREAM_FLATE: ++ if ((F = iof_filter_flate_decoder(N)) != NULL) ++ { ++ if (params != NULL) ++ { ++ if ((P = ppstream_predictor(params, F)) != NULL) ++ return P; ++ iof_close(F); ++ break; ++ } ++ return F; ++ } ++ break; ++ case PPSTREAM_LZW: ++ flags = LZW_DECODER_DEFAULTS; ++ if (params != NULL && ppdict_get_int(params, "EarlyChange", &earlychange) && earlychange == 0) // integer, not boolean ++ flags &= ~LZW_EARLY_INDEX; ++ if ((F = iof_filter_lzw_decoder(N, flags)) != NULL) ++ { ++ if (params != NULL) ++ { ++ if ((P = ppstream_predictor(params, F)) != NULL) ++ return P; ++ iof_close(F); ++ break; ++ } ++ return F; ++ } ++ break; ++ case PPSTREAM_CRYPT: ++ if ((cryptkey = stream->cryptkey) == NULL) ++ return N; // /Identity crypt ++ if (stream->flags & PPSTREAM_ENCRYPTED_AES) ++ return iof_filter_aes_decoder(N, cryptkey, ppstring_size(cryptkey)); ++ if (stream->flags & PPSTREAM_ENCRYPTED_RC4) ++ return iof_filter_rc4_decoder(N, cryptkey, ppstring_size(cryptkey)); ++ return NULL; // if neither AES or RC4 but cryptkey present, something went wrong; see ppstream_info() ++ case PPSTREAM_CCITT: ++ case PPSTREAM_DCT: ++ case PPSTREAM_JBIG2: ++ case PPSTREAM_JPX: ++ case PPSTREAM_UNKNOWN: ++ break; ++ } ++ return NULL; ++} ++ ++#define ppstream_image(type) (type == PPSTREAM_DCT || type == PPSTREAM_JBIG2 || PPSTREAM_JPX) ++ ++#define ppstream_source(stream) iof_filter_stream_coreader((iof_file *)((stream)->input), (size_t)((stream)->offset), (size_t)((stream)->length)) ++#define ppstream_auxsource(filename) iof_filter_file_reader(filename) ++ ++static ppname ppstream_filter_name (ppobj *filterobj, size_t index) ++{ ++ if (filterobj->type == PPNAME) ++ return index == 0 ? filterobj->name : NULL; ++ if (filterobj->type == PPARRAY) ++ return pparray_get_name(filterobj->array, index); ++ return NULL; ++} ++ ++static ppdict * ppstream_filter_params (ppobj *paramsobj, size_t index) ++{ ++ if (paramsobj->type == PPDICT) ++ return index == 0 ? paramsobj->dict : NULL; ++ if (paramsobj->type == PPARRAY) ++ return pparray_rget_dict(paramsobj->array, index); ++ return NULL; ++} ++ ++static const char * ppstream_aux_filename (ppobj *filespec) ++{ // mockup, here we should decode the string ++ if (filespec->type == PPSTRING) ++ { ++ return (const char *)(filespec->string); ++ } ++ // else might be a dict - todo ++ return NULL; ++} ++ ++iof * ppstream_read (ppstream *stream, int decode, int all) ++{ ++ ppdict *dict; ++ iof *I, *F; ++ int codectype, external, owncrypt; ++ ppobj *filterobj, *paramsobj, *filespecobj; ++ ppname filter; ++ ppdict *params; ++ size_t index; ++ const char *filename; ++ ++ if (ppstream_iof(stream) != NULL) ++ return NULL; // usage error ++ ++ dict = stream->dict; ++ if ((filespecobj = ppdict_rget_obj(dict, "F")) != NULL) ++ { ++ filename = ppstream_aux_filename(filespecobj); ++ I = filename != NULL ? ppstream_auxsource(filename) : NULL; ++ external = 1; ++ } ++ else ++ { ++ I = ppstream_source(stream); ++ external = 0; ++ } ++ if (I == NULL) ++ return NULL; ++ /* If the stream is encrypted, decipher is the first to be applied */ ++ owncrypt = (stream->flags & PPSTREAM_ENCRYPTED_OWN) != 0; ++ if (!owncrypt) ++ { ++ if (stream->cryptkey != NULL) ++ { /* implied global crypt */ ++ if ((F = ppstream_decoder(stream, PPSTREAM_CRYPT, NULL, I)) == NULL) ++ goto stream_error; ++ I = F; ++ } /* otherwise no crypt at all or /Identity */ ++ } ++ if (decode || owncrypt) ++ { ++ filterobj = ppdict_rget_obj(dict, external ? "FFilter" : "Filter"); ++ if (filterobj != NULL) ++ { ++ paramsobj = ppdict_rget_obj(dict, external ? "FDecodeParms" : "DecodeParms"); ++ for (index = 0, filter = ppstream_filter_name(filterobj, 0); filter != NULL; filter = ppstream_filter_name(filterobj, ++index)) ++ { ++ params = paramsobj != NULL ? ppstream_filter_params(paramsobj, index) : NULL; ++ codectype = ppstream_codec_type(filter); ++ if ((F = ppstream_decoder(stream, codectype, params, I)) != NULL) ++ { ++ I = F; ++ if (owncrypt && !decode && codectype == PPSTREAM_CRYPT) ++ break; // /Crypt filter should always be first, so in practise we return decrypted but compressed ++ continue; ++ } ++ if (!ppstream_image(codectype)) // something unexpected ++ goto stream_error; ++ else // just treat image data (jpeg/jbig) as the target data ++ break; ++ } ++ } ++ } ++ if (all) ++ iof_load(I); ++ else ++ iof_input(I); ++ stream->I = I; ++ return I; ++stream_error: ++ iof_close(I); ++ return NULL; ++} ++ ++uint8_t * ppstream_first (ppstream *stream, size_t *size, int decode) ++{ ++ iof *I; ++ if ((I = ppstream_read(stream, decode, 0)) != NULL) ++ { ++ *size = (size_t)iof_left(I); ++ return I->pos; ++ } ++ *size = 0; ++ return NULL; ++} ++ ++uint8_t * ppstream_next (ppstream *stream, size_t *size) ++{ ++ iof *I; ++ if ((I = ppstream_iof(stream)) != NULL) ++ { ++ I->pos = I->end; ++ if ((*size = iof_input(I)) > 0) ++ return I->pos; ++ } ++ *size = 0; ++ return NULL; ++} ++ ++uint8_t * ppstream_all (ppstream *stream, size_t *size, int decode) ++{ ++ iof *I; ++ if ((I = ppstream_read(stream, decode, 1)) != NULL) ++ { ++ *size = (size_t)iof_left(I); ++ return I->pos; ++ } ++ *size = 0; ++ return NULL; ++} ++ ++void ppstream_done (ppstream *stream) ++{ ++ iof *I; ++ if ((I = ppstream_iof(stream)) != NULL) ++ { ++ iof_close(I); ++ stream->I = NULL; ++ } ++} ++ ++/* */ ++ ++void ppstream_init_buffers (void) ++{ ++ iof_filters_init(); ++} ++ ++void ppstream_free_buffers (void) ++{ ++ iof_filters_free(); ++} +diff --git a/texk/web2c/luatexdir/luapplib/ppstream.h b/texk/web2c/luatexdir/luapplib/ppstream.h +new file mode 100644 +index 000000000..ae5c19cd9 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppstream.h +@@ -0,0 +1,9 @@ ++ ++#ifndef PP_STREAM_H ++#define PP_STREAM_H ++ ++ppstream * ppstream_create (ppdoc *pdf, ppdict *dict, size_t offset); ++iof * ppstream_read (ppstream *stream, int decode, int all); ++#define ppstream_iof(stream) ((iof *)((stream)->I)) ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/pptest1.c b/texk/web2c/luatexdir/luapplib/pptest1.c +new file mode 100644 +index 000000000..4671849f5 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/pptest1.c +@@ -0,0 +1,86 @@ ++ ++#include ++#include "ppapi.h" ++ ++static const char * sizenum (size_t s) ++{ ++ static char buffer[32]; ++ if (s < 1000) ++ sprintf(buffer, "%uB", (unsigned)s); ++ else if (s < 1000000) ++ sprintf(buffer, "%.2fkB", (double)(s) / 1000); ++ else ++ sprintf(buffer, "%.2fMB", (double)(s) / 1000000); ++ return buffer; ++} ++ ++static const char * crypt_info (ppdoc *pdf) ++{ ++ switch (ppdoc_crypt_status(pdf)) ++ { ++ case PPCRYPT_NONE: ++ return "none"; ++ case PPCRYPT_DONE: ++ return "empty password"; ++ case PPCRYPT_PASS: ++ return "nonempty password"; ++ default: ++ break; ++ } ++ return "this shouldn't happen"; ++} ++ ++static void print_info (ppdoc *pdf) ++{ ++ ppdict *info; ++ ppstring creator, producer; ++ size_t memused, memwaste; ++ ++ if ((info = ppdoc_info(pdf)) != NULL) ++ { ++ if ((creator = ppdict_rget_string(info, "Creator")) != NULL) ++ printf(" creator: %s\n", ppstring_decoded(creator)); ++ if ((producer = ppdict_rget_string(info, "Producer")) != NULL) ++ printf(" producer: %s\n", ppstring_decoded(producer)); ++ } ++ printf(" version: %s\n", ppdoc_version_string(pdf)); ++ printf(" protection: %s\n", crypt_info(pdf)); ++ printf(" filesize: %s\n", sizenum(ppdoc_file_size(pdf))); ++ printf(" objects: " PPUINTF "\n", ppdoc_objects(pdf)); ++ printf(" pagecount: " PPUINTF "\n", ppdoc_page_count(pdf)); ++ memused = ppdoc_memory(pdf, &memwaste); ++ printf(" memused: %s\n", sizenum(memused)); ++ printf(" memwaste: %s\n", sizenum(memwaste)); ++} ++ ++static int usage (const char *argv0) ++{ ++ printf("pplib " pplib_version ", " pplib_author "\n"); ++ printf("usage: %s file1.pdf file2.pdf ...\n", argv0); ++ return 0; ++} ++ ++int main (int argc, const char **argv) ++{ ++ const char *filepath; ++ int a; ++ ppdoc *pdf; ++ ++ if (argc < 2) ++ return usage(argv[0]); ++ for (a = 1; a < argc; ++a) ++ { ++ filepath = argv[a]; ++ printf("loading %s... ", filepath); ++ pdf = ppdoc_load(filepath); ++ if (pdf == NULL) ++ { ++ printf("failed\n"); ++ continue; ++ } ++ printf("done.\n"); ++ print_info(pdf); ++ ppdoc_free(pdf); ++ } ++ return 0; ++} +diff --git a/texk/web2c/luatexdir/luapplib/pptest2.c b/texk/web2c/luatexdir/luapplib/pptest2.c +new file mode 100644 +index 000000000..4b7f66a01 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/pptest2.c +@@ -0,0 +1,139 @@ ++ ++#include ++#include ++#include "ppapi.h" ++ ++static const char * get_file_name (const char *path) ++{ ++ const char *fn, *p; ++ for (fn = p = path; *p != '\0'; ++p) ++ if (*p == '\\' || *p == '/') ++ fn = p + 1; ++ return fn; ++} ++ ++static void box_info (ppdict *pagedict, FILE *fh) ++{ ++ const char *boxes[] = {"MediaBox", "CropBox", "BleedBox", "TrimBox", "ArtBox"}; ++ pprect rect; ++ size_t i; ++ for (i = 0; i < sizeof(boxes) / sizeof(const char *); ++i) ++ if (ppdict_get_box(pagedict, boxes[i], &rect)) ++ fprintf(fh, "%%%% %s [%f %f %f %f]\n", boxes[i], rect.lx, rect.ly, rect.rx, rect.ry); ++} ++ ++static int usage (const char *argv0) ++{ ++ printf("pplib " pplib_version ", " pplib_author "\n"); ++ printf("usage: %s file1.pdf file2.pdf ...\n", argv0); ++ return 0; ++} ++ ++#define OUTDIR "." ++ ++static void log_callback (const char *message, void *alien) ++{ ++ fprintf((FILE *)alien, "\nooops: %s\n", message); ++} ++ ++int main (int argc, const char **argv) ++{ ++ const char *filepath, *filename; ++ int a; ++ ppdoc *pdf; ++ ppref *pageref; ++ ppdict *pagedict; ++ int pageno; ++ char outname[1024]; ++ FILE *fh; ++ ppstream *stream; ++ uint8_t *data; ++ size_t size; ++ ppcontext *context; ++ ppobj *obj; ++ ppname op; ++ size_t operators; ++ ++ if (argc < 2) ++ return usage(argv[0]); ++ ppstream_init_buffers(); ++ pplog_callback(log_callback, stderr); ++ context = ppcontext_new(); ++ for (a = 1; a < argc; ++a) ++ { ++ filepath = argv[a]; ++ printf("loading %s... ", filepath); ++ pdf = ppdoc_load(filepath); ++ if (pdf == NULL) ++ { ++ printf("failed\n"); ++ continue; ++ } ++ printf("done.\n"); ++ switch (ppdoc_crypt_status(pdf)) ++ { ++ case PPCRYPT_NONE: ++ case PPCRYPT_DONE: ++ break; ++ case PPCRYPT_PASS: ++ if (ppdoc_crypt_pass(pdf, "dummy", 5, NULL, 0) == PPCRYPT_DONE || ppdoc_crypt_pass(pdf, NULL, 0, "dummy", 5) == PPCRYPT_DONE) ++ break; ++ printf("sorry, password needed\n"); ++ ppdoc_free(pdf); ++ continue; ++ case PPCRYPT_FAIL: ++ printf("sorry, encryption failed\n"); ++ ppdoc_free(pdf); ++ continue; ++ } ++ filename = get_file_name(filepath); ++ sprintf(outname, OUTDIR "/%s.out", filename); ++ fh = fopen(outname, "wb"); ++ if (fh == NULL) ++ { ++ printf("can't open %s for writing\n", outname); ++ continue; ++ } ++ for (pageref = ppdoc_first_page(pdf), pageno = 1; ++ pageref != NULL; ++ pageref = ppdoc_next_page(pdf), ++pageno) ++ { ++ pagedict = pageref->object.dict; ++ /* decompress contents data */ ++ fprintf(fh, "%%%% PAGE %d\n", pageno); ++ box_info(pagedict, fh); ++ for (stream = ppcontents_first(pagedict); ++ stream != NULL; ++ stream = ppcontents_next(pagedict, stream)) ++ { ++ for (data = ppstream_first(stream, &size, 1); ++ data != NULL; ++ data = ppstream_next(stream, &size)) ++ fwrite(data, size, 1, fh); ++ ppstream_done(stream); ++ } ++ /* now parse contents */ ++ for (stream = ppcontents_first(pagedict); ++ stream != NULL; ++ stream = ppcontents_next(pagedict, stream)) ++ { ++ operators = 0; ++ for (obj = ppcontents_first_op(context, stream, &size, &op); ++ obj != NULL; ++ obj = ppcontents_next_op(context, stream, &size, &op)) ++ ++operators; ++ fprintf(fh, "%%%% OPERATORS count " PPSIZEF "\n", operators); ++ ppstream_done(stream); ++ //obj = ppcontents_parse(context, stream, &size); ++ //fprintf(fh, "%%%% items count " PPSIZEF "\n", size); ++ fprintf(fh, "\n"); ++ } ++ ppcontext_done(context); ++ } ++ fclose(fh); ++ ppdoc_free(pdf); ++ } ++ ppcontext_free(context); ++ ppstream_free_buffers(); ++ return 0; ++} +diff --git a/texk/web2c/luatexdir/luapplib/ppxref.c b/texk/web2c/luatexdir/luapplib/ppxref.c +new file mode 100644 +index 000000000..984f6cbe3 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppxref.c +@@ -0,0 +1,162 @@ ++ ++#include "pplib.h" ++ ++#define PPXREF_MAP_INIT 16 // number of xref sections ++ ++ppxref * ppxref_create (ppdoc *pdf, size_t initsize, size_t xrefoffset) ++{ ++ ppxref *xref; ++ ++ if (initsize == 0) // unknown ++ initsize = PPXREF_MAP_INIT; ++ xref = (ppxref *)ppheap_take(&pdf->heap, sizeof(ppxref) + initsize * sizeof(ppxsec)); ++ xref->sects = (ppxsec *)(xref + 1); ++ xref->size = 0; ++ xref->space = initsize; ++ xref->count = 0; ++ xref->trailer.type = PPNONE; ++ xref->trailer.dict = NULL; ++ xref->prev = NULL; ++ xref->pdf = pdf; ++ xref->offset = xrefoffset; ++ //xref->crypt = NULL; ++ return xref; ++} ++ ++ppxsec * ppxref_push_section (ppxref *xref, ppheap **pheap) ++{ ++ ppxsec *sects; ++ if (xref->size < xref->space) ++ return &xref->sects[xref->size++]; ++ xref->space <<= 1; ++ sects = xref->sects; ++ xref->sects = (ppxsec *)ppheap_take(pheap, xref->space * sizeof(ppxsec)); // waste but rare ++ memcpy(xref->sects, sects, xref->size * sizeof(ppxsec)); ++ return &xref->sects[xref->size++]; ++} ++ ++static void ppxref_sort_sects (ppxsec *left, ppxsec *right) ++{ ++ ppxsec *l, *r, *m, t; ++ ppuint first, last; ++ l = left, r = right, m = l + ((r - l) / 2); ++ first = m->first, last = m->last; ++ do ++ { // don't take first/last from pointer ++ while (l->first < first) ++l; ++ while (r->first > last) --r; ++ if (l <= r) ++ { ++ t = *l; ++ *l = *r; ++ *r = t; ++ ++l, --r; ++ } ++ } while (l <= r); ++ if (l < right) ++ ppxref_sort_sects(l, right); ++ if (r > left) ++ ppxref_sort_sects(left, r); ++} ++ ++int ppxref_sort (ppxref *xref) ++{ ++ if (xref->size == 0) ++ return 0; ++ ppxref_sort_sects(xref->sects, xref->sects + xref->size - 1); ++ return 1; ++} ++ ++ppref * ppxref_find_local (ppxref *xref, ppuint refnumber) ++{ ++ ppxsec *left, *right, *mid; ++ //if (xref->size == 0) // we don't allow that ++ // return NULL; ++ left = xref->sects; ++ right = xref->sects + xref->size - 1; ++ do ++ { ++ mid = left + ((right - left) / 2); ++ if (refnumber > mid->last) ++ left = mid + 1; ++ else if (refnumber < mid->first) ++ right = mid - 1; ++ else ++ return &mid->refs[refnumber - mid->first]; ++ } while (left <= right); ++ return NULL; ++} ++ ++ppref * ppxref_find (ppxref *xref, ppuint refnumber) ++{ ++ ppref *ref; ++ ppxref *other; ++ ++ if ((ref = ppxref_find_local(xref, refnumber)) != NULL) ++ return ref; ++ if (xref->pdf->flags & PPDOC_LINEARIZED) ++ { ++ for (other = xref->pdf->xref; other != NULL; other = other->prev) ++ if (other != xref && (ref = ppxref_find_local(other, refnumber)) != NULL) ++ return ref; ++ } ++ else ++ { ++ for (other = xref->prev; other != NULL; other = other->prev) ++ if ((ref = ppxref_find_local(other, refnumber)) != NULL) ++ return ref; ++ /* This shouldn't happen, but I've met documents that have no linearized dict, ++ but their xrefs are prepared as for linearized; with "older" xrefs referring ++ to "newer". */ ++ for (other = xref->pdf->xref; other != NULL && other != xref; other = other->prev) ++ if ((ref = ppxref_find_local(other, refnumber)) != NULL) ++ return ref; ++ } ++ return NULL; ++} ++ ++ppdict * ppxref_trailer (ppxref *xref) ++{ ++ switch (xref->trailer.type) ++ { ++ case PPDICT: ++ return xref->trailer.dict; ++ case PPSTREAM: ++ return xref->trailer.stream->dict; ++ default: ++ break; ++ } ++ return NULL; ++} ++ ++ppxref * ppdoc_xref (ppdoc *pdf) ++{ ++ return pdf->xref; ++} ++ ++ppxref * ppxref_prev (ppxref *xref) ++{ ++ return xref->prev; ++} ++ ++ppdict * ppxref_catalog (ppxref *xref) ++{ ++ ppdict *trailer; ++ return (trailer = ppxref_trailer(xref)) != NULL ? ppdict_rget_dict(trailer, "Root") : NULL; ++} ++ ++ppdict * ppxref_info (ppxref *xref) ++{ ++ ppdict *trailer; ++ return (trailer = ppxref_trailer(xref)) != NULL ? ppdict_rget_dict(trailer, "Info") : NULL; ++} ++ ++ppref * ppxref_pages (ppxref *xref) ++{ ++ ppdict *dict; ++ ppref *ref; ++ ++ if ((dict = ppxref_catalog(xref)) == NULL || (ref = ppdict_get_ref(dict, "Pages")) == NULL) ++ return NULL; ++ return ref->object.type == PPDICT ? ref : NULL; ++} +diff --git a/texk/web2c/luatexdir/luapplib/ppxref.h b/texk/web2c/luatexdir/luapplib/ppxref.h +new file mode 100644 +index 000000000..86ab98a51 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/ppxref.h +@@ -0,0 +1,35 @@ ++ ++#ifndef PP_XREF_H ++#define PP_XREF_H ++ ++/* ++What we call xref is actually "xref section" in PDF spec and what we call section is "xref subsection". ++Our ppxref is a list of sections, sorted by xrefsection->first and xrefsection->last bounds. Every section ++keeps a list of ppref *refs, enumerated from xrefsection->first to xrefsection->last. To find a reference ++by number we make a binary search over sections bounds, then jump to the proper ppref *ref. ++*/ ++ ++typedef struct { ++ ppuint first; // first reference number in section ++ ppuint last; // last reference number in section ++ ppref *refs; // references list ++} ppxsec; ++ ++struct ppxref { ++ ppxsec *sects; // subsections list ++ size_t size; // actual sections size ++ size_t space; // available sections space ++ ppobj trailer; // trailer dict or stream ++ ppuint count; // count of references in all sections ++ ppxref *prev; // previous xref ++ ppdoc *pdf; // parent pdf to access entries in linearized docs ++ size_t offset; // file offset of xref ++ //ppcrypt *crypt; // per xref encryption state? ++}; ++ ++ppxref * ppxref_create (ppdoc *pdf, size_t initsize, size_t xrefoffset); ++ppxsec * ppxref_push_section (ppxref *xref, ppheap **pheap); ++int ppxref_sort (ppxref *xref); ++ppref * ppxref_find_local (ppxref *xref, ppuint refnumber); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilbasexx.c b/texk/web2c/luatexdir/luapplib/util/utilbasexx.c +new file mode 100644 +index 000000000..6fdd6fb58 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilbasexx.c +@@ -0,0 +1,2108 @@ ++ ++#include "utilnumber.h" ++#include "utilmem.h" ++#include "utilbasexx.h" ++ ++/* filters state structs */ ++ ++struct basexx_state { ++ size_t line, maxline; ++ size_t left; ++ int tail[5]; ++ int flush; ++}; ++ ++struct runlength_state { ++ int run; ++ int flush; ++ int c1, c2; ++ uint8_t *pos; ++}; ++ ++struct eexec_state { ++ int key; ++ int flush; ++ int binary; ++ int c1; ++ size_t line, maxline; /* ascii encoder only */ ++ const char *initbytes; ++}; ++ ++/* config */ ++ ++#if defined(BASEXX_PDF) ++# define ignored(c) (c == 0x20 || c == 0x0A || c == 0x0C || c == 0x0D || c == 0x09 || c == 0x00) ++# define base16_eof(c) (c == '>' || c < 0) ++# define base85_eof(c) (c == '~' || c < 0) ++#else ++# define ignored(c) (c == 0x20 || c == 0x0A || c == 0x0D || c == 0x09) ++# define base16_eof(c) (c < 0) ++# define base85_eof(c) (c < 0) ++#endif ++ ++#define base64_eof(c) (c == '=' || c < 0) ++ ++#define basexx_nl '\x0A' ++#define put_nl(O, line, maxline, n) ((void)((line += n) > maxline && ((line = n), iof_set(O, basexx_nl)))) ++ ++/* tail macros */ ++ ++#define set_tail1(state, c1) (state->left = 1, state->tail[0] = c1) ++#define set_tail2(state, c1, c2) (state->left = 2, state->tail[0] = c1, state->tail[1] = c2) ++#define set_tail3(state, c1, c2, c3) (state->left = 3, state->tail[0] = c1, state->tail[1] = c2, state->tail[2] = c3) ++#define set_tail4(state, c1, c2, c3, c4) (state->left = 4, state->tail[0] = c1, state->tail[1] = c2, state->tail[2] = c3, state->tail[3] = c4) ++#define set_tail5(state, c1, c2, c3, c4, c5) \ ++ (state->left = 5, state->tail[0] = c1, state->tail[1] = c2, state->tail[2] = c3, state->tail[3] = c4, state->tail[4] = c5) ++ ++#define get_tail1(state, c1) (state->left = 0, c1 = state->tail[0]) ++#define get_tail2(state, c1, c2) (state->left = 0, c1 = state->tail[0], c2 = state->tail[1]) ++#define get_tail3(state, c1, c2, c3) (state->left = 0, c1 = state->tail[0], c2 = state->tail[1], c3 = state->tail[2]) ++#define get_tail4(state, c1, c2, c3, c4) (state->left = 0, c1 = state->tail[0], c2 = state->tail[1], c3 = state->tail[2], c4 = state->tail[3]) ++ ++/* basexx state initialization */ ++ ++void basexx_state_init_ln (basexx_state *state, size_t line, size_t maxline) ++{ ++ state->line = line; ++ state->maxline = maxline; ++ state->left = 0; ++ state->flush = 0; ++} ++ ++/* base 16; xxxx|xxxx */ ++ ++iof_status base16_encoded_uc (const void *data, size_t size, iof *O) ++{ ++ const uint8_t *s, *e; ++ for (s = (const uint8_t *)data, e = s + size; s < e; ++s) ++ { ++ if (!iof_ensure(O, 2)) ++ return IOFFULL; ++ iof_set_uc_hex(O, *s); ++ } ++ return IOFEOF; ++} ++ ++iof_status base16_encoded_lc (const void *data, size_t size, iof *O) ++{ ++ const uint8_t *s, *e; ++ for (s = (const uint8_t *)data, e = s + size; s < e; ++s) ++ { ++ if (!iof_ensure(O, 2)) ++ return IOFFULL; ++ iof_set_lc_hex(O, *s); ++ } ++ return IOFEOF; ++} ++ ++iof_status base16_encoded_uc_ln (const void *data, size_t size, iof *O, size_t line, size_t maxline) ++{ ++ const uint8_t *s, *e; ++ for (s = (const uint8_t *)data, e = s + size; s < e; ++s) ++ { ++ if (!iof_ensure(O, 3)) ++ return IOFFULL; ++ put_nl(O, line, maxline, 2); ++ iof_set_uc_hex(O, *s); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encoded_lc_ln (const void *data, size_t size, iof *O, size_t line, size_t maxline) ++{ ++ const uint8_t *s, *e; ++ for (s = (const uint8_t *)data, e = s + size; s < e; ++s) ++ { ++ if (!iof_ensure(O, 3)) ++ return IOFFULL; ++ put_nl(O, line, maxline, 2); ++ iof_set_lc_hex(O, *s); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encode_uc (iof *I, iof *O) ++{ ++ register int c; ++ while (iof_ensure(O, 2)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return IOFEOF; ++ iof_set_uc_hex(O, c); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encode_state_uc (iof *I, iof *O, basexx_state *state) ++{ ++ register int c; ++ while (iof_ensure(O, 2)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ iof_set_uc_hex(O, c); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encode_lc (iof *I, iof *O) ++{ ++ register int c; ++ while (iof_ensure(O, 2)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return IOFEOF; ++ iof_set_lc_hex(O, c); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encode_state_lc (iof *I, iof *O, basexx_state *state) ++{ ++ register int c; ++ while (iof_ensure(O, 2)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ iof_set_lc_hex(O, c); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encode_uc_ln (iof *I, iof *O, size_t line, size_t maxline) ++{ ++ register int c; ++ while (iof_ensure(O, 3)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return IOFEOF; ++ put_nl(O, line, maxline, 2); ++ iof_set_uc_hex(O, c); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encode_state_uc_ln (iof *I, iof *O, basexx_state *state) ++{ ++ register int c; ++ while (iof_ensure(O, 3)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ put_nl(O, state->line, state->maxline, 2); ++ iof_set_uc_hex(O, c); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encode_lc_ln (iof *I, iof *O, size_t line, size_t maxline) ++{ ++ register int c; ++ while (iof_ensure(O, 3)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return IOFEOF; ++ put_nl(O, line, maxline, 2); ++ iof_set_lc_hex(O, c); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_encode_state_lc_ln (iof *I, iof *O, basexx_state *state) ++{ ++ register int c; ++ while (iof_ensure(O, 3)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ put_nl(O, state->line, state->maxline, 2); ++ iof_set_lc_hex(O, c); ++ } ++ return IOFFULL; ++} ++ ++int base16_getc (iof *I) ++{ ++ register int c1, c2; ++ do { c1 = iof_get(I); } while (ignored(c1)); ++ if (base16_eof(c1)) ++ return IOFEOF; ++ do { c2 = iof_get(I); } while (ignored(c2)); ++ if (base16_eof(c2)) ++ { ++ if ((c1 = base16_lookup[c1]) == -1) ++ return IOFERR; ++ return c1<<4; ++ } ++ if ((c1 = base16_lookup[c1]) == -1 || (c2 = base16_lookup[c2]) == -1) ++ return IOFERR; ++ return (c1<<4)|c2; ++} ++ ++int base16_lc_putc (iof *O, int c) ++{ ++ if (iof_ensure(O, 2)) ++ iof_set_lc_hex(O, c); ++ return IOFFULL; ++} ++ ++int base16_uc_putc (iof *O, int c) ++{ ++ if (iof_ensure(O, 2)) ++ iof_set_uc_hex(O, c); ++ return IOFFULL; ++} ++ ++ ++iof_status base16_decode (iof *I, iof *O) ++{ ++ register int c1, c2; ++ while (iof_ensure(O, 1)) ++ { ++ do { c1 = iof_get(I); } while (ignored(c1)); ++ if (base16_eof(c1)) ++ return IOFEOF; ++ do { c2 = iof_get(I); } while (ignored(c2)); ++ if (base16_eof(c2)) ++ { ++ if ((c1 = base16_lookup[c1]) == -1) ++ return IOFERR; ++ iof_set(O, c1<<4); // c2 := '0' ++ return IOFEOF; ++ } ++ if ((c1 = base16_lookup[c1]) == -1 || (c2 = base16_lookup[c2]) == -1) ++ return IOFERR; ++ iof_set(O, (c1<<4)|c2); ++ } ++ return IOFFULL; ++} ++ ++iof_status base16_decode_state (iof *I, iof *O, basexx_state *state) ++{ ++ register int c1, c2, d1, d2; ++ if (!(iof_ensure(O, 1))) ++ return IOFFULL; ++ switch(state->left) ++ { ++ case 0: goto byte0; ++ case 1: get_tail1(state, c1); goto byte1; ++ } ++ while (iof_ensure(O, 1)) ++ { ++ byte0: ++ do { c1 = iof_get(I); } while (ignored(c1)); ++ if (base16_eof(c1)) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ byte1: ++ do { c2 = iof_get(I); } while (ignored(c2)); ++ if (base16_eof(c2)) ++ { ++ set_tail1(state, c1); /* set tail to let the caller display invalid chars */ ++ if (state->flush) ++ { ++ if ((c1 = base16_lookup[c1]) == -1) ++ return IOFERR; ++ iof_set(O, c1<<4); // c2 := '0' ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ if ((d1 = base16_lookup[c1]) == -1 || (d2 = base16_lookup[c2]) == -1) ++ { ++ set_tail2(state, c1, c2); ++ return IOFERR; ++ } ++ iof_set(O, (d1<<4)|d2); ++ } ++ return IOFFULL; ++} ++ ++/* base 64; xxxxxx|xx xxxx|xxxx xx|xxxxxx */ ++ ++const char base64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; ++ ++const int base64_lookup[] = { ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, ++ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, ++ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,9 ,10,11,12,13,14, ++ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, ++ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, ++ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++}; ++ ++#define base64_digit1(c1) base64_alphabet[c1>>2] ++#define base64_digit2(c1, c2) base64_alphabet[((c1&3)<<4)|(c2>>4)] ++#define base64_digit3(c2, c3) base64_alphabet[((c2&15)<<2)|(c3>>6)] ++#define base64_digit4(c3) base64_alphabet[c3&63] ++ ++#define base64_encode_word(O, c1, c2, c3) \ ++ iof_set4(O, base64_digit1(c1), base64_digit2(c1, c2), base64_digit3(c2, c3), base64_digit4(c3)) ++ ++#define base64_encode_tail2(O, c1, c2) \ ++ iof_set3(O, base64_digit1(c1), base64_digit2(c1, c2), base64_digit3(c2, 0)) ++ ++#define base64_encode_tail1(O, c1) \ ++ iof_set2(O, base64_digit1(c1), base64_digit2(c1, 0)) ++ ++iof_status base64_encoded (const void *data, size_t size, iof *O) ++{ ++ const uint8_t *s, *e; ++ uint8_t c1, c2, c3; ++ for (s = (const uint8_t *)data, e = s + size; s + 2 < e; ) ++ { ++ if (!iof_ensure(O, 4)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s++; ++ c3 = *s++; ++ base64_encode_word(O, c1, c2, c3); ++ } ++ switch (e - s) ++ { ++ case 0: ++ break; ++ case 1: ++ if (!iof_ensure(O, 2)) ++ return IOFFULL; ++ c1 = *s; ++ base64_encode_tail1(O, c1); ++ break; ++ case 2: ++ if (!iof_ensure(O, 3)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s; ++ base64_encode_tail2(O, c1, c2); ++ break; ++ } ++ return IOFEOF; ++} ++ ++iof_status base64_encoded_ln (const void *data, size_t size, iof *O, size_t line, size_t maxline) ++{ ++ const uint8_t *s, *e; ++ uint8_t c1, c2, c3; ++ for (s = (const uint8_t *)data, e = s + size; s + 2 < e; ) ++ { ++ if (!iof_ensure(O, 5)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s++; ++ c3 = *s++; ++ put_nl(O, line, maxline, 4); ++ base64_encode_word(O, c1, c2, c3); ++ } ++ switch (e - s) ++ { ++ case 0: ++ break; ++ case 1: ++ if (!iof_ensure(O, 3)) ++ return IOFFULL; ++ c1 = *s; ++ put_nl(O, line, maxline, 2); ++ base64_encode_tail1(O, c1); ++ break; ++ case 2: ++ if (!iof_ensure(O, 4)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s; ++ put_nl(O, line, maxline, 3); ++ base64_encode_tail2(O, c1, c2); ++ break; ++ } ++ return IOFEOF; ++} ++ ++iof_status base64_encode (iof *I, iof *O) ++{ ++ register int c1, c2, c3; ++ while(iof_ensure(O, 4)) ++ { ++ if ((c1 = iof_get(I)) < 0) ++ return IOFEOF; ++ if ((c2 = iof_get(I)) < 0) ++ { ++ base64_encode_tail1(O, c1); ++ return IOFEOF; ++ } ++ if ((c3 = iof_get(I)) < 0) ++ { ++ base64_encode_tail2(O, c1, c2); ++ return IOFEOF; ++ } ++ base64_encode_word(O, c1, c2, c3); ++ } ++ return IOFFULL; ++} ++ ++iof_status base64_encode_state (iof *I, iof *O, basexx_state *state) ++{ ++ register int c1, c2, c3; ++ if (!(iof_ensure(O, 4))) ++ return IOFFULL; ++ switch(state->left) ++ { ++ case 0: goto byte0; ++ case 1: get_tail1(state, c1); goto byte1; ++ case 2: get_tail2(state, c1, c2); goto byte2; ++ } ++ while(iof_ensure(O, 4)) ++ { ++ byte0: ++ if ((c1 = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ byte1: ++ if ((c2 = iof_get(I)) < 0) ++ return (state->flush ? (base64_encode_tail1(O, c1), IOFEOF) : (set_tail1(state, c1), IOFEMPTY)); ++ byte2: ++ if ((c3 = iof_get(I)) < 0) ++ return (state->flush ? (base64_encode_tail2(O, c1, c2), IOFEOF) : (set_tail2(state, c1, c2), IOFEMPTY)); ++ base64_encode_word(O, c1, c2, c3); ++ } ++ return IOFFULL; ++} ++ ++iof_status base64_encode_ln (iof *I, iof *O, size_t line, size_t maxline) ++{ ++ register int c1, c2, c3; ++ while(iof_ensure(O, 5)) ++ { ++ if ((c1 = iof_get(I)) < 0) ++ return IOFEOF; ++ if ((c2 = iof_get(I)) < 0) ++ { ++ put_nl(O, line, maxline, 2); ++ base64_encode_tail1(O, c1); ++ return IOFEOF; ++ } ++ if ((c3 = iof_get(I)) < 0) ++ { ++ put_nl(O, line, maxline, 3); ++ base64_encode_tail2(O, c1, c2); ++ return IOFEOF; ++ } ++ put_nl(O, line, maxline, 4); ++ base64_encode_word(O, c1, c2, c3); ++ } ++ return IOFFULL; ++} ++ ++iof_status base64_encode_state_ln (iof *I, iof *O, basexx_state *state) ++{ ++ register int c1, c2, c3; ++ if (!(iof_ensure(O, 5))) ++ return IOFFULL; ++ switch(state->left) ++ { ++ case 0: goto byte0; ++ case 1: get_tail1(state, c1); goto byte1; ++ case 2: get_tail2(state, c1, c2); goto byte2; ++ } ++ while(iof_ensure(O, 5)) ++ { ++ byte0: ++ if ((c1 = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ byte1: ++ if ((c2 = iof_get(I)) < 0) ++ return (state->flush ? (put_nl(O, state->line, state->maxline, 2), base64_encode_tail1(O, c1), IOFEOF) : (set_tail1(state, c1), IOFEMPTY)); ++ byte2: ++ if ((c3 = iof_get(I)) < 0) ++ return (state->flush ? (put_nl(O, state->line, state->maxline, 3), base64_encode_tail2(O, c1, c2), IOFEOF) : (set_tail2(state, c1, c2), IOFEMPTY)); ++ put_nl(O, state->line, state->maxline, 4); ++ base64_encode_word(O, c1, c2, c3); ++ } ++ return IOFFULL; ++} ++ ++// #define base64_code(c1, c2, c3, c4) ((c1<<18)|(c2<<12)|(c3<<6)|c4) ++ ++#define base64_decode_word(O, c1, c2, c3, c4) \ ++ iof_set3(O, (c1<<2)|(c2>>4), ((c2&15)<<4)|(c3>>2), ((c3&3)<<6)|c4) ++ ++#define base64_decode_tail3(O, c1, c2, c3) \ ++ iof_set2(O, (c1<<2)|(c2>>4), ((c2&15)<<4)|(c3>>2)) ++ ++#define base64_decode_tail2(O, c1, c2) \ ++ iof_set(O, (c1<<2)|(c2>>4)) ++ ++iof_status base64_decode (iof *I, iof *O) ++{ ++ register int c1, c2, c3, c4; ++ while(iof_ensure(O, 3)) ++ { ++ do { c1 = iof_get(I); } while (ignored(c1)); ++ if (base64_eof(c1)) ++ return IOFEOF; ++ do { c2 = iof_get(I); } while (ignored(c2)); ++ if (base64_eof(c2)) ++ return IOFERR; ++ do { c3 = iof_get(I); } while (ignored(c3)); ++ if (base64_eof(c3)) ++ { ++ if ((c1 = base64_lookup[c1]) == -1 || (c2 = base64_lookup[c2]) == -1) ++ return IOFERR; ++ base64_decode_tail2(O, c1, c2); ++ return IOFEOF; ++ } ++ do { c4 = iof_get(I); } while (ignored(c4)); ++ if (base64_eof(c4)) ++ { ++ if ((c1 = base64_lookup[c1]) == -1 || (c2 = base64_lookup[c2]) == -1 || (c3 = base64_lookup[c3]) == -1) ++ return IOFERR; ++ base64_decode_tail3(O, c1, c2, c3); ++ return IOFEOF; ++ } ++ if ((c1 = base64_lookup[c1]) == -1 || (c2 = base64_lookup[c2]) == -1 || ++ (c3 = base64_lookup[c3]) == -1 || (c4 = base64_lookup[c4]) == -1) ++ return IOFERR; ++ base64_decode_word(O, c1, c2, c3, c4); ++ } ++ return IOFFULL; ++} ++ ++iof_status base64_decode_state (iof *I, iof *O, basexx_state *state) ++{ ++ register int c1, c2, c3, c4; ++ register int d1, d2, d3, d4; ++ switch(state->left) ++ { ++ case 0: goto byte0; ++ case 1: get_tail1(state, c1); goto byte1; ++ case 2: get_tail2(state, c1, c2); goto byte2; ++ case 3: get_tail3(state, c1, c2, c3); goto byte3; ++ } ++ while(iof_ensure(O, 3)) ++ { ++ byte0: ++ do { c1 = iof_get(I); } while (ignored(c1)); ++ if (base64_eof(c1)) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ byte1: ++ do { c2 = iof_get(I); } while (ignored(c2)); ++ if (base64_eof(c2)) ++ { ++ set_tail1(state, c1); /* set tail to let the caller make padding or display invalid char in case of error */ ++ return (state->flush ? IOFERR : IOFEMPTY); /* if state->flush then error; tail must have at least two bytes */ ++ } ++ byte2: ++ do { c3 = iof_get(I); } while (ignored(c3)); ++ if (base64_eof(c3)) ++ { ++ set_tail2(state, c1, c2); ++ if (state->flush) ++ { ++ if ((c1 = base64_lookup[c1]) == -1 || (c2 = base64_lookup[c2]) == -1) ++ return IOFERR; ++ base64_decode_tail2(O, c1, c2); ++ return IOFEOF; ++ } ++ else ++ return IOFEMPTY; ++ } ++ byte3: ++ do { c4 = iof_get(I); } while (ignored(c4)); ++ if (base64_eof(c4)) ++ { ++ set_tail3(state, c1, c2, c3); ++ if (state->flush) ++ { ++ if ((c1 = base64_lookup[c1]) == -1 || (c2 = base64_lookup[c2]) == -1 || (c3 = base64_lookup[c3]) == -1) ++ return IOFERR; ++ base64_decode_tail3(O, c1, c2, c3); ++ return IOFEOF; ++ } ++ else ++ return IOFEMPTY; ++ } ++ if ((d1 = base64_lookup[c1]) == -1 || (d2 = base64_lookup[c2]) == -1 || ++ (d3 = base64_lookup[c3]) == -1 || (d4 = base64_lookup[c4]) == -1) ++ { ++ set_tail4(state, c1, c2, c3, c4); ++ return IOFERR; ++ } ++ base64_decode_word(O, d1, d2, d3, d4); ++ } ++ return IOFFULL; ++} ++ ++/* base85 */ ++ ++const char base85_alphabet[] = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu"; /* for completness, not used below */ ++ ++const int base85_lookup[] = { ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, ++ 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, ++ 31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46, ++ 47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62, ++ 63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78, ++ 79,80,81,82,83,84,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++}; ++ ++#define base85_encode_word(O, code) \ ++ (*(O->pos+4) = '!' + code%85, code /= 85, *(O->pos+3) = '!' + code%85, code /= 85, \ ++ *(O->pos+2) = '!' + code%85, code /= 85, *(O->pos+1) = '!' + code%85, code /= 85, \ ++ *(O->pos) = '!' + code, \ ++ O->pos += 5) ++ ++#define base85_encode_tail3(O, code) \ ++ (*(O->pos+3) = '!' + code%85, code /= 85, *(O->pos+2) = '!' + code%85, code /= 85, \ ++ *(O->pos+1) = '!' + code%85, code /= 85, *(O->pos) = '!' + code, \ ++ O->pos += 4) ++ ++#define base85_encode_tail2(O, code) \ ++ (*(O->pos+2) = '!' + code%85, code /= 85, *(O->pos+1) = '!' + code%85, code /= 85, \ ++ *(O->pos) = '!' + code, \ ++ O->pos += 3) ++ ++#define base85_encode_tail1(O, code) \ ++ (*(O->pos+1) = '!' + code%85, code /= 85, *(O->pos) = '!' + code, \ ++ O->pos += 2) ++ ++iof_status base85_encoded (const void *data, size_t size, iof *O) ++{ ++ unsigned int code; ++ const uint8_t *s, *e; ++ uint8_t c1, c2, c3, c4; ++ for (s = (const uint8_t *)data, e = s + size; s + 3 < e; ) ++ { ++ if (!iof_ensure(O, 5)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s++; ++ c3 = *s++; ++ c4 = *s++; ++ code = (c1<<24)|(c2<<16)|(c3<<8)|c4; ++ if (code == 0) ++ { ++ iof_set(O, 'z'); ++ continue; ++ } ++ base85_encode_word(O, code); ++ } ++ switch (e - s) ++ { ++ case 0: ++ break; ++ case 1: ++ if (!iof_ensure(O, 2)) ++ return IOFFULL; ++ c1 = *s; ++ code = (c1<<24)/85/85/85; ++ base85_encode_tail1(O, code); ++ break; ++ case 2: ++ if (!iof_ensure(O, 3)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s; ++ code = ((c1<<24)|(c2<<16))/85/85; ++ base85_encode_tail2(O, code); ++ break; ++ case 3: ++ if (!iof_ensure(O, 4)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s++; ++ c3 = *s; ++ code = ((c1<<24)|(c2<<16)|(c3<<8))/85; ++ base85_encode_tail3(O, code); ++ break; ++ } ++ return IOFEOF; ++} ++ ++iof_status base85_encoded_ln (const void *data, size_t size, iof *O, size_t line, size_t maxline) ++{ ++ unsigned int code; ++ const uint8_t *s, *e; ++ uint8_t c1, c2, c3, c4; ++ for (s = (const uint8_t *)data, e = s + size; s + 3 < e; ) ++ { ++ if (!iof_ensure(O, 6)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s++; ++ c3 = *s++; ++ c4 = *s++; ++ code = (c1<<24)|(c2<<16)|(c3<<8)|c4; ++ if (code == 0) ++ { ++ put_nl(O, line, maxline, 1); ++ iof_set(O, 'z'); ++ continue; ++ } ++ put_nl(O, line, maxline, 5); ++ base85_encode_word(O, code); ++ } ++ switch (e - s) ++ { ++ case 0: ++ break; ++ case 1: ++ if (!iof_ensure(O, 3)) ++ return IOFFULL; ++ c1 = *s; ++ code = (c1<<24)/85/85/85; ++ put_nl(O, line, maxline, 2); ++ base85_encode_tail1(O, code); ++ break; ++ case 2: ++ if (!iof_ensure(O, 4)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s; ++ code = ((c1<<24)|(c2<<16))/85/85; ++ put_nl(O, line, maxline, 3); ++ base85_encode_tail2(O, code); ++ break; ++ case 3: ++ if (!iof_ensure(O, 5)) ++ return IOFFULL; ++ c1 = *s++; ++ c2 = *s++; ++ c3 = *s; ++ code = ((c1<<24)|(c2<<16)|(c3<<8))/85; ++ put_nl(O, line, maxline, 4); ++ base85_encode_tail3(O, code); ++ break; ++ } ++ return IOFEOF; ++} ++ ++iof_status base85_encode (iof *I, iof *O) ++{ ++ register int c1, c2, c3, c4; ++ register unsigned int code; ++ while(iof_ensure(O, 5)) ++ { ++ if ((c1 = iof_get(I)) < 0) ++ return IOFEOF; ++ if ((c2 = iof_get(I)) < 0) ++ { ++ code = (c1<<24)/85/85/85; ++ base85_encode_tail1(O, code); ++ return IOFEOF; ++ } ++ if ((c3 = iof_get(I)) < 0) ++ { ++ code = ((c1<<24)|(c2<<16))/85/85; ++ base85_encode_tail2(O, code); ++ return IOFEOF; ++ } ++ if ((c4 = iof_get(I)) < 0) ++ { ++ code = ((c1<<24)|(c2<<16)|(c3<<8))/85; ++ base85_encode_tail3(O, code); ++ return IOFEOF; ++ } ++ code = (c1<<24)|(c2<<16)|(c3<<8)|c4; ++ if (code == 0) ++ { ++ iof_set(O, 'z'); ++ continue; ++ } ++ /* in btoa 'y' character stays for 0x20202020, but pdf does not support this */ ++ /* if (code == 0x20202020) ++ { ++ iof_set(O, 'y'); ++ continue; ++ } */ ++ base85_encode_word(O, code); ++ } ++ return IOFFULL; ++} ++ ++iof_status base85_encode_state (iof *I, iof *O, basexx_state *state) ++{ ++ register int c1, c2, c3, c4; ++ register unsigned int code; ++ if (!(iof_ensure(O, 5))) ++ return IOFFULL; ++ switch(state->left) ++ { ++ case 0: goto byte0; ++ case 1: get_tail1(state, c1); goto byte1; ++ case 2: get_tail2(state, c1, c2); goto byte2; ++ case 3: get_tail3(state, c1, c2, c3); goto byte3; ++ } ++ while(iof_ensure(O, 5)) ++ { ++ byte0: ++ if ((c1 = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ byte1: ++ if ((c2 = iof_get(I)) < 0) ++ { ++ set_tail1(state, c1); ++ if (state->flush) ++ { ++ code = (c1<<24)/85/85/85; ++ base85_encode_tail1(O, code); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ byte2: ++ if ((c3 = iof_get(I)) < 0) ++ { ++ set_tail2(state, c1, c2); ++ if (state->flush) ++ { ++ code = ((c1<<24)|(c2<<16))/85/85; ++ base85_encode_tail2(O, code); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ byte3: ++ if ((c4 = iof_get(I)) < 0) ++ { ++ set_tail3(state, c1, c2, c3); ++ if (state->flush) ++ { ++ code = ((c1<<24)|(c2<<16)|(c3<<8))/85; ++ base85_encode_tail3(O, code); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ code = (c1<<24)|(c2<<16)|(c3<<8)|c4; ++ if (code == 0) ++ { ++ iof_set(O, 'z'); ++ continue; ++ } ++ base85_encode_word(O, code); ++ } ++ return IOFFULL; ++} ++ ++iof_status base85_encode_ln (iof *I, iof *O, size_t line, size_t maxline) ++{ ++ register int c1, c2, c3, c4; ++ register unsigned int code; ++ while(iof_ensure(O, 6)) ++ { ++ if ((c1 = iof_get(I)) < 0) ++ return IOFEOF; ++ if ((c2 = iof_get(I)) < 0) ++ { ++ code = (c1<<24)/85/85/85; ++ put_nl(O, line, maxline, 2); ++ base85_encode_tail1(O, code); ++ return IOFEOF; ++ } ++ if ((c3 = iof_get(I)) < 0) ++ { ++ code = ((c1<<24)|(c2<<16))/85/85; ++ put_nl(O, line, maxline, 3); ++ base85_encode_tail2(O, code); ++ return IOFEOF; ++ } ++ if ((c4 = iof_get(I)) < 0) ++ { ++ code = ((c1<<24)|(c2<<16)|(c3<<8))/85; ++ put_nl(O, line, maxline, 4); ++ base85_encode_tail3(O, code); ++ return IOFEOF; ++ } ++ code = (c1<<24)|(c2<<16)|(c3<<8)|c4; ++ if (code == 0) ++ { ++ put_nl(O, line, maxline, 1); ++ iof_set(O, 'z'); ++ continue; ++ } ++ put_nl(O, line, maxline, 5); ++ base85_encode_word(O, code); ++ } ++ return IOFFULL; ++} ++ ++iof_status base85_encode_state_ln (iof *I, iof *O, basexx_state *state) ++{ ++ register int c1, c2, c3, c4; ++ register unsigned int code; ++ if (!(iof_ensure(O, 6))) ++ return IOFFULL; ++ switch(state->left) ++ { ++ case 0: goto byte0; ++ case 1: get_tail1(state, c1); goto byte1; ++ case 2: get_tail2(state, c1, c2); goto byte2; ++ case 3: get_tail3(state, c1, c2, c3); goto byte3; ++ } ++ while(iof_ensure(O, 6)) ++ { ++ byte0: ++ if ((c1 = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ byte1: ++ if ((c2 = iof_get(I)) < 0) ++ { ++ set_tail1(state, c1); ++ if (state->flush) ++ { ++ code = (c1<<24)/85/85/85; ++ put_nl(O, state->line, state->maxline, 2); ++ base85_encode_tail1(O, code); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ byte2: ++ if ((c3 = iof_get(I)) < 0) ++ { ++ set_tail2(state, c1, c2); ++ if (state->flush) ++ { ++ code = ((c1<<24)|(c2<<16))/85/85; ++ put_nl(O, state->line, state->maxline, 3); ++ base85_encode_tail2(O, code); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ byte3: ++ if ((c4 = iof_get(I)) < 0) ++ { ++ set_tail3(state, c1, c2, c3); ++ if (state->flush) ++ { ++ code = ((c1<<24)|(c2<<16)|(c3<<8))/85; ++ put_nl(O, state->line, state->maxline, 4); ++ base85_encode_tail3(O, code); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ code = (c1<<24)|(c2<<16)|(c3<<8)|c4; ++ if (code == 0) ++ { ++ put_nl(O, state->line, state->maxline, 1); ++ iof_set(O, 'z'); ++ continue; ++ } ++ put_nl(O, state->line, state->maxline, 5); ++ base85_encode_word(O, code); ++ } ++ return IOFFULL; ++} ++ ++#define base85_code(c1, c2, c3, c4, c5) ((((c1*85+c2)*85+c3)*85+c4)*85+c5) ++ ++iof_status base85_decode (iof *I, iof *O) ++{ ++ register int c1, c2, c3, c4, c5; ++ register unsigned int code; ++ while (iof_ensure(O, 4)) ++ { ++ do { c1 = iof_get(I); } while (ignored(c1)); ++ if (base85_eof(c1)) ++ return IOFEOF; ++ switch (c1) ++ { ++ case 'z': ++ iof_set4(O, '\0', '\0', '\0', '\0'); ++ continue; ++ case 'y': ++ iof_set4(O, ' ', ' ', ' ', ' '); ++ continue; ++ } ++ do { c2 = iof_get(I); } while (ignored(c2)); ++ if (base85_eof(c2)) ++ return IOFERR; ++ do { c3 = iof_get(I); } while (ignored(c3)); ++ if (base85_eof(c3)) ++ { ++ if ((c1 = base85_lookup[c1]) == -1 || (c2 = base85_lookup[c2]) == -1) ++ return IOFERR; ++ code = base85_code(c1, c2, 84, 84, 84); /* padding with 'u' (117); 117-33 = 84 */ ++ iof_set(O, code>>24); ++ return IOFEOF; ++ } ++ do { c4 = iof_get(I); } while (ignored(c4)); ++ if (base85_eof(c4)) ++ { ++ if ((c1 = base85_lookup[c1]) == -1 || (c2 = base85_lookup[c2]) == -1 || (c3 = base85_lookup[c3]) == -1) ++ return IOFERR; ++ code = base85_code(c1, c2, c3, 84, 84); ++ iof_set2(O, code>>24, (code>>16)&255); ++ return IOFEOF; ++ } ++ do { c5 = iof_get(I); } while (ignored(c5)); ++ if (base85_eof(c5)) ++ { ++ if ((c1 = base85_lookup[c1]) == -1 || (c2 = base85_lookup[c2]) == -1 || ++ (c3 = base85_lookup[c3]) == -1 || (c4 = base85_lookup[c4]) == -1) ++ return IOFERR; ++ code = base85_code(c1, c2, c3, c4, 84); ++ iof_set3(O, code>>24, (code>>16)&255, (code>>8)&255); ++ return IOFEOF; ++ } ++ if ((c1 = base85_lookup[c1]) == -1 || (c2 = base85_lookup[c2]) == -1 || (c3 = base85_lookup[c3]) == -1 || ++ (c4 = base85_lookup[c4]) == -1 || (c5 = base85_lookup[c5]) == -1) ++ return IOFERR; ++ code = base85_code(c1, c2, c3, c4, c5); ++ iof_set4(O, code>>24, (code>>16)&255, (code>>8)&255, code&255); ++ } ++ return IOFFULL; ++} ++ ++iof_status base85_decode_state (iof *I, iof *O, basexx_state *state) ++{ ++ register int c1, c2, c3, c4, c5; ++ register int d1, d2, d3, d4, d5; ++ register unsigned int code; ++ if (!(iof_ensure(O, 4))) ++ return IOFFULL; ++ switch(state->left) ++ { ++ case 0: goto byte0; ++ case 1: get_tail1(state, c1); goto byte1; ++ case 2: get_tail2(state, c1, c2); goto byte2; ++ case 3: get_tail3(state, c1, c2, c3); goto byte3; ++ case 4: get_tail4(state, c1, c2, c3, c4); goto byte4; ++ } ++ while (iof_ensure(O, 4)) ++ { ++ byte0: ++ do { c1 = iof_get(I); } while (ignored(c1)); ++ if (base85_eof(c1)) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ switch (c1) ++ { ++ case 'z': ++ iof_set4(O, '\0', '\0', '\0', '\0'); ++ continue; ++ case 'y': ++ iof_set4(O, ' ', ' ', ' ', ' '); ++ continue; ++ } ++ byte1: ++ do { c2 = iof_get(I); } while (ignored(c2)); ++ if (base85_eof(c2)) ++ { ++ set_tail1(state, c1); ++ return (state->flush ? IOFERR : IOFEMPTY); /* if state->flush then error; tail must have at least two bytes */ ++ } ++ byte2: ++ do { c3 = iof_get(I); } while (ignored(c3)); ++ if (base85_eof(c3)) ++ { ++ set_tail2(state, c1, c2); ++ if (state->flush) ++ { ++ if ((c1 = base85_lookup[c1]) == -1 || (c2 = base85_lookup[c2]) == -1) ++ return IOFERR; ++ code = base85_code(c1, c2, 84, 84, 84); ++ iof_set(O, code>>24); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ byte3: ++ do { c4 = iof_get(I); } while (ignored(c4)); ++ if (base85_eof(c4)) ++ { ++ set_tail3(state, c1, c2, c3); ++ if (state->flush) ++ { ++ if ((c1 = base85_lookup[c1]) == -1 || (c2 = base85_lookup[c2]) == -1 || (c3 = base85_lookup[c3]) == -1) ++ return IOFERR; ++ code = base85_code(c1, c2, c3, 84, 84); ++ iof_set2(O, code>>24, (code>>16)&255); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ byte4: ++ do { c5 = iof_get(I); } while (ignored(c5)); ++ if (base85_eof(c5)) ++ { ++ set_tail4(state, c1, c2, c3, c4); ++ if (state->flush) ++ { ++ if ((c1 = base85_lookup[c1]) == -1 || (c2 = base85_lookup[c2]) == -1 || ++ (c3 = base85_lookup[c3]) == -1 || (c4 = base85_lookup[c4]) == -1) ++ return IOFERR; ++ code = base85_code(c1, c2, c3, c4, 84); ++ iof_set3(O, code>>24, (code>>16)&255, (code>>8)&255); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++ } ++ if ((d1 = base85_lookup[c1]) == -1 || (d2 = base85_lookup[c2]) == -1 || (d3 = base85_lookup[c3]) == -1 || ++ (d4 = base85_lookup[c4]) == -1 || (d5 = base85_lookup[c5]) == -1) ++ { ++ set_tail5(state, c1, c2, c3, c4, c5); ++ return IOFERR; ++ } ++ code = base85_code(d1, d2, d3, d4, d5); ++ iof_set4(O, code>>24, (code>>16)&255, (code>>8)&255, code&255); ++ } ++ return IOFFULL; ++} ++ ++/* postscript run length */ ++ ++void runlength_state_init (runlength_state *state) ++{ ++ state->run = -1; ++ state->flush = 0; ++ state->c1 = 0; ++ state->c2 = 0; ++ state->pos = NULL; ++} ++ ++iof_status runlength_encode (iof *I, iof *O) ++{ ++ register int c1, c2, run = -1; ++ uint8_t *pos; ++ c1 = 0; /* avoid warning */ ++ while (iof_ensure(O, 1+128+1)) ++ { /* ensured space for single length byte, up to 128 bytes to be copied, possible eod marker */ ++ pos = O->pos++; ++ switch (run) ++ { ++ case -1: /* initial state; get first byte */ ++ if ((c1 = iof_get(I)) < 0) ++ return (*pos = 128, IOFEOF); ++ run = 0; ++ // fall through ++ case 0: /* `repeat' state; get another byte and compare */ ++ if ((c2 = iof_get(I)) < 0) ++ return (*pos = 0, iof_set2(O, c1, 128), IOFEOF); ++ run = (c1 == c2 ? 257-2 : 0); ++ break; ++ } ++ if (run < 128) ++ { /* single length byte, up to 128 bytes to be copied, possible eod marker */ ++ iof_set(O, c1); ++ for (c1 = c2, c2 = iof_char(I); c1 != c2 && run < 127; c1 = c2, c2 = iof_next(I)) ++ { ++ if (c2 < 0) /* O->pos must not change until next call to calling encoder!!! */ ++ return (*pos = (uint8_t)run+1, iof_set2(O, c1, 128), IOFEOF); ++ iof_set(O, c1); ++ ++run; ++ } ++ } ++ else // if run > 128 ++ { ++ for (c2 = iof_get(I); c1 == c2 && run > 129; c2 = iof_get(I)) ++ --run; ++ if (c2 < 0) ++ return (*pos = (uint8_t)run, iof_set2(O, c1, 128), IOFEOF); ++ iof_set(O, c1); ++ } ++ *pos = (uint8_t)run; ++ c1 = c2; ++ run = 0; ++ } ++ return IOFFULL; ++} ++ ++iof_status runlength_encode_state (iof *I, iof *O, runlength_state *state) ++{ ++ while (iof_ensure(O, 3)) /* single length byte, the byte to be repeated and eod */ ++ { ++ state->pos = O->pos++; ++ switch (state->run) ++ { ++ case -1: /* initial state; get first byte */ ++ if ((state->c1 = iof_get(I)) < 0) ++ return (state->flush ? (*state->pos = 128, IOFEOF) : IOFEMPTY); ++ state->run = 0; ++ // fall through ++ case 0: /* `repeat' state; get another byte and compare */ ++ if ((state->c2 = iof_get(I)) < 0) ++ return (state->flush ? (*state->pos = 0, iof_set2(O, state->c1, 128), IOFEOF) : IOFEMPTY); ++ state->run = (state->c1 == state->c2 ? 257-2 : 0); ++ break; ++ } ++ if (state->run < 128) ++ { /* ensure space for single length byte, up to 128 bytes to be copied, plus possible eod marker, minus those already copied */ ++ if (!iof_ensure(O, 1+128+1-state->run)) ++ return IOFFULL; ++ iof_set(O, state->c1); ++ for (state->c1 = state->c2, state->c2 = iof_char(I); ++ state->c1 != state->c2 && state->run < 127; ++ state->c1 = state->c2, state->c2 = iof_next(I)) ++ { ++ if (state->c2 < 0) /* O->pos must not change until next call to calling encoder!!! */ ++ return (state->flush ? (*state->pos = (uint8_t)state->run+1, iof_set2(O, state->c1, 128), IOFEOF) : IOFEMPTY); ++ iof_set(O, state->c1); ++ ++state->run; ++ } ++ } ++ else // if run > 128 ++ { ++ for (state->c2 = iof_get(I); state->c1 == state->c2 && state->run > 129; state->c2 = iof_get(I)) ++ --state->run; ++ if (state->c2 < 0) ++ return (state->flush ? (*state->pos = (uint8_t)state->run, iof_set2(O, state->c1, 128), IOFEOF) : IOFEMPTY); ++ iof_set(O, state->c1); ++ } ++ *state->pos = (uint8_t)state->run; ++ state->c1 = state->c2; ++ state->run = 0; ++ } ++ return IOFFULL; ++} ++ ++iof_status runlength_decode (iof *I, iof *O) ++{ ++ register int c, run = -1; ++ while (1) ++ { ++ if (run == -1) /* initial state */ ++ { ++ if ((run = iof_get(I)) < 0) ++ { ++ run = -1; /* don't assume IOFEOF == -1 */ ++ return IOFEOF; ++ } ++ } ++ if (run < 128) ++ { /* copy (run + 1) following bytes */ ++ while (run > -1) ++ { ++ if (iof_ensure(O, 1)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return IOFERR; ++ iof_set(O, c); ++ --run; ++ continue; ++ } ++ return IOFFULL; ++ } ++ } ++ else if (run > 128) ++ { /* replicate the following byte (257 - run) times */ ++ if ((c = iof_get(I)) < 0) /* cf. state-wise version; don't change input position until we got this byte */ ++ return IOFERR; ++ while (run < 257) ++ { ++ if (iof_ensure(O, 1)) ++ { ++ iof_set(O, c); ++ ++run; ++ continue; ++ } ++ return IOFFULL; ++ } ++ run = -1; ++ } ++ else // c == 128 ++ return IOFEOF; ++ } ++ // return IOFFULL; ++} ++ ++iof_status runlength_decode_state (iof *I, iof *O, runlength_state *state) ++{ ++ register int c; ++ while (1) ++ { ++ if (state->run == -1) /* initial state */ ++ { ++ if ((state->run = iof_char(I)) < 0) ++ { ++ state->run = -1; /* don't assume IOFEOF == -1 */ ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ } ++ ++I->pos; ++ } ++ if (state->run < 128) ++ { /* copy (state->run + 1) following bytes */ ++ while (state->run > -1) ++ { ++ if (iof_ensure(O, 1)) ++ { ++ if ((c = iof_char(I)) < 0) ++ return (state->flush ? IOFERR : IOFEMPTY); ++ ++I->pos; ++ iof_set(O, c); ++ --state->run; ++ continue; ++ } ++ return IOFFULL; ++ } ++ } ++ else if (state->run > 128) ++ { /* replicate the following byte (257 - state->run) times */ ++ if ((c = iof_char(I)) < 0) ++ return (state->flush ? IOFERR : IOFEMPTY); ++ ++I->pos; ++ while (state->run < 257) ++ { ++ if (iof_ensure(O, 1)) ++ { ++ iof_set(O, c); ++ ++state->run; ++ continue; ++ } ++ return IOFFULL; ++ } ++ state->run = -1; ++ } ++ else // c == 128 ++ return IOFEOF; ++ } ++ // return IOFFULL; ++} ++ ++/* eexec stream filter, type1 fonts spec page 63 */ ++ ++void eexec_state_init_ln (eexec_state *state, size_t line, size_t maxline, const char *initbytes) ++{ ++ state->key = -1; ++ state->flush = 0; ++ state->binary = maxline > 0; ++ state->c1 = -1; ++ state->line = line; ++ state->maxline = maxline; ++ state->initbytes = initbytes; ++} ++ ++#define eexec_getc(I, c1) if ((c1 = iof_get(I)) < 0) return c1 ++#define eexec_key(key, c1) (key = ((((c1) + key)*52845 + 22719) & 65535)) ++#define eexec_decipher(key, c1, c) (c = ((c1)^(key>>8)), eexec_key(key, c1)) ++#define eexec_encipher(key, c1, c) (c = ((c1)^(key>>8)), eexec_key(key, c)) ++ ++#ifndef lps_ignored_char ++# define lps_ignored_char(c) (c == 0x20 || c == 0x0A || c == 0x0C || c == 0x0D || c == 0x09 || c == 0x00) ++#endif ++ ++#define eexec_getx_(I, c) \ ++ do { c = iof_get(I); } while (lps_ignored_char(c)); \ ++ if (c < 0) return IOFEOF; \ ++ if ((c = base16_lookup[c]) < 0) return IOFERR ++ ++#define eexec_getx(I, c1, c2) eexec_getx_(I, c1); \ ++ do { c2 = iof_get(I); } while (lps_ignored_char(c2)); \ ++ if (c2 < 0) c2 = 0; else \ ++ if ((c2 = base16_lookup[c2]) < 0) return IOFERR ++ ++static int eexec_decode_init (iof *I, int *key, int *binary) ++{ ++ int c1, c2, c3, c4; ++ *key = 55665; ++ eexec_getc(I, c1); ++ eexec_getc(I, c2); ++ eexec_getc(I, c3); ++ eexec_getc(I, c4); /* four head bytes */ ++ ++ /* eexec data has no explicit data termination. The caller of eexec_decode() should ensure that either ++ the input iof allows to read no more then necessary, or the output has no more space then 512 bytes, ++ to land safely somewhere in 512 bytes that probably follows eexec */ ++ ++ *binary = (base16_lookup[c1] < 0 || base16_lookup[c2] < 0 || ++ base16_lookup[c3] < 0 || base16_lookup[c4] < 0); ++ if (*binary) ++ { /* gobble 4 random bytes keeping decipher key up-to-date */ ++ eexec_key(*key, c1); eexec_key(*key, c2); ++ eexec_key(*key, c3); eexec_key(*key, c4); ++ } ++ else /* pfa/postscript only, pdf requires binary eexec form */ ++ { /* gobble 4 random bytes (8 hex digits) keeping decipher key up-to-date */ ++ c1 = base16_lookup[c1], c2 = base16_lookup[c2], c3 = base16_lookup[c3], c4 = base16_lookup[c4]; ++ eexec_key(*key, (c1<<4)|c2); eexec_key(*key, (c3<<4)|c4); /* dummy bytes 1, 2 */ ++ eexec_getx(I, c1, c2); eexec_getx(I, c3, c4); ++ eexec_key(*key, (c1<<4)|c2); eexec_key(*key, (c3<<4)|c4); /* dummy bytes 3, 4 */ ++ } ++ return 0; ++} ++ ++iof_status eexec_decode (iof *I, iof *O) ++{ ++ int c, c1, c2, key, binary, status; ++ if ((status = eexec_decode_init(I, &key, &binary)) < 0) ++ return status; ++ if (binary) ++ { ++ while (iof_ensure(O, 1)) ++ { ++ eexec_getc(I, c1); ++ eexec_decipher(key, c1, c); ++ iof_set(O, c); ++ } ++ } ++ else ++ { ++ while (iof_ensure(O, 1)) ++ { ++ eexec_getx(I, c1, c2); ++ eexec_decipher(key, (c1<<4)|c2, c); ++ iof_set(O, c); ++ } ++ } ++ return IOFFULL; ++} ++ ++iof_status eexec_decode_state (iof *I, iof *O, eexec_state *state) ++{ ++ register int c, c1, c2, status; ++ if (state->key == -1) /* initial state */ ++ if ((status = eexec_decode_init(I, &state->key, &state->binary)) < 0) ++ return status; ++ if (state->binary) ++ { ++ while (iof_ensure(O, 1)) ++ { ++ if ((c1 = iof_get(I)) < 0) ++ return (state->flush ? IOFEOF : IOFEMPTY); ++ else if (c1 < 0) ++ return c1; ++ eexec_decipher(state->key, c1, c); ++ iof_set(O, c); ++ } ++ } ++ else ++ { ++ if (state->c1 > -1) ++ { ++ c1 = state->c1; ++ state->c1 = -1; ++ goto byte2; ++ } ++ while (iof_ensure(O, 1)) ++ { ++ eexec_getx_(I, c1); ++ byte2: ++ do { c2 = iof_get(I); } while (lps_ignored_char(c2)); ++ if (c2 < 0) // odd number of hex bytes? hmm... assume c2 is zero ++ { ++ if (state->flush) ++ { ++ eexec_decipher(state->key, (c1<<4), c); ++ iof_set(O, c); ++ return IOFEOF; ++ } ++ state->c1 = c1; ++ return IOFEMPTY; ++ } ++ else if (c2 < 0) ++ return c2; ++ if ((c2 = base16_lookup[c2]) < 0) ++ return IOFERR; ++ eexec_decipher(state->key, (c1<<4)|c2, c); ++ iof_set(O, c); ++ } ++ } ++ return IOFFULL; ++} ++ ++#define EEXEC_INIT_BYTES "" ++ ++iof_status eexec_encode (iof *I, iof *O, size_t line, size_t maxline) ++{ ++ int c1, c, key; ++ const char *p; ++ ++ key = 55665; ++ p = EEXEC_INIT_BYTES; ++ if (maxline == 0) ++ { ++ if (!iof_ensure(O, 4)) ++ return IOFFULL; ++ eexec_encipher(key, p[0], c); iof_set(O, c); ++ eexec_encipher(key, p[1], c); iof_set(O, c); ++ eexec_encipher(key, p[2], c); iof_set(O, c); ++ eexec_encipher(key, p[3], c); iof_set(O, c); ++ while (iof_ensure(O, 1)) ++ { ++ eexec_getc(I, c1); ++ eexec_encipher(key, c1, c); ++ iof_set(O, c); ++ } ++ } ++ else ++ { ++ if (!iof_ensure(O, 8 + 1)) ++ return IOFFULL; ++ eexec_encipher(key, p[0], c); put_nl(O, line, maxline, 2); iof_set_uc_hex(O, c); ++ eexec_encipher(key, p[1], c); put_nl(O, line, maxline, 2); iof_set_uc_hex(O, c); ++ eexec_encipher(key, p[2], c); put_nl(O, line, maxline, 2); iof_set_uc_hex(O, c); ++ eexec_encipher(key, p[3], c); put_nl(O, line, maxline, 2); iof_set_uc_hex(O, c); ++ while (iof_ensure(O, 2 + 1)) ++ { ++ eexec_getc(I, c1); ++ eexec_encipher(key, c1, c); ++ put_nl(O, line, maxline, 2); ++ iof_set_uc_hex(O, c); ++ } ++ } ++ return IOFFULL; ++} ++ ++iof_status eexec_encode_state (iof *I, iof *O, eexec_state *state) ++{ ++ int c, c1; ++ const char *p; ++ ++ if (state->key == -1) ++ { ++ p = state->initbytes != NULL ? state->initbytes : EEXEC_INIT_BYTES; ++ if (state->binary) ++ { ++ if (!iof_ensure(O, 4)) ++ return IOFFULL; ++ state->key = 55665; ++ eexec_encipher(state->key, p[0], c); iof_set(O, c); ++ eexec_encipher(state->key, p[1], c); iof_set(O, c); ++ eexec_encipher(state->key, p[2], c); iof_set(O, c); ++ eexec_encipher(state->key, p[3], c); iof_set(O, c); ++ } ++ else ++ { ++ if (!iof_ensure(O, 8 + 1)) ++ return IOFFULL; ++ state->key = 55665; ++ eexec_encipher(state->key, p[0], c); put_nl(O, state->line, state->maxline, 2); iof_set_uc_hex(O, c); ++ eexec_encipher(state->key, p[1], c); put_nl(O, state->line, state->maxline, 2); iof_set_uc_hex(O, c); ++ eexec_encipher(state->key, p[2], c); put_nl(O, state->line, state->maxline, 2); iof_set_uc_hex(O, c); ++ eexec_encipher(state->key, p[3], c); put_nl(O, state->line, state->maxline, 2); iof_set_uc_hex(O, c); ++ } ++ } ++ if (state->binary) ++ { ++ while (iof_ensure(O, 1)) ++ { ++ if ((c1 = iof_get(I)) < 0) ++ return c1 < 0 ? (state->flush ? IOFEOF : IOFEMPTY) : c1; ++ eexec_encipher(state->key, c1, c); ++ iof_set(O, c); ++ } ++ } ++ else ++ { ++ while (iof_ensure(O, 2 + 1)) ++ { ++ if ((c1 = iof_get(I)) < 0) ++ return c1 < 0 ? (state->flush ? IOFEOF : IOFEMPTY) : c1; ++ eexec_encipher(state->key, c1, c); ++ put_nl(O, state->line, state->maxline, 2); ++ iof_set_uc_hex(O, c); ++ } ++ } ++ return IOFFULL; ++} ++ ++/* ++Type1 charstrings are encoded/decoded in the same way, except that: ++- initial key is 4330 ++- initbytes might be other then 4; /lenIV key in Private dict ++Type1 spec page 63. In practise we don't need iof interface here, ++we always apply the codec in place. ++*/ ++ ++#define type1chstr_key(key, c1) (key = ((((c1) + key)*52845 + 22719) & 65535)) ++#define type1chstr_decipher(key, c1, c) (c = ((c1)^(key>>8)), type1chstr_key(key, c1)) ++#define type1chstr_encipher(key, c1, c) (c = ((c1)^(key>>8)), type1chstr_key(key, c)) ++ ++int type1_charstring_decode (void *data, size_t size, void *outdata, uint8_t leniv) ++{ /* data and outdata may be the same, output size is always size - leniv */ ++ uint8_t *input = (uint8_t *)data, *output = (uint8_t *)outdata; ++ size_t i; ++ int c, c1, key; ++ ++ if (size < 4) ++ return 0; ++ key = 4330; ++ for (i = 0; i < leniv; ++i) ++ { ++ c1 = input[i]; ++ type1chstr_key(key, c1); ++ } ++ for ( ; i < size; ++i) ++ { ++ c1 = input[i]; ++ type1chstr_decipher(key, c1, c); ++ output[i - leniv] = c; ++ } ++ return 1; ++} ++ ++#define TYPE1_CHSTR_INIT_BYTES EEXEC_INIT_BYTES ++ ++int type1_charstring_encode (void *data, size_t size, void *outdata, uint8_t leniv) ++{ /* outdata may be data - leniv, output size is always size + leniv */ ++ uint8_t *input = (uint8_t *)data, *output = (uint8_t *)outdata; ++ size_t i, j; ++ int c, c1, key; ++ ++ key = 4330; ++ for (i = 0, j = 0; i < leniv; ++i) ++ { ++ c1 = TYPE1_CHSTR_INIT_BYTES[j]; ++ //type1chstr_key(key, c1); ++ type1chstr_encipher(key, c1, c); ++ if (++j == 4) ++ j = 0; ++ output[i] = c; ++ } ++ for (i = 0; i < size; ++i) ++ { ++ c1 = input[i]; ++ type1chstr_encipher(key, c1, c); ++ output[i + leniv] = c; ++ } ++ return 1; ++} ++ ++/* filters */ ++ ++// base16 decoder function ++ ++static size_t base16_decoder (iof *F, iof_mode mode) ++{ ++ basexx_state *state; ++ iof_status status; ++ size_t tail; ++ ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ state = iof_filter_state(basexx_state *, F); ++ do { ++ status = base16_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "base16", status); ++ case IOFCLOSE: ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// base16 encoder function ++ ++static size_t base16_encoder (iof *F, iof_mode mode) ++{ ++ basexx_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(basexx_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = base16_encode_state_ln(F, F->next, state); ++ return iof_encoder_retval(F, "base16", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ base16_encoder(F, IOFFLUSH); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// base64 decoder function ++ ++static size_t base64_decoder (iof *F, iof_mode mode) ++{ ++ basexx_state *state; ++ iof_status status; ++ size_t tail; ++ ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ state = iof_filter_state(basexx_state *, F); ++ do { ++ status = base64_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "base64", status); ++ case IOFCLOSE: ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// base64 encoder function ++ ++static size_t base64_encoder (iof *F, iof_mode mode) ++{ ++ basexx_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(basexx_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = base64_encode_state_ln(F, F->next, state); ++ return iof_encoder_retval(F, "base64", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ base64_encoder(F, IOFFLUSH); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// base85 decoder function ++ ++static size_t base85_decoder (iof *F, iof_mode mode) ++{ ++ basexx_state *state; ++ iof_status status; ++ size_t tail; ++ ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ state = iof_filter_state(basexx_state *, F); ++ do { ++ status = base85_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "base85", status); ++ case IOFCLOSE: ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// base85 encoder function ++ ++static size_t base85_encoder (iof *F, iof_mode mode) ++{ ++ basexx_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(basexx_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = base85_encode_state_ln(F, F->next, state); ++ return iof_encoder_retval(F, "base85", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ base85_encoder(F, IOFFLUSH); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// runlength decoder function ++ ++static size_t runlength_decoder (iof *F, iof_mode mode) ++{ ++ runlength_state *state; ++ iof_status status; ++ size_t tail; ++ ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ state = iof_filter_state(runlength_state *, F); ++ do { ++ status = runlength_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "runlength", status); ++ case IOFCLOSE: ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// runlength encoder function ++ ++static size_t runlength_encoder (iof *F, iof_mode mode) ++{ ++ runlength_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(runlength_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = runlength_encode_state(F, F->next, state); ++ return iof_encoder_retval(F, "runlength", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ runlength_encoder(F, IOFFLUSH); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// eexec decoder function ++ ++static size_t eexec_decoder (iof *F, iof_mode mode) ++{ ++ eexec_state *state; ++ iof_status status; ++ size_t tail; ++ ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ state = iof_filter_state(eexec_state *, F); ++ do { ++ status = eexec_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "eexec", status); ++ case IOFCLOSE: ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// eexec encoder function ++ ++static size_t eexec_encoder (iof *F, iof_mode mode) ++{ ++ eexec_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(eexec_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = eexec_encode_state(F, F->next, state); ++ return iof_encoder_retval(F, "eexec", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ eexec_encoder(F, IOFFLUSH); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// ++ ++int iof_filter_basexx_encoder_ln (iof *F, size_t line, size_t maxline) ++{ ++ basexx_state *state; ++ if (maxline > 8 && line < maxline) ++ { ++ state = iof_filter_state(basexx_state *, F); ++ state->line = line; ++ state->maxline = maxline; ++ return 1; ++ } ++ return 0; ++} ++ ++/* base 16 */ ++ ++iof * iof_filter_base16_decoder (iof *N) ++{ ++ iof *I; ++ basexx_state *state; ++ I = iof_filter_reader(base16_decoder, sizeof(basexx_state), &state); ++ iof_setup_next(I, N); ++ basexx_state_init(state); ++ state->flush = 1; // means N is supposed to be continuous input ++ return I; ++} ++ ++iof * iof_filter_base16_encoder (iof *N) ++{ ++ iof *O; ++ basexx_state *state; ++ O = iof_filter_writer(base16_encoder, sizeof(basexx_state), &state); ++ iof_setup_next(O, N); ++ basexx_state_init(state); ++ return O; ++} ++ ++/* base 64 */ ++ ++iof * iof_filter_base64_decoder (iof *N) ++{ ++ iof *I; ++ basexx_state *state; ++ I = iof_filter_reader(base64_decoder, sizeof(basexx_state), &state); ++ iof_setup_next(I, N); ++ basexx_state_init(state); ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_base64_encoder (iof *N) ++{ ++ iof *O; ++ basexx_state *state; ++ O = iof_filter_writer(base64_encoder, sizeof(basexx_state), &state); ++ iof_setup_next(O, N); ++ basexx_state_init(state); ++ return O; ++} ++ ++/* base 85 */ ++ ++iof * iof_filter_base85_decoder (iof *N) ++{ ++ iof *I; ++ basexx_state *state; ++ I = iof_filter_reader(base85_decoder, sizeof(basexx_state), &state); ++ iof_setup_next(I, N); ++ basexx_state_init(state); ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_base85_encoder (iof *N) ++{ ++ iof *O; ++ basexx_state *state; ++ O = iof_filter_writer(base85_encoder, sizeof(basexx_state), &state); ++ iof_setup_next(O, N); ++ basexx_state_init(state); ++ return O; ++} ++ ++/* runlength stream filter */ ++ ++iof * iof_filter_runlength_decoder (iof *N) ++{ ++ iof *I; ++ runlength_state *state; ++ I = iof_filter_reader(runlength_decoder, sizeof(runlength_state), &state); ++ iof_setup_next(I, N); ++ runlength_state_init(state); ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_runlength_encoder (iof *N) ++{ ++ iof *O; ++ runlength_state *state; ++ O = iof_filter_writer(runlength_encoder, sizeof(runlength_state), &state); ++ iof_setup_next(O, N); ++ runlength_state_init(state); ++ return O; ++} ++ ++/* eexec stream filter, type1 fonts spec p. 63 */ ++ ++iof * iof_filter_eexec_decoder (iof *N) ++{ ++ iof *I; ++ eexec_state *state; ++ I = iof_filter_reader(eexec_decoder, sizeof(eexec_state), &state); ++ iof_setup_next(I, N); ++ eexec_state_init(state); ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_eexec_encoder (iof *N) ++{ ++ iof *O; ++ eexec_state *state; ++ O = iof_filter_writer(eexec_encoder, sizeof(eexec_state), &state); ++ iof_setup_next(O, N); ++ eexec_state_init(state); ++ return O; ++} +diff --git a/texk/web2c/luatexdir/luapplib/util/utilbasexx.h b/texk/web2c/luatexdir/luapplib/util/utilbasexx.h +new file mode 100644 +index 000000000..52aac3772 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilbasexx.h +@@ -0,0 +1,133 @@ ++ ++/* base encodings */ ++ ++#ifndef UTIL_BASEXX_H ++#define UTIL_BASEXX_H ++ ++#include "utiliof.h" ++ ++/* base codecs state */ ++ ++typedef struct basexx_state basexx_state; ++ ++#define BASEXX_MAXLINE 80 ++#define BASEXX_PDF ++ ++void basexx_state_init_ln (basexx_state *state, size_t line, size_t maxline); ++#define basexx_state_init(state) basexx_state_init_ln(state, 0, BASEXX_MAXLINE) ++ ++/* base16 */ ++ ++int base16_getc (iof *I); ++int base16_uc_putc (iof *I, int c); ++int base16_lc_putc (iof *I, int c); ++#define base16_putc base16_uc_putc ++ ++iof_status base16_encoded_uc (const void *data, size_t size, iof *O); ++iof_status base16_encoded_lc (const void *data, size_t size, iof *O); ++iof_status base16_encoded_uc_ln (const void *data, size_t size, iof *O, size_t line, size_t maxline); ++iof_status base16_encoded_lc_ln (const void *data, size_t size, iof *O, size_t line, size_t maxline); ++ ++iof_status base16_encode_uc (iof *I, iof *O); ++iof_status base16_encode_lc (iof *I, iof *O); ++iof_status base16_encode_uc_ln (iof *I, iof *O, size_t line, size_t maxline); ++iof_status base16_encode_lc_ln (iof *I, iof *O, size_t line, size_t maxline); ++iof_status base16_decode (iof *I, iof *O); ++ ++#define base16_encoded base16_encoded_uc ++#define base16_encoded_ln base16_encoded_uc_ln ++#define base16_encode base16_encode_uc ++#define base16_encode_ln base16_encode_uc_ln ++ ++iof_status base16_encode_state_uc (iof *I, iof *O, basexx_state *state); ++iof_status base16_encode_state_lc (iof *I, iof *O, basexx_state *state); ++iof_status base16_encode_state_uc_ln (iof *I, iof *O, basexx_state *state); ++iof_status base16_encode_state_lc_ln (iof *I, iof *O, basexx_state *state); ++iof_status base16_decode_state (iof *I, iof *O, basexx_state *state); ++ ++#define base16_encode_state base16_encode_state_uc ++#define base16_encode_state_ln base16_encode_state_uc_ln ++ ++/* base64 */ ++ ++extern const char base64_alphabet[]; ++extern const int base64_lookup[]; ++ ++iof_status base64_encoded (const void *data, size_t size, iof *O); ++iof_status base64_encoded_ln (const void *data, size_t size, iof *O, size_t line, size_t maxline); ++ ++iof_status base64_encode (iof *I, iof *O); ++iof_status base64_encode_ln (iof *I, iof *O, size_t line, size_t maxline); ++iof_status base64_decode (iof *I, iof *O); ++ ++iof_status base64_encode_state (iof *I, iof *O, basexx_state *state); ++iof_status base64_encode_state_ln (iof *I, iof *O, basexx_state *state); ++iof_status base64_decode_state (iof *I, iof *O, basexx_state *state); ++ ++/* base85 */ ++ ++extern const char base85_alphabet[]; ++extern const int base85_lookup[]; ++ ++iof_status base85_encoded (const void *data, size_t size, iof *O); ++iof_status base85_encoded_ln (const void *data, size_t size, iof *O, size_t line, size_t maxline); ++ ++iof_status base85_encode (iof *I, iof *O); ++iof_status base85_encode_ln (iof *I, iof *O, size_t line, size_t maxline); ++iof_status base85_decode (iof *I, iof *O); ++ ++iof_status base85_encode_state (iof *I, iof *O, basexx_state *state); ++iof_status base85_encode_state_ln (iof *I, iof *O, basexx_state *state); ++iof_status base85_decode_state (iof *I, iof *O, basexx_state *state); ++ ++/* run length */ ++ ++typedef struct runlength_state runlength_state; ++ ++void runlength_state_init (runlength_state *state); ++ ++iof_status runlength_encode (iof *I, iof *O); ++iof_status runlength_encode_state (iof *I, iof *O, runlength_state *state); ++ ++iof_status runlength_decode (iof *I, iof *O); ++iof_status runlength_decode_state (iof *I, iof *O, runlength_state *state); ++ ++/* eexec */ ++ ++typedef struct eexec_state eexec_state; ++ ++#define EEXEC_MAXLINE 80 ++ ++void eexec_state_init_ln (eexec_state *state, size_t line, size_t maxline, const char *initbytes); ++#define eexec_state_init(state) eexec_state_init_ln(state, 0, EEXEC_MAXLINE, NULL) ++ ++iof_status eexec_decode (iof *I, iof *O); ++iof_status eexec_decode_state (iof *I, iof *O, eexec_state *state); ++ ++iof_status eexec_encode (iof *I, iof *O, size_t line, size_t maxline); ++iof_status eexec_encode_state (iof *I, iof *O, eexec_state *state); ++ ++iof_status type1_charstring_decode (void *data, size_t size, void *outdata, uint8_t leniv); ++iof_status type1_charstring_encode (void *data, size_t size, void *outdata, uint8_t leniv); ++ ++/* filters */ ++ ++int iof_filter_basexx_encoder_ln (iof *N, size_t line, size_t maxline); ++ ++iof * iof_filter_base16_decoder (iof *N); ++iof * iof_filter_base16_encoder (iof *N); ++ ++iof * iof_filter_base64_decoder (iof *N); ++iof * iof_filter_base64_encoder (iof *N); ++ ++iof * iof_filter_base85_decoder (iof *N); ++iof * iof_filter_base85_encoder (iof *N); ++ ++iof * iof_filter_runlength_decoder (iof *N); ++iof * iof_filter_runlength_encoder (iof *N); ++ ++iof * iof_filter_eexec_decoder (iof *N); ++iof * iof_filter_eexec_encoder (iof *N); ++ ++ ++#endif +diff --git a/texk/web2c/luatexdir/luapplib/util/utilcrypt.c b/texk/web2c/luatexdir/luapplib/util/utilcrypt.c +new file mode 100644 +index 000000000..1750d01bd +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilcrypt.c +@@ -0,0 +1,1191 @@ ++ ++#include "utilmem.h" ++#include "utilcrypt.h" ++#include "utilcryptdef.h" ++#include "utilmd5.h" ++ ++/* rc4 */ ++ ++/* ++Initializer arguments: ++- state - crypt state ++- map - a space for rc4 bytes map; may be left NULL in which case will be allocated ++- vkey - crypt key; may be left NULL iff map is provided and properly initialized ++- keylength - the length of crypt key (from 5 to 16 bytes) ++*/ ++ ++rc4_state * rc4_state_initialize (rc4_state *state, rc4_map *map, const void *vkey, size_t keylength) ++{ ++ int i, j; ++ uint8_t tmp; ++ const uint8_t *key; ++ key = (const uint8_t *)vkey; ++ if (keylength == 0 || keylength > 256) ++ return NULL; ++ state->flags = 0; ++ if (map != NULL) ++ { ++ state->map = map; ++ } ++ else ++ { ++ state->map = (rc4_map *)util_malloc(sizeof(rc4_map)); ++ state->flags |= RC4_STATE_ALLOC; ++ } ++ ++ if (key != NULL) ++ { ++ for (i = 0; i < 256; ++i) ++ state->smap[i] = (uint8_t)i; ++ for (i = 0, j = 0; i < 256; ++i) ++ { ++ j = (j + state->smap[i] + key[i % keylength]) & 255; ++ tmp = state->smap[i]; ++ state->smap[i] = state->smap[j]; ++ state->smap[j] = tmp; ++ } ++ } ++ state->i = 0; ++ state->j = 0; ++ state->flush = 0; /* caller is responsible to override if necessary */ ++ return state; ++} ++ ++void rc4_map_save (rc4_state *state, rc4_map *map) ++{ ++ memcpy(map, state->map, sizeof(rc4_map)); ++} ++ ++void rc4_map_restore (rc4_state *state, rc4_map *map) ++{ ++ memcpy(state->map, map, sizeof(rc4_map)); ++ //state->flags = 0; ++ //state->flush = 0; ++ state->i = 0; ++ state->j = 0; ++} ++ ++static uint8_t rc4_next_random_byte (rc4_state *state) ++{ ++ uint8_t tmp; ++ state->i = (state->i + 1) & 255; ++ state->j = (state->j + state->smap[state->i]) & 255; ++ tmp = state->smap[state->i]; ++ state->smap[state->i] = state->smap[state->j]; ++ state->smap[state->j] = tmp; ++ return state->smap[(state->smap[state->i] + state->smap[state->j]) & 255]; ++} ++ ++iof_status rc4_crypt_state (iof *I, iof *O, rc4_state *state) ++{ ++ uint8_t r; ++ int c; ++ while (iof_ensure(O, 1)) ++ { ++ if ((c = iof_get(I)) < 0) ++ return c == IOFERR ? IOFERR : (state->flush ? IOFEOF : IOFEMPTY); ++ r = rc4_next_random_byte(state); ++ //r = r ^ ((uint8_t)c); ++ //iof_set(O, r); ++ iof_set(O, r ^ ((uint8_t)c)); ++ } ++ return IOFFULL; ++} ++ ++iof_status rc4_crypt (iof *I, iof *O, const void *key, size_t keylength) ++{ ++ int ret; ++ rc4_state state; ++ rc4_map map; ++ if (rc4_state_initialize(&state, &map, key, keylength) == NULL) ++ return IOFERR; ++ state.flush = 1; ++ ret = rc4_crypt_state(I, O, &state); ++ rc4_state_close(&state); ++ return ret; ++} ++ ++/* ++Variants that operates on c-strings can worn inplace, so output and input can be the same address. ++Variant that takes rc4_state pointer expects the state properly initialized. Keep in mind ++the crypt procedure modifies rc4 bytes map. All returns the size of encrypted/decrypted ++data, which is the same as input data length for rc4. ++*/ ++ ++size_t rc4_crypt_data (const void *input, size_t length, void *output, const void *key, size_t keylength) ++{ ++ rc4_state state; ++ rc4_map map; ++ if (rc4_state_initialize(&state, &map, key, keylength) == NULL) ++ return 0; ++ return rc4_crypt_state_data(&state, input, length, output); ++ // no need to call rc4_state_close() ++} ++ ++size_t rc4_crypt_state_data (rc4_state *state, const void *input, size_t length, void *output) ++{ /* state assumed to be initialized and with the proper state of smap */ ++ const uint8_t *inp; ++ uint8_t r, *out; ++ size_t size; ++ inp = (const uint8_t *)input; ++ out = (uint8_t *)output; ++ for (size = 0; size < length; ++size, ++inp, ++out) ++ { ++ r = rc4_next_random_byte(state); ++ *out = r ^ *inp; ++ } ++ return length; ++} ++ ++void rc4_state_close (rc4_state *state) ++{ ++ if (state->smap != NULL && (state->flags & RC4_STATE_ALLOC)) ++ { ++ util_free(state->smap); ++ state->smap = NULL; ++ } ++} ++ ++/* aes; parts of code excerpted from https://github.com/kokke/tiny-AES128-C */ ++ ++static const uint8_t sbox[256] = { ++ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, ++ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, ++ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, ++ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, ++ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, ++ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, ++ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, ++ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, ++ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, ++ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, ++ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, ++ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, ++ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, ++ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, ++ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, ++ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; ++ ++static const uint8_t rsbox[256] = ++{ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, ++ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, ++ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, ++ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, ++ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, ++ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, ++ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, ++ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, ++ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, ++ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, ++ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, ++ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, ++ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, ++ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, ++ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, ++ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; ++ ++/* ++The round constant word array, rcon[i], contains the values given by ++x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) ++Note that i starts at 1, not 0). ++*/ ++ ++static const uint8_t rcon[255] = { ++ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, ++ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, ++ 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, ++ 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, ++ 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, ++ 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, ++ 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, ++ 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, ++ 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, ++ 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, ++ 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, ++ 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, ++ 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, ++ 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, ++ 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, ++ 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb }; ++ ++/* block copying */ ++ ++#define aes_copy_block(output, input) memcpy(output, input, 16) ++ ++static void aes_copy_cbc (uint8_t *data, const uint8_t *input) ++{ ++ uint8_t i; ++ for (i = 0; i < 16; ++i) ++ data[i] ^= input[i]; ++} ++ ++static void aes_copy_xor (uint8_t *data, const uint8_t *input, const uint8_t *iv) ++{ ++ uint8_t i; ++ for (i = 0; i < 16; ++i) ++ data[i] = input[i] ^ iv[i]; ++} ++ ++/* key expansion */ ++ ++#define AES_COLUMNS 4 // constant in aes ++ ++static void key_expansion (aes_state *state, const uint8_t *key) ++{ ++ uint32_t i, j, k; ++ uint8_t t[4]; ++ uint8_t *keydata, keywords, columns; ++ ++ keywords = (uint8_t)(state->keylength >> 2); ++ keydata = (uint8_t *)state->keyblock; ++ ++ /* the first round key is the key itself */ ++ for(i = 0; i < keywords; ++i) ++ { ++ keydata[(i * 4) + 0] = key[(i * 4) + 0]; ++ keydata[(i * 4) + 1] = key[(i * 4) + 1]; ++ keydata[(i * 4) + 2] = key[(i * 4) + 2]; ++ keydata[(i * 4) + 3] = key[(i * 4) + 3]; ++ } ++ ++ /* others derived from the first */ ++ ++ for(columns = AES_COLUMNS * (state->rounds + 1); i < columns; ++i) ++ { ++ for(j = 0; j < 4; ++j) ++ t[j] = keydata[(i - 1) * 4 + j]; ++ if (i % keywords == 0) ++ { ++ /* rotate the 4 bytes in a word to the left once; [a0,a1,a2,a3] becomes [a1,a2,a3,a0] */ ++ k = t[0]; ++ t[0] = t[1]; ++ t[1] = t[2]; ++ t[2] = t[3]; ++ t[3] = k; ++ ++ /* take a four-byte input word and apply the S-box to each of the four bytes to produce an output word */ ++ t[0] = sbox[t[0]]; ++ t[1] = sbox[t[1]]; ++ t[2] = sbox[t[2]]; ++ t[3] = sbox[t[3]]; ++ ++ t[0] = t[0] ^ rcon[i / keywords]; ++ } ++ else if (keywords > 6 && i % keywords == 4) ++ { ++ t[0] = sbox[t[0]]; ++ t[1] = sbox[t[1]]; ++ t[2] = sbox[t[2]]; ++ t[3] = sbox[t[3]]; ++ } ++ keydata[i * 4 + 0] = keydata[(i - keywords) * 4 + 0] ^ t[0]; ++ keydata[i * 4 + 1] = keydata[(i - keywords) * 4 + 1] ^ t[1]; ++ keydata[i * 4 + 2] = keydata[(i - keywords) * 4 + 2] ^ t[2]; ++ keydata[i * 4 + 3] = keydata[(i - keywords) * 4 + 3] ^ t[3]; ++ } ++ ++} ++ ++/* ++An original implementation uses no private buffers except a keyblock. We need private buffers to ++keep a CBC vector between calls and to be able to read input data not necessarily in 16-bytes blocks. ++Encrypter would actually require only one such buffer, as CBC vector is applied on input data before ++the actual cipher procedure. And CBC for the next chunk is simply the output from the previous. ++Decrypter, however, applies the cipher first, then applies CBC to the output with a buffered init ++vector, and the vector for the next call is the row input before cipher. Hence we need two 16-bytes ++buffers for decrypter. ++*/ ++ ++/* ++aes_state * aes_state_initialize_ecb (aes_state *State, uint8_t *keyblock, const uint8_t *key) ++{ ++ state->flags = 0; ++ ++ state->flags |= AES_ECB_MODE; ++ ++ if (keyblock == NULL) ++ { ++ keyblock = util_malloc(sizeof(aes_keyblock)); ++ state->flags |= AES_STATE_ALLOC; ++ } ++ state->keyblock = keyblock; ++ key_expansion(state, key); ++ state->flush = 0; ++ return state; ++} ++*/ ++ ++void aes_pdf_mode (aes_state *state) ++{ ++ state->flags |= AES_INLINE_IV; ++ state->flags &= ~AES_NULL_PADDING; ++} ++ ++/* ++Initialize arguments: ++- state - crypt state ++- keyblock - a space for aes key expansion; can be left NULL in which case will be allocated ++- key - crypt key; can be left NULL iff keyblock is given and properly initialized ++- keylength - the length of the key (16 or 32 bytes) ++- iv - 16-bytes CBC initialization vector; ++ - if left NULL for encoder, one is generated and stored as state->iv ++ - can also be left NULL for decorer, but then AES_INLINE_IV must be set, as this informs decoder to take ++ an initialization vector from the beginning of the encrypted stream ++ ++At the first approach, an initialization vector was copied to state block during initialization and encoders ++assumed that the state block is the current initialization vector. This simplifies encrypting procedure, ++as the output from every 16-bytes chunk encryption is an initialization vector for the next chunk. However, ++it makes api usage cumbersome, as the user has to know that iv may need to be copied to state block ++before each call. ++*/ ++ ++static int aes_key_length (aes_state *state, size_t keylength) ++{ ++ state->keylength = keylength; ++ switch (keylength) ++ { ++ case 16: ++ state->rounds = 10; ++ break; ++ case 24: ++ state->rounds = 12; ++ break; ++ case 32: ++ state->rounds = 14; ++ break; ++ default: ++ return 0; ++ } ++ return 1; ++} ++ ++aes_state * aes_encode_initialize (aes_state *state, aes_keyblock *keyblock, const void *key, size_t keylength, const void *iv) ++{ ++ state->flags = 0; ++ if (!aes_key_length(state, keylength)) ++ return NULL; ++ if (iv != NULL) ++ aes_copy_block(state->iv, iv); ++ else ++ aes_generate_iv(state->iv); ++ state->flags |= AES_HAS_IV; ++ ++ if (keyblock == NULL) ++ { ++ keyblock = (aes_keyblock *)util_malloc(sizeof(aes_keyblock)); ++ state->flags |= AES_STATE_ALLOC; ++ } ++ state->keyblock = keyblock; ++ if (key != NULL) /* if NULL we assume keyblock is given and already expanded */ ++ key_expansion(state, (const uint8_t *)key); ++ state->flush = 0; ++ return state; ++} ++ ++aes_state * aes_decode_initialize (aes_state *state, aes_keyblock *keyblock, const void *key, size_t keylength, const void *iv) ++{ ++ state->flags = 0; ++ if (!aes_key_length(state, keylength)) ++ return NULL; ++ if (iv != NULL) ++ { ++ aes_copy_block(state->iv, iv); ++ state->flags |= AES_HAS_IV; ++ } ++ /* else if AES_INLINE_IV flag is set will be read from input */ ++ ++ if (keyblock == NULL) ++ { ++ keyblock = (aes_keyblock *)util_malloc(sizeof(aes_keyblock)); ++ state->flags |= AES_STATE_ALLOC; ++ } ++ state->keyblock = keyblock; ++ if (key != NULL) /* otherwise keyblock is assumed present and properly initialized */ ++ key_expansion(state, (const uint8_t *)key); ++ state->flush = 0; ++ return state; ++} ++ ++void aes_state_close (aes_state *state) ++{ ++ if (state->keyblock != NULL && (state->flags & AES_STATE_ALLOC)) ++ util_free(state->keyblock); ++} ++ ++/* add round key */ ++ ++static void aes_round_key (aes_block block, aes_block keyblock) ++{ ++ uint8_t i, j; ++ for(i = 0; i < 4; ++i) ++ for(j = 0; j < 4; ++j) ++ block[i][j] ^= keyblock[i][j]; ++} ++ ++#define aes_add_key(block, keyblock, round) aes_round_key(block, (*keyblock)[round]) ++ ++/* substitution */ ++ ++static void aes_encode_sub (aes_block block) ++{ ++ uint8_t i, j, v; ++ for(i = 0; i < 4; ++i) ++ for(j = 0; j < 4; ++j) ++ v = block[i][j], block[i][j] = sbox[v]; ++} ++ ++/* rows shift; the row index is the shift offset, the first order is not shifted */ ++ ++static void aes_encode_shift (aes_block block) ++{ ++ uint8_t tmp; ++ ++ /* 1st row rotated once */ ++ tmp = block[0][1]; ++ block[0][1] = block[1][1]; ++ block[1][1] = block[2][1]; ++ block[2][1] = block[3][1]; ++ block[3][1] = tmp; ++ ++ /* 2nd row rotated twice */ ++ tmp = block[0][2]; ++ block[0][2] = block[2][2]; ++ block[2][2] = tmp; ++ tmp = block[1][2]; ++ block[1][2] = block[3][2]; ++ block[3][2] = tmp; ++ ++ /* 3rd row rotated 3 times */ ++ tmp = block[0][3]; ++ block[0][3] = block[3][3]; ++ block[3][3] = block[2][3]; ++ block[2][3] = block[1][3]; ++ block[1][3] = tmp; ++} ++ ++static uint8_t xtime (uint8_t x) ++{ ++ return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); ++} ++ ++/* mix columns */ ++ ++static void aes_encode_mix (aes_block block) ++{ ++ uint8_t i, tmp, tm, t; ++ ++ for(i = 0; i < 4; ++i) ++ { ++ t = block[i][0]; ++ tmp = block[i][0] ^ block[i][1] ^ block[i][2] ^ block[i][3] ; ++ tm = block[i][0] ^ block[i][1]; tm = xtime(tm); block[i][0] ^= tm ^ tmp; ++ tm = block[i][1] ^ block[i][2]; tm = xtime(tm); block[i][1] ^= tm ^ tmp; ++ tm = block[i][2] ^ block[i][3]; tm = xtime(tm); block[i][2] ^= tm ^ tmp; ++ tm = block[i][3] ^ t ; tm = xtime(tm); block[i][3] ^= tm ^ tmp; ++ } ++} ++ ++/* multiply is used to multiply numbers in the field GF(2^8) */ ++ ++#define multiply(x, y) \ ++ ( ((y & 1) * x) ^ \ ++ ((y>>1 & 1) * xtime(x)) ^ \ ++ ((y>>2 & 1) * xtime(xtime(x))) ^ \ ++ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ ++ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ ++ ++/* mix columns */ ++ ++static void aes_decode_mix (aes_block block) ++{ ++ int i; ++ uint8_t a, b, c, d; ++ ++ for(i = 0; i < 4; ++i) ++ { ++ a = block[i][0]; ++ b = block[i][1]; ++ c = block[i][2]; ++ d = block[i][3]; ++ block[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09); ++ block[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d); ++ block[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b); ++ block[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e); ++ } ++} ++ ++/* inverse substitution */ ++ ++static void aes_decode_sub (aes_block block) ++{ ++ uint8_t i, j, v; ++ for(i = 0; i < 4; ++i) ++ for(j = 0; j < 4; ++j) ++ v = block[i][j], block[i][j] = rsbox[v]; ++} ++ ++/* inverse shift rows */ ++ ++static void aes_decode_shift (aes_block block) ++{ ++ uint8_t tmp; ++ ++ /* 1st row rotated once right */ ++ tmp = block[3][1]; ++ block[3][1] = block[2][1]; ++ block[2][1] = block[1][1]; ++ block[1][1] = block[0][1]; ++ block[0][1] = tmp; ++ ++ /* 2st row rotated twice right */ ++ tmp = block[0][2]; ++ block[0][2] = block[2][2]; ++ block[2][2] = tmp; ++ tmp = block[1][2]; ++ block[1][2] = block[3][2]; ++ block[3][2] = tmp; ++ ++ /* 3rd row rotated 3 times right */ ++ tmp = block[0][3]; ++ block[0][3] = block[1][3]; ++ block[1][3] = block[2][3]; ++ block[2][3] = block[3][3]; ++ block[3][3] = tmp; ++} ++ ++/* aes block encoder */ ++ ++static void aes_encode_cipher (aes_state *state) ++{ ++ uint8_t round; ++ aes_add_key(state->block, state->keyblock, 0); ++ for (round = 1; round < state->rounds; ++round) ++ { ++ aes_encode_sub(state->block); ++ aes_encode_shift(state->block); ++ aes_encode_mix(state->block); ++ aes_add_key(state->block, state->keyblock, round); ++ } ++ aes_encode_sub(state->block); ++ aes_encode_shift(state->block); ++ aes_add_key(state->block, state->keyblock, state->rounds); ++} ++ ++/* aes block decoder */ ++ ++static void aes_decode_cipher (aes_state *state) ++{ ++ uint8_t round; ++ aes_add_key(state->block, state->keyblock, state->rounds); ++ for(round = state->rounds - 1; round > 0; --round) ++ { ++ aes_decode_shift(state->block); ++ aes_decode_sub(state->block); ++ aes_add_key(state->block, state->keyblock, round); ++ aes_decode_mix(state->block); ++ } ++ aes_decode_shift(state->block); ++ aes_decode_sub(state->block); ++ aes_add_key(state->block, state->keyblock, 0); ++} ++ ++/* tail block padding; RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0; pdf spec p. 119 */ ++ ++#define aes_padding(state) ((state->flags & AES_NULL_PADDING) == 0) ++ ++static void aes_put_padding (aes_state *state, uint8_t length) ++{ ++ uint8_t pad; ++ pad = (aes_padding(state)) ? 16 - length : 0; ++ for (; length < 16; ++length) ++ state->data[length] = state->iv[length] ^ pad; ++} ++ ++static int aes_remove_padding (aes_state *state, uint8_t *data, uint8_t *length) ++{ ++ uint8_t pad; ++ *length = 16; /* block length 16 means leave intact */ ++ if (aes_padding(state)) ++ { ++ pad = data[16 - 1]; ++ if (pad > 16) ++ return IOFERR; ++ for ( ; *length > 16 - pad; --(*length)) ++ if (data[*length - 1] != pad) ++ return IOFERR; ++ } ++ else ++ { ++ for ( ; *length > 0; --(*length)) ++ if (data[*length - 1] != '\0') ++ break; ++ } ++ return IOFEOF; ++} ++ ++/* aes codec */ ++ ++/* make the cipher on input xor-ed with iv, save the output as a new iv, write the output */ ++#define aes_encode_output(state, output) \ ++ (aes_encode_cipher(state), aes_copy_block(state->iv, state->data), aes_copy_block(output, state->data), output += 16) ++ ++iof_status aes_encode_state (iof *I, iof *O, aes_state *state) ++{ ++ int c; ++ ++ if (!(state->flags & AES_HAS_IV)) // weird ++ return IOFERR; ++ if ((state->flags & AES_INLINE_IV) && !(state->flags & AES_CONTINUE)) ++ { /* write iv at the beginning of encrypted data */ ++ if (!iof_ensure(O, 16)) ++ return IOFFULL; ++ aes_copy_block(O->pos, state->iv); ++ O->pos += 16; ++ state->flags |= AES_CONTINUE; ++ } ++ while (iof_ensure(O, 16)) ++ { ++ while (state->buffered < 16) ++ { ++ if ((c = iof_get(I)) != IOFEOF) ++ { /* get input byte XORed with iv */ ++ state->data[state->buffered] = state->iv[state->buffered] ^ ((uint8_t)c); ++ ++state->buffered; ++ } ++ else ++ { ++ if (state->flush) ++ { ++ if (state->buffered > 0 || aes_padding(state)) ++ { /* pad the last input chunk; for input divisable by 16, add 16 bytes 0x0f */ ++ aes_put_padding(state, state->buffered); ++ state->buffered = 16; ++ aes_encode_output(state, O->pos); ++ } ++ return IOFEOF; ++ } ++ else ++ return IOFEMPTY; ++ } ++ } ++ aes_encode_output(state, O->pos); ++ state->buffered = 0; ++ } ++ return IOFFULL; ++} ++ ++/* write iv to the output, save the raw input just buffered as iv for the next chunk, make the cipher, write out xoring with iv */ ++#define aes_decode_output(state, output) \ ++ (aes_copy_block(output, state->iv), aes_copy_block(state->iv, state->data), aes_decode_cipher(state), aes_copy_cbc(output, state->data), output += 16) ++ ++iof_status aes_decode_state (iof *I, iof *O, aes_state *state) ++{ ++ int c, ret; ++ uint8_t lastlength; ++ ++ if ((state->flags & AES_INLINE_IV) && !(state->flags & AES_CONTINUE)) ++ { ++ while (state->buffered < 16) ++ { ++ if ((c = iof_get(I)) != IOFEOF) ++ state->iv[state->buffered++] = (uint8_t)c; ++ else ++ return state->flush ? IOFERR : IOFEMPTY; ++ } ++ state->flags |= AES_CONTINUE|AES_HAS_IV; ++ state->buffered = 0; ++ } ++ while (iof_ensure(O, 16)) ++ { ++ while (state->buffered < 16) ++ { ++ if ((c = iof_get(I)) != IOFEOF) ++ state->data[state->buffered++] = (uint8_t)c; ++ else ++ return state->flush ? IOFERR : IOFEMPTY; ++ } ++ aes_decode_output(state, O->pos); ++ if (state->flush) ++ { /* we have to check for EOF here, to remove eventual padding */ ++ if ((c = iof_get(I)) < 0) ++ { /* end of input at 16-bytes boundary; remove padding and quit */ ++ ret = aes_remove_padding(state, O->pos - 16, &lastlength); ++ O->pos -= 16 - lastlength; ++ return ret; ++ } ++ else ++ { /* beginning of the next block */ ++ state->buffered = 1; ++ state->data[0] = (uint8_t)c; ++ } ++ } ++ else ++ state->buffered = 0; ++ } ++ return IOFFULL; ++} ++ ++/* variants that works on c-strings; can work inplace (output==input) except encoder in pdf flavour */ ++ ++/* ++Codecs operating on c-string can generally work inplace (output==input), except encoder with AES_INLINE_IV flag set, ++which outputs 16 bytes of initialization vector at the beginning of encrypted data. All return the size of encrypted/decrypted ++data. Encoders output is the original length padded to a complete 16 bytes (plus eventual 16 bytes of initialization ++vector, if AES_INLINE_IV is used). Default padding is unambiguously removed during decryption. AES_NULL_PADDING flag ++forces using (ambiguous) NULL-byte padding, only if input length module 16 is greater then zero. ++ ++An input data is supposed to be a complete data to be encrypted or decrypted. It is possible, however, to use those ++codecs for scaterred data chunks by manipulating AES_INLINE_IV, AES_NULL_PADDING, AES_CONTINUE flags and data length. ++Caller may assume that c-string codecs do not modify state flags. ++ ++Encoder could actually be optimized by writing an initialization vector to a state block once. After every chunk encryption, ++the output is the initialization vector for the next chunk. Since we use c-string codec variants on short strings, ++the gain is neglectable in comparison with the weight of the aes crypt procedure. ++*/ ++ ++size_t aes_encode_data (const void *input, size_t length, void *output, const void *key, size_t keylength, const void *iv, int flags) ++{ ++ aes_state state; ++ aes_keyblock keyblock; ++ ++ if (aes_encode_initialize(&state, &keyblock, key, keylength, iv) == NULL) ++ return 0; ++ state.flags |= flags; ++ return aes_encode_state_data(&state, input, length, output); ++ // aes_state_close(&state); ++} ++ ++size_t aes_encode_state_data (aes_state *state, const void *input, size_t length, void *output) ++{ ++ const uint8_t *inp; ++ uint8_t *out, tail, t; ++ size_t size; ++ ++ inp = (const uint8_t *)input; ++ out = (uint8_t *)output; ++ ++ if (!(state->flags & AES_HAS_IV)) ++ return 0; ++ if ((state->flags & AES_INLINE_IV) && !(state->flags & AES_CONTINUE)) ++ { ++ aes_copy_block(out, state->iv); ++ out += 16; ++ } ++ // state->flags |= AES_CONTINUE; // do not modify state flags ++ ++ for (size = 0; size + 16 <= length; size += 16) ++ { ++ aes_copy_xor(state->data, inp, state->iv); ++ aes_encode_output(state, out); ++ inp += 16; ++ } ++ ++ if ((tail = (length % 16)) > 0 || aes_padding(state)) ++ { ++ for (t = 0; t < tail; ++t) ++ state->data[t] = inp[t] ^ state->iv[t]; ++ aes_put_padding(state, tail); ++ aes_encode_output(state, out); ++ size += 16; ++ } ++ if (state->flags & AES_INLINE_IV) ++ size += 16; /* iv written at the beginning of encoded data */ ++ ++ return size; ++} ++ ++size_t aes_decode_data (const void *input, size_t length, void *output, const void *key, size_t keylength, const void *iv, int flags) ++{ ++ aes_state state; ++ aes_keyblock keyblock; ++ ++ if (aes_decode_initialize(&state, &keyblock, key, keylength, iv) == NULL) ++ return 0; ++ state.flags |= flags; ++ return aes_decode_state_data(&state, input, length, output); ++ // aes_state_close(&state); ++} ++ ++size_t aes_decode_state_data (aes_state *state, const void *input, size_t length, void *output) ++{ ++ const uint8_t *inp; ++ uint8_t *out, lastlength; ++ size_t size; ++ ++ inp = (const uint8_t *)input; ++ out = (uint8_t *)output; ++ ++ if ((state->flags & AES_INLINE_IV) && !(state->flags & AES_CONTINUE)) ++ { ++ aes_copy_block(state->iv, inp); ++ // state->flags |= AES_HAS_IV; // do not modify state flags ++ inp += 16; ++ length = length >= 16 ? length - 16 : 0; ++ } ++ else if (!(state->flags & AES_HAS_IV)) ++ return 0; ++ // state->flags |= AES_CONTINUE; // do not modify state flags ++ for (size = 0; size + 16 <= length; size += 16) ++ { ++ aes_copy_block(state->data, inp); ++ aes_decode_output(state, out); ++ inp += 16; ++ } ++ ++ if (size >= 16) ++ { ++ aes_remove_padding(state, out - 16, &lastlength); ++ size = size - 16 + lastlength; ++ } ++ ++ return size; ++} ++ ++/* ++pseudo-random bytes chain exceprted from eexec; not expected to have strong cryptographic properties ++we only expect that it is (reasonably) unique and different for each call (not only function call, but also ++a program call). A current trick with mangling pointer value gives satisfactory results, generally different ++for every function call and a programm call. Note that the pseudo-input bytes starts from some inner address ++bits, as they vary better; without that, the first byte tends to be "lazy". ++*/ ++ ++void random_bytes (uint8_t *output, size_t size) ++{ ++ size_t i; ++ uint8_t p; ++ static uint16_t k = 55665; ++ for (i = 0; i < size; ++i) ++ { ++ p = ((uint8_t *)(&output))[(i + 2) % sizeof(uint8_t *)] ^ (uint8_t)size; // pseudo input byte ;) ++ k = (((p + k) * 52845 + 22719) & 65535); // xor-ed with pseudo-random sequence (kept between calls) ++ output[i] = p ^ (k >> 8); ++ } ++} ++ ++void aes_generate_iv (uint8_t output[16]) ++{ ++ random_bytes(output, 16); ++} ++ ++/* filters */ ++ ++// rc4 decoder function ++ ++static size_t rc4_decoder (iof *F, iof_mode mode) ++{ ++ rc4_state *state; ++ iof_status status; ++ size_t tail; ++ ++ state = iof_filter_state(rc4_state *, F); ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ do { ++ status = rc4_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "rc4", status); ++ case IOFCLOSE: ++ rc4_state_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// rc4 encoder function ++ ++static size_t rc4_encoder (iof *F, iof_mode mode) ++{ ++ rc4_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(rc4_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = rc4_encode_state(F, F->next, state); ++ return iof_encoder_retval(F, "rc4", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ rc4_encoder(F, IOFFLUSH); ++ rc4_state_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// aes decoder function ++ ++static size_t aes_decoder (iof *F, iof_mode mode) ++{ ++ aes_state *state; ++ iof_status status; ++ size_t tail; ++ ++ state = iof_filter_state(aes_state *, F); ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ do { ++ status = aes_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "aes", status); ++ case IOFCLOSE: ++ aes_state_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// aes encoder function ++ ++static size_t aes_encoder (iof *F, iof_mode mode) ++{ ++ aes_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(aes_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = aes_encode_state(F, F->next, state); ++ return iof_encoder_retval(F, "aes", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ aes_encoder(F, IOFFLUSH); ++ aes_state_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++iof * iof_filter_rc4_decoder (iof *N, const void *key, size_t keylength) ++{ ++ iof *I; ++ rc4_state *state; ++ ++ I = iof_filter_reader(rc4_decoder, sizeof(rc4_state), &state); ++ iof_setup_next(I, N); ++ if (rc4_state_init(state, key, keylength) == NULL) ++ { ++ iof_discard(I); ++ return NULL; ++ } ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_rc4_encoder (iof *N, const void *key, size_t keylength) ++{ ++ iof *O; ++ rc4_state *state; ++ ++ O = iof_filter_writer(rc4_encoder, sizeof(rc4_state), &state); ++ iof_setup_next(O, N); ++ if (rc4_state_init(state, key, keylength) == NULL) ++ { ++ iof_discard(O); ++ return NULL; ++ } ++ // state->flush = 1; ++ return O; ++} ++ ++/* aes crypt filters */ ++ ++iof * iof_filter_aes_decoder (iof *N, const void *key, size_t keylength) ++{ ++ iof *I; ++ aes_state *state; ++ ++ I = iof_filter_reader(aes_decoder, sizeof(aes_state), &state); ++ iof_setup_next(I, N); ++ if (aes_decode_init(state, key, keylength) == NULL) ++ { ++ iof_discard(I); ++ return NULL; ++ } ++ aes_pdf_mode(state); ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_aes_encoder (iof *N, const void *key, size_t keylength) ++{ ++ iof *O; ++ aes_state *state; ++ ++ O = iof_filter_writer(aes_encoder, sizeof(aes_state), &state); ++ iof_setup_next(O, N); ++ if (aes_encode_init(state, key, keylength) == NULL) ++ { ++ iof_discard(O); ++ return NULL; ++ } ++ aes_pdf_mode(state); ++ // state->flush = 1; ++ return O; ++} ++ ++/* test */ ++ ++/* ++static void show (void *p, size_t size, uint8_t round, uint8_t sym) ++{ ++ uint8_t i; ++ printf("%c%c:", round, sym); ++ for (i = 0; i < size; ++i) ++ printf("%02x", ((uint8_t *)p)[i]); ++ printf("\n"); ++} ++ ++void aes_test (void) ++{ ++ const uint8_t key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; ++ const uint8_t iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; ++ const uint8_t inp[] = { ++ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, ++ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, ++ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, ++ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; ++ const uint8_t out[] = { ++ 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, ++ 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, ++ 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, ++ 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 }; ++ ++ uint8_t input[64], output[64]; ++ size_t inpsize, outsize; ++ int flags = AES_NULL_PADDING; ++ ++ //////////////////////////////////////////////////////////////////////////// ++ ++//#define ENCODETO output ++#define ENCODETO input // inplace ++ ++ inpsize = 64; ++ memcpy(input, inp, inpsize); ++ show(input, inpsize, '>', '>'); ++ outsize = aes_encode_data(input, inpsize, ENCODETO, key, 16, iv, flags); ++ show(ENCODETO, outsize, '<', '<'); ++ if (outsize == inpsize && memcmp(ENCODETO, out, outsize) == 0) ++ printf("ENCODER SUCCESS\n"); ++ else ++ printf("ENCODER FAILURE\n"); ++ ++ //////////////////////////////////////////////////////////////////////////// ++ ++//#define DECODETO input ++#define DECODETO output // in place ++ ++ outsize = 64; ++ memcpy(output, out, outsize); ++ show(output, outsize, '<', '<'); ++ inpsize = aes_decode_data(output, outsize, DECODETO, key, 16, iv, flags); ++ show(DECODETO, inpsize, '>', '>'); ++ if (inpsize == outsize && memcmp(DECODETO, inp, inpsize) == 0) ++ printf("DECODER SUCCESS\n"); ++ else ++ printf("DECODER FAILURE\n"); ++} ++*/ ++ ++/* ++Some example vectors ++ ++================================ AES ECB 128-bit encryption mode ================================ ++ ++Encryption key: 2b7e151628aed2a6abf7158809cf4f3c ++ ++Test vector Cipher text ++6bc1bee22e409f96e93d7e117393172a 3ad77bb40d7a3660a89ecaf32466ef97 ++ae2d8a571e03ac9c9eb76fac45af8e51 f5d3d58503b9699de785895a96fdbaaf ++30c81c46a35ce411e5fbc1191a0a52ef 43b1cd7f598ece23881b00e3ed030688 ++f69f2445df4f9b17ad2b417be66c3710 7b0c785e27e8ad3f8223207104725dd4 ++ ++ ++================================ AES ECB 192-bit encryption mode ================================ ++ ++Encryption key: 8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b ++ ++Test vector Cipher text ++6bc1bee22e409f96e93d7e117393172a bd334f1d6e45f25ff712a214571fa5cc ++ae2d8a571e03ac9c9eb76fac45af8e51 974104846d0ad3ad7734ecb3ecee4eef ++30c81c46a35ce411e5fbc1191a0a52ef ef7afd2270e2e60adce0ba2face6444e ++f69f2445df4f9b17ad2b417be66c3710 9a4b41ba738d6c72fb16691603c18e0e ++ ++ ++================================ AES ECB 256-bit encryption mode ================================ ++ ++Encryption key: 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 ++ ++Test vector Cipher text ++6bc1bee22e409f96e93d7e117393172a f3eed1bdb5d2a03c064b5a7e3db181f8 ++ae2d8a571e03ac9c9eb76fac45af8e51 591ccb10d410ed26dc5ba74a31362870 ++30c81c46a35ce411e5fbc1191a0a52ef b6ed21b99ca6f4f9f153e7b1beafed1d ++f69f2445df4f9b17ad2b417be66c3710 23304b7a39f9f3ff067d8d8f9e24ecc7 ++ ++================================ AES CBC 128-bit encryption mode ================================ ++ ++Encryption key: 2b7e151628aed2a6abf7158809cf4f3c ++ ++Initialization vector Test vector Cipher text ++000102030405060708090A0B0C0D0E0F 6bc1bee22e409f96e93d7e117393172a 7649abac8119b246cee98e9b12e9197d ++7649ABAC8119B246CEE98E9B12E9197D ae2d8a571e03ac9c9eb76fac45af8e51 5086cb9b507219ee95db113a917678b2 ++5086CB9B507219EE95DB113A917678B2 30c81c46a35ce411e5fbc1191a0a52ef 73bed6b8e3c1743b7116e69e22229516 ++73BED6B8E3C1743B7116E69E22229516 f69f2445df4f9b17ad2b417be66c3710 3ff1caa1681fac09120eca307586e1a7 ++ ++================================ AES CBC 192-bit encryption mode ================================ ++ ++Encryption key: 8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b ++ ++Initialization vector Test vector Cipher text ++000102030405060708090A0B0C0D0E0F 6bc1bee22e409f96e93d7e117393172a 4f021db243bc633d7178183a9fa071e8 ++4F021DB243BC633D7178183A9FA071E8 ae2d8a571e03ac9c9eb76fac45af8e51 b4d9ada9ad7dedf4e5e738763f69145a ++B4D9ADA9AD7DEDF4E5E738763F69145A 30c81c46a35ce411e5fbc1191a0a52ef 571b242012fb7ae07fa9baac3df102e0 ++571B242012FB7AE07FA9BAAC3DF102E0 f69f2445df4f9b17ad2b417be66c3710 08b0e27988598881d920a9e64f5615cd ++ ++================================ AES CBC 256-bit encryption mode ================================ ++ ++Encryption key: 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 ++ ++Initialization vector Test vector Cipher text ++000102030405060708090A0B0C0D0E0F 6bc1bee22e409f96e93d7e117393172a f58c4c04d6e5f1ba779eabfb5f7bfbd6 ++F58C4C04D6E5F1BA779EABFB5F7BFBD6 ae2d8a571e03ac9c9eb76fac45af8e51 9cfc4e967edb808d679f777bc6702c7d ++9CFC4E967EDB808D679F777BC6702C7D 30c81c46a35ce411e5fbc1191a0a52ef 39f23369a9d9bacfa530e26304231461 ++39F23369A9D9BACFA530E26304231461 f69f2445df4f9b17ad2b417be66c3710 b2eb05e2c39be9fcda6c19078c6a9d1b ++*/ +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilcrypt.h b/texk/web2c/luatexdir/luapplib/util/utilcrypt.h +new file mode 100644 +index 000000000..e5bf53cc5 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilcrypt.h +@@ -0,0 +1,90 @@ ++#ifndef UTIL_CRYPT_H ++#define UTIL_CRYPT_H ++ ++#include ++#include ++#include "utiliof.h" ++ ++#ifndef UTIL_CRYPT_TIME ++# define UTIL_CRYPT_TIME 0 ++#endif ++ ++/* RC4 */ ++ ++typedef uint8_t rc4_map[256]; ++ ++typedef struct rc4_state rc4_state; ++ ++#define RC4_STATE_ALLOC (1<<0) ++ ++UTILAPI rc4_state * rc4_state_initialize (rc4_state *state, rc4_map *map, const void *vkey, size_t keylength); ++#define rc4_state_init(state, vkey, keylength) rc4_state_initialize(state, NULL, vkey, keylength) ++UTILAPI void rc4_map_save (rc4_state *state, rc4_map *map); ++UTILAPI void rc4_map_restore (rc4_state *state, rc4_map *map); ++ ++/* Codecs operating on iof */ ++ ++UTILAPI iof_status rc4_crypt_state (iof *I, iof *O, rc4_state *state); ++#define rc4_encode_state(I, O, state) rc4_crypt_state(I, O, state) ++#define rc4_decode_state(I, O, state) rc4_crypt_state(I, O, state) ++ ++UTILAPI iof_status rc4_crypt (iof *I, iof *O, const void *key, size_t length); ++#define rc4_encode(I, O) rc4_crypt(I, O, key, length) ++#define rc4_decode(I, O) rc4_crypt(I, O, key, length) ++ ++UTILAPI size_t rc4_crypt_data (const void *input, size_t length, void *output, const void *key, size_t keylength); ++UTILAPI size_t rc4_crypt_state_data (rc4_state *state, const void *input, size_t length, void *output); ++#define rc4_encode_data(input, length, output, key, keylength) rc4_crypt_data(input, length, output, key, keylength) ++#define rc4_decode_data(input, length, output, key, keylength) rc4_crypt_data(input, length, output, key, keylength) ++#define rc4_encode_state_data(state, input, length, output) rc4_crypt_state_data(state, input, length, output) ++#define rc4_decode_state_data(state, input, length, output) rc4_crypt_state_data(state, input, length, output) ++ ++UTILAPI void rc4_state_close (rc4_state *state); ++ ++/* AES */ ++ ++typedef uint8_t aes_block[4][4]; ++typedef aes_block aes_keyblock[15]; // aes128 - 10+1, aes192 - 12+1, aes256 - 14+1 ++ ++typedef struct aes_state aes_state; ++ ++#define AES_STATE_ALLOC (1<<0) ++//#define AES_ECB_MODE (1<<2) ++#define AES_HAS_IV (1<<3) ++#define AES_INLINE_IV (1<<4) ++#define AES_CONTINUE (1<<5) ++#define AES_NULL_PADDING (1<<6) ++ ++UTILAPI void aes_pdf_mode (aes_state *state); ++//UTILAPI aes_state * aes_state_initialize_ecb (aes_state *State, uint8_t *roundkey, const uint8_t *key); ++UTILAPI aes_state * aes_encode_initialize (aes_state *state, aes_keyblock *keyblock, const void *key, size_t keylength, const void *iv); ++UTILAPI aes_state * aes_decode_initialize (aes_state *state, aes_keyblock *keyblock, const void *key, size_t keylength, const void *iv); ++#define aes_encode_init(state, key, keylength) aes_encode_initialize(state, NULL, key, keylength, NULL) ++#define aes_decode_init(state, key, keylength) aes_decode_initialize(state, NULL, key, keylength, NULL) ++ ++UTILAPI void aes_state_close (aes_state *state); ++ ++/* Codecs operating on iof */ ++ ++UTILAPI iof_status aes_encode_state (iof *I, iof *O, aes_state *state); ++UTILAPI iof_status aes_decode_state (iof *I, iof *O, aes_state *state); ++ ++UTILAPI size_t aes_encode_data (const void *input, size_t length, void *output, const void *key, size_t keylength, const void *iv, int flags); ++UTILAPI size_t aes_encode_state_data (aes_state *state, const void *input, size_t length, void *output); ++UTILAPI size_t aes_decode_data (const void *input, size_t length, void *output, const void *key, size_t keylength, const void *iv, int flags); ++UTILAPI size_t aes_decode_state_data (aes_state *state, const void *input, size_t length, void *output); ++ ++/* random bytes generator */ ++ ++UTILAPI void random_bytes (uint8_t *output, size_t size); ++UTILAPI void aes_generate_iv (uint8_t output[16]); ++ ++/* filters */ ++ ++iof * iof_filter_rc4_decoder (iof *N, const void *key, size_t keylength); ++iof * iof_filter_rc4_encoder (iof *N, const void *key, size_t keylength); ++ ++iof * iof_filter_aes_decoder (iof *N, const void *key, size_t keylength); ++iof * iof_filter_aes_encoder (iof *N, const void *key, size_t keylength); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilcryptdef.h b/texk/web2c/luatexdir/luapplib/util/utilcryptdef.h +new file mode 100644 +index 000000000..02d70aa5e +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilcryptdef.h +@@ -0,0 +1,30 @@ ++ ++#ifndef UTIL_CRYPTDEF_H ++#define UTIL_CRYPTDEF_H ++ ++struct rc4_state { ++ union { ++ rc4_map *map; ++ uint8_t *smap; ++ }; ++ int i, j; ++ int flush; ++ int flags; ++}; ++ ++struct aes_state { ++ size_t keylength; ++ int rounds; ++ //int keywords; ++ union { ++ aes_block block; ++ uint8_t data[16]; ++ }; ++ aes_keyblock *keyblock; ++ uint8_t iv[16]; ++ uint8_t buffered; ++ int flush; ++ int flags; ++}; ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utildecl.h b/texk/web2c/luatexdir/luapplib/util/utildecl.h +new file mode 100644 +index 000000000..94ef73413 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utildecl.h +@@ -0,0 +1,28 @@ ++ ++#ifndef UTIL_DECL_H ++#define UTIL_DECL_H ++ ++/* ++UTILDLL - when building .dll ++UTILEXE - when building .exe to import symbols from .dll ++*/ ++ ++#if defined (_WIN32) || defined(_WIN64) ++# ifdef UTILDLL ++# define UTILAPI __declspec(dllexport) ++# define UTILDEF __declspec(dllexport) ++# else ++# ifdef UTILEXE ++# define UTILAPI __declspec(dllimport) ++# define UTILDEF ++# else ++# define UTILAPI ++# define UTILDEF ++# endif ++# endif ++#else ++# define UTILAPI ++# define UTILDEF ++#endif ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilflate.c b/texk/web2c/luatexdir/luapplib/util/utilflate.c +new file mode 100644 +index 000000000..8ecc240c9 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilflate.c +@@ -0,0 +1,319 @@ ++ ++#include "utilmem.h" ++#include "utillog.h" ++#include "utilflate.h" ++#include ++ ++/* flate codec */ ++ ++/* ++Flate codec example provided at http://www.zlib.net/zpipe.c (http://www.zlib.net/zlib_how.html) uses the following scheme: ++- provide input data buffer ++- keep providing output until codec function uses it ++ ++For encoder: ++ ++ z->zalloc = z->zfree = z->zopaque = NULL; ++ deflateInit(z, compression_level); ++ do { ++ z->next_in = ++ z->avail_in = ++ do { ++ z->next_out = ++ z->avail_out = ++ deflate(z, flush); ++ // write obtained output from deflate ++ } while (z->avail_out == 0); ++ assert(z->avail_in == 0); ++ } while (flush != Z_FINISH); ++ deflateEnd(z); ++ ++'z' is an internal codec state of type z_stream, 'flush' is either Z_NO_FLUSH or Z_FINISH at the end of data. ++deflate() ensures to consume the entire input if there are no obstackles to write an output. The inner loop ++provides an output space as long as it is used by deflate(). When deflate() wrote everything it could, ++it leaves z->avail_out > 0, which breaks the inner loop. At this point z->avail_in should also be zero. ++The example documentation claims that the return codes from deflate() doesn't really need to be checked, ++as checking z->avail_out for zero is enough. ++ ++The scheme for decoder is pretty similar, but with substantial differences: ++- the end of stream is automatically found by decoder, so using Z_FINISH flag to indicate an end of stream ++ is not necessary, but if provided, it MUST be given only if the EOF marker actually occurs in the input chunk, ++ and subsequent calls to inflate() must consequently use Z_FINISH ++- calling inflate() as long as it uses the output buffer provided still works for decoder, but inflate() ++ does not ensure to consume the entire input, as it will read until end of stream marker ++- the return code from inflate() must be checked to ensure the proper reaction on invalid data stream and ++ end of stream signals ++- initialization must set an input buffer to NULL or to some existing chunk (the later helps zlib to perform ++ better on inflate(), but inflate() does the research on the first call anyway) ++ ++ z->zalloc = z->zfree = z->zopaque = NULL; ++ z->next_in = NULL, z->avail_in = 0; ++ inflateInit(z); ++ do { ++ z->next_in = ++ z->avail_in = ++ do { ++ z->next_out = ++ z->avail_out = ++ status = inflate(z, flush); ++ // check return status ++ // write obtained output from inflate ++ } while (z->avail_out == 0); ++ } while (status != Z_STREAM_END); ++ inflateEnd(z); ++ ++Our wrapper generally follows "prepare input, keep pomping output" scheme, but we need to support handler function ++breaks on IOFEMPTY and IOFFULL. For a consistent come back from those on subsequent calls to the handler function, ++we use 3 states: ++- FLATE_IN - get input, when got something then goto FALTE_OUT ++- FLATE_OUT - set z_stream buffers and keep writing output until enything to write, then goto FLATE_IN or FLATE_DONE ++- FLATE_DONE - we are done, no return from that state ++Distinction of FLATE_IN and FLATE_OUT states guarantees that we will not get more input until zlib consumes the stuff ++from the previous feed, possibly interrupted by IOFFULL return on filling the output buffer. This distinction is not ++critical, but makes the filter running according to the scheme described above. Note that we set zlib input buffer ++(z->next_in, z->avail_in) at the beginning of FLATE_OUT state. Also note that we always update our buffers according ++to updated avail_in / avail_out values, just after a call to inflate() / deflate(). So no matter what have happens ++between handler calls, zlib input buffer is in sync with ours. ++*/ ++ ++struct flate_state { ++ z_stream z; ++ int flush; ++ int status; ++ int level; /* encoder compression level -1..9 */ ++}; ++ ++enum { ++ FLATE_IN, ++ FLATE_OUT, ++ FLATE_DONE ++}; ++ ++flate_state * flate_decoder_init (flate_state *state) ++{ /* initialize zlib */ ++ z_stream *z = &state->z; ++ z->zalloc = Z_NULL; ++ z->zfree = Z_NULL; ++ z->opaque = Z_NULL; ++ z->avail_in = 0; /* must be initialized before inflateInit() */ ++ z->next_in = Z_NULL; /* ditto */ ++ if (inflateInit(z) != Z_OK) ++ return NULL; ++ state->status = FLATE_IN; ++ return state; ++} ++ ++flate_state * flate_encoder_init (flate_state *state) ++{ ++ z_stream *z = &state->z; ++ z->zalloc = Z_NULL; ++ z->zfree = Z_NULL; ++ z->opaque = Z_NULL; ++ z->avail_in = 0; ++ z->next_in = Z_NULL; ++ state->level = Z_DEFAULT_COMPRESSION; // will probably be moved upward ++ if (deflateInit(z, state->level) != Z_OK) ++ return NULL; ++ state->status = FLATE_IN; ++ return state; ++} ++ ++static const char * zmess (int zstatus) ++{ ++ switch (zstatus) ++ { ++ case Z_OK: return "ok"; ++ case Z_STREAM_END: return "end of stream"; ++ case Z_BUF_ERROR: return "buffer error"; ++ case Z_STREAM_ERROR: return "stream error"; ++ case Z_NEED_DICT: return "need dict"; ++ case Z_DATA_ERROR: return "data error"; ++ case Z_MEM_ERROR: return "memory error"; ++ case Z_VERSION_ERROR: return "version error"; ++ case Z_ERRNO: return "io error"; ++ default: ++ break; ++ } ++ return "unknown error"; ++} ++ ++iof_status flate_decode_state (iof *I, iof *O, flate_state *state) ++{ ++ z_stream *z; ++ int zstatus = Z_OK; ++ z = &state->z; ++ while (state->status != FLATE_DONE) ++ { ++ if (state->status == FLATE_IN) ++ { ++ if (!iof_readable(I)) ++ return state->flush ? IOFERR : IOFEMPTY; ++ state->status = FLATE_OUT; ++ } ++ z->next_in = (Bytef *)I->pos; ++ z->avail_in = (uInt)iof_left(I); ++ do { ++ if (!iof_writable(O)) ++ return IOFFULL; ++ z->next_out = (Bytef *)O->pos; ++ z->avail_out = (uInt)iof_left(O); ++ zstatus = inflate(z, Z_NO_FLUSH); ++ I->pos += iof_left(I) - z->avail_in; ++ O->pos += iof_left(O) - z->avail_out; ++ switch (zstatus) ++ { ++ case Z_OK: ++ case Z_STREAM_END: ++ break; ++ default: ++ loggerf("flate decoder %s (%d)", zmess(zstatus), zstatus); ++ return IOFERR; ++ } ++ } while (z->avail_out == 0); ++ state->status = zstatus == Z_STREAM_END ? FLATE_DONE : FLATE_IN; ++ } ++ return IOFEOF; ++} ++ ++iof_status flate_encode_state (iof *I, iof *O, flate_state *state) ++{ ++ z_stream *z; ++ int zstatus; ++ z = &state->z; ++ while (state->status != FLATE_DONE) ++ { ++ if (state->status == FLATE_IN) ++ { ++ if (!iof_readable(I)) ++ if (!state->flush) ++ return IOFEMPTY; ++ state->status = FLATE_OUT; ++ } ++ z->next_in = (Bytef *)I->pos; ++ z->avail_in = (uInt)iof_left(I); ++ do { ++ if (!iof_writable(O)) ++ return IOFFULL; ++ z->next_out = (Bytef *)O->pos; ++ z->avail_out = (uInt)iof_left(O); ++ zstatus = deflate(z, state->flush ? Z_FINISH : Z_NO_FLUSH); ++ I->pos += iof_left(I) - z->avail_in; ++ O->pos += iof_left(O) - z->avail_out; ++ switch (zstatus) ++ { ++ case Z_OK: ++ case Z_STREAM_END: ++ break; ++ default: ++ loggerf("flate encoder %s (%d)", zmess(zstatus), zstatus); ++ return IOFERR; ++ } ++ } while (z->avail_out == 0); ++ state->status = state->flush ? FLATE_DONE : FLATE_IN; ++ } ++ return IOFEOF; ++} ++ ++ ++void flate_decoder_close (flate_state *state) ++{ ++ inflateEnd(&state->z); ++} ++ ++void flate_encoder_close (flate_state *state) ++{ ++ deflateEnd(&state->z); ++} ++ ++/* filter */ ++ ++// flate decoder function ++ ++static size_t flate_decoder (iof *F, iof_mode mode) ++{ ++ flate_state *state; ++ iof_status status; ++ size_t tail; ++ ++ state = iof_filter_state(flate_state *, F); ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ do { ++ status = flate_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "flate", status); ++ case IOFCLOSE: ++ flate_decoder_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// flate encoder function ++ ++static size_t flate_encoder (iof *F, iof_mode mode) ++{ ++ flate_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(flate_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = flate_encode_state(F, F->next, state); ++ return iof_encoder_retval(F, "flate", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ flate_encoder(F, IOFFLUSH); ++ flate_encoder_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++iof * iof_filter_flate_decoder (iof *N) ++{ ++ iof *I; ++ flate_state *state; ++ I = iof_filter_reader(flate_decoder, sizeof(flate_state), &state); ++ iof_setup_next(I, N); ++ if (flate_decoder_init(state) == NULL) ++ { ++ iof_discard(I); ++ return NULL; ++ } ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_flate_encoder (iof *N) ++{ ++ iof *O; ++ flate_state *state; ++ O = iof_filter_writer(flate_encoder, sizeof(flate_state), &state); ++ iof_setup_next(O, N); ++ if (flate_encoder_init(state) == NULL) ++ { ++ iof_discard(O); ++ return NULL; ++ } ++ return O; ++} +diff --git a/texk/web2c/luatexdir/luapplib/util/utilflate.h b/texk/web2c/luatexdir/luapplib/util/utilflate.h +new file mode 100644 +index 000000000..09bdd6661 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilflate.h +@@ -0,0 +1,21 @@ ++#ifndef UTIL_FLATE_H ++#define UTIL_FLATE_H ++ ++#include "utiliof.h" ++ ++typedef struct flate_state flate_state; ++ ++flate_state * flate_decoder_init (flate_state *state); ++flate_state * flate_encoder_init (flate_state *state); ++ ++iof_status flate_decode_state (iof *I, iof *O, flate_state *state); ++iof_status flate_encode_state (iof *I, iof *O, flate_state *state); ++ ++void flate_decoder_close (flate_state *state); ++void flate_encoder_close (flate_state *state); ++ ++iof * iof_filter_flate_decoder (iof *N); ++iof * iof_filter_flate_encoder (iof *N); ++ ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilfpred.c b/texk/web2c/luatexdir/luapplib/util/utilfpred.c +new file mode 100644 +index 000000000..ce9c129e2 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilfpred.c +@@ -0,0 +1,783 @@ ++/* predictor filters; common for flate and lzw */ ++ ++#include "utilmem.h" ++#include "utillog.h" ++#include "utilfpred.h" ++ ++/* ++Here we implement predictor filters used with flate and lzw compressions in PDF streams. The main idea of data prediction ++is to compute and output the differences between data records instead of this records. Adjacent pixels in images are usually ++similar, so differences between pixel values tends to be zero. And both Flate and LZW performs better when the input ++is rather smooth. Although a preliminary use of predictors is related to bitmap data, The actual need for predictor filter ++came from the fact that xref streams may also be predicted (usually with PNG up-predictor). ++ ++PDF specification allows to use several predictor algorithms, specified by /Predictor key in /DecodeParms dictionary: ++ ++ 1 - no predictor (default) ++ 2 - TIFF horizontal predictor ++ 10 - PNG none predictor ++ 11 - PNG sub predictor ++ 12 - PNG up predictor ++ 13 - PNG average predictor ++ 14 - PNG paeth predictor ++ ++All PNG predictors works on bytes, regardless the image color-depth. While encoding, every input data byte is decreased ++by the appropriate byte of the previous pixel. Even if the pixel does not fit a full byte, PNG predictors use an artificial ++pixel size rounded up to a full byte. PNG predictors utilizes previous (left) pixel, pixel above and previous to above ++pixel. In case of PNG, the type of the predictor is written on a dedicated byte on the beginning of every scanline. It ++means all predictor functions must maintain and information about left, above and left-above pixels. ++ ++Despite the same differencing idea, TIFF predictors are different. The prediction process bases on pixel components, ++which are not necessarily bytes (component of a pixel is added/substracted from a relevant component of a previous ++pixel). In TIFF predictor 2, only the previous (the left) pixel is taken into account, there is no need to keep ++an information about other surrounding pixels. Also there is no expicit algorithm marker in data; the same prediction ++method is applied to all input rows. ++ ++Not surprisingly, predictor encoders and decoders are pretty similar. Encoders take some input value and the previous ++input value (or 0 at the beginning of the scanline) and output a difference between them. Decoders takes an input value, ++previously decoded value (or zero) and outputs their sum. When encoding, the result is cast to the proper unsigned integer, ++when decoding, modulo 256 (or appropriate) is used, which makes encoding and decoding looseless. ++ ++Some extra bits trickery is involved in TIFF predictor function, when components doesn't fit bytes boundary. In that case, ++an input is treated as an bits stream. Every input byte is "buffered" in a larger integer, as its lower bits (from right). ++Every output value is taken from its higher (left) bits. In a special case of bits-per-component equal 1, we buffer all ++pixel bits and use XOR to compute bits difference between pixels. I've excerpted that trick from poppler, but I'm not ++really sure if it works any better, especially when the number of components per pixel is 1. In that case we do a hard ++bit-by-bit work anyway. ++ ++Predictor codecs state keeps a notion of surrounding pixels. PNG predictors uses left, up and upleft ++pixel data, while TIFF predictor (2) only needs the previous (left) pixel. Important to note that PNG ++predictors always work on bytes, no matter of color-depth (bits per component), while TIFF predictor ++works on pixel components, which not necessarily fits into a complete byte. However, for PNG predictor ++the size of a pixel still matters, because 'left' and 'upleft' refers to a corresponding pixel byte, ++not necessarily previous byte. ++ ++In PNG prediction, we record every pixel byte (in decoded form) in state->rowsave. At the end of a scanline ++we copy state->rowsave to state->rowup, so that in the next scanline we can access up-pixel byte. ++Left pixel byte is accessed as state->rowsave (the byte recently stored or virtual left edge byte \0). ++Up-left pixel byte is accessed via state->rowup, but with state->pixelsize offset (same as left byte, possibly \0 ++at the left edge of the row). Both state->rowup and state->rowsave has a safe span of pixelsize bytes on the left, ++that are permanently \0. ++*/ ++ ++#define predictor_component_t unsigned short ++#define predictor_pixel1b_t unsigned int ++ ++typedef struct predictor_state { ++ int default_predictor; /* default predictor indicator */ ++ int current_predictor; /* current predictor, possibly taken from algorithm marker in PNG data */ ++ int rowsamples; /* number of pixels in a scanline (/DecodeParms << /Columns ... >>) */ ++ int compbits; /* number of bits per component (/DecodeParms << /BitsPerComponent ... >>) */ ++ int components; /* number of components (/DecodeParms << /Colors ... >>) */ ++ uint8_t *buffer; /* temporary private buffer area */ ++ uint8_t *rowin; /* an input row buffer position */ ++ int rowsize; /* size of a current scanline in bytes (rounded up) */ ++ int rowend; /* an input buffer end position */ ++ int rowindex; /* an output buffer position */ ++ union { ++ struct { /* used by PNG predictor codecs */ ++ uint8_t *rowup, *rowsave; /* previous scanline buffers */ ++ int predictorbyte; /* flag indicating that algorithm byte is read/written */ ++ int pixelsize; /* number of bytes per pixel (rounded up) */ ++ }; ++ struct { /* used by TIFF predictor codecs */ ++ union { ++ predictor_component_t *prevcomp; /* an array of left pixel components */ ++ predictor_pixel1b_t *prevpixel; /* left pixel value stored on a single integer (for 1bit color-depth) */ ++ }; ++ int compin, compout; /* bit stream buffers */ ++ int bitsin, bitsout; /* bit stream counters */ ++ int sampleindex; /* pixel counter */ ++ int compindex; /* component counter */ ++ int pixbufsize; /* size of pixel buffer in bytes */ ++ }; ++ }; ++ int flush; ++ int status; ++} predictor_state; ++ ++enum { ++ STATUS_LAST = 0, ++ STATUS_CONTINUE = 1 // any value different then IOFEOF, IOFERR, ... ++}; ++ ++predictor_state * predictor_decoder_init (predictor_state *state, int predictor, int rowsamples, int components, int compbits) ++{ ++ int rowsize, pixelsize; ++#define storage_pos(b, p, size) ((b = p), (p += size)) ++ uint8_t *buffer, *p; ++ size_t buffersize; ++ ++ pixelsize = (components * compbits + 7) >> 3; // to bytes, rounded up ++ rowsize = (rowsamples * components * compbits + 7) >> 3; ++ ++ state->default_predictor = state->current_predictor = predictor; ++ state->rowsamples = rowsamples; ++ state->components = components; ++ state->compbits = compbits; ++ ++ if (predictor == 2) ++ { /* tiff predictor */ ++ size_t compbuf, pixbuf; ++ compbuf = state->components * sizeof(predictor_component_t); ++ pixbuf = 1 * sizeof(predictor_pixel1b_t); ++ state->pixbufsize = (int)(compbuf > pixbuf ? compbuf : pixbuf); ++ buffersize = rowsize + state->pixbufsize; ++ buffer = (uint8_t *)util_calloc(buffersize, 1); ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ { /*memory leak */ ++ predictor_component_t *c; ++ if (state->pixbufsize%(sizeof(predictor_component_t))) { ++ c = malloc(state->pixbufsize - state->pixbufsize%(sizeof(predictor_component_t)) + sizeof(predictor_component_t) ); ++ } else { ++ c = malloc(state->pixbufsize); ++ } ++ memcpy(c,(state->rowin + rowsize),state->pixbufsize); ++ if (state->prevcomp){ ++ free(state->prevcomp); ++ } ++ state->prevcomp = c; ++ } ++#else ++ state->prevcomp = (predictor_component_t *)(state->rowin + rowsize); ++#endif ++ state->sampleindex = state->compindex = 0; ++ state->bitsin = state->bitsout = 0; ++ state->compin = state->compout = 0; ++ } ++ else ++ { /* png predictors */ ++ buffersize = (3 * rowsize + 2 * pixelsize + 1) * sizeof(uint8_t); ++ p = buffer = (uint8_t *)util_calloc(buffersize, 1); ++ storage_pos(state->rowin, p, 1 + rowsize); // one extra byte for prediction algorithm tag ++ p += pixelsize; // pixelsize extra bytes for virtual left pixel at the edge, eg. rowup[-1] (permanently \0) ++ storage_pos(state->rowup, p, rowsize); // actual row byte ++ p += pixelsize; // ditto ++ storage_pos(state->rowsave, p, rowsize); ++ state->pixelsize = pixelsize; ++ state->predictorbyte = 0; ++ } ++ state->buffer = buffer; ++ state->rowsize = rowsize; ++ state->rowindex = 0; ++ state->rowend = 0; ++ state->status = STATUS_CONTINUE; ++ return state; ++} ++ ++predictor_state * predictor_encoder_init (predictor_state *state, int predictor, int rowsamples, int components, int compbits) ++{ ++ return predictor_decoder_init(state, predictor, rowsamples, components, compbits); ++} ++ ++void predictor_decoder_close (predictor_state *state) ++{ ++ util_free(state->buffer); ++} ++ ++void predictor_encoder_close (predictor_state *state) ++{ ++ util_free(state->buffer); ++} ++ ++/* ++Predictor type identifiers (pdf spec 76). lpdf doesn't hire the codec if predictor is 1. Predictor 15 indicates ++that the type of PNG prediction algorithm may change in subsequent lines. We always check algorithm marker anyway. ++*/ ++ ++enum predictor_code { ++ NONE_PREDICTOR = 1, ++ TIFF_PREDICTOR = 2, ++ PNG_NONE_PREDICTOR = 10, ++ PNG_SUB_PREDICTOR = 11, ++ PNG_UP_PREDICTOR = 12, ++ PNG_AVERAGE_PREDICTOR = 13, ++ PNG_PAETH_PREDICTOR = 14, ++ PNG_OPTIMUM_PREDICTOR = 15 ++}; ++ ++/* ++All predoctor codecs first read the entire data row into a buffer. This is not crucial for the process, ++but allows to separate read/write states. In particular, there is one place in which codec functions ++may return on EOD. ++*/ ++ ++#define start_row(state) (state->rowindex = 0, state->rowin = state->buffer) ++ ++static int read_scanline (predictor_state *state, iof *I, int size) ++{ ++ int rowtail, left; ++ while ((rowtail = size - state->rowend) > 0) ++ { ++ left = (int)iof_left(I); ++ if (left >= rowtail) ++ { ++ memcpy(state->buffer + state->rowend, I->pos, (size_t)rowtail); ++ state->rowend += rowtail; ++ I->pos += rowtail; ++ start_row(state); ++ break; ++ } ++ else ++ { ++ if ((rowtail = left) > 0) ++ { ++ memcpy(state->buffer + state->rowend, I->pos, (size_t)rowtail); ++ state->rowend += rowtail; ++ I->pos += rowtail; ++ } ++ if (iof_input(I) == 0) ++ { ++ if (state->rowend == 0) // no scanline to process, no more input ++ return state->flush ? IOFEOF : IOFEMPTY; ++ /* If we are here, there is an incomplete scanline in buffer: ++ - if there is a chance for more (state->flush == 0), than wait for more ++ - otherwise encode/decode the last incomplete line? ++ pdf spec p. 76 says that "A row occupies a whole number of bytes", ++ so this situation should be considered abnormal (not found so far). ++ */ ++ if (!state->flush) ++ return IOFEMPTY; ++ loggerf("incomplete scanline in predictor filter"); ++ //return IOFERR; ++ state->status = STATUS_LAST; ++ state->rowsize -= size - state->rowend; ++ start_row(state); ++ break; ++ } ++ } ++ } ++ return STATUS_CONTINUE; ++} ++ ++#define read_row(state, I, size, status) if ((status = read_scanline(state, I, size)) != STATUS_CONTINUE) return status ++ ++#define ensure_output_bytes(O, n) if (!iof_ensure(O, n)) return IOFFULL ++ ++#define tobyte(c) ((unsigned char)(c)) ++#define tocomp(c) ((unsigned short)(c)) ++ ++#define row_byte(state) (state->rowin[state->rowindex]) ++ ++#define up_pixel_byte(state) (state->rowup[state->rowindex]) ++#define upleft_pixel_byte(state) (state->rowup[state->rowindex - state->pixelsize]) ++#define left_pixel_byte(state) (state->rowsave[state->rowindex - state->pixelsize]) ++ ++#define save_pixel_byte(state, c) (state->rowsave[state->rowindex] = c) ++ ++#define left_pixel_component(state) (state->prevcomp[state->compindex]) // tiff predictor with 2, 4, 8, 16 components ++#define left_pixel_value(state) (state->prevpixel[0]) // tiff predictor with 1bit components ++ ++#define save_pixel_component(state, c) ((void)\ ++ ((state->prevcomp[state->compindex] = c), \ ++ (++state->compindex < state->components || (state->compindex = 0)))) ++ ++#define save_pixel_value(state, c) (state->prevpixel[0] = c) ++ ++/* Once the codec function is done with the scanline, we set imaginary left pixel data to zero, and reset row counters to ++zero in order to allow buffering another input scanline. */ ++ ++#define reset_row(state) state->rowend = 0 ++ ++#define reset_png_row(state) (memcpy(state->rowup, state->rowsave, state->rowsize), state->predictorbyte = 0, reset_row(state)) ++ ++#define reset_tiff_row(state) \ ++ memset(state->prevcomp, 0, state->pixbufsize), \ ++ state->bitsin = state->bitsout = 0, \ ++ state->compin = state->compout = 0, \ ++ reset_row(state), \ ++ state->sampleindex = state->compindex = 0 ++ ++/* PNG paeth predictor function; http://www.libpng.org/pub/png/book/chapter09.html ++Compute the base value p := left + up - upleft, then choose that byte the closest ++(of the smallest absolute difference) to the base value. Left byte has a precedence. */ ++ ++ ++static int paeth (predictor_state *state) ++{ ++ int p, p1, p2, p3; ++ p = left_pixel_byte(state) + up_pixel_byte(state) - upleft_pixel_byte(state); ++ p1 = p >= left_pixel_byte(state) ? (p - left_pixel_byte(state)) : (left_pixel_byte(state) - p); ++ p2 = p >= up_pixel_byte(state) ? (p - up_pixel_byte(state)) : (up_pixel_byte(state) - p); ++ p3 = p >= upleft_pixel_byte(state) ? (p - upleft_pixel_byte(state)) : (upleft_pixel_byte(state) - p); ++ return (p1 <= p2 && p1 <= p3) ? left_pixel_byte(state) : (p2 <= p3 ? up_pixel_byte(state) : upleft_pixel_byte(state)); ++} ++ ++/* predictor decoder */ ++ ++iof_status predictor_decode_state (iof *I, iof *O, predictor_state *state) ++{ ++ int status, c, d, outbytes; ++ while (state->status == STATUS_CONTINUE) ++ { ++ if (state->default_predictor >= 10) // PNG predictor? ++ { ++ read_row(state, I, state->rowsize + 1, status); ++ if (state->predictorbyte == 0) ++ { // we could actually check state->rowin <> state->buffer, but we need this flag for encoder anyway ++ state->current_predictor = row_byte(state) + 10; ++ state->predictorbyte = 1; ++ ++state->rowin; ++ } ++ } ++ else ++ { ++ read_row(state, I, state->rowsize, status); ++ } ++ switch (state->current_predictor) ++ { ++ case NONE_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ iof_set(O, c); ++ } ++ reset_row(state); ++ break; ++ case TIFF_PREDICTOR: ++ switch (state->compbits) ++ { ++ case 1: ++ outbytes = (state->components + 7) >> 3; ++ for ( ; state->sampleindex < state->rowsamples; ++state->sampleindex) ++ { ++ ensure_output_bytes(O, outbytes); ++ while (state->bitsin < state->components) ++ { ++ state->compin = (state->compin << 8) | row_byte(state); ++ state->bitsin += 8; ++ ++state->rowindex; ++ } ++ state->bitsin -= state->components; ++ d = state->compin >> state->bitsin; ++ state->compin &= (1 << state->bitsin) - 1; ++ c = d ^ left_pixel_value(state); ++ save_pixel_value(state, c); ++ state->compout = (state->compout << state->components) | c; ++ state->bitsout += state->components; ++ while (state->bitsout >= 8) ++ { ++ state->bitsout -= 8; ++ iof_set(O, state->compout >> state->bitsout); ++ state->compout &= (1 << state->bitsout) - 1; ++ } ++ } ++ if (state->bitsout > 0) ++ { ++ ensure_output_bytes(O, 1); ++ iof_set(O, state->compin << (8 - state->bitsout)); ++ } ++ break; ++ case 2: case 4: ++ for ( ; state->sampleindex < state->rowsamples; ++state->sampleindex) ++ { ++ for ( ; state->compindex < state->components; ) // state->compindex is ++ed by save_pixel_component() ++ { ++ ensure_output_bytes(O, 1); ++ if (state->bitsin < state->compbits) ++ { ++ state->compin = (state->compin << 8) | row_byte(state); ++ state->bitsin += 8; ++ ++state->rowindex; ++ } ++ state->bitsin -= state->compbits; ++ d = state->compin >> state->bitsin; ++ state->compin &= (1 << state->bitsin) - 1; ++ c = (d + left_pixel_component(state)) & 0xff; ++ save_pixel_component(state, c); ++ state->compout = (state->compout << state->compbits) | c; ++ state->bitsout += state->compbits; ++ if (state->bitsout >= 8) ++ { ++ state->bitsout -= 8; ++ iof_set(O, state->compout >> state->bitsout); ++ state->compout &= (1 << state->bitsout) - 1; ++ } ++ } ++ } ++ if (state->bitsout > 0) ++ { ++ ensure_output_bytes(O, 1); ++ iof_set(O, state->compin << (8 - state->bitsout)); ++ } ++ break; ++ case 8: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = (row_byte(state) + left_pixel_component(state)) & 0xff; ++ save_pixel_component(state, c); ++ iof_set(O, c); ++ } ++ break; ++ case 16: ++ for ( ; state->rowindex < state->rowsize - 1; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 2); ++ d = row_byte(state) << 8; ++ ++state->rowindex; ++ d |= row_byte(state); ++ c = (d + left_pixel_component(state)) & 0xff; ++ save_pixel_component(state, c); ++ iof_set2(O, c >> 8, c & 0xff); ++ } ++ break; ++ default: ++ return IOFERR; ++ } ++ reset_tiff_row(state); ++ break; ++ case PNG_NONE_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ save_pixel_byte(state, c); // next row may need it ++ iof_set(O, c); ++ } ++ reset_png_row(state); ++ break; ++ case PNG_SUB_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = (row_byte(state) + left_pixel_byte(state)) & 0xff; ++ save_pixel_byte(state, c); ++ iof_set(O, c); ++ } ++ reset_png_row(state); ++ break; ++ case PNG_UP_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = (row_byte(state) + up_pixel_byte(state)) & 0xff; ++ save_pixel_byte(state, c); ++ iof_set(O, c); ++ } ++ reset_png_row(state); ++ break; ++ case PNG_AVERAGE_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = (row_byte(state) + ((up_pixel_byte(state) + left_pixel_byte(state)) / 2)) & 0xff; ++ save_pixel_byte(state, c); ++ iof_set(O, c); ++ } ++ reset_png_row(state); ++ break; ++ case PNG_PAETH_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = (row_byte(state) + paeth(state)) & 0xff; ++ save_pixel_byte(state, c); ++ iof_set(O, c); ++ } ++ reset_png_row(state); ++ break; ++ //case PNG_OPTIMUM_PREDICTOR: // valid as default_redictor, but not as algorithm identifier byte ++ default: ++ return IOFERR; ++ } ++ } ++ return state->status == STATUS_LAST ? IOFERR : IOFEOF; ++} ++ ++/* predictor encoder */ ++ ++iof_status predictor_encode_state (iof *I, iof *O, predictor_state *state) ++{ ++ int status, c, d, outbytes; ++ while (state->status == STATUS_CONTINUE) ++ { ++ read_row(state, I, state->rowsize, status); ++ if (state->current_predictor >= 10 && state->predictorbyte == 0) ++ { ++ ensure_output_bytes(O, 1); ++ iof_set(O, state->current_predictor - 10); ++ state->predictorbyte = 1; ++ } ++ switch (state->current_predictor) ++ { ++ case NONE_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ iof_set(O, c); ++ } ++ reset_row(state); ++ break; ++ case TIFF_PREDICTOR: ++ switch (state->compbits) ++ { ++ case 1: ++ outbytes = (state->components + 7) >> 3; ++ for ( ; state->sampleindex < state->rowsamples; ++state->sampleindex) ++ { ++ ensure_output_bytes(O, outbytes); ++ while (state->bitsin < state->components) ++ { ++ state->compin = (state->compin << 8) | row_byte(state); ++ state->bitsin += 8; ++ ++state->rowindex; ++ } ++ state->bitsin -= state->components; ++ c = state->compin >> state->bitsin; ++ state->compin &= (1 << state->bitsin) - 1; ++ d = c ^ left_pixel_value(state); ++ save_pixel_value(state, c); ++ state->compout = (state->compout << state->components) | d; ++ state->bitsout += state->components; ++ while (state->bitsout >= 8) ++ { ++ state->bitsout -= 8; ++ iof_set(O, state->compout >> state->bitsout); ++ state->compout &= (1 << state->bitsout) - 1; ++ } ++ } ++ if (state->bitsout > 0) ++ { ++ ensure_output_bytes(O, 1); ++ iof_set(O, state->compin << (8 - state->bitsout)); ++ } ++ break; ++ case 2: case 4: ++ for ( ; state->sampleindex < state->rowsamples; ++state->sampleindex) ++ { ++ for ( ; state->compindex < state->components; ) ++ { ++ ensure_output_bytes(O, 1); ++ if (state->bitsin < state->compbits) ++ { ++ state->compin = (state->compin << 8) | row_byte(state); ++ state->bitsin += 8; ++ ++state->rowindex; ++ } ++ state->bitsin -= state->compbits; ++ c = state->compin >> state->bitsin; ++ state->compin &= (1 << state->bitsin) - 1; ++ d = tocomp(c - left_pixel_component(state)); ++ save_pixel_component(state, c); ++ state->compout = (state->compout << state->compbits) | d; ++ state->bitsout += state->compbits; ++ if (state->bitsout >= 8) ++ { ++ state->bitsout -= 8; ++ iof_set(O, state->compout >> state->bitsout); ++ state->compout &= (1 << state->bitsout) - 1; ++ } ++ } ++ } ++ if (state->bitsout > 0) ++ { ++ ensure_output_bytes(O, 1); ++ iof_set(O, state->compin << (8 - state->bitsout)); ++ } ++ break; ++ case 8: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ d = tobyte(c - left_pixel_component(state)); ++ save_pixel_component(state, c); ++ iof_set(O, d); ++ } ++ break; ++ case 16: ++ for ( ; state->rowindex < state->rowsize - 1; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 2); ++ c = row_byte(state) << 8; ++ ++state->rowindex; ++ c |= row_byte(state); ++ d = tocomp(c - left_pixel_component(state)); ++ save_pixel_component(state, c); ++ iof_set2(O, d >> 8, d & 0xff); ++ } ++ break; ++ default: ++ return IOFERR; ++ } ++ reset_tiff_row(state); ++ break; ++ case PNG_NONE_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ save_pixel_byte(state, c); // next row may need it ++ iof_set(O, c); ++ } ++ reset_png_row(state); ++ break; ++ case PNG_SUB_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ d = tobyte(c - left_pixel_byte(state)); ++ save_pixel_byte(state, c); ++ iof_set(O, d); ++ } ++ reset_png_row(state); ++ break; ++ case PNG_OPTIMUM_PREDICTOR: // not worthy to perform optimization ++ case PNG_UP_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ d = tobyte(c - up_pixel_byte(state)); ++ save_pixel_byte(state, c); ++ iof_set(O, d); ++ } ++ reset_png_row(state); ++ break; ++ case PNG_AVERAGE_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ d = tobyte(c - ((up_pixel_byte(state) + left_pixel_byte(state)) >> 1)); ++ save_pixel_byte(state, c); ++ iof_set(O, d); ++ } ++ reset_png_row(state); ++ break; ++ case PNG_PAETH_PREDICTOR: ++ for ( ; state->rowindex < state->rowsize; ++state->rowindex) ++ { ++ ensure_output_bytes(O, 1); ++ c = row_byte(state); ++ d = tobyte(c - paeth(state)); ++ save_pixel_byte(state, c); ++ iof_set(O, d); ++ } ++ reset_png_row(state); ++ break; ++ default: ++ return IOFERR; ++ } ++ } ++ return state->status == STATUS_LAST ? IOFERR : IOFEOF; ++} ++ ++iof_status predictor_decode (iof *I, iof *O, int predictor, int rowsamples, int components, int compbits) ++{ ++ predictor_state state; ++ int ret; ++ predictor_decoder_init(&state, predictor, rowsamples, components, compbits); ++ state.flush = 1; ++ ret = predictor_decode_state(I, O, &state); ++ predictor_decoder_close(&state); ++ return ret; ++} ++ ++iof_status predictor_encode (iof *I, iof *O, int predictor, int rowsamples, int components, int compbits) ++{ ++ predictor_state state; ++ int ret; ++ predictor_encoder_init(&state, predictor, rowsamples, components, compbits); ++ state.flush = 1; ++ ret = predictor_encode_state(I, O, &state); ++ predictor_encoder_close(&state); ++ return ret; ++} ++ ++/* filters */ ++ ++// predictor decoder function ++ ++static size_t predictor_decoder (iof *F, iof_mode mode) ++{ ++ predictor_state *state; ++ iof_status status; ++ size_t tail; ++ ++ state = iof_filter_state(predictor_state *, F); ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ do { ++ status = predictor_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "predictor", status); ++ case IOFCLOSE: ++ predictor_decoder_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// predictor encoder function ++ ++static size_t predictor_encoder (iof *F, iof_mode mode) ++{ ++ predictor_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(predictor_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = predictor_encode_state(F, F->next, state); ++ return iof_encoder_retval(F, "predictor", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ predictor_encoder(F, IOFFLUSH); ++ predictor_encoder_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++iof * iof_filter_predictor_decoder (iof *N, int predictor, int rowsamples, int components, int compbits) ++{ ++ iof *I; ++ predictor_state *state; ++ I = iof_filter_reader(predictor_decoder, sizeof(predictor_state), &state); ++ iof_setup_next(I, N); ++ if (predictor_decoder_init(state, predictor, rowsamples, components, compbits) == NULL) ++ { ++ iof_discard(I); ++ return NULL; ++ } ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_predictor_encoder (iof *N, int predictor, int rowsamples, int components, int compbits) ++{ ++ iof *O; ++ predictor_state *state; ++ O = iof_filter_writer(predictor_encoder, sizeof(predictor_state), &state); ++ iof_setup_next(O, N); ++ if (predictor_encoder_init(state, predictor, rowsamples, components, compbits) == NULL) ++ { ++ iof_discard(O); ++ return NULL; ++ } ++ return O; ++} +diff --git a/texk/web2c/luatexdir/luapplib/util/utilfpred.h b/texk/web2c/luatexdir/luapplib/util/utilfpred.h +new file mode 100644 +index 000000000..6ae2f8935 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilfpred.h +@@ -0,0 +1,23 @@ ++#ifndef UTIL_FILTER_PREDICTOR_H ++#define UTIL_FILTER_PREDICTOR_H ++ ++#include "utiliof.h" ++ ++typedef struct predictor_state predictor_state; ++ ++predictor_state * predictor_decoder_init (predictor_state *state, int predictor, int rowsamples, int components, int compbits); ++predictor_state * predictor_encoder_init (predictor_state *state, int predictor, int rowsamples, int components, int compbits); ++ ++void predictor_decoder_close (predictor_state *state); ++void predictor_encoder_close (predictor_state *state); ++ ++iof_status predictor_decode_state (iof *I, iof *O, predictor_state *state); ++iof_status predictor_encode_state (iof *I, iof *O, predictor_state *state); ++ ++iof_status predictor_decode (iof *I, iof *O, int predictor, int rowsamples, int components, int compbits); ++iof_status predictor_encode (iof *I, iof *O, int predictor, int rowsamples, int components, int compbits); ++ ++iof * iof_filter_predictor_decoder (iof *N, int predictor, int rowsamples, int components, int compbits); ++iof * iof_filter_predictor_encoder (iof *N, int predictor, int rowsamples, int components, int compbits); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utiliof.c b/texk/web2c/luatexdir/luapplib/util/utiliof.c +new file mode 100644 +index 000000000..7d07cf12d +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utiliof.c +@@ -0,0 +1,2949 @@ ++/* input/iutput stream */ ++ ++#include ++#include ++#include ++ ++#include "utilmem.h" ++#include "utillog.h" ++#include "utiliof.h" ++ ++/* commons */ ++ ++void * iof_copy_data (const void *data, size_t size) ++{ ++ return memcpy(util_malloc(size), data, size); ++} ++ ++uint8_t * iof_copy_file_data (const char *filename, size_t *psize) ++{ ++ FILE *file; ++ size_t size; ++ uint8_t *data; ++ if ((file = fopen(filename, "rb")) == NULL) ++ return NULL; ++ fseek(file, 0, SEEK_END); ++ size = (size_t)ftell(file); ++ data = (uint8_t *)util_malloc(size); ++ fseek(file, 0, SEEK_SET); ++ if ((*psize = fread(data, 1, size, file)) != size) ++ { ++ util_free(data); ++ data = NULL; ++ } ++ fclose(file); ++ return data; ++} ++ ++uint8_t * iof_copy_file_handle_data (FILE *file, size_t *psize) ++{ ++ size_t size; ++ uint8_t *data; ++ //long offset = ftell(file); // keep offset intact? ++ fseek(file, 0, SEEK_END); ++ size = (size_t)ftell(file); ++ data = (uint8_t *)util_malloc(size); ++ fseek(file, 0, SEEK_SET); ++ if ((*psize = fread(data, 1, size, file)) != size) ++ { ++ util_free(data); ++ data = NULL; ++ } ++ //fseek(file, offset, SEEK_SET) ++ return data; ++} ++ ++FILE * iof_get_file (iof *F) ++{ ++ if (F->flags & IOF_FILE) ++ return iof_file_get_file(F->iofile); ++ if (F->flags & IOF_FILE_HANDLE) ++ return F->file; ++ return NULL; ++} ++ ++const char * iof_status_kind (iof_status status) ++{ ++ switch (status) ++ { ++ case IOFEOF: ++ return "IOFEOF"; ++ case IOFERR: ++ return "IOFERR"; ++ case IOFEMPTY: ++ return "IOFEMPTY"; ++ case IOFFULL: ++ return "IOFFULL"; ++ default: ++ break; ++ } ++ return "(unknown)"; ++} ++ ++/* shared pseudofile */ ++ ++#define IOF_FILE_DEFAULTS 0 ++ ++iof_file * iof_file_new (FILE *file) ++{ ++ iof_file *iofile = (iof_file *)util_malloc(sizeof(iof_file)); ++ iof_file_set_fh(iofile, file); ++ iofile->offset = NULL; ++ iofile->size = 0; ++ iofile->name = NULL; ++ iofile->refcount = 0; ++ iofile->flags = IOF_FILE_DEFAULTS|IOF_ALLOC; ++ return iofile; ++} ++ ++iof_file * iof_file_init (iof_file *iofile, FILE *file) ++{ ++ iof_file_set_fh(iofile, file); ++ iofile->offset = NULL; ++ iofile->size = 0; ++ iofile->name = NULL; ++ iofile->refcount = 0; ++ iofile->flags = IOF_FILE_DEFAULTS; ++ return iofile; ++} ++ ++iof_file * iof_file_rdata (const void *data, size_t size) ++{ ++ iof_file *iofile = (iof_file *)util_malloc(sizeof(iof_file)); ++ iofile->rbuf = iofile->rpos = (const uint8_t *)data; ++ iofile->rend = iofile->rbuf + size; ++ iofile->offset = NULL; ++ iofile->size = 0; ++ iofile->name = NULL; ++ iofile->refcount = 0; ++ iofile->flags = IOF_FILE_DEFAULTS|IOF_ALLOC|IOF_DATA; ++ return iofile; ++} ++ ++iof_file * iof_file_rdata_init (iof_file *iofile, const void *data, size_t size) ++{ ++ iofile->rbuf = iofile->rpos = (const uint8_t *)data; ++ iofile->rend = iofile->rbuf + size; ++ iofile->offset = NULL; ++ iofile->size = 0; // letse keep it consequently set to zero (only for user disposal) ++ iofile->name = NULL; ++ iofile->refcount = 0; ++ iofile->flags = IOF_FILE_DEFAULTS|IOF_DATA; ++ return iofile; ++} ++ ++iof_file * iof_file_wdata (void *data, size_t size) ++{ ++ return iof_file_rdata((const void *)data, size); ++} ++ ++iof_file * iof_file_wdata_init (iof_file *iofile, void *data, size_t size) ++{ ++ return iof_file_rdata_init(iofile, (const void *)data, size); ++} ++ ++/* typical uses so far */ ++ ++iof_file * iof_file_reader_from_file_handle (iof_file *iofile, const char *filename, FILE *file, int preload, int closefile) ++{ ++ uint8_t *data; ++ size_t size; ++ ++ if (preload) ++ { ++ if ((data = iof_copy_file_handle_data(file, &size)) == NULL) ++ { ++ if (closefile) ++ fclose(file); ++ return NULL; ++ } ++ if (iofile == NULL) ++ iofile = iof_file_rdata(data, size); ++ else ++ iof_file_rdata_init(iofile, data, size); ++ iofile->flags |= IOF_BUFFER_ALLOC; ++ if (closefile) ++ fclose(file); ++ } ++ else ++ { ++ if (iofile == NULL) ++ iofile = iof_file_new(file); ++ else ++ iof_file_init(iofile, file); ++ if (closefile) ++ iofile->flags |= IOF_CLOSE_FILE; ++ } ++ if (filename != NULL) ++ iof_file_set_name(iofile, filename); ++ return iofile; ++} ++ ++iof_file * iof_file_reader_from_file (iof_file *iofile, const char *filename, int preload) ++{ ++ FILE *file; ++ if ((file = fopen(filename, "rb")) == NULL) ++ return NULL; ++ return iof_file_reader_from_file_handle(iofile, filename, file, preload, 1); ++} ++ ++iof_file * iof_file_reader_from_data (iof_file *iofile, const void *data, size_t size, int preload, int freedata) ++{ ++ void *newdata; ++ if (data == NULL) ++ return NULL; ++ if (preload) ++ { ++ newdata = iof_copy_data(data, size); ++ if (iofile == NULL) ++ iofile = iof_file_rdata(newdata, size); ++ else ++ iof_file_rdata_init(iofile, newdata, size); ++ iofile->flags |= IOF_BUFFER_ALLOC; ++ //if (freedata) // hardly makes sense... we can't free const void * ++ // util_free((void *)data); ++ } ++ else ++ { ++ if (iofile == NULL) ++ iofile = iof_file_rdata(data, size); ++ else ++ iof_file_rdata_init(iofile, data, size); ++ if (freedata) ++ iofile->flags |= IOF_BUFFER_ALLOC; ++ } ++ return iofile; ++} ++ ++/* ++iof_file * iof_file_writer_from_file (iof_file *iofile, const char *filename) ++{ ++ FILE *file; ++ if ((file = fopen(filename, "wb")) == NULL) ++ return NULL; ++ if (iofile == NULL) ++ iofile = iof_file_new(file); ++ else ++ iof_file_init(iofile, file); ++ iofile->flags |= IOF_CLOSE_FILE; ++ iof_file_set_name(iofile, filename); ++ return iofile; ++} ++*/ ++ ++/* ++Because of limited number of FILE* handles available, we may need to close contained handle ++between accessing it. In applications so far (fonts, images) we typically need the source ++to parse the file on creation and to rewrite or reload the data on dump. All iof_file api ++functions assume that iofile has FILE* opened. Reopening it on every access (ftell, fseek, ++read/write) makes no sense, as we would effectively loose control. If the caller invalidates ++iofile by closing and nulling its file handle, it is also responsible to reopen when necessary. ++*/ ++ ++int iof_file_close_input (iof_file *iofile) ++{ ++ FILE *file; ++ if (iofile->flags & IOF_DATA) ++ return 0; ++ if ((file = iof_file_get_fh(iofile)) == NULL) ++ return 0; ++ fclose(file); ++ iof_file_set_fh(iofile, NULL); ++ iofile->flags &= ~IOF_RECLOSE_FILE; ++ iofile->flags |= IOF_REOPEN_FILE; ++ return 1; ++} ++ ++int iof_file_reopen_input (iof_file *iofile) ++{ // returns true if iofile readable ++ FILE *file; ++ const char *filename; ++ if (iofile->flags & IOF_DATA) ++ return 1; ++ if ((file = iof_file_get_fh(iofile)) != NULL) ++ return 1; // if present, assumed readable ++ if ((filename = iofile->name) == NULL || (file = fopen(filename, "rb")) == NULL) ++ return 0; ++ iof_file_set_fh(iofile, file); ++ iofile->flags &= ~IOF_REOPEN_FILE; ++ iofile->flags |= IOF_RECLOSE_FILE; ++ return 1; ++} ++ ++/* freeing iof_file */ ++ ++void iof_file_free (iof_file *iofile) ++{ ++ FILE *file; ++ if (iofile->flags & IOF_DATA) ++ { ++ if (iofile->flags & IOF_BUFFER_ALLOC) ++ { ++ iofile->flags &= ~IOF_BUFFER_ALLOC; ++ if (iofile->buf != NULL) ++ { ++ util_free(iofile->buf); ++ iofile->buf = iofile->pos = iofile->end = NULL; ++ } ++ } ++ } ++ else if ((file = iof_file_get_fh(iofile)) != NULL) ++ { ++ if (iofile->flags & IOF_CLOSE_FILE) ++ fclose(file); ++ iof_file_set_fh(iofile, NULL); ++ } ++ iof_file_set_name(iofile, NULL); ++ if (iofile->flags & IOF_ALLOC) ++ util_free(iofile); ++} ++ ++/* set filename for reopen */ ++ ++void iof_file_set_name (iof_file *iofile, const char *name) ++{ ++ if (iofile->name != NULL) ++ util_free(iofile->name); ++ if (name != NULL) ++ iofile->name = iof_copy_data(name, strlen(name) + 1); ++ else ++ iofile->name = NULL; ++} ++ ++/* seek */ ++ ++int iof_file_seek (iof_file *iofile, long offset, int whence) ++{ ++ if (iofile->flags & IOF_DATA) ++ { ++ switch (whence) ++ { ++ case SEEK_SET: ++ if (offset >= 0 && iofile->buf + offset <= iofile->end) ++ { ++ iofile->pos = iofile->buf + offset; ++ return 0; ++ } ++ return -1; ++ case SEEK_CUR: ++ if ((offset >= 0 && iofile->pos + offset <= iofile->end) || (offset < 0 && iofile->pos + offset >= iofile->buf)) ++ { ++ iofile->pos += offset; ++ return 0; ++ } ++ return -1; ++ case SEEK_END: ++ if (offset <= 0 && iofile->end + offset >= iofile->buf) ++ { ++ iofile->pos = iofile->end + offset; ++ return 0; ++ } ++ return -1; ++ } ++ return -1; ++ } ++ return fseek(iof_file_get_fh(iofile), offset, whence); ++} ++ ++/* */ ++ ++long iof_file_tell (iof_file *iofile) ++{ ++ return (iofile->flags & IOF_DATA) ? (long)(iofile->pos - iofile->buf) : ftell(iof_file_get_fh(iofile)); ++} ++ ++size_t iof_file_size (iof_file *iofile) ++{ ++ long pos, size; ++ FILE *file; ++ if (iofile->flags & IOF_DATA) ++ return (size_t)iof_space(iofile); ++ file = iof_file_get_fh(iofile); ++ pos = ftell(file); ++ fseek(file, 0, SEEK_END); ++ size = ftell(file); ++ fseek(file, pos, SEEK_SET); ++ return size; ++} ++ ++int iof_file_eof (iof_file *iofile) ++{ ++ if (iofile->flags & IOF_DATA) ++ return iofile->pos == iofile->end ? -1 : 0; ++ return feof(iof_file_get_fh(iofile)); ++} ++ ++int iof_file_flush (iof_file *iofile) ++{ ++ if (iofile->flags & IOF_DATA) ++ return 0; ++ return fflush(iof_file_get_fh(iofile)); ++} ++ ++size_t iof_file_read (void *ptr, size_t size, size_t items, iof_file *iofile) ++{ ++ if (iofile->flags & IOF_DATA) ++ { ++ size_t bytes = size * items; ++ if (bytes > (size_t)iof_left(iofile)) ++ bytes = (size_t)iof_left(iofile); ++ memcpy(ptr, iofile->pos, bytes); ++ iofile->pos += bytes; ++ return bytes / size; // number of elements read ++ } ++ return fread(ptr, size, items, iof_file_get_fh(iofile)); ++} ++ ++static size_t iof_file_data_resizeto (iof_file *iofile, size_t space) ++{ ++ uint8_t *newbuf; ++ size_t size; ++ size = iof_size(iofile); ++ if (iofile->flags & IOF_BUFFER_ALLOC) ++ { ++ newbuf = (uint8_t *)util_realloc(iofile->buf, space); ++ } ++ else ++ { ++ newbuf = (uint8_t *)util_malloc(space); ++ if (size > 0) ++ memcpy(newbuf, iofile->buf, size); ++ iofile->flags |= IOF_BUFFER_ALLOC; ++ } ++ iofile->buf = newbuf; ++ iofile->pos = newbuf + size; ++ iofile->end = newbuf + space; ++ return space - size; ++} ++ ++#define iof_file_data_resize(iofile) iof_file_data_resizeto(iofile, iof_space(iofile) << 1) ++ ++size_t iof_file_write (const void *ptr, size_t size, size_t items, iof_file *iofile) ++{ ++ if (iofile->flags & IOF_DATA) ++ { ++ size_t space, sizesofar, bytes; ++ bytes = size * items; ++ if (bytes > (size_t)iof_left(iofile)) ++ { ++ if ((space = iof_space(iofile)) == 0) // allow iofile->buf/end initially NULL ++ space = BUFSIZ; ++ for (sizesofar = iof_size(iofile), space <<= 1; sizesofar + bytes > space; space <<= 1) ++ ; ++ if (iof_file_data_resizeto(iofile, space) == 0) ++ return 0; ++ } ++ memcpy(iofile->pos, ptr, bytes); ++ iofile->pos += bytes; ++ return bytes / size; ++ } ++ return fwrite(ptr, size, items, iof_file_get_fh(iofile)); ++} ++ ++size_t iof_file_ensure (iof_file *iofile, size_t bytes) ++{ ++ if (iofile->flags & IOF_DATA) ++ { ++ size_t space, sizesofar, left; ++ left = (size_t)iof_left(iofile); ++ if (bytes > left) ++ { ++ if ((space = iof_space(iofile)) == 0) // allow iofile->buf/end initially NULL ++ space = BUFSIZ; ++ for (sizesofar = iof_size(iofile), space <<= 1; sizesofar + bytes > space; space <<= 1); ++ return iof_file_data_resizeto(iofile, space); ++ } ++ return left; ++ } ++ return 0; ++} ++ ++int iof_file_getc (iof_file *iofile) ++{ ++ if (iofile->flags & IOF_DATA) ++ return iofile->pos < iofile->end ? *iofile->pos++ : IOFEOF; ++ return fgetc(iof_file_get_fh(iofile)); ++} ++ ++int iof_file_putc (iof_file *iofile, int c) ++{ ++ if (iofile->flags & IOF_DATA) ++ { ++ if (iofile->pos >= iofile->end) ++ if (iof_file_data_resize(iofile) == 0) ++ return IOFEOF; ++ *iofile->pos++ = (uint8_t)c; ++ return c; ++ } ++ return fputc(c, iof_file_get_fh(iofile)); ++} ++ ++static int iof_file_sync (iof_file *iofile, size_t *offset) ++{ ++ if (iofile->offset != offset) ++ { ++ if (iofile->offset != NULL) ++ *iofile->offset = iof_file_tell(iofile); ++ iofile->offset = offset; ++ if (offset) // let offset be NULL ++ return iof_file_seek(iofile, (long)*offset, SEEK_SET); ++ } ++ return 0; ++} ++ ++//#define iof_file_unsync(iofile, poffset) (void)((iofile)->offset == poffset && (((iofile)->offset = NULL), 0)) ++#define iof_file_unsync(iofile, poffset) ((void)poffset, (iofile)->offset = NULL) ++ ++/* iof seek */ ++ ++#define iof_reader_reset(I) ((I)->pos = (I)->end = (I)->buf) ++#define iof_reader_reseek_file(I, offset, whence) (fseek((I)->file, offset, whence) == 0 ? (iof_reader_reset(I), 0) : -1) ++#define iof_reader_reseek_iofile(I, offset, whence) (iof_file_seek((I)->iofile, offset, whence) == 0 ? (iof_reader_reset(I), 0) : -1) ++ ++#define iof_writer_reset(O) ((O)->pos = (O)->buf) ++#define iof_writer_reseek_file(O, offset, whence) (iof_flush(O), (fseek((O)->file, offset, whence) == 0 ? (iof_writer_reset(O), 0) : -1)) ++#define iof_writer_reseek_iofile(O, offset, whence) (iof_flush(O), (iof_file_seek((O)->iofile, offset, whence) == 0 ? (iof_writer_reset(O), 0) : -1)) ++ ++static int iof_reader_seek_data (iof *I, long offset, int whence) ++{ ++ switch (whence) ++ { ++ case SEEK_SET: ++ if (offset >= 0 && I->buf + offset <= I->end) ++ { ++ I->pos = I->buf + offset; ++ return 0; ++ } ++ return -1; ++ case SEEK_CUR: ++ if ((offset >= 0 && I->pos + offset <= I->end) || (offset < 0 && I->pos + offset >= I->buf)) ++ { ++ I->pos += offset; ++ return 0; ++ } ++ return -1; ++ case SEEK_END: ++ if (offset <= 0 && I->end + offset >= I->buf) ++ { ++ I->pos = I->end + offset; ++ return 0; ++ } ++ return -1; ++ } ++ return -1; ++} ++ ++static int iof_reader_seek_iofile (iof *I, long offset, int whence) ++{ ++ long fileoffset; ++ switch (whence) ++ { ++ case SEEK_SET: ++ fileoffset = iof_file_tell(I->iofile); ++ if (offset <= fileoffset && offset >= fileoffset - iof_space(I)) ++ { ++ I->pos = I->end - (fileoffset - offset); ++ return 0; ++ } ++ return iof_reader_reseek_iofile(I, offset, SEEK_SET); ++ case SEEK_CUR: ++ if ((offset >= 0 && I->pos + offset <= I->end) || (offset < 0 && I->pos + offset >= I->buf)) ++ { ++ I->pos += offset; ++ return 0; ++ } ++ return iof_reader_reseek_iofile(I, offset, SEEK_CUR); ++ case SEEK_END: ++ return iof_reader_reseek_iofile(I, offset, SEEK_END); // can we do better? ++ } ++ return -1; ++} ++ ++static int iof_reader_seek_file (iof *I, long offset, int whence) ++{ ++ long fileoffset; ++ switch (whence) ++ { ++ case SEEK_SET: ++ fileoffset = ftell(I->file); ++ if (offset <= fileoffset && offset >= fileoffset - iof_space(I)) ++ { ++ I->pos = I->end - (fileoffset - offset); ++ return 0; ++ } ++ return iof_reader_reseek_file(I, offset, SEEK_SET); ++ case SEEK_CUR: ++ if ((offset >= 0 && I->pos + offset <= I->end) || (offset < 0 && I->pos + offset >= I->buf)) ++ { ++ I->pos += offset; ++ return 0; ++ } ++ return iof_reader_reseek_file(I, offset, SEEK_CUR); ++ case SEEK_END: ++ return iof_reader_reseek_file(I, offset, SEEK_END); // can we do better? ++ } ++ return -1; ++} ++ ++int iof_reader_seek (iof *I, long offset, int whence) ++{ ++ I->flags &= ~IOF_STOPPED; ++ if (I->flags & IOF_FILE) ++ return iof_reader_seek_iofile(I, offset, whence); ++ if (I->flags & IOF_FILE_HANDLE) ++ return iof_reader_seek_file(I, offset, whence); ++ if (I->flags & IOF_DATA) ++ return iof_reader_seek_data(I, offset, whence); ++ return -1; ++} ++ ++int iof_reader_reseek (iof *I, long offset, int whence) ++{ ++ I->flags &= ~IOF_STOPPED; ++ if (I->flags & IOF_FILE) ++ return iof_reader_reseek_iofile(I, offset, whence); ++ if (I->flags & IOF_FILE_HANDLE) ++ return iof_reader_reseek_file(I, offset, whence); ++ if (I->flags & IOF_DATA) ++ return iof_reader_seek_data(I, offset, whence); ++ return -1; ++} ++ ++static int iof_writer_seek_data (iof *O, long offset, int whence) ++{ ++ /* ++ fseek() allows to seek after the end of file. Seeking does not increase the output file. ++ No byte is written before fwirte(). It seems to fill the gap with zeros. Until we really need that, ++ no seeking out of bounds for writers. ++ */ ++ O->flags &= ~IOF_STOPPED; ++ return iof_reader_seek_data(O, offset, whence); ++} ++ ++static int iof_writer_seek_iofile (iof *O, long offset, int whence) ++{ ++ long fileoffset; ++ switch (whence) ++ { ++ case SEEK_SET: ++ fileoffset = iof_file_tell(O->iofile); ++ if (offset >= fileoffset && offset <= fileoffset + iof_space(O)) ++ { ++ O->pos = O->buf + (offset - fileoffset); ++ return 0; ++ } ++ return iof_writer_reseek_iofile(O, offset, SEEK_SET); ++ case SEEK_CUR: ++ if ((offset >=0 && O->pos + offset <= O->end) || (offset < 0 && O->pos + offset >= O->buf)) ++ { ++ O->pos += offset; ++ return 0; ++ } ++ return iof_writer_reseek_iofile(O, offset, SEEK_CUR); ++ case SEEK_END: ++ return iof_writer_reseek_iofile(O, offset, SEEK_END); ++ } ++ return -1; ++} ++ ++static int iof_writer_seek_file (iof *O, long offset, int whence) ++{ ++ long fileoffset; ++ switch (whence) ++ { ++ case SEEK_SET: ++ fileoffset = ftell(O->file); ++ if (offset >= fileoffset && offset <= fileoffset + iof_space(O)) ++ { ++ O->pos = O->buf + (offset - fileoffset); ++ return 0; ++ } ++ return iof_writer_reseek_file(O, offset, SEEK_SET); ++ case SEEK_CUR: ++ if ((offset >=0 && O->pos + offset <= O->end) || (offset < 0 && O->pos + offset >= O->buf)) ++ { ++ O->pos += offset; ++ return 0; ++ } ++ return iof_writer_reseek_file(O, offset, SEEK_CUR); ++ case SEEK_END: ++ return iof_writer_reseek_file(O, offset, SEEK_END); ++ } ++ return -1; ++} ++ ++int iof_writer_seek (iof *I, long offset, int whence) ++{ ++ I->flags &= ~IOF_STOPPED; ++ if (I->flags & IOF_FILE) ++ return iof_writer_seek_iofile(I, offset, whence); ++ if (I->flags & IOF_FILE_HANDLE) ++ return iof_writer_seek_file(I, offset, whence); ++ if (I->flags & IOF_DATA) ++ return iof_writer_seek_data(I, offset, whence); ++ return -1; ++} ++ ++int iof_writer_reseek (iof *I, long offset, int whence) ++{ ++ I->flags &= ~IOF_STOPPED; ++ if (I->flags & IOF_FILE) ++ return iof_writer_reseek_iofile(I, offset, whence); ++ if (I->flags & IOF_FILE_HANDLE) ++ return iof_writer_reseek_file(I, offset, whence); ++ if (I->flags & IOF_DATA) ++ return iof_writer_seek_data(I, offset, whence); ++ return -1; ++} ++ ++int iof_seek (iof *F, long offset, int whence) ++{ ++ return (F->flags & IOF_WRITER) ? iof_writer_seek(F, offset, whence) : iof_reader_seek(F, offset, whence); ++} ++ ++int iof_reseek (iof *F, long offset, int whence) ++{ ++ return (F->flags & IOF_WRITER) ? iof_writer_reseek(F, offset, whence) : iof_reader_reseek(F, offset, whence); ++} ++ ++/* tell */ ++ ++long iof_reader_tell (iof *I) ++{ ++ if (I->flags & IOF_FILE) ++ return iof_file_tell(I->iofile) - (long)iof_left(I); ++ if (I->flags & IOF_FILE_HANDLE) ++ return ftell(I->file) - (long)iof_left(I); ++ //if (I->flags & IOF_DATA) ++ return (long)iof_size(I); ++} ++ ++long iof_writer_tell (iof *O) ++{ ++ if (O->flags & IOF_FILE) ++ return iof_file_tell(O->iofile) + (long)iof_size(O); ++ if (O->flags & IOF_FILE_HANDLE) ++ return ftell(O->file) + (long)iof_size(O); ++ //if (I->flags & IOF_DATA) ++ return (long)iof_size(O); ++} ++ ++long iof_tell (iof *I) ++{ ++ return (I->flags & IOF_WRITER) ? iof_writer_tell(I) : iof_reader_tell(I); ++} ++ ++size_t iof_fsize (iof *I) ++{ ++ size_t pos, size; ++ if (I->flags & IOF_FILE) ++ return iof_file_size(I->iofile); ++ if (I->flags & IOF_FILE_HANDLE) ++ { ++ pos = (size_t)ftell(I->file); ++ fseek(I->file, 0, SEEK_END); ++ size = (size_t)ftell(I->file); ++ fseek(I->file, (long)pos, SEEK_SET); ++ return size; ++ } ++ //if (I->flags & IOF_DATA) ++ return (size_t)iof_space(I); ++} ++ ++/* save reader tail */ ++ ++size_t iof_save_tail (iof *I) ++{ ++ size_t size, left; ++ size = iof_size(I); ++ left = iof_left(I); ++ if (size >= left) ++ memcpy(I->buf, I->pos, left); ++ else ++ memmove(I->buf, I->pos, left); ++ return left; ++} ++ ++size_t iof_input_save_tail (iof *I, size_t back) ++{ ++ size_t size; ++ I->flags |= IOF_TAIL; ++ I->pos -= back; ++ size = iof_input(I); ++ I->pos += back; ++ I->flags &= ~IOF_TAIL; ++ return size; // + back - back ++} ++ ++/* read from file */ ++ ++/* iof free*/ ++ ++static size_t file_read (iof *I); ++static size_t file_load (iof *I); ++ ++static size_t file_reader (iof *I, iof_mode mode) ++{ ++ switch (mode) ++ { ++ case IOFREAD: ++ return file_read(I); ++ case IOFLOAD: ++ return file_load(I); ++ case IOFCLOSE: ++ iof_free(I); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++iof * iof_setup_file_handle_reader (iof *I, void *buffer, size_t space, FILE *f) ++{ ++ if (I == NULL) ++ iof_setup_reader(I, buffer, space); ++ else ++ iof_reader_buffer(I, buffer, space); ++ iof_setup_file(I, f); ++ I->more = file_reader; ++ return I; ++} ++ ++iof * iof_setup_file_reader (iof *I, void *buffer, size_t space, const char *filename) ++{ ++ FILE *f; ++ if ((f = fopen(filename, "rb")) == NULL) ++ return NULL; ++ if (I == NULL) ++ iof_setup_reader(I, buffer, space); ++ else ++ iof_reader_buffer(I, buffer, space); ++ iof_setup_file(I, f); ++ I->flags |= IOF_CLOSE_FILE; ++ I->more = file_reader; ++ return I; ++} ++ ++/* write to file */ ++ ++static size_t file_write (iof *O, int flush); ++ ++static size_t file_writer (iof *O, iof_mode mode) ++{ ++ switch (mode) ++ { ++ case IOFWRITE: ++ return file_write(O, 0); ++ case IOFFLUSH: ++ return file_write(O, 1); ++ case IOFCLOSE: ++ file_write(O, 1); ++ iof_free(O); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++iof * iof_setup_file_handle_writer (iof *O, void *buffer, size_t space, FILE *f) ++{ ++ if (O == NULL) ++ iof_setup_writer(O, buffer, space); ++ else ++ iof_writer_buffer(O, buffer, space); ++ iof_setup_file(O, f); ++ O->more = file_writer; ++ return O; ++} ++ ++iof * iof_setup_file_writer (iof *O, void *buffer, size_t space, const char *filename) ++{ ++ FILE *f; ++ if ((f = fopen(filename, "wb")) == NULL) ++ return NULL; ++ if (O == NULL) ++ iof_setup_writer(O, buffer, space); ++ else ++ iof_writer_buffer(O, buffer, space); ++ iof_setup_file(O, f); ++ O->flags |= IOF_CLOSE_FILE; ++ O->more = file_writer; ++ return O; ++} ++ ++/* a dedicated handler for stdout/stderr */ ++ ++static size_t stdout_writer (iof *O, iof_mode mode) ++{ ++ switch(mode) ++ { ++ case IOFWRITE: ++ { ++ fwrite(O->buf, sizeof(uint8_t), iof_size(O), stdout); ++ O->pos = O->buf; ++ return O->space; ++ } ++ case IOFCLOSE: ++ case IOFFLUSH: ++ { ++ fwrite(O->buf, sizeof(uint8_t), iof_size(O), stdout); ++ fflush(stdout); ++ O->pos = O->buf; ++ return 0; ++ } ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static size_t stderr_writer (iof *O, iof_mode mode) ++{ ++ switch(mode) ++ { ++ case IOFWRITE: ++ { ++ fwrite(O->buf, sizeof(uint8_t), iof_size(O), stderr); ++ O->pos = O->buf; ++ return O->space; ++ } ++ case IOFCLOSE: ++ case IOFFLUSH: ++ { ++ fwrite(O->buf, sizeof(uint8_t), iof_size(O), stderr); ++ fflush(stderr); ++ O->pos = O->buf; ++ return 0; ++ } ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static uint8_t iof_stdout_buffer[BUFSIZ]; ++iof iof_stdout = IOF_WRITER_STRUCT(stdout_writer, NULL, iof_stdout_buffer, BUFSIZ, 0); ++ ++static uint8_t iof_stderr_buffer[BUFSIZ]; ++iof iof_stderr = IOF_WRITER_STRUCT(stderr_writer, NULL, iof_stderr_buffer, BUFSIZ, 0); ++ ++/* read from somewhere */ ++ ++iof * iof_reader (iof *I, void *link, iof_handler reader, const void *m, size_t bytes) ++{ ++ I->space = 0; ++ I->link = link; ++ I->more = reader; ++ I->flags = 0; ++ I->refcount = 0; ++ if (m != NULL) ++ { ++ I->rbuf = I->rpos = (const uint8_t *)m; ++ I->rend = (const uint8_t *)m + bytes; ++ return I; ++ } ++ return NULL; ++} ++ ++iof * iof_string_reader (iof *I, const void *s, size_t bytes) ++{ ++ I->space = 0; ++ I->link = NULL; ++ I->more = NULL; ++ I->flags = 0; // iof_string() sets IOF_DATA ++ I->refcount = 0; ++ if (s != NULL) ++ return iof_string(I, s, bytes); ++ return NULL; ++} ++ ++/* write somewhere */ ++ ++iof * iof_writer (iof *O, void *link, iof_handler writer, void *m, size_t bytes) ++{ ++ O->space = 0; ++ O->link = link; ++ O->more = writer; ++ O->flags = 0; ++ O->refcount = 0; ++ if (m != NULL && bytes > 0) ++ { ++ O->buf = O->pos = (uint8_t *)m; ++ O->end = (uint8_t *)m + bytes; ++ return O; ++ } ++ // return iof_null(O); ++ return NULL; ++} ++ ++/* write to growing bytes buffer */ ++ ++static size_t iof_mem_handler (iof *O, iof_mode mode) ++{ ++ switch(mode) ++ { ++ case IOFWRITE: ++ return iof_resize_buffer(O); ++ case IOFCLOSE: ++ iof_free(O); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++iof * iof_setup_buffer (iof *O, void *buffer, size_t space) ++{ ++ if (O == NULL) ++ iof_setup_writer(O, buffer, space); ++ else ++ iof_writer_buffer(O, buffer, space); ++ O->link = NULL; ++ O->flags |= IOF_DATA; ++ O->more = iof_mem_handler; ++ return O; ++} ++ ++iof * iof_setup_buffermin (iof *O, void *buffer, size_t space, size_t min) ++{ ++ if ((O = iof_setup_buffer(O, buffer, space)) != NULL && space < min) // just allocate min now to avoid further rewriting ++ { ++ O->buf = O->pos = (uint8_t *)util_malloc(min); ++ O->flags |= IOF_BUFFER_ALLOC; ++ O->end = O->buf + min; ++ } ++ return O; ++} ++ ++iof * iof_buffer_create (size_t space) ++{ ++ uint8_t *buffer; ++ iof *O; ++ space += sizeof(iof); ++ buffer = util_malloc(space); ++ if ((O = iof_setup_buffer(NULL, buffer, space)) != NULL) ++ O->flags |= IOF_ALLOC; ++ return O; ++} ++ ++/* set/get */ ++ ++int iof_getc (iof *I) ++{ ++ if (iof_readable(I)) ++ return *I->pos++; ++ return IOFEOF; ++} ++ ++int iof_putc (iof *O, int u) ++{ ++ if (iof_writable(O)) ++ { ++ iof_set(O, u); ++ return (uint8_t)u; ++ } ++ return IOFFULL; ++} ++ ++size_t iof_skip (iof *I, size_t bytes) ++{ ++ while (bytes) ++ { ++ if (iof_readable(I)) ++ ++I->pos; ++ else ++ break; ++ --bytes; ++ } ++ return bytes; ++} ++ ++/* from iof to iof */ ++ ++iof_status iof_pass (iof *I, iof *O) ++{ ++ size_t leftin, leftout; ++ if ((leftin = iof_left(I)) == 0) ++ leftin = iof_input(I); ++ while (leftin) ++ { ++ if ((leftout = iof_left(O)) == 0) ++ if ((leftout = iof_output(O)) == 0) ++ return IOFFULL; ++ while (leftin > leftout) ++ { ++ memcpy(O->pos, I->pos, leftout); ++ I->pos += leftout; ++ O->pos = O->end; /* eq. += leftout */ ++ leftin -= leftout; ++ if ((leftout = iof_output(O)) == 0) ++ return IOFFULL; ++ } ++ if (leftin) ++ { ++ memcpy(O->pos, I->pos, leftin); ++ I->pos = I->end; /* eq. += leftin */ ++ O->pos += leftin; ++ } ++ leftin = iof_input(I); ++ } ++ return IOFEOF; ++} ++ ++/* read n-bytes */ ++ ++size_t iof_read (iof *I, void *to, size_t size) ++{ ++ size_t leftin, done = 0; ++ char *s = (char *)to; ++ ++ if ((leftin = iof_left(I)) == 0) ++ if ((leftin = iof_input(I)) == 0) ++ return done; ++ while (size > leftin) ++ { ++ memcpy(s, I->pos, leftin * sizeof(uint8_t)); ++ size -= leftin; ++ done += leftin; ++ s += leftin; ++ I->pos = I->end; ++ if ((leftin = iof_input(I)) == 0) ++ return done; ++ } ++ if (size) ++ { ++ memcpy(s, I->pos, size * sizeof(uint8_t)); ++ I->pos += size; ++ done += size; ++ } ++ return done; ++} ++ ++/* rewrite FILE content (use fseek if needed) */ ++ ++size_t iof_write_file_handle (iof *O, FILE *file) ++{ ++ size_t leftout, size, readout; ++ if ((leftout = iof_left(O)) == 0) ++ if ((leftout = iof_output(O)) == 0) ++ return 0; ++ size = 0; ++ do { ++ readout = fread(O->pos, 1, leftout, file); ++ O->pos += readout; ++ size += readout; ++ } while(readout == leftout && (leftout = iof_output(O)) > 0); ++ return size; ++} ++ ++size_t iof_write_file (iof *O, const char *filename) ++{ ++ FILE *file; ++ size_t size; ++ if ((file = fopen(filename, "rb")) == NULL) ++ return 0; ++ size = iof_write_file_handle(O, file); ++ fclose(file); ++ return size; ++} ++ ++size_t iof_write_iofile (iof *O, iof_file *iofile, int savepos) ++{ ++ long offset; ++ size_t size; ++ FILE *file; ++ if (iofile->flags & IOF_DATA) ++ return iof_write(O, iofile->pos, (size_t)(iofile->end - iofile->pos)); ++ file = iof_file_get_fh(iofile); ++ if (savepos) ++ { ++ offset = ftell(file); ++ size = iof_write_file_handle(O, file); ++ fseek(file, offset, SEEK_SET); ++ return size; ++ } ++ return iof_write_file_handle(O, file); ++} ++ ++/* write n-bytes */ ++ ++size_t iof_write (iof *O, const void *data, size_t size) ++{ ++ size_t leftout, done = 0; ++ const char *s = (const char *)data; ++ if ((leftout = iof_left(O)) == 0) ++ if ((leftout = iof_output(O)) == 0) ++ return done; ++ while (size > leftout) ++ { ++ memcpy(O->pos, s, leftout * sizeof(uint8_t)); ++ size -= leftout; ++ done += leftout; ++ s += leftout; ++ O->pos = O->end; ++ if ((leftout = iof_output(O)) == 0) ++ return done; ++ } ++ if (size) ++ { ++ memcpy(O->pos, s, size * sizeof(uint8_t)); ++ O->pos += size; ++ done += size; ++ } ++ return done; ++} ++ ++/* write '\0'-terminated string */ ++ ++iof_status iof_puts (iof *O, const void *data) ++{ ++ const char *s = (const char *)data; ++ while (*s) ++ { ++ if (iof_writable(O)) ++ iof_set(O, *s++); ++ else ++ return IOFFULL; ++ } ++ return IOFEOF; // ? ++} ++ ++size_t iof_put_string (iof *O, const void *data) ++{ ++ const char *p, *s = (const char *)data; ++ for (p = s; *p != '\0' && iof_writable(O); iof_set(O, *p++)); ++ return p - s; ++} ++ ++/* write byte n-times */ ++ ++/* ++iof_status iof_repc (iof *O, char c, size_t bytes) ++{ ++ while (bytes) ++ { ++ if (iof_writable(O)) ++ iof_set(O, c); ++ else ++ return IOFFULL; ++ --bytes; ++ } ++ return IOFEOF; // ? ++} ++*/ ++ ++size_t iof_repc (iof *O, char c, size_t bytes) ++{ ++ size_t leftout, todo = bytes; ++ if ((leftout = iof_left(O)) == 0) ++ if ((leftout = iof_output(O)) == 0) ++ return 0; ++ while (bytes > leftout) ++ { ++ memset(O->pos, c, leftout); ++ bytes -= leftout; ++ O->pos = O->end; ++ if ((leftout = iof_output(O)) == 0) ++ return todo - bytes; ++ } ++ if (bytes) ++ { ++ memset(O->pos, c, bytes); ++ O->pos += bytes; ++ } ++ return todo; ++} ++ ++/* putfs */ ++ ++#define IOF_FMT_SIZE 1024 ++ ++size_t iof_putfs (iof *O, const char *format, ...) ++{ ++ static char buffer[IOF_FMT_SIZE]; ++ va_list args; ++ va_start(args, format); ++ if (vsnprintf(buffer, IOF_FMT_SIZE, format, args) > 0) ++ { ++ va_end(args); ++ return iof_put_string(O, buffer); ++ } ++ else ++ { ++ va_end(args); ++ return iof_write(O, buffer, IOF_FMT_SIZE); ++ } ++} ++ ++/* integer from iof; return 1 on success, 0 otherwise */ ++ ++int iof_get_int32 (iof *I, int32_t *number) ++{ ++ int sign, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ if (!base10_digit(c)) return 0; ++ iof_read_integer(I, c, *number); ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++int iof_get_intlw (iof *I, intlw_t *number) ++{ ++ int sign, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ if (!base10_digit(c)) return 0; ++ iof_read_integer(I, c, *number); ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++int iof_get_int64 (iof *I, int64_t *number) ++{ ++ int sign, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ if (!base10_digit(c)) return 0; ++ iof_read_integer(I, c, *number); ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++int iof_get_uint32 (iof *I, uint32_t *number) ++{ ++ int c = iof_char(I); ++ if (!base10_digit(c)) return 0; ++ iof_read_integer(I, c, *number); ++ return 1; ++} ++ ++int iof_get_uintlw (iof *I, uintlw_t *number) ++{ ++ int c = iof_char(I); ++ if (!base10_digit(c)) return 0; ++ iof_read_integer(I, c, *number); ++ return 1; ++} ++ ++int iof_get_uint64 (iof *I, uint64_t *number) ++{ ++ int c = iof_char(I); ++ if (!base10_digit(c)) return 0; ++ iof_read_integer(I, c, *number); ++ return 1; ++} ++ ++int iof_get_int32_radix (iof *I, int32_t *number, int radix) ++{ ++ int sign, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ if (!base10_digit(c)) return 0; ++ iof_read_radix(I, c, *number, radix); ++ if (sign) *number = -*number; ++ return 1; ++ ++} ++ ++int iof_get_intlw_radix (iof *I, intlw_t *number, int radix) ++{ ++ int sign, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ if (!base10_digit(c)) return 0; ++ iof_read_radix(I, c, *number, radix); ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++int iof_get_int64_radix (iof *I, int64_t *number, int radix) ++{ ++ int sign, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ if (!base10_digit(c)) return 0; ++ iof_read_radix(I, c, *number, radix); ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++int iof_get_uint32_radix (iof *I, uint32_t *number, int radix) ++{ ++ int c = iof_char(I); ++ if (!base10_digit(c)) return 0; ++ iof_read_radix(I, c, *number, radix); ++ return 1; ++} ++ ++int iof_get_uintlw_radix (iof *I, uintlw_t *number, int radix) ++{ ++ int c = iof_char(I); ++ if (!base10_digit(c)) return 0; ++ iof_read_radix(I, c, *number, radix); ++ return 1; ++} ++ ++int iof_get_uint64_radix (iof *I, uint64_t *number, int radix) ++{ ++ int c = iof_char(I); ++ if (!base10_digit(c)) return 0; ++ iof_read_radix(I, c, *number, radix); ++ return 1; ++} ++ ++/* get roman to uint16_t, cf. roman_to_uint16() from utilnumber.c*/ ++ ++/* todo: some trick in place of this macro horror? */ ++ ++#define roman1000(c) (c == 'M' || c == 'm') ++#define roman500(c) (c == 'D' || c == 'd') ++#define roman100(c) (c == 'C' || c == 'c') ++#define roman50(c) (c == 'L' || c == 'l') ++#define roman10(c) (c == 'X' || c == 'x') ++#define roman5(c) (c == 'V' || c == 'v') ++#define roman1(c) (c == 'I' || c == 'i') ++ ++#define roman100s(I, c) \ ++ (roman100(c) ? (100 + ((c = iof_next(I), roman100(c)) ? (100 + ((c = iof_next(I), roman100(c)) ? (c = iof_next(I), 100) : 0)) : 0)) : 0) ++#define roman10s(I, c) \ ++ (roman10(c) ? (10 + ((c = iof_next(I), roman10(c)) ? (10 + ((c = iof_next(I), roman10(c)) ? (c = iof_next(I), 10) : 0)) : 0)) : 0) ++#define roman1s(I, c) \ ++ (roman1(c) ? (1 + ((c = iof_next(I), roman1(c)) ? (1 + ((c = iof_next(I), roman1(c)) ? (c = iof_next(I), 1) : 0)) : 0)) : 0) ++ ++int iof_get_roman (iof *I, uint16_t *number) ++{ ++ int c; ++ /* M */ ++ for (*number = 0, c = iof_char(I); roman1000(c); *number += 1000, c = iof_next(I)); ++ /* D C */ ++ if (roman500(c)) ++ { ++ c = iof_next(I); ++ *number += 500 + roman100s(I, c); ++ } ++ else if (roman100(c)) ++ { ++ c = iof_next(I); ++ if (roman1000(c)) ++ { ++ c = iof_next(I); ++ *number += 900; ++ } ++ else if (roman500(c)) ++ { ++ c = iof_next(I); ++ *number += 400; ++ } ++ else ++ *number += 100 + roman100s(I, c); ++ } ++ /* L X */ ++ if (roman50(c)) ++ { ++ c = iof_next(I); ++ *number += 50 + roman10s(I, c); ++ } ++ else if (roman10(c)) ++ { ++ c = iof_next(I); ++ if (roman100(c)) ++ { ++ c = iof_next(I); ++ *number += 90; ++ } ++ else if (roman50(c)) ++ { ++ c = iof_next(I); ++ *number += 40; ++ } ++ else ++ *number += 10 + roman10s(I, c); ++ } ++ /* V I */ ++ if (roman5(c)) ++ { ++ c = iof_next(I); ++ *number += 5 + roman1s(I, c); ++ } ++ else if (roman1(c)) ++ { ++ c = iof_next(I); ++ if (roman10(c)) ++ { ++ c = iof_next(I); ++ *number += 9; ++ } ++ else if (roman5(c)) ++ { ++ c = iof_next(I); ++ *number += 4; ++ } ++ else ++ *number += 1 + roman1s(I, c); ++ } ++ return 1; ++} ++ ++/* double from iof; return 1 on success */ ++ ++int iof_get_double (iof *I, double *number) // cf. string_to_double() ++{ ++ int sign, exponent10, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ iof_scan_decimal(I, c, *number); ++ if (c == '.') ++ { ++ c = iof_next(I); ++ iof_scan_fraction(I, c, *number, exponent10); ++ } ++ else ++ exponent10 = 0; ++ if (c == 'e' || c == 'E') ++ { ++ c = iof_next(I); ++ iof_scan_exponent10(I, c, exponent10); ++ } ++ double_exp10(*number, exponent10); ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++int iof_get_float (iof *I, float *number) // cf. string_to_float() in utilnumber.c ++{ ++ int sign, exponent10, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ iof_scan_decimal(I, c, *number); ++ if (c == '.') ++ { ++ c = iof_next(I); ++ iof_scan_fraction(I, c, *number, exponent10); ++ } ++ else ++ exponent10 = 0; ++ if (c == 'e' || c == 'E') ++ { ++ c = iof_next(I); ++ iof_scan_exponent10(I, c, exponent10); ++ } ++ float_exp10(*number, exponent10); ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++int iof_conv_double (iof *I, double *number) // cf. convert_to_double() in utilnumber.c ++{ ++ int sign, exponent10, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ iof_scan_decimal(I, c, *number); ++ if (c == '.' || c == ',') ++ { ++ c = iof_next(I); ++ iof_scan_fraction(I, c, *number, exponent10); ++ if (exponent10 < 0) ++ double_negative_exp10(*number, exponent10); ++ } ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++int iof_conv_float (iof *I, float *number) // cf. convert_to_float() ++{ ++ int sign, exponent10, c = iof_char(I); ++ iof_scan_sign(I, c, sign); ++ iof_scan_decimal(I, c, *number); ++ if (c == '.' || c == ',') ++ { ++ c = iof_next(I); ++ iof_scan_fraction(I, c, *number, exponent10); ++ if (exponent10 < 0) ++ float_negative_exp10(*number, exponent10); ++ } ++ if (sign) *number = -*number; ++ return 1; ++} ++ ++/* integer to iof; return a number of written bytes */ ++ ++#define iof_copy_number_buffer(O, s, p) for (p = s; *p && iof_writable(O); iof_set(O, *p), ++p) ++ ++size_t iof_put_int32 (iof *O, int32_t number) ++{ ++ const char *s, *p; ++ s = int32_to_string(number); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_intlw (iof *O, intlw_t number) ++{ ++ const char *s, *p; ++ s = intlw_to_string(number); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_int64 (iof *O, int64_t number) ++{ ++ const char *s, *p; ++ s = int64_to_string(number); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_uint32 (iof *O, uint32_t number) ++{ ++ const char *s, *p; ++ s = uint32_to_string(number); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_uintlw (iof *O, uintlw_t number) ++{ ++ const char *s, *p; ++ s = uintlw_to_string(number); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_uint64 (iof *O, uint64_t number) ++{ ++ const char *s, *p; ++ s = uint64_to_string(number); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_int32_radix (iof *O, int32_t number, int radix) ++{ ++ const char *s, *p; ++ s = int32_to_radix(number, radix); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_intlw_radix (iof *O, intlw_t number, int radix) ++{ ++ const char *s, *p; ++ s = intlw_to_radix(number, radix); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_int64_radix (iof *O, int64_t number, int radix) ++{ ++ const char *s, *p; ++ s = int64_to_radix(number, radix); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_uint32_radix (iof *O, uint32_t number, int radix) ++{ ++ const char *s, *p; ++ s = uint32_to_radix(number, radix); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_uintlw_radix (iof *O, uintlw_t number, int radix) ++{ ++ const char *s, *p; ++ s = uintlw_to_radix(number, radix); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_uint64_radix (iof *O, uint64_t number, int radix) ++{ ++ const char *s, *p; ++ s = uint64_to_radix(number, radix); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++/* roman numerals */ ++ ++size_t iof_put_roman_uc (iof *O, uint16_t number) ++{ ++ const char *s, *p; ++ s = uint16_to_roman_uc(number); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_roman_lc (iof *O, uint16_t number) ++{ ++ const char *s, *p; ++ s = uint16_to_roman_lc(number); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++/* double/float to iof; return the number of written bytes */ ++ ++size_t iof_put_double (iof *O, double number, int digits) ++{ ++ const char *s, *p; ++ s = double_to_string(number, digits); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++size_t iof_put_float (iof *O, float number, int digits) ++{ ++ const char *s, *p; ++ s = float_to_string(number, digits); ++ iof_copy_number_buffer(O, s, p); ++ return p - s; ++} ++ ++/* iof to binary integer; pretty common */ ++ ++int iof_get_be_uint2 (iof *I, uint32_t *pnumber) ++{ ++ int c1, c2; ++ if ((c1 = iof_get(I)) < 0 || (c2 = iof_get(I)) < 0) ++ return 0; ++ *pnumber = (c1<<8)|c2; ++ return 1; ++} ++ ++int iof_get_be_uint3 (iof *I, uint32_t *pnumber) ++{ ++ int c1, c2, c3; ++ if ((c1 = iof_get(I)) < 0 || (c2 = iof_get(I)) < 0 || (c3 = iof_get(I)) < 0) ++ return 0; ++ *pnumber = (c1<<16)|(c2<<8)|c3; ++ return 1; ++} ++ ++int iof_get_be_uint4 (iof *I, uint32_t *pnumber) ++{ ++ int c1, c2, c3, c4; ++ if ((c1 = iof_get(I)) < 0 || (c2 = iof_get(I)) < 0 || (c3 = iof_get(I)) < 0 || (c4 = iof_get(I)) < 0) ++ return 0; ++ *pnumber = (c1<<24)|(c2<<16)|(c3<<8)|c4; ++ return 1; ++} ++ ++int iof_get_le_uint2 (iof *I, uint32_t *pnumber) ++{ ++ int c1, c2; ++ if ((c1 = iof_get(I)) < 0 || (c2 = iof_get(I)) < 0) ++ return 0; ++ *pnumber = (c2<<8)|c1; ++ return 1; ++} ++ ++int iof_get_le_uint3 (iof *I, uint32_t *pnumber) ++{ ++ int c1, c2, c3; ++ if ((c1 = iof_get(I)) < 0 || (c2 = iof_get(I)) < 0 || (c3 = iof_get(I)) < 0) ++ return 0; ++ *pnumber = (c3<<16)|(c2<<8)|c1; ++ return 1; ++} ++ ++int iof_get_le_uint4 (iof *I, uint32_t *pnumber) ++{ ++ int c1, c2, c3, c4; ++ if ((c1 = iof_get(I)) < 0 || (c2 = iof_get(I)) < 0 || (c3 = iof_get(I)) < 0 || (c4 = iof_get(I)) < 0) ++ return 0; ++ *pnumber = (c4<<24)|(c3<<16)|(c2<<8)|c1; ++ return 1; ++} ++ ++/* iof input data */ ++ ++uint8_t * iof_file_input_data (iof_file *iofile, size_t *psize, int *isnew) ++{ ++ uint8_t *data; ++ if (iofile->flags & IOF_DATA) ++ { ++ data = iofile->buf; ++ *psize = iofile->end - iofile->buf; ++ *isnew = 0; ++ return data; ++ } ++ if (iof_file_reopen(iofile)) ++ { ++ data = iof_copy_file_handle_data(iof_file_get_fh(iofile), psize); ++ *isnew = 1; ++ iof_file_reclose(iofile); ++ return data; ++ } ++ return NULL; ++} ++ ++/* ++uint8_t * iof_file_reader_data (iof_file *iofile, size_t *size) ++{ ++ uint8_t *data; ++ if (!(iofile->flags & IOF_DATA) || iofile->pos == NULL || (*size = (size_t)iof_left(iofile)) == 0) ++ return NULL; ++ if (iofile->flags & IOF_BUFFER_ALLOC) ++ { ++ data = iofile->buf; // iofile->pos; // returned must be freeable, makes sense when ->buf == ->pos ++ iofile->flags &= ~IOF_BUFFER_ALLOC; ++ iofile->buf = iofile->pos = iofile->end = NULL; ++ return data; ++ } ++ data = (uint8_t *)util_malloc(*size); ++ memcpy(data, iofile->buf, *size); ++ return data; ++} ++ ++uint8_t * iof_file_writer_data (iof_file *iofile, size_t *size) ++{ ++ uint8_t *data; ++ if (!(iofile->flags & IOF_DATA) || iofile->buf == NULL || (*size = (size_t)iof_size(iofile)) == 0) ++ return NULL; ++ if (iofile->flags & IOF_BUFFER_ALLOC) ++ { ++ iofile->flags &= ~IOF_BUFFER_ALLOC; ++ data = iofile->buf; ++ iofile->buf = iofile->pos = iofile->end = NULL; ++ return data; ++ } ++ data = (uint8_t *)util_malloc(*size); ++ memcpy(data, iofile->buf, *size); ++ return data; ++} ++*/ ++ ++uint8_t * iof_reader_data (iof *I, size_t *psize) ++{ ++ uint8_t *data; ++ *psize = (size_t)iof_left(I); ++ if (I->flags & IOF_BUFFER_ALLOC) ++ { ++ data = I->buf; // actually I->pos, but we have to return something freeable ++ I->flags &= ~IOF_BUFFER_ALLOC; ++ I->buf = NULL; ++ } ++ else ++ { ++ data = util_malloc(*psize); ++ memcpy(data, I->pos, *psize); ++ } ++ iof_close(I); ++ return data; ++} ++ ++ ++uint8_t * iof_writer_data (iof *O, size_t *psize) ++{ ++ uint8_t *data; ++ *psize = (size_t)iof_size(O); ++ if (O->flags & IOF_BUFFER_ALLOC) ++ { ++ data = O->buf; ++ O->flags &= ~IOF_BUFFER_ALLOC; ++ O->buf = NULL; ++ } ++ else ++ { ++ data = util_malloc(*psize); ++ memcpy(data, O->buf, *psize); ++ } ++ iof_close(O); ++ return data; ++} ++ ++size_t iof_reader_to_file_handle (iof *I, FILE *file) ++{ ++ size_t size; ++ for (size = 0; iof_readable(I); I->pos = I->end) ++ size += fwrite(I->buf, sizeof(uint8_t), iof_left(I), file); ++ return size; ++} ++ ++size_t iof_reader_to_file (iof *I, const char *filename) ++{ ++ FILE *file; ++ size_t size; ++ if ((file = fopen(filename, "wb")) == NULL) ++ return 0; ++ for (size = 0; iof_readable(I); I->pos = I->end) ++ size += fwrite(I->buf, sizeof(uint8_t), iof_left(I), file); ++ fclose(file); ++ return size; ++} ++ ++/* debug */ ++ ++size_t iof_data_to_file (const void *data, size_t size, const char *filename) ++{ ++ FILE *fh; ++ if ((fh = fopen(filename, "wb")) == NULL) ++ return 0; ++ // size = fwrite(data, size, sizeof(uint8_t), fh); // WRONG, this always returns 1, as fwrite returns the number of elements successfully written out ++ size = fwrite(data, sizeof(uint8_t), size, fh); ++ fclose(fh); ++ return size; ++} ++ ++size_t iof_result_to_file_handle (iof *F, FILE *file) ++{ ++ const void *data; ++ size_t size; ++ data = iof_result(F, size); ++ return iof_data_to_file_handle(data, size, file); ++} ++ ++size_t iof_result_to_file (iof *F, const char *filename) ++{ ++ const void *data; ++ size_t size; ++ data = iof_result(F, size); ++ return iof_data_to_file(data, size, filename); ++} ++ ++void iof_debug (iof *I, const char *filename) ++{ ++ FILE *file = fopen(filename, "wb"); ++ if (file != NULL) ++ { ++ fprintf(file, ">>> buf %p <<<\n", I->buf); ++ fwrite(I->buf, sizeof(uint8_t), iof_size(I), file); ++ fprintf(file, "\n>>> pos %p (%ld) <<<\n", I->pos, (long)iof_size(I)); ++ fwrite(I->pos, sizeof(uint8_t), iof_left(I), file); ++ fprintf(file, "\n>>> end %p (%ld) <<<\n", I->end, (long)iof_left(I)); ++ fwrite(I->end, sizeof(uint8_t), I->space - iof_space(I), file); ++ fprintf(file, "\n>>> end of buffer %p (%ld) <<<\n", I->buf + I->space, (long)(I->buf + I->space - I->end)); ++ fclose(file); ++ } ++} ++ ++/* common filters api */ ++ ++/* sizes of filter states on x64 ++size of iof_filter: 640 (no longer used; sizeof(iof) + sizeof larger state) ++size of file_state: 16 ++size of stream_state: 16 ++size of flate_state: 104 ++size of lzw_state: 56 ++size of predictor_state: 104 ++size of basexx_state: 48 ++size of basexx_state: 48 ++size of basexx_state: 48 ++size of eexec_state: 40 ++size of runlength_state: 24 ++size of rc4_state: 24 ++size of aes_state: 72 ++size of img_state: 576 ++size of img: 496 ++*/ ++ ++typedef struct iof_heap iof_heap; ++ ++struct iof_heap { ++ uint8_t *data, *pos; ++ size_t size, space; ++ iof_heap *next, *prev; ++ int refcount; ++}; ++ ++typedef struct { ++ iof_heap *heap; ++} iof_heap_ghost; ++ ++static iof_heap * iof_buffers_heap = NULL; ++static iof_heap * iof_filters_heap = NULL; ++ ++#define IOF_HEAP_FILTERS_COUNT 4 ++#define IOF_BUFFER_SIZE 262144 // (1<<18) ++#define IOF_FILTER_SIZE 1024 ++// sizeof(iof_filter) on x64 is now 640, img_state 576, img 496, others 16-104 ++#define IOF_BUFFER_HEAP_SIZE (IOF_HEAP_FILTERS_COUNT * (IOF_BUFFER_SIZE + sizeof(iof_heap_ghost))) ++#define IOF_FILTER_HEAP_SIZE (IOF_HEAP_FILTERS_COUNT * (IOF_FILTER_SIZE + sizeof(iof_heap_ghost))) ++ ++static iof_heap * iof_heap_new (size_t space) ++{ ++ iof_heap *iofheap; ++ iofheap = (iof_heap *)util_malloc(sizeof(iof_heap) + space); ++ iofheap->data = iofheap->pos = (uint8_t *)(iofheap + 1); ++ iofheap->size = iofheap->space = space; ++ iofheap->next = NULL; ++ iofheap->prev = NULL; ++ iofheap->refcount = 0; ++ return iofheap; ++} ++ ++#define iof_heap_free(iofheap) util_free(iofheap) ++ ++void iof_filters_init (void) ++{ ++ if (iof_buffers_heap == NULL) ++ iof_buffers_heap = iof_heap_new(IOF_BUFFER_HEAP_SIZE); ++ if (iof_filters_heap == NULL) ++ iof_filters_heap = iof_heap_new(IOF_FILTER_HEAP_SIZE); ++} ++ ++void iof_filters_free (void) ++{ ++ iof_heap *heap, *next; ++ for (heap = iof_buffers_heap; heap != NULL; heap = next) ++ { ++ next = heap->next; ++ if (heap->refcount != 0) ++ loggerf("not closed iof filters left (%d)", heap->refcount); ++ if (next != NULL) ++ loggerf("iof filters heap left"); ++ iof_heap_free(heap); ++ } ++ iof_buffers_heap = NULL; ++ for (heap = iof_filters_heap; heap != NULL; heap = next) ++ { ++ next = heap->next; ++ if (heap->refcount != 0) ++ loggerf("not closed iof buffers left (%d)", heap->refcount); ++ if (next != NULL) ++ loggerf("iof buffers heap left"); ++ iof_heap_free(heap); ++ } ++ iof_filters_heap = NULL; ++} ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#define iof_heap_get(hp, ghost, data, siz) \ ++ (ghost = (iof_heap_ghost *)((void*)((hp)->pos)), \ ++ ghost->heap = hp, \ ++ data = (uint8_t *)(ghost + 1), \ ++ (hp)->pos += siz, \ ++ (hp)->size -= siz, \ ++ ++(hp)->refcount) ++#else ++#define iof_heap_get(hp, ghost, data, siz) \ ++ (ghost = (iof_heap_ghost *)((hp)->pos), \ ++ ghost->heap = hp, \ ++ data = (uint8_t *)(ghost + 1), \ ++ (hp)->pos += siz, \ ++ (hp)->size -= siz, \ ++ ++(hp)->refcount) ++ ++#endif ++ ++ ++static void * iof_heap_take (iof_heap **pheap, size_t size) ++{ ++ uint8_t *data; ++ iof_heap_ghost *ghost; ++ iof_heap *heap, *newheap, *next; ++ ++ heap = *pheap; ++ size += sizeof(iof_heap_ghost); ++ if (heap->size >= size) ++ { /* take cheap mem from main heap */ ++ iof_heap_get(heap, ghost, data, size); ++ return data; ++ } ++ if (size <= heap->space >> 1) ++ { /* make new cheap heap, make it front */ ++ *pheap = newheap = iof_heap_new(heap->space); ++ newheap->next = heap; ++ heap->prev = newheap; ++ iof_heap_get(newheap, ghost, data, size); ++ return data; ++ } ++ /* size much larger than expected? should not happen. ++ make a single-item heap, keep the front heap intact. */ ++ newheap = iof_heap_new(size); ++ if ((next = heap->next) != NULL) ++ { ++ newheap->next = next; ++ next->prev = newheap; ++ } ++ heap->next = newheap; ++ newheap->prev = heap; ++ iof_heap_get(newheap, ghost, data, size); ++ return data; ++} ++ ++void iof_heap_back (void *data) ++{ ++ iof_heap_ghost *ghost; ++ iof_heap *heap, *next, *prev; ++ ++ ghost = ((iof_heap_ghost *)data) - 1; ++ heap = ghost->heap; ++ if (heap->refcount == 0) ++ loggerf("invalid use of iof heap, refcount < 0"); ++ if (--heap->refcount <= 0) ++ { ++ if ((prev = heap->prev) != NULL) ++ { /* free the heap */ ++ if ((next = heap->next) != NULL) ++ prev->next = next, next->prev = prev; ++ else ++ prev->next = NULL; ++ iof_heap_free(heap); ++ } ++ else ++ { /* this is the front heap, just reset */ ++ heap->pos = heap->data; ++ heap->size = heap->space; ++ } ++ } ++} ++ ++void * iof_filter_new (size_t size) ++{ // to be removed ++ void *data; ++ ++ iof_filters_init(); ++ data = iof_heap_take(&iof_filters_heap, size); ++ return memset(data, 0, size); ++} ++ ++static uint8_t * iof_filter_buffer_new (size_t *psize) ++{ ++ iof_filters_init(); ++ *psize = IOF_BUFFER_SIZE; ++ return iof_heap_take(&iof_buffers_heap, IOF_BUFFER_SIZE); ++} ++ ++iof * iof_filter_reader_new (iof_handler handler, size_t statesize, void **pstate) ++{ ++ iof *F; ++ void *filter; ++ uint8_t *buffer; ++ size_t buffersize; ++ ++ iof_filters_init(); ++ filter = iof_heap_take(&iof_filters_heap, sizeof(iof) + statesize); ++ F = (iof *)memset(filter, 0, sizeof(iof) + statesize); ++ buffer = iof_filter_buffer_new(&buffersize); ++ iof_reader_buffer(F, buffer, buffersize); ++ F->flags |= IOF_HEAP|IOF_BUFFER_HEAP; ++ F->more = handler; ++ *pstate = (F + 1); ++ return F; ++} ++ ++iof * iof_filter_reader_with_buffer_new (iof_handler handler, size_t statesize, void **pstate, void *buffer, size_t buffersize) ++{ // for filters that has own buffer (string, some image filters) ++ iof *F; ++ void *filter; ++ iof_filters_init(); ++ filter = iof_heap_take(&iof_filters_heap, sizeof(iof) + statesize); ++ F = (iof *)memset(filter, 0, sizeof(iof) + statesize); ++ iof_reader_buffer(F, buffer, buffersize); ++ F->flags |= IOF_HEAP; ++ F->more = handler; ++ *pstate = (F + 1); ++ return F; ++} ++ ++iof * iof_filter_writer_new (iof_handler handler, size_t statesize, void **pstate) ++{ ++ iof *F; ++ void *filter; ++ uint8_t *buffer; ++ size_t buffersize; ++ ++ iof_filters_init(); ++ filter = iof_heap_take(&iof_filters_heap, sizeof(iof) + statesize); ++ F = (iof *)memset(filter, 0, sizeof(iof) + statesize); ++ buffer = iof_filter_buffer_new(&buffersize); ++ iof_writer_buffer(F, buffer, buffersize); ++ F->flags |= IOF_HEAP|IOF_BUFFER_HEAP; ++ F->more = handler; ++ *pstate = (F + 1); ++ return F; ++} ++ ++iof * iof_filter_writer_with_buffer_new (iof_handler handler, size_t statesize, void **pstate, void *buffer, size_t size) ++{ ++ iof *F; ++ void *filter; ++ size_t buffersize; ++ ++ iof_filters_init(); ++ filter = iof_heap_take(&iof_filters_heap, sizeof(iof) + statesize); ++ F = (iof *)memset(filter, 0, sizeof(iof) + statesize); ++ buffer = iof_filter_buffer_new(&buffersize); ++ iof_writer_buffer(F, buffer, buffersize); ++ F->flags |= IOF_HEAP; ++ F->more = handler; ++ *pstate = (F + 1); ++ return F; ++} ++ ++/* close */ ++ ++#define iof_close_next(F) ((void)(iof_decref((F)->next), (F)->next = NULL, 0)) ++/* when filter creation fails, we should take care to destroy the filter but leave ->next intact */ ++#define iof_clear_next(F) ((void)(iof_unref((F)->next), (F)->next = NULL, 0)) ++ ++#define iof_close_buffer(F) ((void)\ ++ ((F)->buf != NULL ? \ ++ ((F->flags & IOF_BUFFER_ALLOC) ? (util_free((F)->buf), (F)->buf = NULL, 0) : \ ++ ((F->flags & IOF_BUFFER_HEAP) ? (iof_filter_buffer_free((F)->buf), (F)->buf = NULL, 0) : ((F)->buf = NULL, 0))) : 0)) ++ ++/* closing underlying file handle */ ++ ++static void iof_close_file (iof *F) ++{ ++ FILE *file; ++ //if (F->flags & IOF_FILE_HANDLE) ++ //{ ++ if ((file = F->file) != NULL) ++ { ++ if (F->flags & IOF_CLOSE_FILE) ++ fclose(F->file); ++ F->file = NULL; ++ } ++ //} ++} ++ ++/* a very special variant for reader filters initiated with iof_file_reopen(). It also calls ++ iof_file_reclose(), which takes an effect only if previously reopened, but better to keep ++ all this thin ice separated. Used in filters: iofile_reader, iofile_stream_reader, image ++ decoders. */ ++ ++static void iof_close_iofile (iof *F) ++{ ++ iof_file *iofile; ++ //if (F->flags & IOF_FILE) ++ //{ ++ if ((iofile = F->iofile) != NULL) ++ { ++ iof_file_unsync(iofile, NULL); ++ iof_file_reclose(iofile); // takes an effect iff prevoiusly reopened ++ iof_file_decref(iofile); ++ F->iofile = NULL; ++ } ++ //} ++} ++ ++void iof_free (iof *F) ++{ ++ if (F->flags & IOF_FILE_HANDLE) ++ iof_close_file(F); ++ else if (F->flags & IOF_FILE) ++ iof_close_iofile(F); ++ else if (F->flags & IOF_NEXT) ++ iof_close_next(F); ++ iof_close_buffer(F); ++ if (F->flags & IOF_HEAP) ++ iof_filter_free(F); ++ else if (F->flags & IOF_ALLOC) ++ util_free(F); ++} ++ ++void iof_discard (iof *F) ++{ // so far used only on failed filters creation; as iof_free() but don't dare to release ->next ++ if (F->flags & IOF_FILE_HANDLE) ++ iof_close_file(F); ++ else if (F->flags & IOF_FILE) ++ iof_close_iofile(F); ++ else if (F->flags & IOF_NEXT) ++ iof_close_next(F); ++ iof_close_buffer(F); ++ if (F->flags & IOF_HEAP) ++ iof_filter_free(F); ++ else if (F->flags & IOF_ALLOC) ++ util_free(F); ++} ++ ++/* resizing buffer */ ++ ++size_t iof_resize_buffer_to (iof *O, size_t space) ++{ ++ uint8_t *buf; ++ ++ if (O->flags & IOF_BUFFER_ALLOC) ++ { ++ buf = (uint8_t *)util_realloc(O->buf, space); ++ } ++ else ++ { ++ buf = (uint8_t *)util_malloc(space); ++ memcpy(buf, O->buf, iof_size(O)); ++ if (O->flags & IOF_BUFFER_HEAP) ++ { ++ iof_filter_buffer_free(O->buf); ++ O->flags &= ~IOF_BUFFER_HEAP; ++ } ++ O->flags |= IOF_BUFFER_ALLOC; ++ ++ } ++ O->pos = buf + iof_size(O); ++ O->end = buf + space; ++ O->buf = buf; ++ O->space = space; ++ return iof_left(O); ++} ++ ++/* */ ++ ++size_t iof_decoder_retval (iof *I, const char *type, iof_status status) ++{ ++ switch (status) ++ { ++ case IOFERR: ++ case IOFEMPTY: // should never happen as we set state.flush = 1 on decoders init ++ loggerf("%s decoder error (%d, %s)", type, status, iof_status_kind(status)); ++ I->flags |= IOF_STOPPED; ++ return 0; ++ case IOFEOF: // this is the last chunk, ++ I->flags |= IOF_STOPPED; // so stop it and fall ++ case IOFFULL: // prepare pointers to read from I->buf ++ I->end = I->pos; ++ I->pos = I->buf; ++ return I->end - I->buf; ++ } ++ loggerf("%s decoder bug, invalid retval %d", type, status); ++ return 0; ++} ++ ++size_t iof_encoder_retval (iof *O, const char *type, iof_status status) ++{ ++ switch (status) ++ { ++ case IOFERR: ++ case IOFFULL: ++ loggerf("%s encoder error (%d, %s)", type, status, iof_status_kind(status)); ++ return 0; ++ case IOFEMPTY: ++ O->pos = O->buf; ++ O->end = O->buf + O->space; ++ return O->space; ++ case IOFEOF: ++ return 0; ++ } ++ loggerf("%s encoder bug, invalid retval %d", type, status); ++ return 0; ++} ++ ++/* file/stream state */ ++ ++typedef struct { ++ size_t length; ++ size_t offset; ++} file_state; ++ ++ ++#define file_state_init(state, off, len) ((state)->offset = off, (state)->length = len) ++ ++typedef struct { ++ size_t length; ++ size_t offset; ++} stream_state; ++ ++#define stream_state_init(state, off, len) ((state)->offset = off, (state)->length = len) ++ ++static size_t file_read (iof *I) ++{ ++ size_t bytes, tail; ++ if (I->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(I); ++ if ((bytes = tail + fread(I->buf + tail, sizeof(uint8_t), I->space - tail, I->file)) < I->space) ++ I->flags |= IOF_STOPPED; ++ I->pos = I->buf; ++ I->end = I->buf + bytes; ++ return bytes; ++} ++ ++static size_t iofile_read (iof *I, size_t *poffset) ++{ ++ size_t bytes, tail; ++ if (I->flags & IOF_STOPPED) ++ return 0; ++ iof_file_sync(I->iofile, poffset); ++ tail = iof_tail(I); ++ if ((bytes = tail + iof_file_read(I->buf + tail, sizeof(uint8_t), I->space - tail, I->iofile)) < I->space) ++ { ++ I->flags |= IOF_STOPPED; ++ iof_file_unsync(I->iofile, poffset); ++ } ++ I->pos = I->buf; ++ I->end = I->buf + bytes; ++ return bytes; ++} ++ ++static size_t file_load (iof *I) ++{ ++ size_t bytes, left, tail; ++ if (I->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(I); ++ I->pos = I->buf + tail; ++ I->end = I->buf + I->space; /* don't assume its done when initializing the filter */ ++ left = I->space - tail; ++ do { ++ bytes = fread(I->pos, sizeof(uint8_t), left, I->file); ++ I->pos += bytes; ++ } while (bytes == left && (left = iof_resize_buffer(I)) > 0); ++ I->flags |= IOF_STOPPED; ++ return iof_loaded(I); ++} ++ ++static size_t iofile_load (iof *I, size_t *poffset) ++{ ++ size_t bytes, left, tail; ++ if (I->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(I); ++ I->pos = I->buf + tail; ++ I->end = I->buf + I->space; /* don't assume its done when initializing the filter */ ++ left = I->space - tail; ++ iof_file_sync(I->iofile, poffset); ++ do { ++ bytes = iof_file_read(I->pos, sizeof(uint8_t), left, I->iofile); ++ I->pos += bytes; ++ } while (bytes == left && (left = iof_resize_buffer(I)) > 0); ++ I->flags |= IOF_STOPPED; ++ iof_file_unsync(I->iofile, poffset); ++ return iof_loaded(I); ++} ++ ++static size_t filter_file_reader (iof *I, iof_mode mode) ++{ ++ switch (mode) ++ { ++ case IOFREAD: ++ return file_read(I); ++ case IOFLOAD: ++ return file_load(I); ++ case IOFCLOSE: ++ iof_free(I); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++static size_t filter_iofile_reader (iof *I, iof_mode mode) ++{ ++ file_state *state; ++ state = iof_filter_state(file_state *, I); ++ switch (mode) ++ { ++ case IOFREAD: ++ return iofile_read(I, &state->offset); ++ case IOFLOAD: ++ return iofile_load(I, &state->offset); ++ case IOFCLOSE: ++ iof_free(I); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++static size_t file_write (iof *O, int flush) ++{ ++ size_t bytes; ++ if ((bytes = iof_size(O)) > 0) ++ if (bytes != fwrite(O->buf, sizeof(uint8_t), bytes, O->file)) ++ return 0; ++ if (flush) ++ fflush(O->file); ++ O->end = O->buf + O->space; // remains intact actually ++ O->pos = O->buf; ++ return O->space; ++} ++ ++static size_t iofile_write (iof *O, size_t *poffset, int flush) ++{ ++ size_t bytes; ++ iof_file_sync(O->iofile, poffset); ++ if ((bytes = iof_size(O)) > 0) ++ { ++ if (bytes != iof_file_write(O->buf, sizeof(uint8_t), bytes, O->iofile)) ++ { ++ iof_file_unsync(O->iofile, poffset); ++ return 0; ++ } ++ } ++ if (flush) ++ iof_file_flush(O->iofile); ++ O->end = O->buf + O->space; // remains intact actually ++ O->pos = O->buf; ++ return O->space; ++} ++ ++static size_t filter_file_writer (iof *O, iof_mode mode) ++{ ++ switch (mode) ++ { ++ case IOFWRITE: ++ return file_write(O, 0); ++ case IOFFLUSH: ++ return file_write(O, 1); ++ case IOFCLOSE: ++ file_write(O, 1); ++ iof_free(O); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++static size_t filter_iofile_writer (iof *O, iof_mode mode) ++{ ++ file_state *state; ++ state = iof_filter_state(file_state *, O); ++ switch (mode) ++ { ++ case IOFWRITE: ++ return iofile_write(O, &state->offset, 0); ++ case IOFFLUSH: ++ return iofile_write(O, &state->offset, 1); ++ case IOFCLOSE: ++ iofile_write(O, &state->offset, 1); ++ iof_free(O); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++/* filter from FILE* */ ++ ++iof * iof_filter_file_handle_reader (FILE *file) ++{ ++ iof *I; ++ file_state *state; ++ if (file == NULL) ++ return NULL; ++ I = iof_filter_reader(filter_file_reader, sizeof(file_state), &state); ++ iof_setup_file(I, file); ++ file_state_init(state, 0, 0); ++ return I; ++} ++ ++iof * iof_filter_file_handle_writer (FILE *file) ++{ ++ iof *O; ++ file_state *state; ++ if (file == NULL) ++ return NULL; ++ O = iof_filter_writer(filter_file_writer, sizeof(file_state), &state); ++ iof_setup_file(O, file); ++ file_state_init(state, 0, 0); ++ return O; ++} ++ ++/* filter from iof_file * */ ++ ++iof * iof_filter_iofile_reader (iof_file *iofile, size_t offset) ++{ ++ iof *I; ++ file_state *state; ++ if (!iof_file_reopen(iofile)) ++ return NULL; ++ I = iof_filter_reader(filter_iofile_reader, sizeof(file_state), &state); ++ iof_setup_iofile(I, iofile); ++ file_state_init(state, offset, 0); ++ return I; ++} ++ ++iof * iof_filter_iofile_writer (iof_file *iofile, size_t offset) ++{ ++ iof *O; ++ file_state *state; ++ O = iof_filter_writer(filter_iofile_writer, sizeof(file_state), &state); ++ iof_setup_iofile(O, iofile); ++ file_state_init(state, offset, 0); ++ return O; ++} ++ ++/* filter from filename */ ++ ++iof * iof_filter_file_reader (const char *filename) ++{ ++ iof *I; ++ file_state *state; ++ FILE *file; ++ if ((file = fopen(filename, "rb")) == NULL) ++ return NULL; ++ I = iof_filter_reader(filter_file_reader, sizeof(file_state), &state); ++ iof_setup_file(I, file); ++ file_state_init(state, 0, 0); ++ I->flags |= IOF_CLOSE_FILE; ++ return I; ++} ++ ++iof * iof_filter_file_writer (const char *filename) ++{ ++ iof *O; ++ file_state *state; ++ FILE *file; ++ if ((file = fopen(filename, "wb")) == NULL) ++ return NULL; ++ O = iof_filter_writer(filter_file_writer, sizeof(file_state), &state); ++ iof_setup_file(O, file); ++ file_state_init(state, 0, 0); ++ O->flags |= IOF_CLOSE_FILE; ++ return O; ++} ++ ++/* from string */ ++ ++static size_t dummy_handler (iof *I, iof_mode mode) ++{ ++ switch (mode) ++ { ++ case IOFCLOSE: ++ iof_free(I); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++iof * iof_filter_string_reader (const void *s, size_t length) ++{ ++ iof *I; ++ void *dummy; ++ I = iof_filter_reader_with_buffer(dummy_handler, 0, &dummy, NULL, 0); ++ I->rbuf = I->rpos = (const uint8_t *)s; ++ I->rend = (const uint8_t *)s + length; ++ // I->space = length; ++ return I; ++} ++ ++iof * iof_filter_string_writer (const void *s, size_t length) ++{ ++ iof *O; ++ void *dummy; ++ O = iof_filter_reader_with_buffer(dummy_handler, 0, &dummy, NULL, 0); ++ O->rbuf = O->rpos = (const uint8_t *)s; ++ O->rend = (const uint8_t *)s + length; ++ // O->space = length; ++ return O; ++} ++ ++iof * iof_filter_buffer_writer (size_t size) ++{ // filter alternative of iof_buffer_create() ++ iof *O; ++ void *dummy; ++ uint8_t *buffer; ++ if (size > IOF_BUFFER_SIZE) ++ { ++ buffer = (uint8_t *)util_malloc(size); ++ O = iof_filter_writer_with_buffer(iof_mem_handler, 0, &dummy, buffer, size); ++ O->flags |= IOF_BUFFER_ALLOC; ++ return O; ++ } ++ return iof_filter_writer(iof_mem_handler, 0, &dummy); ++} ++ ++/* stream */ ++ ++static size_t file_stream_read (iof *I, size_t *plength) ++{ ++ size_t bytes, tail; ++ if (I->flags & IOF_STOPPED || *plength == 0) ++ return 0; ++ tail = iof_tail(I); ++ if (I->space - tail >= *plength) ++ { ++ bytes = tail + fread(I->buf + tail, sizeof(uint8_t), *plength, I->file); ++ I->flags |= IOF_STOPPED; ++ *plength = 0; ++ } ++ else ++ { ++ bytes = tail + fread(I->buf + tail, sizeof(uint8_t), I->space - tail, I->file); ++ *plength -= bytes - tail; ++ } ++ I->pos = I->buf; ++ I->end = I->buf + bytes; ++ return bytes; ++} ++ ++static size_t iofile_stream_read (iof *I, size_t *plength, size_t *poffset) ++{ ++ size_t bytes, tail; ++ if (I->flags & IOF_STOPPED || *plength == 0) ++ return 0; ++ tail = iof_tail(I); ++ iof_file_sync(I->iofile, poffset); ++ if (I->space - tail >= *plength) ++ { ++ bytes = tail + iof_file_read(I->buf + tail, sizeof(uint8_t), *plength, I->iofile); ++ iof_file_unsync(I->iofile, poffset); ++ I->flags |= IOF_STOPPED; ++ *plength = 0; ++ } ++ else ++ { ++ bytes = tail + iof_file_read(I->buf + tail, sizeof(uint8_t), I->space - tail, I->iofile); ++ *plength -= bytes - tail; ++ } ++ I->pos = I->buf; ++ I->end = I->buf + bytes; ++ return bytes; ++} ++ ++static size_t file_stream_load (iof *I, size_t *plength) ++{ ++ size_t bytes, tail; ++ if (I->flags & IOF_STOPPED || *plength == 0) ++ return 0; ++ tail = iof_tail(I); ++ if (I->space - tail < *plength) ++ if (iof_resize_buffer_to(I, tail + *plength) == 0) ++ return 0; ++ bytes = tail + fread(I->buf + tail, sizeof(uint8_t), *plength, I->file); ++ I->flags |= IOF_STOPPED; ++ *plength = 0; ++ I->pos = I->buf; ++ I->end = I->buf + bytes; ++ return bytes; ++} ++ ++static size_t iofile_stream_load (iof *I, size_t *plength, size_t *poffset) ++{ ++ size_t bytes, tail; ++ if (I->flags & IOF_STOPPED || *plength == 0) ++ return 0; ++ iof_file_sync(I->iofile, poffset); ++ tail = iof_tail(I); ++ if (I->space - tail < *plength) ++ if (iof_resize_buffer_to(I, tail + *plength) == 0) ++ return 0; ++ bytes = tail + iof_file_read(I->buf + tail, sizeof(uint8_t), *plength, I->iofile); ++ iof_file_unsync(I->iofile, poffset); ++ I->flags |= IOF_STOPPED; ++ *plength = 0; ++ I->pos = I->buf; ++ I->end = I->buf + bytes; ++ return bytes; ++} ++ ++static size_t filter_file_stream_reader (iof *I, iof_mode mode) ++{ ++ stream_state *state; ++ state = iof_filter_state(stream_state *, I); ++ switch(mode) ++ { ++ case IOFREAD: ++ return file_stream_read(I, &state->length); ++ case IOFLOAD: ++ return file_stream_load(I, &state->length); ++ case IOFCLOSE: ++ iof_free(I); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++static size_t filter_iofile_stream_reader (iof *I, iof_mode mode) ++{ ++ stream_state *state; ++ state = iof_filter_state(stream_state *, I); ++ switch(mode) ++ { ++ case IOFREAD: ++ return iofile_stream_read(I, &state->length, &state->offset); ++ case IOFLOAD: ++ return iofile_stream_load(I, &state->length, &state->offset); ++ case IOFCLOSE: ++ iof_free(I); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++iof * iof_filter_stream_reader (FILE *file, size_t offset, size_t length) ++{ ++ iof *I; ++ stream_state *state; ++ I = iof_filter_reader(filter_file_stream_reader, sizeof(stream_state), &state); ++ iof_setup_file(I, file); ++ stream_state_init(state, offset, length); ++ fseek(file, (long)offset, SEEK_SET); // or perhaps it should be call in file_stream_read(), like iof_file_sync()? ++ return I; ++} ++ ++iof * iof_filter_stream_coreader (iof_file *iofile, size_t offset, size_t length) ++{ ++ iof *I; ++ stream_state *state; ++ if (!iof_file_reopen(iofile)) ++ return NULL; ++ I = iof_filter_reader(filter_iofile_stream_reader, sizeof(stream_state), &state); ++ iof_setup_iofile(I, iofile); ++ stream_state_init(state, offset, length); ++ return I; ++} ++ ++static size_t file_stream_write (iof *O, size_t *plength, int flush) ++{ ++ size_t bytes; ++ if ((bytes = iof_size(O)) > 0) ++ { ++ if (bytes != fwrite(O->buf, sizeof(uint8_t), bytes, O->file)) ++ { ++ *plength += bytes; ++ return 0; ++ } ++ } ++ if (flush) ++ fflush(O->file); ++ *plength += bytes; ++ O->end = O->buf + O->space; // remains intact ++ O->pos = O->buf; ++ return O->space; ++} ++ ++static size_t iofile_stream_write (iof *O, size_t *plength, size_t *poffset, int flush) ++{ ++ size_t bytes; ++ if ((bytes = iof_size(O)) > 0) ++ { ++ iof_file_sync(O->iofile, poffset); ++ if (bytes != iof_file_write(O->buf, sizeof(uint8_t), bytes, O->iofile)) ++ { ++ *plength += bytes; ++ iof_file_unsync(O->iofile, poffset); ++ return 0; ++ } ++ } ++ if (flush) ++ iof_file_flush(O->iofile); ++ *plength += bytes; ++ O->end = O->buf + O->space; // remains intact ++ O->pos = O->buf; ++ return O->space; ++} ++ ++static size_t filter_file_stream_writer (iof *O, iof_mode mode) ++{ ++ stream_state *state; ++ state = iof_filter_state(stream_state *, O); ++ switch (mode) ++ { ++ case IOFWRITE: ++ return file_stream_write(O, &state->length, 0); ++ case IOFFLUSH: ++ return file_stream_write(O, &state->length, 1); ++ case IOFCLOSE: ++ file_stream_write(O, &state->length, 1); ++ iof_free(O); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++static size_t filter_iofile_stream_writer (iof *O, iof_mode mode) ++{ ++ stream_state *state; ++ state = iof_filter_state(stream_state *, O); ++ switch (mode) ++ { ++ case IOFWRITE: ++ return iofile_stream_write(O, &state->length, &state->offset, 0); ++ case IOFFLUSH: ++ return iofile_stream_write(O, &state->length, &state->offset, 1); ++ case IOFCLOSE: ++ iofile_stream_write(O, &state->length, &state->offset, 1); ++ iof_free(O); ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++iof * iof_filter_stream_writer (FILE *file) ++{ ++ iof *O; ++ stream_state *state; ++ O = iof_filter_writer(filter_file_stream_writer, sizeof(stream_state), &state); ++ iof_setup_file(O, file); ++ stream_state_init(state, 0, 0); ++ return O; ++} ++ ++iof * iof_filter_stream_cowriter (iof_file *iofile, size_t offset) ++{ ++ iof *O; ++ stream_state *state; ++ O = iof_filter_writer(filter_iofile_stream_writer, sizeof(stream_state), &state); ++ iof_setup_iofile(O, iofile); ++ stream_state_init(state, offset, 0); ++ return O; ++} ++ ++/* very specific for images; get input from already created strem filter, exchange the filter but keep the buffer */ ++ ++FILE * iof_filter_file_reader_source (iof *I, size_t *poffset, size_t *plength) ++{ ++ stream_state *sstate; ++ file_state *fstate; ++ if (I->more == filter_file_stream_reader) // I is the result of iof_filter_stream_reader() ++ { ++ sstate = iof_filter_state(stream_state *, I); ++ *poffset = sstate->offset; ++ *plength = sstate->length; // might be 0 but it is ok for file readers ++ return I->file; ++ } ++ if (I->more == filter_file_reader) ++ { ++ fstate = iof_filter_state(file_state *, I); ++ *poffset = fstate->offset; ++ *plength = fstate->length; // might be 0 but it is ok for file readers ++ return I->file; ++ } ++ return NULL; ++} ++ ++iof_file * iof_filter_file_coreader_source (iof *I, size_t *poffset, size_t *plength) ++{ ++ stream_state *sstate; ++ file_state *fstate; ++ if (I->more == filter_iofile_stream_reader) // I is the result of iof_filter_stream_coreader() ++ { ++ sstate = iof_filter_state(stream_state *, I); ++ *poffset = sstate->offset; ++ *plength = sstate->length; ++ return I->iofile; ++ } ++ if (I->more == filter_iofile_reader) ++ { ++ fstate = iof_filter_state(file_state *, I); ++ *poffset = fstate->offset; ++ *plength = fstate->length; ++ return I->iofile; ++ } ++ return NULL; ++} ++ ++iof * iof_filter_reader_replacement (iof *P, iof_handler handler, size_t statesize, void **pstate) ++{ // called after iof_filter_file_reader_source(), no need to check if F is filter from iof heap and if has buffer from iof heap ++ iof *F; ++ F = iof_filter_reader_with_buffer(handler, statesize, pstate, P->buf, P->space); ++ F->flags |= IOF_BUFFER_HEAP; ++ //iof_reader_buffer(P, NULL, 0); ++ //P->flags &= ~IOF_BUFFER_HEAP; ++ iof_filter_free(P); ++ return F; ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/texk/web2c/luatexdir/luapplib/util/utiliof.h b/texk/web2c/luatexdir/luapplib/util/utiliof.h +new file mode 100644 +index 000000000..853a9ae52 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utiliof.h +@@ -0,0 +1,669 @@ ++ ++#ifndef UTIL_IOF_H ++#define UTIL_IOF_H ++ ++#include // for FILE * ++#include // for errno ++#include // for strerror() ++#include // for uintN_t ++ ++#include "utildecl.h" ++#include "utilnumber.h" ++ ++/* handler call modes */ ++ ++typedef enum { ++ IOFREAD = 0, /* read to buffer */ ++ IOFLOAD = 1, /* read all to buffer */ ++ IOFWRITE = 2, /* write buffer to the output */ ++ IOFFLUSH = 3, /* flush buffer to the output */ ++ IOFCLOSE = 4 /* (flush and) close */ ++} iof_mode; ++ ++/* return statuses */ ++ ++typedef enum { ++ IOFEOF = -1, /* end of input */ ++ IOFEMPTY = -2, /* end of input buffer*/ ++ IOFFULL = -3, /* end of output buffer */ ++ IOFERR = -4 /* error */ ++} iof_status; ++ ++const char * iof_status_kind (iof_status status); ++ ++/* iof_file */ ++ ++typedef struct iof_file { ++ union { ++ FILE *iofh; // access via iof_file_get_fh / iof_file_set_fh (below) ++ union { ++ struct { uint8_t *buf, *pos, *end; }; ++ struct { const uint8_t *rbuf, *rpos, *rend; }; // to trick compiler warnings about cast discarding const ++ }; ++ }; ++ size_t *offset; ++ char *name; ++ size_t size; ++ int refcount; ++ int flags; ++} iof_file; ++ ++/* iof handler function */ ++ ++typedef struct iof iof; ++typedef size_t (*iof_handler) (iof *I, iof_mode mode); ++ ++/* iof structure */ ++ ++#define IOF_MEMBERS \ ++ union { \ ++ struct { uint8_t *buf, *pos, *end; }; \ ++ struct { uint16_t *hbuf, *hpos, *hend; }; \ ++ struct { uint32_t *ibuf, *ipos, *iend; }; \ ++ struct { const uint8_t *rbuf, *rpos, *rend; }; \ ++ }; \ ++ size_t space; \ ++ iof_handler more; \ ++ union { iof *next; FILE *file; iof_file *iofile; void *link; }; \ ++ int flags; \ ++ int refcount ++ ++/* ++ buf -- the beginning of buffer ++ pos -- the current position ++ end -- the end of buffer ++ space -- private space size, not always eq. (end - buf) ++ more -- handler function ++ next/file/iofile/link -- reader source or writer target ++ source -- source filter ++ flags -- private filter info ++ refcount -- refcount ++*/ ++ ++struct iof { ++ IOF_MEMBERS; ++}; ++ ++typedef void (*iof_dump_function) (const void *value, iof *O); ++ ++/* flags */ ++ ++#define IOF_ALLOC (1<<0) // iof is allocated ++#define IOF_HEAP (1<<1) // iof taken from iof heap ++#define IOF_BUFFER_ALLOC (1<<2) // buffer allocated ++#define IOF_BUFFER_HEAP (1<<3) // buffer taken from iof heap ++ ++#define IOF_SHORT (1<<4) // buffer uses 16bit integers ++#define IOF_LONG (1<<5) // buffer uses 32bit integers ++ ++#define IOF_TAIL (1<<6) // preserve reader tail ++#define IOF_READER (1<<7) // is reader ++#define IOF_WRITER (1<<8) // is writer ++ ++#define IOF_DATA (1<<9) // binds some memory ++#define IOF_FILE_HANDLE (1<<10) // links FILE * ++#define IOF_FILE (1<<11) // links iof_file * ++#define IOF_NEXT (1<<12) // links next iof * ++#define IOF_CLOSE_FILE (1<<13) // close FILE * on free ++#define IOF_REOPEN_FILE (1<<14) // close/reopen mode for iof_file ++#define IOF_RECLOSE_FILE (1<<15) // ditto ++ ++#define IOF_STOPPED (1<<16) // stopped ++ ++// #define IOF_CUSTOM (1<<17) // first custom flag ++ ++#define IOF_BUFSIZ (sizeof(iof) + BUFSIZ*sizeof(uint8_t)) ++ ++/* ++reading buffer -- all of buf, pos, end pointers are initialized to the beginning of the private buffer, ++ next call to a handler function moves the end pointer to bufer+space ++writer -- buf and pos pointers initialized to the beginning of the buffer, end initialized to bufer+space ++ ++Every call to handler returns size_t number of bytes ++available (to write/read) or 0 if there is no more space. ++ ++We usually align the data buffer just after the iof structure. ++This is convenient, especially when a memory for the structure ++and its buffer is to be allocated. In the case of growing output ++buffers we used to check if the memory of the buffer is allocated ++by the handler function using test (O->buf != (O+1)). We don't use ++it any longer not to rely on little secrets. Now there is an explicit ++IOF_BUFFER_ALLOC flag for that. IOF_ALLOC tells if the structure ++itself is taken from malloc (not used so far). Assuming the buffer size ++is way larger the sizeof(iof) ++*/ ++ ++/* initializers */ ++ ++#define IOF_READER_STRUCT(handler, file, buffer, size, flags) \ ++ { {{ (uint8_t *)(buffer), (uint8_t *)(buffer), (uint8_t *)(buffer) }}, size, handler, { file }, flags|IOF_READER, 0 } ++ ++#define IOF_WRITER_STRUCT(handler, file, buffer, size, flags) \ ++ { {{ (uint8_t *)(buffer), (uint8_t *)(buffer), (uint8_t *)(buffer) + size }}, size, handler, { file }, flags|IOF_WRITER, 0 } ++ ++#define IOF_STRING_STRUCT(buffer, size) \ ++ { {{ (uint8_t *)(buffer), (uint8_t *)(buffer), (uint8_t *)(buffer) + size }}, size, NULL, { NULL }, 0|IOF_READER|IOF_DATA, 0 } ++ ++#define IOF_STRING() IOF_STRING_STRUCT(0, 0) ++ ++/* refcount */ ++ ++#define iof_incref(I) (++(I)->refcount) ++#define iof_decref(I) ((void)(--(I)->refcount <= 0 && iof_close(I))) ++#define iof_unref(I) (--(I)->refcount) ++ ++/* setting up iof and buffer from mem buffer of a given size */ ++ ++#define iof_setup_reader(I, buffer, size) \ ++ ((I) = (iof *)(buffer), iof_reader_buffer(I, (I)+1, size - sizeof(iof))) ++ ++#define iof_setup_writer(O, buffer, size) \ ++ ((O) = (iof *)buffer, iof_writer_buffer(O, (O)+1, size - sizeof(iof))) ++ ++/* binding buffer of a given size */ ++ ++#define iof_reader_buffer(I, buffer, size) \ ++ ((I)->buf = (I)->pos = (I)->end = (uint8_t *)(buffer), \ ++ (I)->space = size, (I)->flags = 0|IOF_READER, (I)->refcount = 0) ++ ++#define iof_writer_buffer(O, buffer, size) \ ++ ((O)->buf = (O)->pos = (uint8_t *)(buffer), \ ++ (O)->end = (uint8_t *)(buffer) + size, \ ++ (O)->space = size, (O)->flags = 0|IOF_WRITER, (O)->refcount = 0) ++ ++/* basics */ ++ ++#define iof_space(I) ((I)->end - (I)->buf) ++#define iof_left(I) ((I)->end - (I)->pos) ++#define iof_size(I) ((I)->pos - (I)->buf) ++ ++#define iof_input(I) ((I)->more ? (I)->more((I), IOFREAD) : 0lu) ++#define iof_load(I) ((I)->more ? (I)->more((I), IOFLOAD) : 0lu) ++ ++#define iof_output(O) ((O)->more ? (O)->more((O), IOFWRITE) : 0lu) ++//#define iof_flush(O) ((O)->pos > (O)->buf && (O)->more ? (O)->more(O, IOFFLUSH) : 0lu) ++// flush should be unconditional, because encoders emits EOD markers only on flush ++#define iof_flush(O) ((O)->more ? (O)->more(O, IOFFLUSH) : 0lu) ++#define iof_close(O) ((O)->more ? (O)->more(O, IOFCLOSE) : 0lu) ++ ++#define iof_stop(F) ((void)(F->pos = F->end = F->buf, F->flags |= IOF_STOPPED)) ++ ++/* ++Rewriting reader tail to the beginning of new data portion; readers reacting on IOFREAD ++mode must be aware of some not yet read data, but treat it necessary only if IOF_TAIL flag is set. ++Parsers using iof input may protect not yet read data when there may be a need to put bytes ++back to the stream. This is trivial when I->pos > I->buf, as we can make a move by --I->pos. ++But when there is a need to put back more then one byte, we can protect the data tail, so that ++realoder will rewrite it to the beginning of new data chunk. ++ ++ iof_tail(I) - internal, used by iof handlers at IOFREAD mode ++ iof_protect_tail(I) - used by parsers to ensure some bytes chunk in one piece ++ ++*/ ++ ++size_t iof_save_tail (iof *I); ++#define iof_tail(I) (((I)->flags & IOF_TAIL) && (I)->pos < (I)->end ? iof_save_tail(I) : 0) ++ ++size_t iof_input_save_tail (iof *I, size_t back); ++#define iof_protect_tail(I, back, length) ((iof_left(I) >= (length) - (back)) ? 1 : (iof_input_save_tail(I, back) >= length - back)) ++ ++//uint8_t * iof_tail_data (iof *I, size_t *ptail); ++//#define iof_tail_free(data) util_free(data) ++ ++/* panic */ ++ ++// #define iof_panic(mess) return 0 ++#ifndef iof_panic ++ #define iof_panic(mess) (fputs(mess, stderr), abort()) ++#endif ++//#define iof_memory_error() iof_panic(strerror(errno)) ++#define iof_fwrite_error() iof_panic(strerror(errno)) ++ ++/* generic helpers */ ++ ++UTILAPI uint8_t * iof_copy_file_data (const char *filename, size_t *psize); ++UTILAPI uint8_t * iof_copy_file_handle_data (FILE *file, size_t *psize); ++ ++/* In the future we may need releasing file handle and restoring it from iofile->name, so access file handle via macros */ ++ ++#define iof_file_get_fh(iofile) ((iofile)->iofh) ++#define iof_file_set_fh(iofile, fh) ((iofile)->iofh = fh) ++#define iof_file_get_file(iofile) (((iofile)->flags & IOF_DATA) ? NULL : iof_file_get_fh(iofile)) ++FILE * iof_get_file (iof *F); ++ ++/* basic iof_file interface */ ++ ++iof_file * iof_file_new (FILE *file); ++iof_file * iof_file_init (iof_file *iofile, FILE *file); ++ ++iof_file * iof_file_rdata (const void *data, size_t size); ++iof_file * iof_file_wdata (void *data, size_t size); ++ ++iof_file * iof_file_rdata_init (iof_file *iofile, const void *data, size_t size); ++iof_file * iof_file_wdata_init (iof_file *iofile, void *data, size_t size); ++ ++iof_file * iof_file_reader_from_file_handle (iof_file *iofile, const char *filename, FILE *file, int preload, int closefile); ++iof_file * iof_file_reader_from_file (iof_file *iofile, const char *filename, int preload); ++iof_file * iof_file_reader_from_data (iof_file *iofile, const void *data, size_t size, int preload, int freedata); ++//iof_file * iof_file_writer_from_file (iof_file *iofile, const char *filename); ++ ++void * iof_copy_data (const void *data, size_t size); ++#define iof_data_free(data) util_free(data) ++#define iof_file_wdata_copy(data, size) iof_file_wdata(iof_copy_data(data, size), size) ++#define iof_file_rdata_copy(data, size) iof_file_rdata(iof_copy_data(data, size), size) ++ ++void iof_file_free (iof_file *iofile); ++ ++#define iof_file_get_name(iofile) ((iofile)->name) ++void iof_file_set_name (iof_file *iofile, const char *name); ++ ++#define iof_file_incref(iofile) (++(iofile)->refcount) ++#define iof_file_decref(iofile) ((void)(--(iofile)->refcount <= 0 && (iof_file_free(iofile), 0))) ++ ++int iof_file_seek (iof_file *iofile, long offset, int whence); ++long iof_file_tell (iof_file *iofile); ++size_t iof_file_size (iof_file *iofile); ++int iof_file_eof (iof_file *iofile); ++ ++size_t iof_file_read (void *ptr, size_t size, size_t items, iof_file *iofile); ++size_t iof_file_write (const void *ptr, size_t size, size_t items, iof_file *iofile); ++size_t iof_file_ensure (iof_file *iofile, size_t bytes); ++int iof_file_flush (iof_file *iofile); ++ ++int iof_file_getc (iof_file *iofile); ++int iof_file_putc (iof_file *iofile, int c); ++ ++int iof_file_close_input (iof_file *iofile); ++int iof_file_reopen_input (iof_file *iofile); ++ ++#define iof_file_reopen(iofile) (((iofile)->flags & IOF_REOPEN_FILE) ? iof_file_reopen_input(iofile) : 1) ++#define iof_file_reclose(iofile) (void)(((iofile)->flags & IOF_RECLOSE_FILE) ? iof_file_close_input(iofile) : 0) ++ ++/* wrappers of basic operations for iof */ ++ ++int iof_reader_seek (iof *I, long offset, int whence); ++int iof_reader_reseek (iof *I, long offset, int whence); ++int iof_writer_seek (iof *I, long offset, int whence); ++int iof_writer_reseek (iof *I, long offset, int whence); ++ ++int iof_seek (iof *I, long offset, int whence); ++int iof_reseek (iof *I, long offset, int whence); ++ ++long iof_reader_tell (iof *I); ++long iof_writer_tell (iof *I); ++long iof_tell (iof *I); ++size_t iof_fsize (iof *I); ++ ++#define iof_setup_iofile(I, f) (iof_file_incref(f), (I)->iofile = f, (I)->flags |= IOF_FILE) ++#define iof_setup_file(I, fh) ((I)->file = fh, (I)->flags |= IOF_FILE_HANDLE) ++#define iof_setup_next(I, N) ((I)->next = N, iof_incref(N), (I)->flags |= IOF_NEXT) ++ ++/* file handler reader and writer */ ++ ++UTILAPI iof * iof_setup_file_handle_reader (iof *I, void *buffer, size_t space, FILE *f); ++UTILAPI iof * iof_setup_file_handle_writer (iof *O, void *buffer, size_t space, FILE *f); ++ ++#define iof_get_file_handle_reader(buffer, space, fh) iof_setup_file_handle_reader(NULL, buffer, space, fh) ++#define iof_get_file_handle_writer(buffer, space, fh) iof_setup_file_handle_writer(NULL, buffer, space, fh) ++ ++/* file reader and writer */ ++ ++UTILAPI iof * iof_setup_file_reader (iof *I, void *buffer, size_t space, const char *filename); ++UTILAPI iof * iof_setup_file_writer (iof *O, void *buffer, size_t space, const char *filename); ++ ++#define iof_get_file_reader(buffer, space, filename) iof_setup_file_reader(NULL, buffer, space, filename) ++#define iof_get_file_writer(buffer, space, filename) iof_setup_file_writer(NULL, buffer, space, filename) ++ ++/* mem writer */ ++ ++UTILAPI iof * iof_setup_buffer (iof *O, void *buffer, size_t space); ++UTILAPI iof * iof_setup_buffermin (iof *O, void *buffer, size_t space, size_t min); ++ ++#define iof_buffer(buffer, space) iof_setup_buffer(NULL, buffer, space) ++#define iof_buffermin(buffer, space, min) iof_setup_buffermin(NULL, buffer, space, min) ++ ++UTILAPI iof * iof_buffer_create (size_t space); ++#define iof_buffer_new() iof_buffer_create(BUFSIZ) ++ ++/* custom handler */ ++ ++UTILAPI iof * iof_reader (iof *I, void *link, iof_handler reader, const void *s, size_t bytes); ++UTILAPI iof * iof_writer (iof *O, void *link, iof_handler writer, void *s, size_t bytes); ++ ++/* stdout wrapper */ ++ ++extern UTILAPI iof iof_stdout; ++extern UTILAPI iof iof_stderr; ++ ++/* simple string reader */ ++ ++UTILAPI iof * iof_string_reader (iof *I, const void *s, size_t bytes); ++ ++#define iof_string(I, s, bytes) \ ++ (((I)->rbuf = (I)->rpos = (const uint8_t *)s), ((I)->rend = (I)->rbuf + (bytes)), ((I)->flags |= IOF_DATA), (I)) ++ ++/* dummies */ ++ ++UTILAPI iof * iof_dummy (void *buffer, size_t space); ++UTILAPI iof * iof_null (void *buffer, size_t space); ++ ++/* checking available space */ ++ ++#define iof_loadable(I) ((I)->pos < (I)->end || iof_load(I)) ++#define iof_readable(I) ((I)->pos < (I)->end || iof_input(I)) ++#define iof_writable(O) ((O)->pos < (O)->end || iof_output(O)) ++ ++#define iof_hloadable iof_loadable ++#define iof_iloadable iof_loadable ++ ++#define iof_hreadable iof_readable ++#define iof_ireadable iof_readable ++ ++#define iof_hwritable iof_writable ++#define iof_iwritable iof_writable ++ ++/* ensure space to write several bytes (several means less then I->space) */ ++ ++#define iof_ensure(O, n) ((O)->pos+(n)-1 < (O)->end || iof_output(O)) // iof_ensure(O, 1) eq iof_writable(O) ++#define iof_hensure(O, n) ((O)->hpos+(n)-1 < (O)->hend || iof_output(O)) ++#define iof_iensure(O, n) ((O)->ipos+(n)-1 < (O)->iend || iof_output(O)) ++ ++/* reading */ ++ ++UTILAPI int iof_getc (iof *I); ++UTILAPI int iof_hgetc (iof *I); ++UTILAPI int iof_igetc (iof *I); ++ ++// UTILAPI int iof_cmp (iof *I, const char *s); ++// UTILAPI int iof_cmpn (iof *I, const char *s, size_t bytes); ++ ++UTILAPI iof_status iof_pass (iof *I, iof *O); ++#define iof_hpass iof_pass ++#define iof_ipass iof_pass ++ ++/* readers helpers */ ++ ++UTILAPI size_t iof_read (iof *I, void *s, size_t bytes); ++UTILAPI size_t iof_hread (iof *I, void *s, size_t bytes); ++UTILAPI size_t iof_iread (iof *I, void *s, size_t bytes); ++ ++UTILAPI size_t iof_skip (iof *I, size_t bytes); ++UTILAPI size_t iof_hskip (iof *I, size_t bytes); ++UTILAPI size_t iof_iskip (iof *I, size_t bytes); ++ ++/* get */ ++ ++#define iof_pos(I) (*(I)->pos++) ++#define iof_hpos(I) (*(I)->hpos++) ++#define iof_ipos(I) (*(I)->ipos++) ++ ++#define iof_get(I) (iof_readable(I) ? (int)(*(I)->pos++) : IOFEOF) ++#define iof_hget(I) (iof_hreadable(I) ? (int)(*(I)->hpos++) : IOFEOF) ++#define iof_iget(I) (iof_ireadable(I) ? (int)(*(I)->ipos++) : IOFEOF) ++ ++#define iof_char(I) (iof_readable(I) ? (int)(*(I)->pos) : IOFEOF) ++#define iof_hcurr(I) (iof_hreadable(I) ? (int)(*(I)->hpos) : IOFEOF) ++#define iof_icurr(I) (iof_ireadable(I) ? (int)(*(I)->ipos) : IOFEOF) ++ ++#define iof_next(I) (++(I)->pos, iof_char(I)) ++#define iof_hnext(I) (++(I)->hpos, iof_hcurr(I)) ++#define iof_inext(I) (++(I)->ipos, iof_icurr(I)) ++ ++/* unget */ ++ ++/* ++If possible, we just move the position backward. If it is not possible to ++move backward, we call iof_backup(I, c) that sets all pointers to the end of ++a private backup space, then moves buf AND pos pointers backward and set c at ++pos (==buf). We can backup characters as long as there is a private space. If ++several calls to iof_backup() are followed by iof_get(), pos pointer ++increases in normal way and so the use of another iof_unget() works just fine ++by moving the position. Once we swallow all backup characters (when ++pos==end), backup handler restores the previous pointers. ++ ++Obviously we assume that the character provided to iof_unget() is always the ++character just obtained from iof_get(). We CAN'T just overwrite the character ++at a given position as the space we read may not be writable. ++ ++When backup is in use, we can only get bytes until automatically restored. ++*/ ++ ++/* backup */ ++ ++/* ++#define iof_uses_backup(I) ((I)->more == iof_unget_handler) ++ ++#define iof_save(I, B) \ ++ ((B)->buf = (I)->buf, (B)->pos = (I)->pos, (B)->end = (I)->end, (B)->space = (I)->space, \ ++ (B)->link = I->link, (B)->more = (I)->more, (B)->flags = (I)->flags) ++#define iof_restore(B, I) iof_save(I, B) ++ ++#define iof_unget(I, c) \ ++ ((void)(c == (uint8_t)c ? ((I)->pos > (I)->buf ? --(I)->pos : iof_backup(I, c)) : 0) ++int iof_backup (iof *I, int c); ++*/ ++ ++/* writing */ ++ ++UTILAPI size_t iof_write_file_handle (iof *O, FILE *file); ++UTILAPI size_t iof_write_file (iof *O, const char *filename); ++UTILAPI size_t iof_write_iofile (iof *O, iof_file *iofile, int savepos); ++ ++UTILAPI int iof_putc (iof *O, int u); ++UTILAPI int iof_hputc (iof *O, int u); ++UTILAPI int iof_iputc (iof *O, int u); ++ ++UTILAPI size_t iof_write (iof *O, const void *data, size_t size); ++UTILAPI size_t iof_hwrite (iof *O, const void *data, size_t size); ++UTILAPI size_t iof_iwrite (iof *O, const void *data, size_t size); ++ ++UTILAPI iof_status iof_puts (iof *O, const void *data); ++UTILAPI size_t iof_put_string (iof *O, const void *data); ++UTILAPI size_t iof_putfs (iof *O, const char *format, ...); ++UTILAPI size_t iof_repc (iof *O, char c, size_t bytes); ++ ++#define iof_putl(O, s) iof_write(O, "" s, sizeof(s)-1) ++//#define iof_putl iof_puts ++ ++#define iof_set(O, c) (*(O)->pos++ = (uint8_t)(c)) ++#define iof_set2(O, c1, c2) (iof_set(O, c1), iof_set(O, c2)) ++#define iof_set3(O, c1, c2, c3) (iof_set(O, c1), iof_set(O, c2), iof_set(O, c3)) ++#define iof_set4(O, c1, c2, c3, c4) (iof_set(O, c1), iof_set(O, c2), iof_set(O, c3), iof_set(O, c4)) ++#define iof_set5(O, c1, c2, c3, c4, c5) (iof_set(O, c1), iof_set(O, c2), iof_set(O, c3), iof_set(O, c4), iof_set(O, c5)) ++ ++#define iof_hset(O, c) (*(O)->hpos++ = (uint16_t)(c)) ++#define iof_iset(O, c) (*(O)->ipos++ = (uint32_t)(c)) ++ ++#define iof_put(O, c) ((void)iof_ensure(O, 1), iof_set(O, c)) ++#define iof_put2(O, c1, c2) ((void)iof_ensure(O, 2), iof_set2(O, c1, c2)) ++#define iof_put3(O, c1, c2, c3) ((void)iof_ensure(O, 3), iof_set3(O, c1, c2, c3)) ++#define iof_put4(O, c1, c2, c3, c4) ((void)iof_ensure(O, 4), iof_set4(O, c1, c2, c3, c4)) ++#define iof_put5(O, c1, c2, c3, c4, c5) ((void)iof_ensure(O, 5), iof_set5(O, c1, c2, c3, c4, c5)) ++ ++#define iof_hput(O, c) ((void)iof_hensure(O, 1), iof_hset(O, c)) ++#define iof_iput(O, c) ((void)iof_iensure(O, 1), iof_iset(O, c)) ++ ++#define iof_put_uc_hex(O, c) iof_put2(O, base16_uc_digit1(c), base16_uc_digit2(c)) ++#define iof_put_lc_hex(O, c) iof_put2(O, base16_lc_digit1(c), base16_lc_digit2(c)) ++#define iof_set_uc_hex(O, c) iof_set2(O, base16_uc_digit1(c), base16_uc_digit2(c)) ++#define iof_set_lc_hex(O, c) iof_set2(O, base16_lc_digit1(c), base16_lc_digit2(c)) ++#define iof_put_hex iof_put_uc_hex ++#define iof_set_hex iof_set_uc_hex ++ ++/* number from iof; return 1 on success, 0 otherwise */ ++ ++#define iof_scan_sign(I, c, sign) _scan_sign(c, sign, iof_next(I)) ++#define iof_scan_integer(I, c, number) _scan_integer(c, number, iof_next(I)) ++#define iof_scan_radix(I, c, number, radix) _scan_radix(c, number, radix, iof_next(I)) ++#define iof_read_integer(I, c, number) _read_integer(c, number, iof_next(I)) ++#define iof_read_radix(I, c, number, radix) _read_radix(c, number, radix, iof_next(I)) ++ ++#define iof_scan_decimal(I, c, number) _scan_decimal(c, number, iof_next(I)) ++#define iof_scan_fraction(I, c, number, exponent10) _scan_fraction(c, number, exponent10, iof_next(I)) ++#define iof_scan_exponent10(I, c, exponent10) _scan_exponent10(c, exponent10, iof_next(I)) ++ ++UTILAPI int iof_get_int32 (iof *I, int32_t *number); ++UTILAPI int iof_get_intlw (iof *I, intlw_t *number); ++UTILAPI int iof_get_int64 (iof *I, int64_t *number); ++ ++UTILAPI int iof_get_uint32 (iof *I, uint32_t *number); ++UTILAPI int iof_get_uintlw (iof *I, uintlw_t *number); ++UTILAPI int iof_get_uint64 (iof *I, uint64_t *number); ++ ++UTILAPI int iof_get_int32_radix (iof *I, int32_t *number, int radix); ++UTILAPI int iof_get_intlw_radix (iof *I, intlw_t *number, int radix); ++UTILAPI int iof_get_int64_radix (iof *I, int64_t *number, int radix); ++ ++UTILAPI int iof_get_uint32_radix (iof *I, uint32_t *number, int radix); ++UTILAPI int iof_get_uintlw_radix (iof *I, uintlw_t *number, int radix); ++UTILAPI int iof_get_uint64_radix (iof *I, uint64_t *number, int radix); ++ ++UTILAPI int iof_get_roman (iof *I, unsigned short int *number); ++ ++UTILAPI int iof_get_double (iof *I, double *number); ++UTILAPI int iof_get_float (iof *I, float *number); ++ ++UTILAPI int iof_conv_double (iof *I, double *number); ++UTILAPI int iof_conv_float (iof *I, float *number); ++ ++/* number to iof; return a number of written bytes */ ++ ++UTILAPI size_t iof_put_int32 (iof *O, int32_t number); ++UTILAPI size_t iof_put_intlw (iof *O, intlw_t number); ++UTILAPI size_t iof_put_int64 (iof *O, int64_t number); ++ ++UTILAPI size_t iof_put_uint32 (iof *O, uint32_t number); ++UTILAPI size_t iof_put_uintlw (iof *O, uintlw_t number); ++UTILAPI size_t iof_put_uint64 (iof *O, uint64_t number); ++ ++UTILAPI size_t iof_put_int32_radix (iof *O, int32_t number, int radix); ++UTILAPI size_t iof_put_intlw_radix (iof *O, intlw_t number, int radix); ++UTILAPI size_t iof_put_int64_radix (iof *O, int64_t number, int radix); ++ ++UTILAPI size_t iof_put_uint32_radix (iof *O, uint32_t number, int radix); ++UTILAPI size_t iof_put_uintlw_radix (iof *O, uintlw_t number, int radix); ++UTILAPI size_t iof_put_uint64_radix (iof *O, uint64_t number, int radix); ++ ++UTILAPI size_t iof_put_roman_uc (iof *O, uint16_t number); ++UTILAPI size_t iof_put_roman_lc (iof *O, uint16_t number); ++#define iof_put_roman(I, number) iof_put_roman_uc(I, number) ++ ++UTILAPI size_t iof_put_double(iof *O, double number, int digits); ++UTILAPI size_t iof_put_float(iof *O, float number, int digits); ++ ++/* common stuff for binary integers */ ++ ++UTILAPI int iof_get_be_uint2 (iof *I, uint32_t *pnumber); ++UTILAPI int iof_get_be_uint3 (iof *I, uint32_t *pnumber); ++UTILAPI int iof_get_be_uint4 (iof *I, uint32_t *pnumber); ++ ++UTILAPI int iof_get_le_uint2 (iof *I, uint32_t *pnumber); ++UTILAPI int iof_get_le_uint3 (iof *I, uint32_t *pnumber); ++UTILAPI int iof_get_le_uint4 (iof *I, uint32_t *pnumber); ++ ++// iof_set() and iof_put() suite casts arguments to uint8_t, so we don't need &0xff mask ++ ++#define iof_set_be_uint1(O, u) iof_set(O, u) ++#define iof_set_be_uint2(O, u) iof_set2(O, (u)>>8, u) ++#define iof_set_be_uint3(O, u) iof_set3(O, (u)>>16, (u)>>8, u) ++#define iof_set_be_uint4(O, u) iof_set4(O, (u)>>24, (u)>>16, (u)>>8, u) ++ ++#define iof_set_le_uint1(O, u) iof_set(O, u) ++#define iof_set_le_uint2(O, u) iof_set2(O, u, (u)>>8) ++#define iof_set_le_uint3(O, u) iof_set3(O, u, (u)>>8, (u)>>16) ++#define iof_set_le_uint4(O, u) iof_set4(O, u, (u)>>8, (u)>>16, (u)>>24) ++ ++#define iof_put_be_uint1(O, u) iof_put(O, u) ++#define iof_put_be_uint2(O, u) iof_put2(O, (u)>>8, u) ++#define iof_put_be_uint3(O, u) iof_put3(O, (u)>>16, (u)>>8, u) ++#define iof_put_be_uint4(O, u) iof_put4(O, (u)>>24, (u)>>16, (u)>>8, u) ++ ++#define iof_put_le_uint1(O, u) iof_put(O, u) ++#define iof_put_le_uint2(O, u) iof_put2(O, u, (u)>>8) ++#define iof_put_le_uint3(O, u) iof_put3(O, u, (u)>>8, (u)>>16) ++#define iof_put_le_uint4(O, u) iof_put4(O, u, (u)>>8, (u)>>16, (u)>>24) ++ ++/* buffer results */ ++ ++#define iof_reader_result(I, size) ((size = iof_left(I)), (I)->pos) ++#define iof_writer_result(I, size) ((size = iof_size(I)), (I)->buf) ++#define iof_result(I, size) (((I)->flags & IOF_READER) ? iof_reader_result(I, size) : iof_writer_result(I, size)) ++ ++uint8_t * iof_file_input_data (iof_file *iofile, size_t *psize, int *isnew); ++//uint8_t * iof_file_reader_data (iof_file *iofile, size_t *size); ++//uint8_t * iof_file_writer_data (iof_file *iofile, size_t *size); ++ ++uint8_t * iof_reader_data (iof *I, size_t *psize); ++uint8_t * iof_writer_data (iof *O, size_t *psize); ++size_t iof_reader_to_file_handle (iof *I, FILE *file); ++size_t iof_reader_to_file (iof *I, const char *filename); ++ ++#define iof_loaded(I) ((I)->end = (I)->pos, (I)->pos = (I)->buf, iof_left(I)) ++ ++#define iof_data_to_file_handle(data, size, file) fwrite(data, sizeof(uint8_t), size, file) ++UTILAPI size_t iof_data_to_file (const void *data, size_t size, const char *filename); ++ ++UTILAPI size_t iof_result_to_file_handle (iof *F, FILE *file); ++UTILAPI size_t iof_result_to_file (iof *F, const char *filename); ++UTILAPI void iof_debug (iof *I, const char *filename); ++ ++/* common filters allocator */ ++ ++void iof_filters_init (void); ++void iof_filters_free (void); ++ ++void * iof_filter_new (size_t size); ++void iof_heap_back (void *data); ++#define iof_filter_free(F) iof_heap_back(F) ++#define iof_filter_buffer_free(data) iof_heap_back(data) ++ ++// &((void *)pstate ++ ++iof * iof_filter_reader_new (iof_handler handler, size_t statesize, void **pstate); ++#define iof_filter_reader(handler, statesize, pstate) iof_filter_reader_new(handler, statesize, (void **)(pstate)) ++iof * iof_filter_reader_with_buffer_new (iof_handler handler, size_t statesize, void **pstate, void *buffer, size_t buffersize); ++#define iof_filter_reader_with_buffer(handler, statesize, pstate, buffer, buffersize) iof_filter_reader_with_buffer_new(handler, statesize, (void **)(pstate), buffer, buffersize) ++iof * iof_filter_writer_new (iof_handler handler, size_t statesize, void **pstate); ++#define iof_filter_writer(handler, statesize, pstate) iof_filter_writer_new(handler, statesize, (void **)(pstate)) ++iof * iof_filter_writer_with_buffer_new (iof_handler handler, size_t statesize, void **pstate, void *buffer, size_t buffersize); ++#define iof_filter_writer_with_buffer(handler, statesize, pstate, buffer, buffersize) iof_filter_writer_with_buffer_new(handler, statesize, (void **)(pstate), buffer, buffersize) ++ ++#define iof_filter_state(statetype, F) (statetype)((F) + 1) ++ ++void iof_free (iof *F); ++void iof_discard (iof *F); ++ ++size_t iof_resize_buffer_to (iof *O, size_t space); ++#define iof_resize_buffer(O) iof_resize_buffer_to(O, (O)->space << 1) ++ ++size_t iof_decoder_retval (iof *I, const char *type, iof_status status); ++size_t iof_encoder_retval (iof *O, const char *type, iof_status status); ++ ++/* filters */ ++ ++iof * iof_filter_file_handle_reader (FILE *file); ++iof * iof_filter_file_handle_writer (FILE *file); ++ ++iof * iof_filter_iofile_reader (iof_file *iofile, size_t offset); ++iof * iof_filter_iofile_writer (iof_file *iofile, size_t offset); ++ ++iof * iof_filter_file_reader (const char *filename); ++iof * iof_filter_file_writer (const char *filename); ++ ++iof * iof_filter_string_reader (const void *s, size_t length); ++iof * iof_filter_string_writer (const void *s, size_t length); ++ ++iof * iof_filter_buffer_writer (size_t size); ++ ++iof * iof_filter_stream_reader (FILE *file, size_t offset, size_t length); ++iof * iof_filter_stream_coreader (iof_file *iofile, size_t offset, size_t length); ++ ++iof * iof_filter_stream_writer (FILE *file); ++iof * iof_filter_stream_cowriter (iof_file *iofile, size_t offset); ++ ++FILE * iof_filter_file_reader_source (iof *I, size_t *poffset, size_t *plength); ++iof_file * iof_filter_file_coreader_source (iof *I, size_t *poffset, size_t *plength); ++iof * iof_filter_reader_replacement (iof *P, iof_handler handler, size_t statesize, void **pstate); ++#define iof_filter_reader_replace(P, handler, statesize, pstate) iof_filter_reader_replacement(P, handler, statesize, (void **)(pstate)) ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utillog.c b/texk/web2c/luatexdir/luapplib/util/utillog.c +new file mode 100644 +index 000000000..1b38f7467 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utillog.c +@@ -0,0 +1,60 @@ ++ ++#include ++#include // strlen ++#include ++#include "utillog.h" ++ ++#define LOGGER_BUFFER_SIZE 256 ++#define LOGGER_PREFIX_SIZE 32 ++ ++typedef struct { ++ logger_function callback; ++ void *context; ++ size_t pfxlen; ++} logger_struct; ++ ++static logger_struct logger = { 0, NULL, 0 }; ++ ++static char logger_buffer[LOGGER_BUFFER_SIZE+LOGGER_PREFIX_SIZE]; ++ ++void loggerf (const char *format, ...) ++{ ++ va_list args; ++ int length; ++ ++ va_start(args, format); ++ length = vsnprintf(logger_buffer + logger.pfxlen, LOGGER_BUFFER_SIZE, format, args); ++ if (length > 0) ++ { ++ if (length > LOGGER_BUFFER_SIZE) ++ length = LOGGER_BUFFER_SIZE; ++ } ++ else ++ { ++ loggerf("logger encoding error '%s'", format); ++ length = (int)strlen(logger_buffer); ++ } ++ length += (int)logger.pfxlen; ++ if (logger.callback) ++ logger.callback(logger_buffer, logger.context); ++ else ++ printf("\n%s\n", logger_buffer); ++ va_end(args); ++} ++ ++void logger_callback (logger_function callback, void *context) ++{ ++ logger.callback = callback; ++ logger.context = context; ++} ++ ++int logger_prefix (const char *prefix) ++{ ++ size_t pfxlen; ++ pfxlen = strlen(prefix); ++ if (pfxlen > LOGGER_PREFIX_SIZE) ++ return 0; ++ memcpy(logger_buffer, prefix, pfxlen); ++ logger.pfxlen = pfxlen; ++ return 1; ++} +diff --git a/texk/web2c/luatexdir/luapplib/util/utillog.h b/texk/web2c/luatexdir/luapplib/util/utillog.h +new file mode 100644 +index 000000000..c30e0ff0f +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utillog.h +@@ -0,0 +1,10 @@ ++ ++#ifndef UTIL_LOG_H ++#define UTIL_LOG_H ++ ++typedef void (*logger_function) (const char *message, void *alien); ++void loggerf (const char *format, ...); ++void logger_callback (logger_function callback, void *context); ++int logger_prefix (const char *prefix); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utillzw.c b/texk/web2c/luatexdir/luapplib/util/utillzw.c +new file mode 100644 +index 000000000..4cd221214 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utillzw.c +@@ -0,0 +1,701 @@ ++/* lzw implementation for postscript/pdf filters ++# Notes on LZW ++ ++# Encoder ++ ++Initially the table contains 256 entires for single bytes. Encoder consumes ++input bytes trying to find the longest sequence stored so far in the table. ++Once it finds a sequence that is not present in the table, it outputs the table ++index of the longest sequence found (accumulated bytes except the last ++consumed) and pushes the new sequence (accumulated bytes including the last ++one) on the top of the table. The last taken byte is not yet written to the ++output, it becomes the beginning of the new sequence to accumulate. Initially, ++encoder outputs 9-bit codes. While the table grows, the number of bits for each ++code increases up to 12. In example, after adding a table entry of index 511 it ++is high time to switch to 10-bit bytes. /EarlyChange=true parameter in stream ++dictionary (both postscript and pdf) informs to increase the number of bits one ++code earlier then necessary. Looks pretty much like an early days bug that ++became a specification :) I have never found a PDF having /EarlyChange key ++specified anyway. ++ ++Once the table becomes full (or when encoder decides it is worthy), ++a clear-table marker (code 256) purges the table and restores codes length to ++9. End-of-data marker (code 257) ends the stream. Conventionally, the beginning ++of the stream starts with clear-table marker. ++ ++Postscript allows to provide a /UnitLength which determines the bit length of ++codes. The above description assumes UnitLength=8 (default). Allowed values are ++from 3 to 8. Different UnitLength also affects markers; clear-table is then ++2^UnitLength and end-of-data marker is 2^UnitLenth+1. ++ ++Encoder outputs 9-12bit codes that are packed into bytes using high-bits-first ++scheme (default) or low-bits-scheme. ++ ++PDF spec p. 73 (PS spec p. 135 gives an mistaken output sequence and so ++mistaken output bytes) ++ ++Input character sequence (decimal) ++45 45 45 45 45 65 45 45 45 66 ++ ++Output 9bit codes (decimal) ++256 45 258 258 65 259 66 257 ++ ++Output 9bit codes (binary) ++100000000 000101101 100000010 100000010 001000001 100000011 001000010 100000001 ++ ++Output bytes (LowBitsFirst=false); eight high-order bits of code becomes ++the first byte, remaining low-order bit of code becomes the high-order bit of the ++next byte; ++10000000 00001011 01100000 01010000 00100010 00001100 00001100 10000101 00000001 ++-> 80 0B 60 50 22 0C 0C 85 01 ++ ++Output bytes (binary, LowBitsFirst=true); eight low-order bits of code becomes ++the first byte, remaining high-order bit of code becomes low-order bit of the ++next byte; ++00000000 01011011 00001000 00010100 00011000 01100100 10100000 10000000 10010000 ++-> 00 5B 08 14 18 64 A0 80 90 ++ ++# Decoder ++ ++Decoder consumes input bytes transforming them to 9 to 12 bit codes. Initially ++it starts with 9bit codes and the table of 258 fixed codes (same as encoder). ++Basically, it interprets incoming codes as table indices (except 256 and 257 ++markers) and it outputs byte sequences stored at given indices. It also ++upbuilds the table and changes the number of bits of codes when necessary. The ++key point on lzw is that both encoder and decoder builds the table ++synchronously. ++ ++However, decoder needs some "knowledge" about how encoder works to be able to ++interpret a table index that it doesn't have so far. Look that the output from ++encoder in the example above. The first output code is conventional clear-table ++(256). Then comes a code 45. So far so good, decoder interprets code 45 as ++a (fixed) entry of the table, emitting byte 45. The next code is 258, which is ++should be interpreted as an index in the table. Oops, encoder doesn't have one ++yet. If that occurs, it means that encoder was able to output the new entry ++code just after adding it to a table. It means that ++ ++ sequence_before + next_byte == next_byte + sequence_after ++ ++This may happen not only for sequences like 45 45 45, but also symmetric series ++such as abcbabcba; abcb + a == a + bcba. Decoder must be aware of that and if ++it gets a code one larger than the top table index, it should create one on-fly ++by appending last entry sequence by the first by of the last entry. ++ ++# UnitLength ++ ++Postscript specification mentions about UnitLength parameter that can be used ++in LZW decoder (not allowed in encoder), with possible values from 3 to 8. This ++parameter determines the number of bits per code; form UnitLength + 1 to 12. It ++also determines which codes are used for clear-table marker (2^UnitLength) and ++end-of-data marker ((2^UnitLength)+1). Postscript specification says (page 134): ++ ++"Initially, the code length is (UnitLength + 1) bits and the table contains only ++entries for the (2^UnitLength + 2) fixed codes. As encoding proceeds, entries are ++appended to the table, associating new codes with longer and longer input character ++sequences. The encoding and decoding filters maintain identical copies of ++this table." ++ ++Later on page 136 Postscript specification says: ++ ++"Data that has been LZW-encoded with a UnitLength less than 8 consists only of ++codes in the range 0 to 2^UnitLength - 1; consequently, the LZWDecode filter produces ++only codes in that range when read. UnitLength also affects the encoded ++representation, as described above." ++ ++UnitLength (Postscript only) and LowBitsFirst are used only by decoder. ++EarlyChange should obviously be respected by both encoder and decoder. When ++table index reaches current bit length boundary (511, 1023, ...) it must react ++by increasing the number of bits of input code. But if the index reaches it ++maximum value (when the table is full), decoder is NOT supposed to clear the ++table. When the table is full, encoder must emit clear-table marker and it ++emits this code using 12 bits and reinitialize code bits after that. It means ++that, when the table is full, decoder should get one more 12-bit code (which ++should be clear-table marker) and actually clear the table and reinitialize ++code bits after that. ++ ++# Clear-table vs last entry track (after tries and checks) ++ ++It is also not quite clear what should actually happen when encoder gets a full ++table and it is supposed to emit clear-table marker. When it gets full, it ++means that it has just appended another entry to the table. And that happens ++only the input sequence collected so far plus the last byte is not present in ++the table. Encoder is supposed to output the table index of the present ++sequence and set the recent byte as a starting index of the new sequence to be ++collected. Even if it is time to clear the table, encoder is still supposed to ++keep the track of the last table entry. Decoder, however, must drop the track of the ++last code on clear-table. ++ ++# Decoder table vs encoder table ++ ++While decoding we need query lzw table by (subsequent) numeric codes and output ++character sequences stored in the table. While encoding we need to query the ++table on every input byte and fetch indices pointing to character sequences. ++Note that we never need to query the entire table for the longest sequence ++found so far. The encoder table do not need to access the longest character ++sequence at one piece. It is enough to keep the track of the current table ++index and the very next byte. We organize an encoder table into a search tree, ++where every node contains its table index (value) and last byte (key). Except ++initial tree content, every node is created on the base of the previous node ++and it conceptually point the sequence represented by that nodo consists of the ++previous node sequence plus the next byte. ++ ++Every new node is a descendant of the node it has been derived from. Every node ++has a map (a search subtree) indexed by suffix byte value, pointing to ++descendants nodes. Every node also has binary tentackles (left/right fields) ++necessary to search the map (except initials, every node lives in a map of some ++ancestor node). The key point is that on every input byte we don't search the ++entire tree, but only the map of the current node children. The map tree is ++a simple binary tree with no balancing mechanism (not worthy to optimize an ++ephemeric structure that may be upbuilt more often then queried). ++ ++In our implementation, decoder table requires 4069 entries (topmost index 4095). ++Encoder table, however, needs 4097 entries to handle the case when EarlyIndex ++parameter is 0 (I have never a chance to test that in practise). The node of index ++4096 might be added to a search tree, but its code is never emitted; the lookup ++is purged just after adding that node. ++ ++todo: ++- support for LowBitsFirst encoding ++*/ ++ ++#include "utilmem.h" ++#include "utillzw.h" ++ ++/* filter state struct */ ++ ++typedef struct lzw_entry { ++ union { ++ const char *rdata; // to be able to init with string literal ++ char *data; ++ }; ++ int size; ++} lzw_entry; ++ ++#define lzw_index short ++ ++typedef struct lzw_node lzw_node; ++ ++struct lzw_node { ++ lzw_index index; ++ unsigned char suffix; ++ lzw_node *left; ++ lzw_node *right; ++ lzw_node *map; ++}; ++ ++struct lzw_state { ++ union { ++ lzw_node *lookup; /* encoder table */ ++ lzw_entry *table; /* decoder table */ ++ }; ++ lzw_index index; /* table index */ ++ union { ++ lzw_node *lastnode; /* previous encoder table node */ ++ struct { ++ lzw_entry *lastentry; /* previous decoder table entry */ ++ int tailbytes; /* num of bytes of lastentry not yet written out */ ++ }; ++ }; ++ int basebits; /* /UnitLength parameter (8) */ ++ int codebits; /* current code bits */ ++ int lastbyte; /* previosly read byte */ ++ int tailbits; /* lastbyte bits not yet consumed */ ++ int flush; /* encoder */ ++ int flags; /* options */ ++}; ++ ++/* macros */ ++ ++#define LZW_MIN_BITS 3 ++#define LZW_MAX_BITS 12 ++#define LZW_TABLE_SIZE (1 << LZW_MAX_BITS) ++#define LZW_LOOKUP_SIZE (LZW_TABLE_SIZE + 1) ++ ++#define lzw_bit_range(bits) (bits >= LZW_MIN_BITS && bits <= LZW_BASE_BITS) ++#define lzw_base_bits(flags) (flags & ((1 << 4) - 1)) // 4 low bits of flags is basebits (UnitLength) ++ ++#define lzw_initial_codes(state) (1 << state->basebits) ++#define lzw_clear_code(state) lzw_initial_codes(state) ++#define lzw_eod_code(state) (lzw_initial_codes(state) + 1) ++#define lzw_initial_index(state) (lzw_initial_codes(state) + 2) ++ ++#define lzw_max_index(state) ((1 << state->codebits) - ((state->flags & LZW_EARLY_INDEX) ? 1 : 0)) ++#define lzw_check_bits(state) ((void)(state->index == lzw_max_index(state) && state->codebits < LZW_MAX_BITS && ++state->codebits)) ++ ++#define lzw_malloc util_malloc ++#define lzw_free util_free ++ ++/* decoder */ ++ ++static struct lzw_entry lzw_initial_table[] = { ++ {{"\x00"}, 1}, {{"\x01"}, 1}, {{"\x02"}, 1}, {{"\x03"}, 1}, {{"\x04"}, 1}, {{"\x05"}, 1}, {{"\x06"}, 1}, {{"\x07"}, 1}, {{"\x08"}, 1}, {{"\x09"}, 1}, {{"\x0A"}, 1}, {{"\x0B"}, 1}, {{"\x0C"}, 1}, {{"\x0D"}, 1}, {{"\x0E"}, 1}, {{"\x0F"}, 1}, ++ {{"\x10"}, 1}, {{"\x11"}, 1}, {{"\x12"}, 1}, {{"\x13"}, 1}, {{"\x14"}, 1}, {{"\x15"}, 1}, {{"\x16"}, 1}, {{"\x17"}, 1}, {{"\x18"}, 1}, {{"\x19"}, 1}, {{"\x1A"}, 1}, {{"\x1B"}, 1}, {{"\x1C"}, 1}, {{"\x1D"}, 1}, {{"\x1E"}, 1}, {{"\x1F"}, 1}, ++ {{"\x20"}, 1}, {{"\x21"}, 1}, {{"\x22"}, 1}, {{"\x23"}, 1}, {{"\x24"}, 1}, {{"\x25"}, 1}, {{"\x26"}, 1}, {{"\x27"}, 1}, {{"\x28"}, 1}, {{"\x29"}, 1}, {{"\x2A"}, 1}, {{"\x2B"}, 1}, {{"\x2C"}, 1}, {{"\x2D"}, 1}, {{"\x2E"}, 1}, {{"\x2F"}, 1}, ++ {{"\x30"}, 1}, {{"\x31"}, 1}, {{"\x32"}, 1}, {{"\x33"}, 1}, {{"\x34"}, 1}, {{"\x35"}, 1}, {{"\x36"}, 1}, {{"\x37"}, 1}, {{"\x38"}, 1}, {{"\x39"}, 1}, {{"\x3A"}, 1}, {{"\x3B"}, 1}, {{"\x3C"}, 1}, {{"\x3D"}, 1}, {{"\x3E"}, 1}, {{"\x3F"}, 1}, ++ {{"\x40"}, 1}, {{"\x41"}, 1}, {{"\x42"}, 1}, {{"\x43"}, 1}, {{"\x44"}, 1}, {{"\x45"}, 1}, {{"\x46"}, 1}, {{"\x47"}, 1}, {{"\x48"}, 1}, {{"\x49"}, 1}, {{"\x4A"}, 1}, {{"\x4B"}, 1}, {{"\x4C"}, 1}, {{"\x4D"}, 1}, {{"\x4E"}, 1}, {{"\x4F"}, 1}, ++ {{"\x50"}, 1}, {{"\x51"}, 1}, {{"\x52"}, 1}, {{"\x53"}, 1}, {{"\x54"}, 1}, {{"\x55"}, 1}, {{"\x56"}, 1}, {{"\x57"}, 1}, {{"\x58"}, 1}, {{"\x59"}, 1}, {{"\x5A"}, 1}, {{"\x5B"}, 1}, {{"\x5C"}, 1}, {{"\x5D"}, 1}, {{"\x5E"}, 1}, {{"\x5F"}, 1}, ++ {{"\x60"}, 1}, {{"\x61"}, 1}, {{"\x62"}, 1}, {{"\x63"}, 1}, {{"\x64"}, 1}, {{"\x65"}, 1}, {{"\x66"}, 1}, {{"\x67"}, 1}, {{"\x68"}, 1}, {{"\x69"}, 1}, {{"\x6A"}, 1}, {{"\x6B"}, 1}, {{"\x6C"}, 1}, {{"\x6D"}, 1}, {{"\x6E"}, 1}, {{"\x6F"}, 1}, ++ {{"\x70"}, 1}, {{"\x71"}, 1}, {{"\x72"}, 1}, {{"\x73"}, 1}, {{"\x74"}, 1}, {{"\x75"}, 1}, {{"\x76"}, 1}, {{"\x77"}, 1}, {{"\x78"}, 1}, {{"\x79"}, 1}, {{"\x7A"}, 1}, {{"\x7B"}, 1}, {{"\x7C"}, 1}, {{"\x7D"}, 1}, {{"\x7E"}, 1}, {{"\x7F"}, 1}, ++ {{"\x80"}, 1}, {{"\x81"}, 1}, {{"\x82"}, 1}, {{"\x83"}, 1}, {{"\x84"}, 1}, {{"\x85"}, 1}, {{"\x86"}, 1}, {{"\x87"}, 1}, {{"\x88"}, 1}, {{"\x89"}, 1}, {{"\x8A"}, 1}, {{"\x8B"}, 1}, {{"\x8C"}, 1}, {{"\x8D"}, 1}, {{"\x8E"}, 1}, {{"\x8F"}, 1}, ++ {{"\x90"}, 1}, {{"\x91"}, 1}, {{"\x92"}, 1}, {{"\x93"}, 1}, {{"\x94"}, 1}, {{"\x95"}, 1}, {{"\x96"}, 1}, {{"\x97"}, 1}, {{"\x98"}, 1}, {{"\x99"}, 1}, {{"\x9A"}, 1}, {{"\x9B"}, 1}, {{"\x9C"}, 1}, {{"\x9D"}, 1}, {{"\x9E"}, 1}, {{"\x9F"}, 1}, ++ {{"\xA0"}, 1}, {{"\xA1"}, 1}, {{"\xA2"}, 1}, {{"\xA3"}, 1}, {{"\xA4"}, 1}, {{"\xA5"}, 1}, {{"\xA6"}, 1}, {{"\xA7"}, 1}, {{"\xA8"}, 1}, {{"\xA9"}, 1}, {{"\xAA"}, 1}, {{"\xAB"}, 1}, {{"\xAC"}, 1}, {{"\xAD"}, 1}, {{"\xAE"}, 1}, {{"\xAF"}, 1}, ++ {{"\xB0"}, 1}, {{"\xB1"}, 1}, {{"\xB2"}, 1}, {{"\xB3"}, 1}, {{"\xB4"}, 1}, {{"\xB5"}, 1}, {{"\xB6"}, 1}, {{"\xB7"}, 1}, {{"\xB8"}, 1}, {{"\xB9"}, 1}, {{"\xBA"}, 1}, {{"\xBB"}, 1}, {{"\xBC"}, 1}, {{"\xBD"}, 1}, {{"\xBE"}, 1}, {{"\xBF"}, 1}, ++ {{"\xC0"}, 1}, {{"\xC1"}, 1}, {{"\xC2"}, 1}, {{"\xC3"}, 1}, {{"\xC4"}, 1}, {{"\xC5"}, 1}, {{"\xC6"}, 1}, {{"\xC7"}, 1}, {{"\xC8"}, 1}, {{"\xC9"}, 1}, {{"\xCA"}, 1}, {{"\xCB"}, 1}, {{"\xCC"}, 1}, {{"\xCD"}, 1}, {{"\xCE"}, 1}, {{"\xCF"}, 1}, ++ {{"\xD0"}, 1}, {{"\xD1"}, 1}, {{"\xD2"}, 1}, {{"\xD3"}, 1}, {{"\xD4"}, 1}, {{"\xD5"}, 1}, {{"\xD6"}, 1}, {{"\xD7"}, 1}, {{"\xD8"}, 1}, {{"\xD9"}, 1}, {{"\xDA"}, 1}, {{"\xDB"}, 1}, {{"\xDC"}, 1}, {{"\xDD"}, 1}, {{"\xDE"}, 1}, {{"\xDF"}, 1}, ++ {{"\xE0"}, 1}, {{"\xE1"}, 1}, {{"\xE2"}, 1}, {{"\xE3"}, 1}, {{"\xE4"}, 1}, {{"\xE5"}, 1}, {{"\xE6"}, 1}, {{"\xE7"}, 1}, {{"\xE8"}, 1}, {{"\xE9"}, 1}, {{"\xEA"}, 1}, {{"\xEB"}, 1}, {{"\xEC"}, 1}, {{"\xED"}, 1}, {{"\xEE"}, 1}, {{"\xEF"}, 1}, ++ {{"\xF0"}, 1}, {{"\xF1"}, 1}, {{"\xF2"}, 1}, {{"\xF3"}, 1}, {{"\xF4"}, 1}, {{"\xF5"}, 1}, {{"\xF6"}, 1}, {{"\xF7"}, 1}, {{"\xF8"}, 1}, {{"\xF9"}, 1}, {{"\xFA"}, 1}, {{"\xFB"}, 1}, {{"\xFC"}, 1}, {{"\xFD"}, 1}, {{"\xFE"}, 1}, {{"\xFF"}, 1} ++}; ++ ++#define lzw_entry_at(state, index) (&state->table[index]) ++ ++static lzw_state * lzw_decoder_init_table (lzw_state *state, lzw_entry *table, int flags) ++{ ++ state->basebits = lzw_base_bits(flags); // first four bits or flags ++ if (!lzw_bit_range(state->basebits)) ++ return NULL; ++ state->flags = flags; ++ if ((state->table = table) == NULL) ++ { ++ state->table = (lzw_entry *)lzw_malloc(LZW_TABLE_SIZE * sizeof(lzw_entry)); ++ state->flags |= LZW_TABLE_ALLOC; ++ } ++ memcpy(state->table, lzw_initial_table, (size_t)lzw_initial_codes(state)*sizeof(lzw_entry)); ++ // memset(&state->table[lzw_initial_codes(state)], 0, 2*sizeof(lzw_entry)); // eod and clear entries never accessed ++ state->codebits = state->basebits + 1; ++ state->index = lzw_initial_index(state); ++ state->lastentry = NULL; ++ state->tailbytes = 0; ++ state->lastbyte = 0; ++ state->tailbits = 0; ++ return state; ++} ++ ++lzw_state * lzw_decoder_init (lzw_state *state, int flags) ++{ ++ return lzw_decoder_init_table(state, NULL, flags); ++} ++ ++static void lzw_decoder_clear (lzw_state *state) ++{ ++ lzw_entry *entry; ++ lzw_index initindex = lzw_initial_index(state); ++ while (state->index > initindex) ++ { ++ entry = lzw_entry_at(state, --state->index); ++ lzw_free(entry->data); ++ // entry->data = NULL; ++ // entry->size = 0; ++ } ++ state->lastentry = NULL; ++ state->tailbytes = 0; ++ state->codebits = state->basebits + 1; ++} ++ ++void lzw_decoder_close (lzw_state *state) ++{ ++ lzw_decoder_clear(state); ++ if (state->flags & LZW_TABLE_ALLOC) ++ lzw_free(state->table); ++} ++ ++static int lzw_next_entry (lzw_state *state, lzw_entry *nextentry) ++{ ++ lzw_entry *lastentry, *newentry; ++ if ((lastentry = state->lastentry) == NULL) ++ return 1; /* its ok */ ++ if (state->index == LZW_TABLE_SIZE) ++ return 0; /* invalid input; eod marker expected earlier */ ++ /* put the new entry on the top of the table */ ++ newentry = lzw_entry_at(state, state->index++); ++ /* its size is the last entrtyy size plus 1 */ ++ newentry->size = lastentry->size + 1; ++ /* its content is the content of the last entry, */ ++ newentry->data = (char *)lzw_malloc((size_t)newentry->size); ++ memcpy(newentry->data, lastentry->data, lastentry->size); ++ /* plus the first byte of the new entry (usually fixed code entry) */ ++ newentry->data[newentry->size - 1] = nextentry->data[0]; ++ return 1; ++} ++ ++#define lzw_write_bytes(O, state) ((state->tailbytes -= (int)iof_write(O, state->lastentry->data, (size_t)state->tailbytes)) == 0) ++ ++iof_status lzw_decode_state (iof *I, iof *O, lzw_state *state) ++{ ++ const lzw_index clear = lzw_clear_code(state), eod = lzw_eod_code(state); ++ lzw_index code; ++ lzw_entry *entry; ++ if (state->lastentry != NULL) ++ { /* write out the tail from the last call */ ++ if (state->tailbytes > 0 && !lzw_write_bytes(O, state)) ++ return IOFFULL; ++ /* do what we normally do at the end of the loop body below */ ++ lzw_check_bits(state); ++ } ++ // if (state->flags & LZW_LOW_BITS_FIRST) ++ // return IOFERR; ++ while (1) ++ { ++ /* get input code of length state->codebits */ ++ code = (state->lastbyte & ((1 << state->tailbits) - 1)) << (state->codebits - state->tailbits); ++ for (state->tailbits -= state->codebits; state->tailbits < 0; ) ++ { ++ get_code: ++ if ((state->lastbyte = iof_get(I)) < 0) ++ return state->flush ? IOFEOF : state->lastbyte; ++ state->tailbits += 8; ++ if (state->tailbits < 0) ++ { ++ code |= (state->lastbyte << (-state->tailbits)); ++ goto get_code; ++ } ++ else ++ { ++ code |= (state->lastbyte >> state->tailbits); ++ break; ++ } ++ } ++ /* interpret the code */ ++ if (code < state->index) ++ { /* single byte code or special marker */ ++ if (code == clear) ++ { ++ lzw_decoder_clear(state); ++ continue; ++ } ++ if (code == eod) ++ return IOFEOF; ++ entry = lzw_entry_at(state, code); ++ if (!lzw_next_entry(state, entry)) ++ return IOFERR; ++ } ++ else if (code == state->index) ++ { /* apparently encoder has emitted the code of the key just created (see notes) */ ++ if (!lzw_next_entry(state, state->lastentry)) ++ return IOFERR; ++ entry = lzw_entry_at(state, state->index - 1); ++ } ++ else ++ { /* invalid input code */ ++ return IOFERR; ++ } ++ /* record the entry found */ ++ state->lastentry = entry; ++ /* emit the sequence pointed by that entry */ ++ state->tailbytes = entry->size; ++ if (!lzw_write_bytes(O, state)) ++ return IOFFULL; ++ /* check and update code bits */ ++ lzw_check_bits(state); ++ } ++ return state->lastbyte; // never reached ++} ++ ++/* encoder */ ++ ++#define lzw_node_at(state, index) (&state->lookup[index]) ++ ++#define lzw_node_init(node, i, c) (node->index = i, node->suffix = c, node->left = NULL, node->right = NULL, node->map = NULL) ++ ++static lzw_state * lzw_encoder_init_table (lzw_state *state, lzw_node *lookup, int flags) ++{ ++ lzw_index index; ++ lzw_node *node; ++ state->basebits = lzw_base_bits(flags); // first four bits of flags is base bits of code (default 8) ++ if (!lzw_bit_range(state->basebits)) ++ return NULL; ++ state->flags = flags; ++ if ((state->lookup = lookup) == NULL) ++ { ++ state->lookup = lzw_malloc(LZW_LOOKUP_SIZE*sizeof(lzw_node)); ++ state->flags |= LZW_TABLE_ALLOC; ++ } ++ state->index = lzw_initial_index(state); ++ for (index = 0; index < lzw_initial_codes(state); ++index) ++ { ++ node = lzw_node_at(state, index); ++ lzw_node_init(node, index, (unsigned char)index); ++ } ++ state->codebits = state->basebits + 1; ++ state->lastnode = NULL; ++ state->lastbyte = 0; ++ state->tailbits = 0; ++ return state; ++} ++ ++lzw_state * lzw_encoder_init (lzw_state *state, int flags) ++{ ++ return lzw_encoder_init_table(state, NULL, flags); ++} ++ ++void lzw_encoder_close (lzw_state *state) ++{ ++ if (state->flags & LZW_TABLE_ALLOC) ++ lzw_free(state->lookup); ++} ++ ++static void lzw_encoder_clear (lzw_state *state) ++{ ++ lzw_node *node; ++ lzw_index index; ++ /* clear fixed nodes */ ++ for (index = 0; index < lzw_initial_codes(state); ++index) ++ { ++ node = lzw_node_at(state, index); ++ lzw_node_init(node, index, (unsigned char)index); ++ } ++ /* reset table index */ ++ state->index = lzw_initial_index(state); ++ /* reset code bits */ ++ state->codebits = state->basebits + 1; ++} ++ ++static void lzw_put_code (iof *O, lzw_state *state, lzw_index code, int todobits) ++{ ++ int leftbits, rightbits; ++ do ++ { ++ leftbits = 8 - state->tailbits; ++ rightbits = todobits - leftbits; ++ if (rightbits >= 0) ++ { ++ state->lastbyte |= (code >> rightbits); ++ iof_put(O, state->lastbyte); ++ code = code & ((1 << rightbits) - 1); ++ todobits -= leftbits; ++ state->lastbyte = 0; ++ state->tailbits = 0; ++ } ++ else ++ { ++ state->lastbyte |= (code << (-rightbits)); ++ state->tailbits += todobits; ++ return; ++ } ++ } while (1); ++} ++ ++static iof_status lzw_encode_last (iof *O, lzw_state *state) ++{ ++ if (state->flush) ++ { ++ /* put the last code if any */ ++ if (state->lastnode != NULL) ++ lzw_put_code(O, state, state->lastnode->index, state->codebits); ++ /* put eod marker, */ ++ lzw_put_code(O, state, lzw_eod_code(state), state->codebits); ++ /* with tail bits set to 0 */ ++ if (state->tailbits > 0) ++ lzw_put_code(O, state, 0, 8 - state->tailbits); ++ return IOFEOF; ++ } ++ return IOFEMPTY; ++} ++ ++static lzw_node * lzw_node_push (lzw_state *state, unsigned char suffix) ++{ ++ lzw_node *node; ++ node = lzw_node_at(state, state->index); ++ lzw_node_init(node, state->index, suffix); ++ ++state->index; ++ return node; ++} ++ ++static int lzw_next_node (lzw_state *state, unsigned char suffix) ++{ ++ lzw_node *node; ++ if ((node = state->lastnode->map) == NULL) ++ { ++ state->lastnode->map = lzw_node_push(state, suffix); ++ return 0; ++ } ++ while (1) ++ { ++ if (suffix < node->suffix) ++ { ++ if (node->left == NULL) ++ { ++ node->left = lzw_node_push(state, suffix); ++ return 0; ++ } ++ node = node->left; ++ } ++ else if (suffix > node->suffix) ++ { ++ if (node->right == NULL) ++ { ++ node->right = lzw_node_push(state, suffix); ++ return 0; ++ } ++ node = node->right; ++ } ++ else ++ { ++ state->lastnode = node; ++ return 1; ++ } ++ } ++ return 0; // never reached ++} ++ ++iof_status lzw_encode_state (iof *I, iof *O, lzw_state *state) ++{ ++ int byte; ++ if (state->lastnode == NULL) ++ { /* first call only; following convention, put clear-table marker */ ++ if (!iof_ensure(O, 2)) ++ return IOFFULL; ++ lzw_put_code(O, state, lzw_clear_code(state), state->codebits); ++ /* get the first input byte and initialize the current table entry */ ++ if ((byte = iof_get(I)) < 0) ++ return lzw_encode_last(O, state); ++ state->lastnode = lzw_node_at(state, byte); ++ } ++ while (iof_ensure(O, 2)) ++ { /* we need to write at most 2 bytes on each iteration */ ++ if ((byte = iof_get(I)) < 0) ++ return lzw_encode_last(O, state); ++ if (lzw_next_node(state, (unsigned char)byte) == 0) ++ { /* means that the key hasn't been found and the new entry has just been created */ ++ /* output the code pointing the longest sequence so far */ ++ lzw_put_code(O, state, state->lastnode->index, state->codebits); ++ /* update code bits */ ++ if (state->index == lzw_max_index(state) + 1) ++ { ++ if (state->codebits < LZW_MAX_BITS) ++ ++state->codebits; ++ else ++ { ++ /* put clear-table marker */ ++ lzw_put_code(O, state, lzw_clear_code(state), state->codebits); ++ /* reset the table */ ++ lzw_encoder_clear(state); ++ } ++ } ++ /* in any case, recent byte becomes the current table code */ ++ state->lastnode = lzw_node_at(state, byte); ++ } ++ /* otherwise no new entry is appended and state->lastnode points the longer sequence just found */ ++ } ++ return IOFFULL; ++} ++ ++/* single call codecs */ ++ ++iof_status lzw_decode (iof *I, iof *O, int flags) ++{ ++ lzw_state state = { { 0 } }; // shut overactive warnings ++ lzw_entry table[LZW_TABLE_SIZE]; ++ int ret; ++ lzw_decoder_init_table(&state, table, flags); ++ state.flush = 1; ++ ret = lzw_decode_state(I, O, &state); ++ // iof_flush(O); // ? ++ lzw_decoder_close(&state); ++ return ret; ++} ++ ++iof_status lzw_encode (iof *I, iof *O, int flags) ++{ ++ lzw_state state; ++ lzw_node lookup[LZW_LOOKUP_SIZE]; ++ int ret; ++ lzw_encoder_init_table(&state, lookup, flags); ++ state.flush = 1; ++ ret = lzw_encode_state(I, O, &state); ++ // iof_flush(O); // ? ++ lzw_encoder_close(&state); ++ return ret; ++} ++ ++/* filters */ ++ ++// lzw decoder function ++ ++static size_t lzw_decoder (iof *F, iof_mode mode) ++{ ++ lzw_state *state; ++ iof_status status; ++ size_t tail; ++ ++ state = iof_filter_state(lzw_state *, F); ++ switch(mode) ++ { ++ case IOFLOAD: ++ case IOFREAD: ++ if (F->flags & IOF_STOPPED) ++ return 0; ++ tail = iof_tail(F); ++ F->pos = F->buf + tail; ++ F->end = F->buf + F->space; ++ do { ++ status = lzw_decode_state(F->next, F, state); ++ } while (mode == IOFLOAD && status == IOFFULL && iof_resize_buffer(F)); ++ return iof_decoder_retval(F, "lzw", status); ++ case IOFCLOSE: ++ lzw_decoder_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// lzw encoder function ++ ++static size_t lzw_encoder (iof *F, iof_mode mode) ++{ ++ lzw_state *state; ++ iof_status status; ++ ++ state = iof_filter_state(lzw_state *, F); ++ switch (mode) ++ { ++ case IOFFLUSH: ++ state->flush = 1; ++ // fall through ++ case IOFWRITE: ++ F->end = F->pos; ++ F->pos = F->buf; ++ status = lzw_encode_state(F, F->next, state); ++ return iof_encoder_retval(F, "lzw", status); ++ case IOFCLOSE: ++ if (!state->flush) ++ lzw_encoder(F, IOFFLUSH); ++ lzw_encoder_close(state); ++ iof_free(F); ++ return 0; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++iof * iof_filter_lzw_decoder (iof *N, int flags) ++{ ++ iof *I; ++ lzw_state *state; ++ I = iof_filter_reader(lzw_decoder, sizeof(lzw_state), &state); ++ iof_setup_next(I, N); ++ if (lzw_decoder_init(state, flags) == NULL) ++ { ++ iof_discard(I); ++ return NULL; ++ } ++ state->flush = 1; ++ return I; ++} ++ ++iof * iof_filter_lzw_encoder (iof *N, int flags) ++{ ++ iof *O; ++ lzw_state *state; ++ O = iof_filter_writer(lzw_encoder, sizeof(lzw_state), &state); ++ iof_setup_next(O, N); ++ if (lzw_encoder_init(state, flags) == NULL) ++ { ++ iof_discard(O); ++ return NULL; ++ } ++ return O; ++} +diff --git a/texk/web2c/luatexdir/luapplib/util/utillzw.h b/texk/web2c/luatexdir/luapplib/util/utillzw.h +new file mode 100644 +index 000000000..9e3a085d4 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utillzw.h +@@ -0,0 +1,30 @@ ++#ifndef UTIL_LZW_H ++#define UTIL_LZW_H ++ ++#include "utiliof.h" ++ ++typedef struct lzw_state lzw_state; ++ ++#define LZW_BASE_BITS 8 ++#define LZW_TABLE_ALLOC (1<<4) ++#define LZW_EARLY_INDEX (1<<5) ++//#define LZW_LOW_BITS_FIRST (1<<6) ++#define LZW_DECODER_DEFAULTS (LZW_BASE_BITS|LZW_EARLY_INDEX|0) ++#define LZW_ENCODER_DEFAULTS (LZW_BASE_BITS|LZW_EARLY_INDEX|0) ++ ++lzw_state * lzw_decoder_init (lzw_state *state, int flags); ++lzw_state * lzw_encoder_init (lzw_state *state, int flags); ++ ++void lzw_decoder_close (lzw_state *state); ++void lzw_encoder_close (lzw_state *state); ++ ++iof_status lzw_encode_state (iof *I, iof *O, lzw_state *state); ++iof_status lzw_decode_state (iof *I, iof *O, lzw_state *state); ++ ++iof_status lzw_encode (iof *I, iof *O, int flags); ++iof_status lzw_decode (iof *I, iof *O, int flags); ++ ++iof * iof_filter_lzw_decoder (iof *N, int flags); ++iof * iof_filter_lzw_encoder (iof *N, int flags); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilmd5.c b/texk/web2c/luatexdir/luapplib/util/utilmd5.c +new file mode 100644 +index 000000000..a2926b0aa +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilmd5.c +@@ -0,0 +1,413 @@ ++/* md5 implementation by Peter Deutsch (ghost@aladdin.com) with some convenience shorthands */ ++ ++/* begin of md5.c */ ++ ++/* ++ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++ ++ L. Peter Deutsch ++ ghost@aladdin.com ++ ++ */ ++/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ ++/* ++ Independent implementation of MD5 (RFC 1321). ++ ++ This code implements the MD5 Algorithm defined in RFC 1321, whose ++ text is available at ++ http://www.ietf.org/rfc/rfc1321.txt ++ The code is derived from the text of the RFC, including the test suite ++ (section A.5) but excluding the rest of Appendix A. It does not include ++ any code or documentation that is identified in the RFC as being ++ copyrighted. ++ ++ The original and principal author of md5.c is L. Peter Deutsch ++ . Other authors are noted in the change history ++ that follows (in reverse chronological order): ++ ++ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order ++ either statically or dynamically; added missing #include ++ in library. ++ 2002-03-11 lpd Corrected argument list for main(), and added int return ++ type, in test program and T value program. ++ 2002-02-21 lpd Added missing #include in test program. ++ 2000-07-03 lpd Patched to eliminate warnings about "constant is ++ unsigned in ANSI C, signed in traditional"; made test program ++ self-checking. ++ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. ++ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). ++ 1999-05-03 lpd Original version. ++ */ ++ ++//#include "md5.h" ++#include "utilmd5.h" ++#include ++ ++#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ ++#ifdef ARCH_IS_BIG_ENDIAN ++# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) ++#else ++# define BYTE_ORDER 0 ++#endif ++ ++#define T_MASK ((uint32_t)~0) ++#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) ++#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) ++#define T3 0x242070db ++#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) ++#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) ++#define T6 0x4787c62a ++#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) ++#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) ++#define T9 0x698098d8 ++#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) ++#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) ++#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) ++#define T13 0x6b901122 ++#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) ++#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) ++#define T16 0x49b40821 ++#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) ++#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) ++#define T19 0x265e5a51 ++#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) ++#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) ++#define T22 0x02441453 ++#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) ++#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) ++#define T25 0x21e1cde6 ++#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) ++#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) ++#define T28 0x455a14ed ++#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) ++#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) ++#define T31 0x676f02d9 ++#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) ++#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) ++#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) ++#define T35 0x6d9d6122 ++#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) ++#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) ++#define T38 0x4bdecfa9 ++#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) ++#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) ++#define T41 0x289b7ec6 ++#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) ++#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) ++#define T44 0x04881d05 ++#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) ++#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) ++#define T47 0x1fa27cf8 ++#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) ++#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) ++#define T50 0x432aff97 ++#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) ++#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) ++#define T53 0x655b59c3 ++#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) ++#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) ++#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) ++#define T57 0x6fa87e4f ++#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) ++#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) ++#define T60 0x4e0811a1 ++#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) ++#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) ++#define T63 0x2ad7d2bb ++#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) ++ ++ ++static void ++md5_process(md5_state_t *pms, const uint8_t *data /*[64]*/) ++{ ++ uint32_t ++ a = pms->abcd[0], b = pms->abcd[1], ++ c = pms->abcd[2], d = pms->abcd[3]; ++ uint32_t t; ++#if BYTE_ORDER > 0 ++ /* Define storage only for big-endian CPUs. */ ++ uint32_t X[16]; ++#else ++ /* Define storage for little-endian or both types of CPUs. */ ++ uint32_t xbuf[16]; ++ const uint32_t *X; ++#endif ++ ++ { ++#if BYTE_ORDER == 0 ++ /* ++ * Determine dynamically whether this is a big-endian or ++ * little-endian machine, since we can use a more efficient ++ * algorithm on the latter. ++ */ ++ static const int w = 1; ++ ++ if (*((const uint8_t *)&w)) /* dynamic little-endian */ ++#endif ++#if BYTE_ORDER <= 0 /* little-endian */ ++ { ++ /* ++ * On little-endian machines, we can process properly aligned ++ * data without copying it. ++ */ ++ if (!((data - (const uint8_t *)0) & 3)) { ++ /* data are properly aligned */ ++ X = (const uint32_t *)data; ++ } else { ++ /* not aligned */ ++ memcpy(xbuf, data, 64); ++ X = xbuf; ++ } ++ } ++#endif ++#if BYTE_ORDER == 0 ++ else /* dynamic big-endian */ ++#endif ++#if BYTE_ORDER >= 0 /* big-endian */ ++ { ++ /* ++ * On big-endian machines, we must arrange the bytes in the ++ * right order. ++ */ ++ const uint8_t *xp = data; ++ int i; ++ ++# if BYTE_ORDER == 0 ++ X = xbuf; /* (dynamic only) */ ++# else ++# define xbuf X /* (static only) */ ++# endif ++ for (i = 0; i < 16; ++i, xp += 4) ++ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); ++ } ++#endif ++ } ++ ++#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) ++ ++ /* Round 1. */ ++ /* Let [abcd k s i] denote the operation ++ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ ++#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) ++#define SET(a, b, c, d, k, s, Ti)\ ++ t = a + F(b,c,d) + X[k] + Ti;\ ++ a = ROTATE_LEFT(t, s) + b ++ /* Do the following 16 operations. */ ++ SET(a, b, c, d, 0, 7, T1); ++ SET(d, a, b, c, 1, 12, T2); ++ SET(c, d, a, b, 2, 17, T3); ++ SET(b, c, d, a, 3, 22, T4); ++ SET(a, b, c, d, 4, 7, T5); ++ SET(d, a, b, c, 5, 12, T6); ++ SET(c, d, a, b, 6, 17, T7); ++ SET(b, c, d, a, 7, 22, T8); ++ SET(a, b, c, d, 8, 7, T9); ++ SET(d, a, b, c, 9, 12, T10); ++ SET(c, d, a, b, 10, 17, T11); ++ SET(b, c, d, a, 11, 22, T12); ++ SET(a, b, c, d, 12, 7, T13); ++ SET(d, a, b, c, 13, 12, T14); ++ SET(c, d, a, b, 14, 17, T15); ++ SET(b, c, d, a, 15, 22, T16); ++#undef SET ++ ++ /* Round 2. */ ++ /* Let [abcd k s i] denote the operation ++ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ ++#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) ++#define SET(a, b, c, d, k, s, Ti)\ ++ t = a + G(b,c,d) + X[k] + Ti;\ ++ a = ROTATE_LEFT(t, s) + b ++ /* Do the following 16 operations. */ ++ SET(a, b, c, d, 1, 5, T17); ++ SET(d, a, b, c, 6, 9, T18); ++ SET(c, d, a, b, 11, 14, T19); ++ SET(b, c, d, a, 0, 20, T20); ++ SET(a, b, c, d, 5, 5, T21); ++ SET(d, a, b, c, 10, 9, T22); ++ SET(c, d, a, b, 15, 14, T23); ++ SET(b, c, d, a, 4, 20, T24); ++ SET(a, b, c, d, 9, 5, T25); ++ SET(d, a, b, c, 14, 9, T26); ++ SET(c, d, a, b, 3, 14, T27); ++ SET(b, c, d, a, 8, 20, T28); ++ SET(a, b, c, d, 13, 5, T29); ++ SET(d, a, b, c, 2, 9, T30); ++ SET(c, d, a, b, 7, 14, T31); ++ SET(b, c, d, a, 12, 20, T32); ++#undef SET ++ ++ /* Round 3. */ ++ /* Let [abcd k s t] denote the operation ++ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ ++#define H(x, y, z) ((x) ^ (y) ^ (z)) ++#define SET(a, b, c, d, k, s, Ti)\ ++ t = a + H(b,c,d) + X[k] + Ti;\ ++ a = ROTATE_LEFT(t, s) + b ++ /* Do the following 16 operations. */ ++ SET(a, b, c, d, 5, 4, T33); ++ SET(d, a, b, c, 8, 11, T34); ++ SET(c, d, a, b, 11, 16, T35); ++ SET(b, c, d, a, 14, 23, T36); ++ SET(a, b, c, d, 1, 4, T37); ++ SET(d, a, b, c, 4, 11, T38); ++ SET(c, d, a, b, 7, 16, T39); ++ SET(b, c, d, a, 10, 23, T40); ++ SET(a, b, c, d, 13, 4, T41); ++ SET(d, a, b, c, 0, 11, T42); ++ SET(c, d, a, b, 3, 16, T43); ++ SET(b, c, d, a, 6, 23, T44); ++ SET(a, b, c, d, 9, 4, T45); ++ SET(d, a, b, c, 12, 11, T46); ++ SET(c, d, a, b, 15, 16, T47); ++ SET(b, c, d, a, 2, 23, T48); ++#undef SET ++ ++ /* Round 4. */ ++ /* Let [abcd k s t] denote the operation ++ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ ++#define I(x, y, z) ((y) ^ ((x) | ~(z))) ++#define SET(a, b, c, d, k, s, Ti)\ ++ t = a + I(b,c,d) + X[k] + Ti;\ ++ a = ROTATE_LEFT(t, s) + b ++ /* Do the following 16 operations. */ ++ SET(a, b, c, d, 0, 6, T49); ++ SET(d, a, b, c, 7, 10, T50); ++ SET(c, d, a, b, 14, 15, T51); ++ SET(b, c, d, a, 5, 21, T52); ++ SET(a, b, c, d, 12, 6, T53); ++ SET(d, a, b, c, 3, 10, T54); ++ SET(c, d, a, b, 10, 15, T55); ++ SET(b, c, d, a, 1, 21, T56); ++ SET(a, b, c, d, 8, 6, T57); ++ SET(d, a, b, c, 15, 10, T58); ++ SET(c, d, a, b, 6, 15, T59); ++ SET(b, c, d, a, 13, 21, T60); ++ SET(a, b, c, d, 4, 6, T61); ++ SET(d, a, b, c, 11, 10, T62); ++ SET(c, d, a, b, 2, 15, T63); ++ SET(b, c, d, a, 9, 21, T64); ++#undef SET ++ ++ /* Then perform the following additions. (That is increment each ++ of the four registers by the value it had before this block ++ was started.) */ ++ pms->abcd[0] += a; ++ pms->abcd[1] += b; ++ pms->abcd[2] += c; ++ pms->abcd[3] += d; ++} ++ ++void ++pplib_md5_init(md5_state_t *pms) ++{ ++ pms->count[0] = pms->count[1] = 0; ++ pms->abcd[0] = 0x67452301; ++ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; ++ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; ++ pms->abcd[3] = 0x10325476; ++} ++ ++void ++md5_add(md5_state_t *pms, const void *input, size_t size) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ int nbytes = (int)size; // PJ ++ int left = nbytes; ++ int offset = (pms->count[0] >> 3) & 63; ++ uint32_t nbits = (uint32_t)(nbytes << 3); ++ ++ if (nbytes <= 0) ++ return; ++ ++ /* Update the message length. */ ++ pms->count[1] += nbytes >> 29; ++ pms->count[0] += nbits; ++ if (pms->count[0] < nbits) ++ pms->count[1]++; ++ ++ /* Process an initial partial block. */ ++ if (offset) { ++ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); ++ ++ memcpy(pms->buf + offset, p, copy); ++ if (offset + copy < 64) ++ return; ++ p += copy; ++ left -= copy; ++ md5_process(pms, pms->buf); ++ } ++ ++ /* Process full blocks. */ ++ for (; left >= 64; p += 64, left -= 64) ++ md5_process(pms, p); ++ ++ /* Process a final partial block. */ ++ if (left) ++ memcpy(pms->buf, p, left); ++} ++ ++void ++md5_put(md5_state_t *pms, uint8_t digest[16]) ++{ ++ static const uint8_t pad[64] = { ++ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++ }; ++ uint8_t data[8]; ++ int i; ++ ++ /* Save the length before padding. */ ++ for (i = 0; i < 8; ++i) ++ data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3)); ++ /* Pad to 56 bytes mod 64. */ ++ md5_add(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); ++ /* Append the length. */ ++ md5_add(pms, data, 8); ++ for (i = 0; i < 16; ++i) ++ digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); ++} ++ ++/* end of md5.h */ ++ ++static md5_state_t state; ++ ++void pplib_md5 (const void *input, size_t length, uint8_t output[16]) ++{ ++ pplib_md5_init(&state); ++ md5_add(&state, input, length); ++ md5_put(&state, output); ++} ++ ++void md5init (void) ++{ ++ pplib_md5_init(&state); ++} ++ ++void md5add (const void *input, size_t length) ++{ ++ md5_add(&state, input, length); ++} ++ ++void md5put (uint8_t output[16]) ++{ ++ md5_put(&state, output); ++} +diff --git a/texk/web2c/luatexdir/luapplib/util/utilmd5.h b/texk/web2c/luatexdir/luapplib/util/utilmd5.h +new file mode 100644 +index 000000000..2fb4bc58d +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilmd5.h +@@ -0,0 +1,115 @@ ++#ifndef UTIL_MD5_H ++#define UTIL_MD5_H ++ ++#include // for uintX8_t ++#include // for size_t ++#include "utildecl.h" ++ ++/* begin of md5.h */ ++ ++/* ++ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++ ++ L. Peter Deutsch ++ ghost@aladdin.com ++ ++ */ ++/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ ++/* ++ Independent implementation of MD5 (RFC 1321). ++ ++ This code implements the MD5 Algorithm defined in RFC 1321, whose ++ text is available at ++ http://www.ietf.org/rfc/rfc1321.txt ++ The code is derived from the text of the RFC, including the test suite ++ (section A.5) but excluding the rest of Appendix A. It does not include ++ any code or documentation that is identified in the RFC as being ++ copyrighted. ++ ++ The original and principal author of md5.h is L. Peter Deutsch ++ . Other authors are noted in the change history ++ that follows (in reverse chronological order): ++ ++ 2002-04-13 lpd Removed support for non-ANSI compilers; removed ++ references to Ghostscript; clarified derivation from RFC 1321; ++ now handles byte order either statically or dynamically. ++ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. ++ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); ++ added conditionalization for C++ compilation from Martin ++ Purschke . ++ 1999-05-03 lpd Original version. ++ */ ++ ++//#ifndef md5_INCLUDED ++//# define md5_INCLUDED ++ ++/* ++ * This package supports both compile-time and run-time determination of CPU ++ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be ++ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is ++ * defined as non-zero, the code will be compiled to run only on big-endian ++ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to ++ * run on either big- or little-endian CPUs, but will run slightly less ++ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. ++ */ ++ ++// PJ: replaced with uint8_t and uint32_t ++//typedef unsigned char md5_byte_t; /* 8-bit byte */ ++//typedef unsigned int md5_word_t; /* 32-bit word */ ++ ++/* Define the state of the MD5 Algorithm. */ ++typedef struct md5_state_s { ++ uint32_t count[2]; /* message length in bits, lsw first */ ++ uint32_t abcd[4]; /* digest buffer */ ++ uint8_t buf[64]; /* accumulate block */ ++} md5_state_t; ++ ++#ifdef __cplusplus ++extern "C" ++{ ++#endif ++ ++/* Initialize the algorithm. */ ++UTILAPI void pplib_md5_init (md5_state_t *pms); ++ ++/* Append a string to the message. */ ++//void md5_add(md5_state_t *pms, const uint8_t *data, int nbytes); // PJ ++UTILAPI void md5_add (md5_state_t *pms, const void *input, size_t size); ++ ++/* Finish the message and return the digest. */ ++//void md5_finish(md5_state_t *pms, uint8_t digest[16]); // PJ ++UTILAPI void md5_put (md5_state_t *pms, uint8_t digest[16]); ++ ++#ifdef __cplusplus ++} /* end extern "C" */ ++#endif ++ ++//#endif /* md5_INCLUDED */ ++ ++/* end of md5.h */ ++ ++#define md5_state md5_state_t ++ ++UTILAPI void md5init (void); ++UTILAPI void md5add (const void *input, size_t length); ++UTILAPI void md5put (uint8_t output[16]); ++ ++UTILAPI void pplib_md5 (const void *input, size_t length, uint8_t output[16]); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilmem.c b/texk/web2c/luatexdir/luapplib/util/utilmem.c +new file mode 100644 +index 000000000..1fa00a0c1 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilmem.c +@@ -0,0 +1,68 @@ ++ ++#include // for memcpy ++ ++#include "utilmem.h" ++#include "utillog.h" ++ ++#ifndef util_memerr ++# if defined(_WIN64) || defined(__MINGW32__) ++# define util_memerr(size) { loggerf("ooops, not enough memory (%I64u)", ((unsigned long long)(size))); abort(); } ++# else ++# define util_memerr(size) { loggerf("ooops, not enough memory (%llu)", ((unsigned long long)(size))); abort(); } ++# endif ++#endif ++ ++void * util_malloc (size_t size) ++{ ++ void *m; ++ if ((m = malloc(size)) == NULL) ++ util_memerr(size); ++ return m; ++} ++ ++void * util_calloc (size_t num, size_t size) ++{ ++ void *m; ++ if ((m = calloc(num, size)) == NULL) ++ util_memerr(size); ++ return m; ++} ++ ++void * util_realloc (void *m, size_t size) ++{ ++ if ((m = realloc(m, size)) == NULL) ++ util_memerr(size); ++ return m; ++} ++ ++/* common array resizer ++ ++data -- the beginning of array ++unit -- sizeof array element ++size -- current array size ++extra -- requested extra size ++space -- pointer to available space ++allocated -- flag indicating if *data has been allocated (with malloc) ++ ++*/ ++ ++void util_resize (void **data, size_t unit, size_t size, size_t extra, size_t *space, int allocated) ++{ ++ if (*space == 0) ++ *space = 4; // ... better keep *space non-zero to avoid it ++ do { *space <<= 1; } ++ while (size + extra > *space); ++ ++ if (allocated) ++ { ++ *data = util_realloc(*data, *space * unit); ++ } ++ else ++ { ++ void *newdata = util_malloc(*space * unit); ++ if (*data != NULL) ++ memcpy(newdata, *data, size * unit); ++ *data = newdata; ++ } ++} ++ +diff --git a/texk/web2c/luatexdir/luapplib/util/utilmem.h b/texk/web2c/luatexdir/luapplib/util/utilmem.h +new file mode 100644 +index 000000000..e2d128618 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilmem.h +@@ -0,0 +1,59 @@ ++ ++#ifndef UTIL_MEM_H ++#define UTIL_MEM_H ++ ++#include // for size_t and alloc functions ++#include "utildecl.h" ++ ++UTILAPI void * util_malloc (size_t size); ++UTILAPI void * util_calloc (size_t num, size_t size); ++UTILAPI void * util_realloc (void *m, size_t size); ++ ++#define util_free free // not a call ++ ++/* arrays */ ++ ++#define array_header(element_type, integer_type) \ ++ element_type *data; integer_type size; integer_type space ++ ++#define array_of(element_type) array_header(element_type, size_t) ++ ++#define array_data_is_allocated(array) ((array)->data != (void *)((array) + 1)) ++ ++#define array_create(array, array_type, element_type, init) \ ++ (array = (array_type *)util_malloc(sizeof(array_type) + ((init) > 0 ? ((init) * sizeof(element_type)) : 1)), \ ++ array_init(array, element_type, init)) ++ ++#define array_init(array, element_type, init) \ ++ ((array)->data = (element_type *)((array) + 1), (array)->size = 0, (array)->space = init) ++ ++#define array_init_data(array, element_type, init) \ ++ ((array)->data = (element_type *)util_malloc((init) * sizeof(element_type)), (array)->size = 0, (array)->space = init) ++ ++#define array_free_data(array) \ ++ ((void)(array_data_is_allocated(array) && (util_free((array)->data), 0))) ++ ++#define array_free(array) \ ++ ((void)(array_free_data(array), (util_free(array), 0))) ++ ++void util_resize (void **data, size_t unit, size_t size, size_t extra, size_t *space, int allocated); ++ ++#define array_ensure(array, unit, extra) \ ++ ((void)((array)->size + (extra) > (array)->space && \ ++ (util_resize((void **)(&(array)->data), unit, (array)->size, extra, &(array)->space, array_data_is_allocated(array)), 0))) ++ ++#define array_ensure_alloc(array, unit, extra, isalloc) \ ++ ((void)((array)->size + (extra) > (array)->space && \ ++ (util_resize((void **)(&(array)->data), unit, (array)->size, extra, &(array)->space, isalloc), 0))) ++ ++#define array_at(array, index) ((array)->data + index) ++#define array_index(array, index) (index < (array)->size ? array_at(array, index) : NULL) ++#define array_top(array) ((array)->data + (array)->size - 1) ++ ++#define array_from_bottom(array, index) array_at(array, index - 1) ++#define array_from_top(array, negindex) array_at(array, (array)->size + (negindex)) ++ ++#define array_push(array) ((array)->data + ((array)->size)++) ++#define array_pop(array) ((array)->data + --((array)->size)) ++ ++#endif +diff --git a/texk/web2c/luatexdir/luapplib/util/utilnumber.c b/texk/web2c/luatexdir/luapplib/util/utilnumber.c +new file mode 100644 +index 000000000..c87261e15 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilnumber.c +@@ -0,0 +1,1325 @@ ++ ++#include /* for log10() and floor() */ ++#include /* for printf() */ ++ ++#include "utilnumber.h" ++ ++const int base10_lookup[] = { ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++}; ++ ++const int base16_lookup[] = { ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, ++ -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++}; ++ ++const int base26_lookup[] = { ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, ++ 16,17,18,19,20,21,22,23,24,25,26,-1,-1,-1,-1,-1, ++ -1, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, ++ 16,17,18,19,20,21,22,23,24,25,26,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++}; ++ ++const int base36_lookup[] = { ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, ++ -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, ++ 25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1, ++ -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, ++ 25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ++ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ++}; ++ ++/* integer from string; return a pointer to character next to the last digit */ ++ ++#define string_scan_sign(s, c, sign) _scan_sign(c, sign, *++s) ++#define string_scan_integer(s, c, number) _scan_integer(c, number, *++s) ++#define string_scan_radix(s, c, number, radix) _scan_radix(c, number, radix, *++s) ++#define string_read_integer(s, c, number) _read_integer(c, number, *++s) ++#define string_read_radix(s, c, number, radix) _read_radix(c, number, radix, *++s) ++ ++const char * string_to_int32 (const char *s, int32_t *number) ++{ ++ int sign, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_integer(s, c, *number); ++ if (sign) *number = -*number; ++ return s; ++} ++ ++const char * string_to_intlw (const char *s, intlw_t *number) ++{ ++ int sign, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_integer(s, c, *number); ++ if (sign) *number = -*number; ++ return s; ++} ++ ++const char * string_to_int64 (const char *s, int64_t *number) ++{ ++ int sign, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_integer(s, c, *number); ++ if (sign) *number = -*number; ++ return s; ++} ++ ++const char * string_to_uint32 (const char *s, uint32_t *number) ++{ ++ int c = *s; ++ string_scan_integer(s, c, *number); ++ return s; ++} ++ ++const char * string_to_uintlw (const char *s, uintlw_t *number) ++{ ++ int c = *s; ++ string_scan_integer(s, c, *number); ++ return s; ++} ++ ++const char * string_to_uint64 (const char *s, uint64_t *number) ++{ ++ int c = *s; ++ string_scan_integer(s, c, *number); ++ return s; ++} ++ ++const char * radix_to_int32 (const char *s, int32_t *number, int radix) ++{ ++ int sign, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_radix(s, c, *number, radix); ++ if (sign) *number = -*number; ++ return s; ++} ++ ++const char * radix_to_intlw (const char *s, intlw_t *number, int radix) ++{ ++ int sign, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_radix(s, c, *number, radix); ++ if (sign) *number = -*number; ++ return s; ++} ++ ++const char * radix_to_int64 (const char *s, int64_t *number, int radix) ++{ ++ int sign, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_radix(s, c, *number, radix); ++ if (sign) *number = -*number; ++ return s; ++} ++ ++const char * radix_to_uint32 (const char *s, uint32_t *number, int radix) ++{ ++ int c = *s; ++ string_scan_radix(s, c, *number, radix); ++ return s; ++} ++ ++const char * radix_to_uintlw (const char *s, uintlw_t *number, int radix) ++{ ++ int c = *s; ++ string_scan_radix(s, c, *number, radix); ++ return s; ++} ++ ++const char * radix_to_uint64 (const char *s, uint64_t *number, int radix) ++{ ++ int c = *s; ++ string_scan_radix(s, c, *number, radix); ++ return s; ++} ++ ++/* roman to uint16_t */ ++ ++#define roman1000(c) (c == 'M' || c == 'm') ++#define roman500(c) (c == 'D' || c == 'd') ++#define roman100(c) (c == 'C' || c == 'c') ++#define roman50(c) (c == 'L' || c == 'l') ++#define roman10(c) (c == 'X' || c == 'x') ++#define roman5(c) (c == 'V' || c == 'v') ++#define roman1(c) (c == 'I' || c == 'i') ++ ++#define roman100s(p) (roman100(*p) ? (100 + ((++p, roman100(*p)) ? (100 + ((++p, roman100(*p)) ? (++p, 100) : 0)) : 0)) : 0) ++#define roman10s(p) (roman10(*p) ? (10 + ((++p, roman10(*p)) ? (10 + ((++p, roman10(*p)) ? (++p, 10) : 0)) : 0)) : 0) ++#define roman1s(p) (roman1(*p) ? (1 + ((++p, roman1(*p)) ? (1 + ((++p, roman1(*p)) ? (++p, 1) : 0)) : 0)) : 0) ++ ++const char * roman_to_uint16 (const char *s, uint16_t *number) ++{ ++ const char *p; ++ /* M */ ++ for (*number = 0, p = s; roman1000(*p); *number += 1000, ++p); ++ /* D C */ ++ if (roman500(*p)) ++ { ++ ++p; ++ *number += 500 + roman100s(p); ++ } ++ else if (roman100(*p)) ++ { ++ ++p; ++ if (roman1000(*p)) ++ { ++ ++p; ++ *number += 900; ++ } ++ else if (roman500(*p)) ++ { ++ ++p; ++ *number += 400; ++ } ++ else ++ *number += 100 + roman100s(p); ++ } ++ /* L X */ ++ if (roman50(*p)) ++ { ++ ++p; ++ *number += 50 + roman10s(p); ++ } ++ else if (roman10(*p)) ++ { ++ ++p; ++ if (roman100(*p)) ++ { ++ ++p; ++ *number += 90; ++ } ++ else if (roman50(*p)) ++ { ++ ++p; ++ *number += 40; ++ } ++ else ++ *number += 10 + roman10s(p); ++ } ++ /* V I */ ++ if (roman5(*p)) ++ { ++ ++p; ++ *number += 5 + roman1s(p); ++ } ++ else if (roman1(*p)) ++ { ++ ++p; ++ if (roman10(*p)) ++ { ++ ++p; ++ *number += 9; ++ } ++ else if (roman5(*p)) ++ { ++ ++p; ++ *number += 4; ++ } ++ else ++ *number += 1 + roman1s(p); ++ } ++ return p; ++} ++ ++/* integer to string; return a pointer to null-terminated static const string */ ++ ++static char integer_buffer[MAX_INTEGER_DIGITS] = {'\0'}; ++#define end_of_integer_buffer (integer_buffer + MAX_INTEGER_DIGITS - 1) ++ ++/* writing integers */ ++ ++#define number_printrev_signed(p, number, quotient) \ ++ do { \ ++ quotient = number; number /= 10; \ ++ *--p = base10_palindrome[9 + (quotient - number*10)]; \ ++ } while (number); \ ++ if (quotient < 0) *--p = '-' ++ ++#define number_printrev_unsigned(p, number, quotient) \ ++ do { \ ++ quotient = number; number /= 10; \ ++ *--p = (char)(quotient - integer_multiplied10(number)) + '0'; \ ++ } while (number) ++ ++char * int32_as_string (int32_t number, char **e) ++{ ++ char *p; ++ int quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_signed(p, number, quotient); ++ return p; ++} ++ ++char * intlw_as_string (intlw_t number, char **e) ++{ ++ char *p; ++ intlw_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_signed(p, number, quotient); ++ return p; ++} ++ ++char * int64_as_string (int64_t number, char **e) ++{ ++ char *p; ++ int64_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_signed(p, number, quotient); ++ return p; ++} ++ ++char * uint32_as_string (uint32_t number, char **e) ++{ ++ char *p; ++ uint32_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned(p, number, quotient); ++ return p; ++} ++ ++char * uintlw_as_string (uintlw_t number, char **e) ++{ ++ char *p; ++ uintlw_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned(p, number, quotient); ++ return p; ++} ++ ++char * uint64_as_string (uint64_t number, char **e) ++{ ++ char *p; ++ uint64_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned(p, number, quotient); ++ return p; ++} ++ ++/* radix variant */ ++ ++#define number_printrev_signed_radix_uc(p, number, radix, quotient) \ ++ do { \ ++ quotient = number; number /= radix; \ ++ *--p = base36_uc_palindrome[MAX_RADIX - 1 + (quotient - number*radix)]; \ ++ } while (number) ++ ++#define number_printrev_signed_radix_lc(p, number, radix, quotient) \ ++ do { \ ++ quotient = number; number /= radix; \ ++ *--p = base36_lc_palindrome[MAX_RADIX - 1 + (quotient - number*radix)]; \ ++ } while (number) ++ ++#define number_printrev_signed_radix(p, number, radix, quotient) \ ++ do { \ ++ if (radix > 0) { number_printrev_signed_radix_uc(p, number, radix, quotient); } \ ++ else { radix = -radix; number_printrev_signed_radix_lc(p, number, radix, quotient); } \ ++ if (quotient < 0) *--p = '-'; \ ++ } while (0) ++ ++#define number_printrev_unsigned_radix_uc(p, number, radix, quotient) \ ++ do { \ ++ quotient = number; number /= radix; \ ++ *--p = base36_uc_alphabet[quotient % radix]; \ ++ } while (number) ++ ++#define number_printrev_unsigned_radix_lc(p, number, radix, quotient) \ ++ do { \ ++ quotient = number; number /= radix; \ ++ *--p = base36_lc_alphabet[quotient % radix]; \ ++ } while (number) ++ ++#define number_printrev_unsigned_radix(p, number, radix, quotient) \ ++ do { \ ++ if (radix > 0) { number_printrev_unsigned_radix_uc(p, number, radix, quotient); } \ ++ else { radix = -radix; number_printrev_unsigned_radix_lc(p, number, radix, quotient); } \ ++ } while (0) ++ ++char * int32_as_radix (int number, int radix, char **e) ++{ ++ char *p; ++ int quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_signed_radix(p, number, radix, quotient); ++ return p; ++} ++ ++char * intlw_as_radix (intlw_t number, int radix, char **e) ++{ ++ char *p; ++ intlw_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_signed_radix(p, number, radix, quotient); ++ return p; ++} ++ ++char * int64_as_radix (int64_t number, int radix, char **e) ++{ ++ char *p; ++ int64_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_signed_radix(p, number, radix, quotient); ++ return p; ++} ++ ++char * uint32_as_radix (uint32_t number, int radix, char **e) ++{ ++ char *p; ++ uint32_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_radix(p, number, radix, quotient); ++ return p; ++} ++ ++char * uintlw_as_radix (uintlw_t number, int radix, char **e) ++{ ++ char *p; ++ uintlw_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_radix(p, number, radix, quotient); ++ return p; ++} ++ ++char * uint64_as_radix (uint64_t number, int radix, char **e) ++{ ++ char *p; ++ uint64_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_radix(p, number, radix, quotient); ++ return p; ++} ++ ++/* aaa, aab, aac, ...; unsigned only. 0 gives empty string */ ++ ++#define string_scan_alpha(s, c, number, radix) \ ++ for (number = 0, c = *s; (c = base26_value(c)) > 0; number = number * radix + c, c = *++s) ++ ++const char * alpha_to_uint32 (const char *s, uint32_t *number) ++{ ++ int c; ++ string_scan_alpha(s, c, *number, 26); ++ return s; ++} ++ ++const char * alpha_to_uintlw (const char *s, uintlw_t *number) ++{ ++ int c; ++ string_scan_alpha(s, c, *number, 26); ++ return s; ++} ++ ++const char * alpha_to_uint64 (const char *s, uint64_t *number) ++{ ++ int c; ++ string_scan_alpha(s, c, *number, 26); ++ return s; ++} ++ ++#define number_printrev_unsigned_alpha_uc(p, number, radix, quotient) \ ++ while (number > 0) { \ ++ quotient = --number; number /= radix; \ ++ *--p = base26_uc_alphabet[quotient % radix]; \ ++ } ++ ++#define number_printrev_unsigned_alpha_lc(p, number, radix, quotient) \ ++ while (number > 0) { \ ++ quotient = --number; number /= radix; \ ++ *--p = base26_lc_alphabet[quotient % radix]; \ ++ } ++ ++char * uint32_as_alpha_uc (uint32_t number, char **e) ++{ ++ char *p; ++ uint32_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_alpha_uc(p, number, 26, quotient); ++ return p; ++} ++ ++char * uint32_as_alpha_lc (uint32_t number, char **e) ++{ ++ char *p; ++ uint32_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_alpha_lc(p, number, 26, quotient); ++ return p; ++} ++ ++char * uintlw_as_alpha_uc (uintlw_t number, char **e) ++{ ++ char *p; ++ uintlw_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_alpha_uc(p, number, 26, quotient); ++ return p; ++} ++ ++char * uintlw_as_alpha_lc (uintlw_t number, char **e) ++{ ++ char *p; ++ uintlw_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_alpha_lc(p, number, 26, quotient); ++ return p; ++} ++ ++char * uint64_as_alpha_uc (uint64_t number, char **e) ++{ ++ char *p; ++ uint64_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_alpha_uc(p, number, 26, quotient); ++ return p; ++} ++ ++char * uint64_as_alpha_lc (uint64_t number, char **e) ++{ ++ char *p; ++ uint64_t quotient; ++ p = end_of_integer_buffer; *p = '\0'; ++ if (e != NULL) *e = p; ++ number_printrev_unsigned_alpha_lc(p, number, 26, quotient); ++ return p; ++} ++ ++/* a variant of alphabetic, a, b, c, ..., z, aa, bb, cc, ..., zz (eg. pdf page labelling) */ ++ ++#define string_scan_alphan(s, c, number, radix) \ ++ do { \ ++ number = 0; \ ++ if ((c = base26_value(*s)) > 0) { \ ++ number = c; \ ++ while (c == base26_value(*++s)) number += radix; \ ++ } \ ++ } while (0) ++ ++const char * alphan_to_uint32 (const char *s, uint32_t *number) ++{ ++ int c; ++ string_scan_alphan(s, c, *number, 26); ++ return s; ++} ++ ++const char * alphan_to_uintlw (const char *s, uintlw_t *number) ++{ ++ int c; ++ string_scan_alphan(s, c, *number, 26); ++ return s; ++} ++ ++const char * alphan_to_uint64 (const char *s, uintlw_t *number) ++{ ++ int c; ++ string_scan_alphan(s, c, *number, 26); ++ return s; ++} ++ ++#define number_print_alphan_uc(s, c, number, radix) \ ++ if (number > 0) { \ ++ for (c = (--number) % radix, number -= c; ; number -= radix) { \ ++ *s++ = base26_uc_alphabet[c]; \ ++ if (number == 0 || p >= end_of_integer_buffer) break; \ ++ } \ ++ } ++ ++#define number_print_alphan_lc(s, c, number, radix) \ ++ if (number > 0) { \ ++ for (c = (--number) % radix, number -= c; ; number -= radix) { \ ++ *s++ = base26_lc_alphabet[c]; \ ++ if (number == 0 || p >= end_of_integer_buffer) break; \ ++ } \ ++ } ++ ++char * uint32_as_alphan_uc (uint32_t number, char **e) ++{ ++ char *p; ++ uint8_t c; ++ p = integer_buffer; ++ number_print_alphan_uc(p, c, number, 26); ++ *p = '\0'; if (e != NULL) *e = p; ++ return integer_buffer; ++} ++ ++char * uint32_as_alphan_lc (uint32_t number, char **e) ++{ ++ char *p; ++ uint8_t c; ++ p = integer_buffer; ++ number_print_alphan_lc(p, c, number, 26); ++ *p = '\0'; if (e != NULL) *e = p; ++ return integer_buffer; ++} ++ ++char * uintlw_as_alphan_uc (uintlw_t number, char **e) ++{ ++ char *p; ++ uint8_t c; ++ p = integer_buffer; ++ number_print_alphan_uc(p, c, number, 26); ++ *p = '\0'; if (e != NULL) *e = p; ++ return integer_buffer; ++} ++ ++char * uintlw_as_alphan_lc (uintlw_t number, char **e) ++{ ++ char *p; ++ uint8_t c; ++ p = integer_buffer; ++ number_print_alphan_lc(p, c, number, 26); ++ *p = '\0'; if (e != NULL) *e = p; ++ return integer_buffer; ++} ++ ++char * uint64_as_alphan_uc (uint64_t number, char **e) ++{ ++ char *p; ++ uint8_t c; ++ p = integer_buffer; ++ number_print_alphan_uc(p, c, number, 26); ++ *p = '\0'; if (e != NULL) *e = p; ++ return integer_buffer; ++} ++ ++char * uint64_as_alphan_lc (uint64_t number, char **e) ++{ ++ char *p; ++ uint8_t c; ++ p = integer_buffer; ++ number_print_alphan_lc(p, c, number, 26); ++ *p = '\0'; if (e != NULL) *e = p; ++ return integer_buffer; ++} ++ ++/* roman numeral */ ++ ++/* todo: large roman numerals? http://mathforum.org/library/drmath/view/57569.html */ ++ ++#define base_roman_uc_alphabet "MDCLXVI" ++#define base_roman_lc_alphabet "mdclxvi" ++ ++static const uint32_t base_roman_values[] = { 1000, 500, 100, 50, 10, 5, 1 }; ++ ++#define integer_to_roman(p, number, alphabet) \ ++ { \ ++ uint32_t k, j, v, u; \ ++ for (j = 0, v = base_roman_values[0]; number > 0; ) \ ++ { \ ++ if (number >= v) \ ++ { \ ++ *p++ = alphabet[j]; \ ++ number -= v; \ ++ continue; \ ++ } \ ++ if (j & 1) \ ++ k = j + 1; \ ++ else \ ++ k = j + 2; \ ++ u = base_roman_values[k]; \ ++ if (number + u >= v) \ ++ { \ ++ *p++ = alphabet[k]; \ ++ number += u; \ ++ } \ ++ else \ ++ v = base_roman_values[++j]; \ ++ } \ ++ } ++ ++char * uint16_as_roman_uc (uint16_t number, char **e) ++{ ++ char *p = integer_buffer; ++ integer_to_roman(p, number, base_roman_uc_alphabet); ++ if (e != NULL) ++ *e = p; ++ *p = '\0'; ++ return integer_buffer; ++} ++ ++char * uint16_as_roman_lc (uint16_t number, char **e) ++{ ++ char *p = integer_buffer; ++ integer_to_roman(p, number, base_roman_lc_alphabet); ++ if (e != NULL) ++ *e = p; ++ *p = '\0'; ++ return integer_buffer; ++} ++ ++/* IEEE-754 */ ++ ++#define BINARY_MODF 1 ++ ++#define NOT_A_NUMBER_STRING "NaN" ++#define INFINITY_STRING "INF" ++#define SIGNED_INFINITY 1 ++#define SIGNED_ZERO 0 ++#define SIGNED_NOT_A_NUMBER 0 ++#define RADIX_CHAR '.' ++ ++/* double/float to decimal */ ++ ++typedef struct ieee_double { ++ union { ++ double number; ++ uint64_t bits; ++ }; ++ uint64_t fraction; ++ int exponent, sign; ++} ieee_double; ++ ++typedef struct ieee_float { ++ union { ++ float number; ++ uint32_t bits; ++ }; ++ uint32_t fraction; ++ int exponent, sign; ++} ieee_float; ++ ++#define IEEE_DOUBLE_BIAS 1023 ++#define IEEE_DOUBLE_MIN_EXPONENT -1023 ++#define IEEE_DOUBLE_MAX_EXPONENT (0x7ff - IEEE_DOUBLE_BIAS) ++ ++#define IEEE_FLOAT_BIAS 127 ++#define IEEE_FLOAT_MIN_EXPONENT -127 ++#define IEEE_FLOAT_MAX_EXPONENT (0xff - IEEE_FLOAT_BIAS) ++ ++#define ieee_double_fraction(i) (i & 0x000fffffffffffffull) ++#define ieee_double_exponent(i) ((0x7ff & (i >> 52)) - IEEE_DOUBLE_BIAS) ++#define ieee_double_sign(ieee_number) ((void)((ieee_number.sign = ieee_number.bits >> 63) && (ieee_number.number = -ieee_number.number))) ++#define ieee_double_init(ieee_number, number) \ ++ ieee_number.number = number, \ ++ ieee_number.fraction = ieee_double_fraction(ieee_number.bits), \ ++ ieee_number.exponent = ieee_double_exponent(ieee_number.bits) ++ ++#define ieee_float_fraction(i) (i & 0x007fffff) ++#define ieee_float_exponent(i) ((0xff & (i >> 23)) - IEEE_FLOAT_BIAS) ++#define ieee_float_sign(ieee_number) ((void)((ieee_number.sign = ieee_number.bits >> 31) && (ieee_number.number = -ieee_number.number))) ++#define ieee_float_init(ieee_number, number) \ ++ ieee_number.number = number, \ ++ ieee_number.fraction = ieee_float_fraction(ieee_number.bits), \ ++ ieee_number.exponent = ieee_float_exponent(ieee_number.bits) ++ ++/* special cases */ ++ ++#define ieee_double_is_zero(ieee_number) (ieee_number.number == 0) // || ieee_double_too_small(ieee_number) ? ++#define ieee_double_too_small(ieee_number) (ieee_number.exponent == 0 && ieee_number.fraction != 0) // denormalized, implicit fracion bit not set ++ ++#define ieee_float_is_zero(ieee_number) (ieee_number.number == 0) // || ieee_float_too_small(ieee_number) ? ++#define ieee_float_too_small(ieee_number) (ieee_number.exponent == 0 && ieee_number.fraction != 0) ++ ++#define ieee_double_zero_string(ieee_number) (SIGNED_ZERO && ieee_number.sign ? "-0" : "0") ++#define ieee_double_infinity_string(ieee_number) (SIGNED_INFINITY && ieee_number.sign ? "-" INFINITY_STRING : INFINITY_STRING) ++ ++#define ieee_float_zero_string ieee_double_zero_string ++#define ieee_float_infinity_string ieee_double_infinity_string ++ ++#define ieee_double_special_case(ieee_number) (ieee_number.exponent == IEEE_DOUBLE_MAX_EXPONENT) ++#define ieee_double_special_string(ieee_number) (ieee_number.fraction ? NOT_A_NUMBER_STRING : ieee_double_infinity_string(ieee_number)) ++ ++#define ieee_float_special_case(ieee_number) (ieee_number.exponent == IEEE_FLOAT_MAX_EXPONENT) ++#define ieee_float_special_string(ieee_number) (ieee_number.fraction ? NOT_A_NUMBER_STRING : ieee_float_infinity_string(ieee_number)) ++ ++#if 0 ++ ++const double double_binary_power10[] = ++{ ++ 1.0e1, 1.0e2, 1.0e4, 1.0e8, 1.0e16, 1.0e32, 1.0e64, 1.0e128, 1.0e256 ++}; ++ ++const float float_binary_power10[] = ++{ ++ 1.0e1, 1.0e2, 1.0e4, 1.0e8, 1.0e16, 1.0e32 ++}; ++ ++const double double_binary_negpower10[] = ++{ ++ 1.0e-1, 1.0e-2, 1.0e-4, 1.0e-8, 1.0e-16, 1.0e-32 ++}; ++ ++const float float_binary_negpower10[] = ++{ ++ 1.0e-1, 1.0e-2, 1.0e-4, 1.0e-8, 1.0e-16, 1.0e-32 ++}; ++ ++#else ++ ++const double double_decimal_power10[] = { ++ 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, ++ 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, ++ 1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, 1.0e25, 1.0e26, 1.0e27, 1.0e28, 1.0e29, ++ 1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, 1.0e35, 1.0e36, 1.0e37, 1.0e38, 1.0e39, ++ 1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49, ++ 1.0e50, 1.0e51, 1.0e52, 1.0e53, 1.0e54, 1.0e55, 1.0e56, 1.0e57, 1.0e58, 1.0e59, ++ 1.0e60, 1.0e61, 1.0e62, 1.0e63, 1.0e64, 1.0e65, 1.0e66, 1.0e67, 1.0e68, 1.0e69, ++ 1.0e70, 1.0e71, 1.0e72, 1.0e73, 1.0e74, 1.0e75, 1.0e76, 1.0e77, 1.0e78, 1.0e79, ++ 1.0e80, 1.0e81, 1.0e82, 1.0e83, 1.0e84, 1.0e85, 1.0e86, 1.0e87, 1.0e88, 1.0e89, ++ 1.0e90, 1.0e91, 1.0e92, 1.0e93, 1.0e94, 1.0e95, 1.0e96, 1.0e97, 1.0e98, 1.0e99, ++ 1.0e100, 1.0e101, 1.0e102, 1.0e103, 1.0e104, 1.0e105, 1.0e106, 1.0e107, 1.0e108, 1.0e109, ++ 1.0e110, 1.0e111, 1.0e112, 1.0e113, 1.0e114, 1.0e115, 1.0e116, 1.0e117, 1.0e118, 1.0e119, ++ 1.0e120, 1.0e121, 1.0e122, 1.0e123, 1.0e124, 1.0e125, 1.0e126, 1.0e127, 1.0e128, 1.0e129, ++ 1.0e130, 1.0e131, 1.0e132, 1.0e133, 1.0e134, 1.0e135, 1.0e136, 1.0e137, 1.0e138, 1.0e139, ++ 1.0e140, 1.0e141, 1.0e142, 1.0e143, 1.0e144, 1.0e145, 1.0e146, 1.0e147, 1.0e148, 1.0e149, ++ 1.0e150, 1.0e151, 1.0e152, 1.0e153, 1.0e154, 1.0e155, 1.0e156, 1.0e157, 1.0e158, 1.0e159, ++ 1.0e160, 1.0e161, 1.0e162, 1.0e163, 1.0e164, 1.0e165, 1.0e166, 1.0e167, 1.0e168, 1.0e169, ++ 1.0e170, 1.0e171, 1.0e172, 1.0e173, 1.0e174, 1.0e175, 1.0e176, 1.0e177, 1.0e178, 1.0e179, ++ 1.0e180, 1.0e181, 1.0e182, 1.0e183, 1.0e184, 1.0e185, 1.0e186, 1.0e187, 1.0e188, 1.0e189, ++ 1.0e190, 1.0e191, 1.0e192, 1.0e193, 1.0e194, 1.0e195, 1.0e196, 1.0e197, 1.0e198, 1.0e199, ++ 1.0e200, 1.0e201, 1.0e202, 1.0e203, 1.0e204, 1.0e205, 1.0e206, 1.0e207, 1.0e208, 1.0e209, ++ 1.0e210, 1.0e211, 1.0e212, 1.0e213, 1.0e214, 1.0e215, 1.0e216, 1.0e217, 1.0e218, 1.0e219, ++ 1.0e220, 1.0e221, 1.0e222, 1.0e223, 1.0e224, 1.0e225, 1.0e226, 1.0e227, 1.0e228, 1.0e229, ++ 1.0e230, 1.0e231, 1.0e232, 1.0e233, 1.0e234, 1.0e235, 1.0e236, 1.0e237, 1.0e238, 1.0e239, ++ 1.0e240, 1.0e241, 1.0e242, 1.0e243, 1.0e244, 1.0e245, 1.0e246, 1.0e247, 1.0e248, 1.0e249, ++ 1.0e250, 1.0e251, 1.0e252, 1.0e253, 1.0e254, 1.0e255, 1.0e256, 1.0e257, 1.0e258, 1.0e259, ++ 1.0e260, 1.0e261, 1.0e262, 1.0e263, 1.0e264, 1.0e265, 1.0e266, 1.0e267, 1.0e268, 1.0e269, ++ 1.0e270, 1.0e271, 1.0e272, 1.0e273, 1.0e274, 1.0e275, 1.0e276, 1.0e277, 1.0e278, 1.0e279, ++ 1.0e280, 1.0e281, 1.0e282, 1.0e283, 1.0e284, 1.0e285, 1.0e286, 1.0e287, 1.0e288, 1.0e289, ++ 1.0e290, 1.0e291, 1.0e292, 1.0e293, 1.0e294, 1.0e295, 1.0e296, 1.0e297, 1.0e298, 1.0e299, ++ 1.0e300, 1.0e301, 1.0e302, 1.0e303, 1.0e304, 1.0e305, 1.0e306, 1.0e307, 1.0e308 ++}; ++ ++const float float_decimal_power10[] = { ++ 1.0e0f, 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f, 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, ++ 1.0e10f, 1.0e11f, 1.0e12f, 1.0e13f, 1.0e14f, 1.0e15f, 1.0e16f, 1.0e17f, 1.0e18f, 1.0e19f, ++ 1.0e20f, 1.0e21f, 1.0e22f, 1.0e23f, 1.0e24f, 1.0e25f, 1.0e26f, 1.0e27f, 1.0e28f, 1.0e29f, ++ 1.0e30f, 1.0e31f, 1.0e32f, 1.0e33f, 1.0e34f, 1.0e35f, 1.0e36f, 1.0e37f, 1.0e38f ++}; ++ ++const double double_decimal_negpower10[] = { ++ 1.0e0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8, 1.0e-9, ++ 1.0e-10, 1.0e-11, 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16, 1.0e-17, 1.0e-18, 1.0e-19, ++ 1.0e-20, 1.0e-21, 1.0e-22, 1.0e-23, 1.0e-24, 1.0e-25, 1.0e-26, 1.0e-27, 1.0e-28, 1.0e-29, ++ 1.0e-30, 1.0e-31, 1.0e-32, 1.0e-33, 1.0e-34, 1.0e-35, 1.0e-36, 1.0e-37, 1.0e-38, 1.0e-39, ++ 1.0e-40, 1.0e-41, 1.0e-42, 1.0e-43, 1.0e-44, 1.0e-45, 1.0e-46, 1.0e-47, 1.0e-48, 1.0e-49, ++ 1.0e-50, 1.0e-51, 1.0e-52, 1.0e-53, 1.0e-54, 1.0e-55, 1.0e-56, 1.0e-57, 1.0e-58, 1.0e-59, ++ 1.0e-60, 1.0e-61, 1.0e-62, 1.0e-63, 1.0e-64, 1.0e-65, 1.0e-66, 1.0e-67, 1.0e-68, 1.0e-69, ++ 1.0e-70, 1.0e-71, 1.0e-72, 1.0e-73, 1.0e-74, 1.0e-75, 1.0e-76, 1.0e-77, 1.0e-78, 1.0e-79, ++ 1.0e-80, 1.0e-81, 1.0e-82, 1.0e-83, 1.0e-84, 1.0e-85, 1.0e-86, 1.0e-87, 1.0e-88, 1.0e-89, ++ 1.0e-90, 1.0e-91, 1.0e-92, 1.0e-93, 1.0e-94, 1.0e-95, 1.0e-96, 1.0e-97, 1.0e-98, 1.0e-99, ++ 1.0e-100, 1.0e-101, 1.0e-102, 1.0e-103, 1.0e-104, 1.0e-105, 1.0e-106, 1.0e-107, 1.0e-108, 1.0e-109, ++ 1.0e-110, 1.0e-111, 1.0e-112, 1.0e-113, 1.0e-114, 1.0e-115, 1.0e-116, 1.0e-117, 1.0e-118, 1.0e-119, ++ 1.0e-120, 1.0e-121, 1.0e-122, 1.0e-123, 1.0e-124, 1.0e-125, 1.0e-126, 1.0e-127, 1.0e-128, 1.0e-129, ++ 1.0e-130, 1.0e-131, 1.0e-132, 1.0e-133, 1.0e-134, 1.0e-135, 1.0e-136, 1.0e-137, 1.0e-138, 1.0e-139, ++ 1.0e-140, 1.0e-141, 1.0e-142, 1.0e-143, 1.0e-144, 1.0e-145, 1.0e-146, 1.0e-147, 1.0e-148, 1.0e-149, ++ 1.0e-150, 1.0e-151, 1.0e-152, 1.0e-153, 1.0e-154, 1.0e-155, 1.0e-156, 1.0e-157, 1.0e-158, 1.0e-159, ++ 1.0e-160, 1.0e-161, 1.0e-162, 1.0e-163, 1.0e-164, 1.0e-165, 1.0e-166, 1.0e-167, 1.0e-168, 1.0e-169, ++ 1.0e-170, 1.0e-171, 1.0e-172, 1.0e-173, 1.0e-174, 1.0e-175, 1.0e-176, 1.0e-177, 1.0e-178, 1.0e-179, ++ 1.0e-180, 1.0e-181, 1.0e-182, 1.0e-183, 1.0e-184, 1.0e-185, 1.0e-186, 1.0e-187, 1.0e-188, 1.0e-189, ++ 1.0e-190, 1.0e-191, 1.0e-192, 1.0e-193, 1.0e-194, 1.0e-195, 1.0e-196, 1.0e-197, 1.0e-198, 1.0e-199, ++ 1.0e-200, 1.0e-201, 1.0e-202, 1.0e-203, 1.0e-204, 1.0e-205, 1.0e-206, 1.0e-207, 1.0e-208, 1.0e-209, ++ 1.0e-210, 1.0e-211, 1.0e-212, 1.0e-213, 1.0e-214, 1.0e-215, 1.0e-216, 1.0e-217, 1.0e-218, 1.0e-219, ++ 1.0e-220, 1.0e-221, 1.0e-222, 1.0e-223, 1.0e-224, 1.0e-225, 1.0e-226, 1.0e-227, 1.0e-228, 1.0e-229, ++ 1.0e-230, 1.0e-231, 1.0e-232, 1.0e-233, 1.0e-234, 1.0e-235, 1.0e-236, 1.0e-237, 1.0e-238, 1.0e-239, ++ 1.0e-240, 1.0e-241, 1.0e-242, 1.0e-243, 1.0e-244, 1.0e-245, 1.0e-246, 1.0e-247, 1.0e-248, 1.0e-249, ++ 1.0e-250, 1.0e-251, 1.0e-252, 1.0e-253, 1.0e-254, 1.0e-255, 1.0e-256, 1.0e-257, 1.0e-258, 1.0e-259, ++ 1.0e-260, 1.0e-261, 1.0e-262, 1.0e-263, 1.0e-264, 1.0e-265, 1.0e-266, 1.0e-267, 1.0e-268, 1.0e-269, ++ 1.0e-270, 1.0e-271, 1.0e-272, 1.0e-273, 1.0e-274, 1.0e-275, 1.0e-276, 1.0e-277, 1.0e-278, 1.0e-279, ++ 1.0e-280, 1.0e-281, 1.0e-282, 1.0e-283, 1.0e-284, 1.0e-285, 1.0e-286, 1.0e-287, 1.0e-288, 1.0e-289, ++ 1.0e-290, 1.0e-291, 1.0e-292, 1.0e-293, 1.0e-294, 1.0e-295, 1.0e-296, 1.0e-297, 1.0e-298, 1.0e-299, ++ 1.0e-300, 1.0e-301, 1.0e-302, 1.0e-303, 1.0e-304, 1.0e-305, 1.0e-306, 1.0e-307, 1.0e-308 ++}; ++ ++const float float_decimal_negpower10[] = { ++ 1.0e0f, 1.0e-1f, 1.0e-2f, 1.0e-3f, 1.0e-4f, 1.0e-5f, 1.0e-6f, 1.0e-7f, 1.0e-8f, 1.0e-9f, ++ 1.0e-10f, 1.0e-11f, 1.0e-12f, 1.0e-13f, 1.0e-14f, 1.0e-15f, 1.0e-16f, 1.0e-17f, 1.0e-18f, 1.0e-19f, ++ 1.0e-20f, 1.0e-21f, 1.0e-22f, 1.0e-23f, 1.0e-24f, 1.0e-25f, 1.0e-26f, 1.0e-27f, 1.0e-28f, 1.0e-29f, ++ 1.0e-30f, 1.0e-31f, 1.0e-32f, 1.0e-33f, 1.0e-34f, 1.0e-35f, 1.0e-36f, 1.0e-37f, 1.0e-38f ++}; ++ ++#endif ++ ++/* scale number by floor(log10(number)) + 1 so that the result is in range [0.1, 1) */ ++ ++#define ieee_double_exponent10(ieee_number) ((int)floor(log10(ieee_number.number)) + 1) ++#define ieee_float_exponent10(ieee_number) ((int)floorf(log10f(ieee_number.number)) + 1) // floorf, log10f ? ++ ++#define ieee_double_exp10(ieee_number, exponent10) \ ++ exponent10 = ieee_double_exponent10(ieee_number); \ ++ if (exponent10 > 0) { \ ++ double_negative_exp10(ieee_number.number, -exponent10); \ ++ ieee_number.fraction = ieee_double_fraction(ieee_number.bits); \ ++ ieee_number.exponent = ieee_double_exponent(ieee_number.bits); \ ++ } else if (exponent10 < 0) { \ ++ double_positive_exp10(ieee_number.number, -exponent10); \ ++ ieee_number.fraction = ieee_double_fraction(ieee_number.bits); \ ++ ieee_number.exponent = ieee_double_exponent(ieee_number.bits); \ ++ } ++ ++#define ieee_float_exp10(ieee_number, exponent10) \ ++ exponent10 = ieee_float_exponent10(ieee_number); \ ++ if (exponent10 > 0) { \ ++ float_negative_exp10(ieee_number.number, -exponent10); \ ++ ieee_number.fraction = ieee_float_fraction(ieee_number.bits); \ ++ ieee_number.exponent = ieee_float_exponent(ieee_number.bits); \ ++ } else if (exponent10 < 0) { \ ++ float_positive_exp10(ieee_number.number, -exponent10); \ ++ ieee_number.fraction = ieee_float_fraction(ieee_number.bits); \ ++ ieee_number.exponent = ieee_float_exponent(ieee_number.bits); \ ++ } ++ ++#if BINARY_MODF ++ ++/* unhide implicit bit 53, produce 56-bit denormalised fraction (binary exponent already in range [-4, -1]) */ ++ ++#define ieee_double_denormalize(ieee_number) \ ++ (ieee_number.exponent == IEEE_DOUBLE_MIN_EXPONENT ? (++ieee_number.exponent, 0) : (ieee_number.fraction |= (1ull<<52))), \ ++ ieee_number.fraction <<= (ieee_number.exponent + 4) ++ ++/* unhide implicit bit 24, produce 27-bit denormalized fraction (binary exponent already in range [-4, -1]) */ ++ ++#define ieee_float_denormalize(ieee_number) \ ++ (ieee_number.exponent == IEEE_FLOAT_MIN_EXPONENT ? (++ieee_number.exponent, 0) : (ieee_number.fraction |= (1<<23))), \ ++ ieee_number.fraction <<= (ieee_number.exponent + 4) ++ ++/* turn off significant bits over 56 (integer part), multiply by 10, return new integer part (subsequent decimal digit) */ ++ ++#define ieee_double_binary_fraction(ieee_number) \ ++ (ieee_number.fraction &= ((1ull<<56) - 1), \ ++ ieee_number.fraction = (ieee_number.fraction << 1) + (ieee_number.fraction << 3), \ ++ ieee_number.fraction >> 56) ++ ++/* turn off significant bits over 27 (integer part), multiply by 10, return the integer part (subsequent decimal digit) */ ++ ++#define ieee_float_binary_fraction(ieee_number) \ ++ (ieee_number.fraction &= ((1<<27) - 1), \ ++ ieee_number.fraction = (ieee_number.fraction << 1) + (ieee_number.fraction << 3), \ ++ ieee_number.fraction >> 27) ++ ++#define ieee_double_decimal(ieee_number, exponent10, digits, p) \ ++ ieee_number_decimal(ieee_double_binary_fraction, ieee_number, exponent10, digits, p) ++#define ieee_float_decimal(ieee_number, exponent10, digits, p) \ ++ ieee_number_decimal(ieee_float_binary_fraction, ieee_number, exponent10, digits, p) ++#define ieee_double_decimal_dot(ieee_number, exponent10, digits, p, dot) \ ++ ieee_number_decimal_dot(ieee_double_binary_fraction, ieee_number, exponent10, digits, p, dot) ++#define ieee_float_decimal_dot(ieee_number, exponent10, digits, p, dot) \ ++ ieee_number_decimal_dot(ieee_float_binary_fraction, ieee_number, exponent10, digits, p, dot) ++ ++#else ++ ++/* generic method */ ++ ++#define ieee_double_decimal_fraction(ieee_number, i) (ieee_number.number = modf(10*ieee_number.number, &i), i) ++#define ieee_float_decimal_fraction(ieee_number, i) (ieee_number.number = (float)modf(10*ieee_number.number, &i), i) // ??? ++ ++#define ieee_double_decimal(ieee_number, exponent10, digits, p) \ ++ ieee_number_decimal(ieee_double_decimal_fraction, ieee_number, exponent10, digits, p) ++#define ieee_float_decimal(ieee_number, exponent10, digits, p) \ ++ ieee_number_decimal(ieee_float_decimal_fraction, ieee_number, exponent10, digits, p) ++#define ieee_double_decimal_dot(ieee_number, exponent10, digits, p, dot) \ ++ ieee_number_decimal_dot(ieee_double_decimal_fraction, ieee_number, exponent10, digits, p, dot) ++#define ieee_float_decimal_dot(ieee_number, exponent10, digits, p, dot) \ ++ ieee_number_decimal_dot(ieee_float_decimal_fraction, ieee_number, exponent10, digits, p, dot) ++ ++#endif ++ ++#define ieee_number_decimal(method, ieee_number, exponent10, digits, p) \ ++ ieee_double_denormalize(ieee_number); \ ++ if (ieee_number.sign) *p++ = '-'; \ ++ if (exponent10 <= 0) \ ++ for (*p++ = '0', *p++ = RADIX_CHAR; exponent10 && digits; *p++ = '0', ++exponent10, --digits); \ ++ else \ ++ { \ ++ do { *p++ = '0' + (char)method(ieee_number); } while (--exponent10); \ ++ *p++ = RADIX_CHAR; \ ++ } \ ++ for ( ; digits && ieee_number.fraction; --digits) \ ++ *p++ = '0' + (char)method(ieee_number) ++ ++#define ieee_number_decimal_dot(method, ieee_number, exponent10, digits, p, dot) \ ++ ieee_double_denormalize(ieee_number); \ ++ if (ieee_number.sign) *p++ = '-'; \ ++ if (exponent10 <= 0) \ ++ { \ ++ *p++ = '0'; \ ++ if (dot != NULL) *dot = p; \ ++ for (*p++ = RADIX_CHAR; exponent10 && digits; *p++ = '0', ++exponent10, --digits); \ ++ } \ ++ else \ ++ { \ ++ do { *p++ = '0' + (char)method(ieee_number); } while (--exponent10); \ ++ if (dot != NULL) *dot = p; \ ++ *p++ = RADIX_CHAR; \ ++ } \ ++ for ( ; digits && ieee_number.fraction; --digits) \ ++ *p++ = '0' + (char)method(ieee_number) ++ ++/* rounding to nearest integer */ ++ ++#if BINARY_MODF ++/* check if the mantissa has the most significant bit set, means >= 0.5 */ ++# define ieee_double_half(ieee_number) (ieee_number.fraction & (1ull<<55)) ++# define ieee_float_half(ieee_number) (ieee_number.fraction & (1<<26)) ++#else ++# define ieee_double_half(ieee_number) (ieee_number.number >= 0.5) ++# define ieee_float_half(ieee_number) (ieee_number.number >= 0.5) ++#endif ++ ++/* rounding to nearest integer */ ++ ++#define buffer_ceil(s, p, sign) \ ++ { \ ++ while (*--p == '9'); \ ++ if (*p != RADIX_CHAR) ++*p++; \ ++ else { \ ++ char *q; \ ++ for (q = p - 1; ; --q) { \ ++ if (*q < '9') { ++*q; break; } \ ++ *q = '0'; \ ++ if (q == s) \ ++ *--s = '1'; \ ++ else if (sign && q - 1 == s) \ ++ *s = '1', *--s = '-'; \ ++ } \ ++ } \ ++ } ++ ++#define buffer_remove_trailing_zeros(s, p, sign) \ ++ { \ ++ while (*--p == '0'); \ ++ if (*p != RADIX_CHAR) \ ++ ++p; \ ++ else if (!SIGNED_ZERO && sign && p - 2 == s && *(p - 1) == '0') \ ++ p -= 2, *p++ = '0'; \ ++ } ++ ++// if digits parameter was initially less then exponent10, then exponent10 > 0 and ieee_double_half(ieee_number) is irrelevant ++#define ieee_double_round(ieee_number, exponent10, s, p) \ ++ if (exponent10 == 0 && ieee_double_half(ieee_number)) \ ++ { buffer_ceil(s, p, ieee_number.sign); } \ ++ else \ ++ { buffer_remove_trailing_zeros(s, p, ieee_number.sign); } ++ ++#define ieee_float_round(ieee_number, exponent10, s, p) \ ++ if (exponent10 == 0 && ieee_float_half(ieee_number)) \ ++ { buffer_ceil(s, p, ieee_number.sign); } \ ++ else \ ++ { buffer_remove_trailing_zeros(s, p, ieee_number.sign); } ++ ++/* double to decimal */ ++ ++static char number_buffer[512]; ++ ++#define ieee_copy_special_string(special, p, _p) \ ++ for (p = (char *)number_buffer, _p = special; ; ++p, ++_p) { \ ++ if ((*p = *_p) == '\0') break; \ ++ } ++ ++#define ieee_copy_special_string_re(special, p, _p, r, e) \ ++ for (p = (char *)number_buffer, _p = special; ; ++p, ++_p) { \ ++ if ((*p = *_p) == '\0') { \ ++ if (r != NULL) *r = NULL; \ ++ if (e != NULL) *e = p; \ ++ break; \ ++ } \ ++ } ++ ++char * double_to_string (double number, int digits) ++{ ++ ieee_double ieee_number; ++ int exponent10; ++ char *s, *p; const char *_p; ++ ieee_double_init(ieee_number, number); ++ ieee_double_sign(ieee_number); ++ if (ieee_double_is_zero(ieee_number)) // to avoid crash on log10(number) ++ { ++ ieee_copy_special_string(ieee_double_zero_string(ieee_number), p, _p); ++ return (char *)number_buffer; ++ } ++ if (ieee_double_special_case(ieee_number)) ++ { ++ ieee_copy_special_string(ieee_double_special_string(ieee_number), p, _p); ++ return (char *)number_buffer; ++ } ++ s = p = number_buffer + 1; ++ ieee_double_exp10(ieee_number, exponent10); ++ ieee_double_decimal(ieee_number, exponent10, digits, p); ++ ieee_double_round(ieee_number, exponent10, s, p); ++ *p = '\0'; ++ return s; ++} ++ ++char * double_as_string (double number, int digits, char **r, char **e) ++{ ++ ieee_double ieee_number; ++ int exponent10; ++ char *s, *p; const char *_p; ++ s = p = number_buffer + 1; ++ ieee_double_init(ieee_number, number); ++ ieee_double_sign(ieee_number); ++ if (ieee_double_is_zero(ieee_number)) // to avoid crash on log10(number) ++ { ++ ieee_copy_special_string_re(ieee_double_zero_string(ieee_number), p, _p, r, e); ++ return (char *)number_buffer; ++ } ++ if (ieee_double_special_case(ieee_number)) ++ { ++ ieee_copy_special_string_re(ieee_double_special_string(ieee_number), p, _p, r, e); ++ return (char *)number_buffer; ++ } ++ ieee_double_exp10(ieee_number, exponent10); ++ ieee_double_decimal_dot(ieee_number, exponent10, digits, p, r); ++ ieee_double_round(ieee_number, exponent10, s, p); ++ if (e != NULL) *e = p; ++ *p = '\0'; ++ return s; ++} ++ ++/* float to decimal */ ++ ++char * float_to_string (float number, int digits) ++{ ++ ieee_float ieee_number; ++ int exponent10; ++ char *s, *p; const char *_p; ++ ieee_float_init(ieee_number, number); ++ ieee_float_sign(ieee_number); ++ if (ieee_float_is_zero(ieee_number)) ++ { ++ ieee_copy_special_string(ieee_float_zero_string(ieee_number), p, _p); ++ return (char *)number_buffer; ++ } ++ if (ieee_float_special_case(ieee_number)) ++ { ++ ieee_copy_special_string(ieee_float_special_string(ieee_number), p, _p); ++ return (char *)number_buffer; ++ } ++ s = p = number_buffer + 1; ++ ieee_float_exp10(ieee_number, exponent10); ++ ieee_float_decimal(ieee_number, exponent10, digits, p); ++ ieee_float_round(ieee_number, exponent10, s, p); ++ *p = '\0'; ++ return s; ++} ++ ++char * float_as_string (float number, int digits, char **r, char **e) ++{ ++ ieee_float ieee_number; ++ int exponent10; ++ char *s, *p; const char *_p; ++ s = p = number_buffer + 1; ++ ieee_float_init(ieee_number, number); ++ ieee_float_sign(ieee_number); ++ if (ieee_float_is_zero(ieee_number)) ++ { ++ ieee_copy_special_string_re(ieee_float_zero_string(ieee_number), p, _p, r, e); ++ return (char *)number_buffer; ++ } ++ if (ieee_float_special_case(ieee_number)) ++ { ++ ieee_copy_special_string_re(ieee_float_special_string(ieee_number), p, _p, r, e); ++ return (char *)number_buffer; ++ } ++ ieee_float_exp10(ieee_number, exponent10); ++ ieee_float_decimal_dot(ieee_number, exponent10, digits, p, r); ++ ieee_float_round(ieee_number, exponent10, s, p); ++ if (e != NULL) *e = p; ++ *p = '\0'; ++ return s; ++} ++ ++/* decimal string to double/float */ ++ ++#define string_scan_decimal(s, c, number) _scan_decimal(c, number, *++s) ++#define string_scan_fraction(s, c, number, exponent10) _scan_fraction(c, number, exponent10, *++s) ++#define string_scan_exponent10(s, c, exponent10) _scan_exponent10(c, exponent10, *++s) ++ ++const char * string_to_double (const char *s, double *number) ++{ ++ int sign, exponent10, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_decimal(s, c, *number); ++ if (c == '.') ++ { ++ c = *++s; ++ string_scan_fraction(s, c, *number, exponent10); ++ } ++ else ++ exponent10 = 0; ++ if (c == 'e' || c == 'E') ++ { ++ c = *++s; ++ string_scan_exponent10(s, c, exponent10); ++ } ++ double_exp10(*number, exponent10); ++ if (sign) *number = -*number; ++ return s; ++} ++ ++const char * string_to_float (const char *s, float *number) ++{ ++ int sign, exponent10, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_decimal(s, c, *number); ++ if (c == '.') ++ { ++ c = *++s; ++ string_scan_fraction(s, c, *number, exponent10); ++ } ++ else ++ exponent10 = 0; ++ if (c == 'e' || c == 'E') ++ { ++ c = *++s; ++ string_scan_exponent10(s, c, exponent10); ++ } ++ float_exp10(*number, exponent10); ++ if (sign) *number = -*number; ++ return s; ++} ++ ++/* conventional form */ ++ ++const char * convert_to_double (const char *s, double *number) ++{ ++ int sign, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_decimal(s, c, *number); ++ if (c == '.' || c == ',') ++ { ++ int exponent10; ++ c = *++s; ++ string_scan_fraction(s, c, *number, exponent10); ++ if (exponent10 < 0) ++ double_negative_exp10(*number, exponent10); ++ } ++ if (sign) *number = -*number; ++ return s; ++} ++ ++const char * convert_to_float (const char *s, float *number) ++{ ++ int sign, c = *s; ++ string_scan_sign(s, c, sign); ++ string_scan_decimal(s, c, *number); ++ if (c == '.' || c == ',') ++ { ++ int exponent10; ++ c = *++s; ++ string_scan_fraction(s, c, *number, exponent10); ++ if (exponent10 < 0) ++ float_negative_exp10(*number, exponent10); ++ } ++ if (sign) *number = -*number; ++ return s; ++} ++ ++/* pretty common stuff */ ++ ++size_t bytes_to_hex_lc (const void *input, size_t size, unsigned char *output) ++{ ++ size_t i; ++ const unsigned char *p; ++ for (i = 0, p = (const unsigned char *)input; i < size; ++i, ++p) ++ { ++ *output++ = base16_lc_digit1(*p); ++ *output++ = base16_lc_digit2(*p); ++ } ++ *output = '\0'; ++ return 2*size + 1; ++} ++ ++size_t bytes_to_hex_uc (const void *input, size_t size, unsigned char *output) ++{ ++ size_t i; ++ const unsigned char *p; ++ for (i = 0, p = (const unsigned char *)input; i < size; ++i, ++p) ++ { ++ *output++ = base16_uc_digit1(*p); ++ *output++ = base16_uc_digit2(*p); ++ } ++ *output = '\0'; ++ return 2*size + 1; ++} ++ ++size_t hex_to_bytes (const void *input, size_t size, unsigned char *output) ++{ ++ size_t i; ++ int c1, c2; ++ const unsigned char *p; ++ for (i = 1, p = (const unsigned char *)input; i < size; i += 2) ++ { ++ c1 = base16_value(*p); ++ ++p; ++ c2 = base16_value(*p); ++ ++p; ++ if (c1 >= 0 && c2 >= 0) ++ *output++ = (unsigned char)((c1<<4)|c2); ++ else ++ break; ++ } ++ return i >> 1; ++} ++ ++void print_as_hex (const void *input, size_t bytes) ++{ ++ const unsigned char *p; ++ for (p = (const unsigned char *)input; bytes > 0; --bytes, ++p) ++ printf("%02x", *p); ++} +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilnumber.h b/texk/web2c/luatexdir/luapplib/util/utilnumber.h +new file mode 100644 +index 000000000..3c96571b5 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilnumber.h +@@ -0,0 +1,354 @@ ++#ifndef UTIL_NUMBER_H ++#define UTIL_NUMBER_H ++ ++#include // for size_t ++ ++#include "utilplat.h" ++#include "utildecl.h" ++ ++/* since 'long' isn't long for msvc64/mingw64, we need a type for machine word */ ++ ++#if !defined(__cplusplus) || !defined(_MSC_VER) ++# include // int*_t types are in standard in msvc++ ++#endif ++ ++//#if defined(MSVC64) || defined(__MINGW64__) || defined(__x86_64__) || UINTPTR_MAX > 0xffffffff ++//# define BIT64 ++//#else ++//# define BIT64 ++//#endif ++ ++#if defined(_WIN64) || defined(__MINGW32__) ++# define INT64F "%I64d" ++# define UINT64F "%I64u" ++#else ++# define INT64F "%lld" ++# define UINT64F "%llu" ++#endif ++ ++#if defined(MSVC64) ++# define intlw_t int64_t ++# define uintlw_t uint64_t ++# define INTLW(N) N##I64 ++# define UINTLW(N) N##UI64 ++# define INTLWF INT64F ++# define UINTLWF UINT64F ++#elif defined(__MINGW64__) ++# define intlw_t int64_t ++# define uintlw_t uint64_t ++# define INTLW(N) N##LL ++# define UINTLW(N) N##ULL ++# define INTLWF INT64F ++# define UINTLWF UINT64F ++#else // 32bit or sane 64bit (LP64) ++# define intlw_t long ++# define uintlw_t size_t /*unsigned long*/ ++# define INTLW(N) N##L ++# define UINTLW(N) N##UL ++# define INTLWF "%ld" ++# define UINTLWF "%lu" ++#endif ++ ++/* basic constants */ ++ ++#define MAX_RADIX 36 ++// #define MAX_INTEGER_DIGITS 65 /* 64-bit number in binary form plus '\0' */ ++#define MAX_INTEGER_DIGITS 128 // to handle romannumeral of short int ++ ++#define base36_uc_alphabet "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++#define base36_lc_alphabet "0123456789abcdefghijklmnopqrstuvwxyz" ++ ++#define base26_uc_alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++#define base26_lc_alphabet "abcdefghijklmnopqrstuvwxyz" ++extern const int base26_lookup[]; ++ ++#define base36_lc_palindrome "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" ++#define base36_uc_palindrome "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++ ++extern const int base36_lookup[]; ++ ++#define base10_palindrome "9876543210123456789" ++#define base10_alphabet "0123456789" ++extern const int base10_lookup[]; ++ ++#define base16_uc_alphabet "0123456789ABCDEF" ++#define base16_lc_alphabet "0123456789abcdef" ++extern const int base16_lookup[]; ++ ++#define base16_uc_digit1(c) base16_uc_alphabet[(c)>>4] ++#define base16_uc_digit2(c) base16_uc_alphabet[(c)&15] ++#define base16_lc_digit1(c) base16_lc_alphabet[(c)>>4] ++#define base16_lc_digit2(c) base16_lc_alphabet[(c)&15] ++ ++#define base8_digit(c) ((unsigned)(c - '0') <= (unsigned)('7' - '0')) ++#define base8_value(c) (base8_digit(c) ? (c) - '0' : -1) ++ ++#define base10_digit(c) ((unsigned)(c - '0') <= (unsigned)('9' - '0')) ++#define base10_value(c) (base10_lookup[(uint8_t)c]) ++ ++#define base16_digit(c) (base16_lookup[(uint8_t)c] >= 0) ++#define base16_value(c) (base16_lookup[(uint8_t)c]) ++ ++#define base26_digit(c) (base26_lookup[(uint8_t)c] >= 0) ++#define base26_value(c) (base26_lookup[(uint8_t)c]) ++ ++#define base36_digit(c) (base36_lookup[(uint8_t)c] >= 0) ++#define base36_value(c) (base36_lookup[(uint8_t)c]) ++ ++//#define base_digit(c, radix) ((unsigned)(base36_lookup[c]) < (unsigned)(radix)) ++//#define base_value(c, radix) (base_digit(c, radix) ? base36_lookup[c] : -1) ++ ++/* integer from string; return a pointer to character next to the last digit */ ++ ++UTILAPI const char * string_to_int32 (const char *s, int32_t *number); ++UTILAPI const char * string_to_intlw (const char *s, intlw_t *number); ++UTILAPI const char * string_to_int64 (const char *s, int64_t *number); ++ ++UTILAPI const char * string_to_uint32 (const char *s, uint32_t *number); ++UTILAPI const char * string_to_uintlw (const char *s, uintlw_t *number); ++UTILAPI const char * string_to_uint64 (const char *s, uint64_t *number); ++ ++UTILAPI const char * radix_to_int32 (const char *s, int32_t *number, int radix); ++UTILAPI const char * radix_to_intlw (const char *s, intlw_t *number, int radix); ++UTILAPI const char * radix_to_int64 (const char *s, int64_t *number, int radix); ++ ++UTILAPI const char * radix_to_uint32 (const char *s, uint32_t *number, int radix); ++UTILAPI const char * radix_to_uintlw (const char *s, uintlw_t *number, int radix); ++UTILAPI const char * radix_to_uint64 (const char *s, uint64_t *number, int radix); ++ ++UTILAPI const char * roman_to_uint16 (const char *s, uint16_t *number); ++ ++UTILAPI const char * alpha_to_uint32 (const char *s, uint32_t *number); ++UTILAPI const char * alpha_to_uintlw (const char *s, uintlw_t *number); ++UTILAPI const char * alpha_to_uint64 (const char *s, uint64_t *number); ++ ++UTILAPI const char * alphan_to_uint32 (const char *s, uint32_t *number); ++UTILAPI const char * alphan_to_uintlw (const char *s, uintlw_t *number); ++UTILAPI const char * alphan_to_uint64 (const char *s, uintlw_t *number); ++ ++/* ++integer to string; return a pointer to null-terminated static const string ++same but also stores pointer to trailing null (to be used for firther formatting) ++*/ ++ ++UTILAPI char * int32_as_string (int32_t number, char **e); ++UTILAPI char * intlw_as_string (intlw_t number, char **e); ++UTILAPI char * int64_as_string (int64_t number, char **e); ++ ++UTILAPI char * uint32_as_string (uint32_t number, char **e); ++UTILAPI char * uintlw_as_string (uintlw_t number, char **e); ++UTILAPI char * uint64_as_string (uint64_t number, char **e); ++ ++#define int32_to_string(number) int32_as_string(number, NULL) ++#define intlw_to_string(number) intlw_as_string(number, NULL) ++#define int64_to_string(number) int64_as_string(number, NULL) ++ ++#define uint32_to_string(number) uint32_as_string(number, NULL) ++#define uintlw_to_string(number) uintlw_as_string(number, NULL) ++#define uint64_to_string(number) uint64_as_string(number, NULL) ++ ++UTILAPI char * int32_as_radix (int32_t number, int radix, char **e); ++UTILAPI char * intlw_as_radix (intlw_t number, int radix, char **e); ++UTILAPI char * int64_as_radix (int64_t number, int radix, char **e); ++ ++UTILAPI char * uint32_as_radix (uint32_t number, int radix, char **e); ++UTILAPI char * uintlw_as_radix (uintlw_t number, int radix, char **e); ++UTILAPI char * uint64_as_radix (uint64_t number, int radix, char **e); ++ ++#define int32_to_radix(number, radix) int32_as_radix(number, radix, NULL) ++#define intlw_to_radix(number, radix) intlw_as_radix(number, radix, NULL) ++#define int64_to_radix(number, radix) int64_as_radix(number, radix, NULL) ++ ++#define uint32_to_radix(number, radix) uint32_as_radix(number, radix, NULL) ++#define uintlw_to_radix(number, radix) uintlw_as_radix(number, radix, NULL) ++#define uint64_to_radix(number, radix) uint64_as_radix(number, radix, NULL) ++ ++UTILAPI char * uint32_as_alpha_uc (uint32_t number, char **e); ++UTILAPI char * uint32_as_alpha_lc (uint32_t number, char **e); ++UTILAPI char * uintlw_as_alpha_uc (uintlw_t number, char **e); ++UTILAPI char * uintlw_as_alpha_lc (uintlw_t number, char **e); ++UTILAPI char * uint64_as_alpha_uc (uint64_t number, char **e); ++UTILAPI char * uint64_as_alpha_lc (uint64_t number, char **e); ++ ++#define uint32_to_alpha_uc(number) uint32_as_alpha_uc(number, NULL) ++#define uint32_to_alpha_lc(number) uint32_as_alpha_lc(number, NULL) ++#define uintlw_to_alpha_uc(number) uintlw_as_alpha_uc(number, NULL) ++#define uintlw_to_alpha_lc(number) uintlw_as_alpha_lc(number, NULL) ++#define uint64_to_alpha_uc(number) uint64_as_alpha_uc(number, NULL) ++#define uint64_to_alpha_lc(number) uint64_as_alpha_lc(number, NULL) ++ ++UTILAPI char * uint32_as_alphan_uc (uint32_t number, char **e); ++UTILAPI char * uint32_as_alphan_lc (uint32_t number, char **e); ++UTILAPI char * uintlw_as_alphan_uc (uintlw_t number, char **e); ++UTILAPI char * uintlw_as_alphan_lc (uintlw_t number, char **e); ++UTILAPI char * uint64_as_alphan_uc (uint64_t number, char **e); ++UTILAPI char * uint64_as_alphan_lc (uint64_t number, char **e); ++ ++#define uint32_to_alphan_uc(number) uint32_as_alpha_uc(number, NULL) ++#define uint32_to_alphan_lc(number) uint32_as_alpha_lc(number, NULL) ++#define uintlw_to_alphan_uc(number) uintlw_as_alpha_uc(number, NULL) ++#define uintlw_to_alphan_lc(number) uintlw_as_alpha_lc(number, NULL) ++#define uint64_to_alphan_uc(number) uint64_as_alpha_uc(number, NULL) ++#define uint64_to_alphan_lc(number) uint64_as_alpha_lc(number, NULL) ++ ++/* roman numeral (limited to uint16_t) */ ++ ++UTILAPI char * uint16_as_roman_uc (uint16_t number, char **e); ++UTILAPI char * uint16_as_roman_lc (uint16_t number, char **e); ++ ++#define uint16_to_roman_uc(number) uint16_as_roman_uc(number, NULL) ++#define uint16_to_roman_lc(number) uint16_as_roman_lc(number, NULL) ++ ++#define uint16_as_roman(number) uint16_as_roman_uc(number) ++#define uint16_to_roman(number) uint16_to_roman_uc(number) ++ ++/* double/float to string */ ++ ++UTILAPI char * double_to_string (double number, int digits); ++UTILAPI char * float_to_string (float number, int digits); ++ ++UTILAPI char * double_as_string (double number, int digits, char **r, char **e); ++UTILAPI char * float_as_string (float number, int digits, char **r, char **e); ++ ++/* string to double/float */ ++ ++UTILAPI const char * string_to_double (const char *s, double *number); ++UTILAPI const char * string_to_float (const char *s, float *number); ++ ++/* convenience form accepting comma among a dot, with not exp notation (eg. pdf) */ ++ ++UTILAPI const char * convert_to_double (const char *s, double *number); ++UTILAPI const char * convert_to_float (const char *s, float *number); ++ ++/* binary data parsers helpers */ ++ ++#define get_byte1(i) ((i)&255) ++#define get_byte2(i) (((i)>>8)&255) ++#define get_byte3(i) (((i)>>16)&255) ++#define get_byte4(i) (((i)>>24)&255) ++ ++#define read_uint16be_as(s, int_type) ((int_type)((s[0]<<8)|s[1])) ++#define read_uint32be_as(s, int_type) ((int_type)((s[0]<<24)|(s[1]<<16)|(s[2]<<8)|s[3])) ++ ++#define read_uint16le_as(s, int_type) ((int_type)((s[1]<<8)|s[0])) ++#define read_uint32le_as(s, int_type) ((int_type)((s[3]<<24)|(s[2]<<16)|(s[1]<<8)|s[0])) ++ ++#define read_uint16_native(s) (*((uint16_t *)(s))) ++#define read_uint32_native(s) (*((uint32_t *)(s))) ++#define read_int16_native(s) (*((int16_t *)(s))) ++#define read_int32_native(s) (*((int32_t *)(s))) ++ ++#define scan_uint16be_as(s, int_type) (s += 2, (int_type)((s[-2]<<8)|s[-1])) ++#define scan_uint32be_as(s, int_type) (s += 4, (int_type)((s[-4]<<24)|(s[-3]<<16)|(s[-2]<<8)|s[-1])) ++ ++#define scan_uint16le_as(s, int_type) (s += 2, (int_type)((s[-1]<<8)|s[-2])) ++#define scan_uint32le_as(s, int_type) (s += 4, (int_type)((s[-1]<<24)|(s[-2]<<16)|(s[-3]<<8)|s[-4])) ++ ++#define scan_uint16_native(s) (s += 2, read_uint16_native(s-2)) ++#define scan_uint32_native(s) (s += 4, read_uint32_native(s-4)) ++#define scan_int16_native(s) (s += 2, read_int16_native(s-2)) ++#define scan_int32_native(s) (s += 4, read_int32_native(s-4)) ++ ++#define read_fixed16_16_as(s, float_type) (((float_type)read_uint32be_as(s, signed int))/(1<<16)) ++#define read_fixed2_14_as(s, float_type) (((float_type)read_uint16be_as(s, signed short))/(1<<14)) ++ ++#define scan_fixed16_16_as(s, float_type) (((float_type)scan_uint32be_as(s, signed int))/(1<<16)) ++#define scan_fixed2_14_as(s, float_type) (((float_type)scan_uint16be_as(s, signed short))/(1<<14)) ++ ++/* internal procedures */ ++ ++#define _scan_sign(c, sign, next) \ ++ do { if (c == '-') { sign = 1; c = next; } else if (c == '+') { sign = 0; c = next; } else sign = 0; } while (0) ++ ++#define integer_multiplied10(number) (((number) << 1) + ((number) << 3)) ++ ++#define _scan_integer(c, number, next) \ ++ for (number = 0; base10_digit(c); number = integer_multiplied10(number) + (c - '0'), c = next) ++#define _scan_radix(c, number, radix, next) \ ++ for (number = 0; (c = base36_value(c)) >= 0 && c < radix; number = number * radix + c, c = next) ++ ++#define _read_integer(c, number, next) \ ++ for (number = c - '0', c = next; base10_digit(c); number = integer_multiplied10(number) + (c - '0'), c = next) ++#define _read_radix(c, number, radix, next) \ ++ for (number = c - '0', c = next; (c = base36_value(c)) >= 0 && c < radix; number = number * radix + c, c = next) ++ ++/* rationals */ ++ ++#define _scan_decimal(c, number, next) \ ++ for (number = 0; base10_digit(c); number = number*10 + (c - '0'), c = next) ++#define _scan_fraction(c, number, exponent10, next) \ ++ for (exponent10 = 0; base10_digit(c); --exponent10, number = number*10 + (c - '0'), c = next) ++ ++#define _scan_exponent10(c, exponent10, next) \ ++ do { \ ++ int eexponent10, eexpsign; \ ++ _scan_sign(c, eexpsign, next); \ ++ _scan_integer(c, eexponent10, next); \ ++ if (eexpsign) \ ++ exponent10 -= eexponent10; \ ++ else \ ++ exponent10 += eexponent10; \ ++ } while(0) ++ ++#if 0 ++ ++// kept just for sentiment ;) ++ ++extern const double double_binary_power10[]; ++extern const float float_binary_power10[]; ++extern const double double_binary_negpower10[]; ++extern const float float_binary_negpower10[]; ++ ++#define double_negative_exp10(number, exponent) \ ++{ const double *bp10; int e = ((exponent) < 511 ? 511 : -(exponent)); \ ++ for (bp10 = double_binary_negpower10; e > 0; e >>= 1, ++bp10) \ ++ if (e & 1) number *= *bp10; } ++ ++#define float_negative_exp10(number, exponent) \ ++{ const float *bp10; int e = ((exponent) < 64 ? 64 : -(exponent)); \ ++ for (bp10 = float_binary_negpower10; e > 0; e >>= 1, ++bp10) \ ++ if (e & 1) number *= *bp10; } ++ ++#define double_positive_exp10(number, exponent) \ ++{ const double *bp10; int e = ((exponent) > 511 ? 511 : (exponent)); \ ++ for (bp10 = double_binary_power10; e > 0; e >>= 1, ++bp10) \ ++ if (e & 1) number *= *bp10; } ++ ++#define float_positive_exp10(number, exponent) \ ++{ const float *bp10; int e = ((exponent) > 64 ? 64 : (exponent)); \ ++ for (bp10 = double_binary_power10; e > 0; e >>= 1, ++bp10) \ ++ if (e & 1) number *= *bp10; } ++ ++#define double_exp10(number, exponent) \ ++ if ((exponent) < 0) double_negative_exp10(number, exponent) else if ((exponent) > 0) double_positive_exp10(number, exponent) ++ ++#define float_exp10(number, exponent) \ ++ if ((exponent) < 0) float_negative_exp10(number, exponent) else if ((exponent) > 0) float_positive_exp10(number, exponent) ++ ++#else ++ ++extern const double double_decimal_power10[]; ++extern const float float_decimal_power10[]; ++extern const double double_decimal_negpower10[]; ++extern const float float_decimal_negpower10[]; ++ ++#define double_negative_exp10(number, exponent) ((number) *= double_decimal_negpower10[(exponent) < -308 ? 308 : -(exponent)]) ++#define double_positive_exp10(number, exponent) ((number) *= double_decimal_power10[(exponent) > 308 ? 308 : (exponent)]) ++ ++#define float_negative_exp10(number, exponent) ((number) *= float_decimal_negpower10[(exponent) < -38 ? 38 : -(exponent)]) ++#define float_positive_exp10(number, exponent) ((number) *= float_decimal_power10[(exponent) > 38 ? 38 : (exponent)]) ++ ++#define double_exp10(number, exponent) ((void)(((exponent) < 0 && double_negative_exp10(number, exponent)) || (((exponent) > 0 && double_positive_exp10(number, exponent))))) ++#define float_exp10(number, exponent) ((void)(((exponent) < 0 && float_negative_exp10(number, exponent)) || (((exponent) > 0 && float_positive_exp10(number, exponent))))) ++ ++#endif ++ ++/* pretty common stuff */ ++ ++#define bytes_to_hex(input, size, output) bytes_to_hex_lc(input, size, output) ++UTILAPI size_t bytes_to_hex_lc (const void *input, size_t size, uint8_t *output); ++UTILAPI size_t bytes_to_hex_uc (const void *input, size_t size, uint8_t *output); ++UTILAPI size_t hex_to_bytes (const void *input, size_t size, uint8_t *output); ++UTILAPI void print_as_hex (const void *input, size_t bytes); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilplat.h b/texk/web2c/luatexdir/luapplib/util/utilplat.h +new file mode 100644 +index 000000000..037a1e231 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilplat.h +@@ -0,0 +1,23 @@ ++ ++#ifndef UTIL_PLAT_H ++#define UTIL_PLAT_H ++ ++#if defined(_WIN32) || defined(WIN32) ++# ifdef _MSC_VER ++# if defined(_M_64) || defined(_WIN64) ++# define MSVC64 ++# else ++# define MSVC32 ++# endif ++# else ++# if defined(__MINGW64__) ++# define MINGW64 ++# else ++# if defined(__MINGW32__) ++# define MINGW32 ++# endif ++# endif ++# endif ++#endif ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/util/utilsha.c b/texk/web2c/luatexdir/luapplib/util/utilsha.c +new file mode 100644 +index 000000000..665320730 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilsha.c +@@ -0,0 +1,1290 @@ ++/* sha2 implementation by Aaron D. Gifford (http://www.aarongifford.com) */ ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#include ++#endif ++ ++#ifndef BYTE_ORDER ++#define BYTE_ORDER LITTLE_ENDIAN ++#endif ++/* begin of sha2.c */ ++ ++/* ++ * FILE: sha2.c ++ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/ ++ * ++ * Copyright (c) 2000-2001, Aaron D. Gifford ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * $Id: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $ ++ */ ++ ++#include /* memcpy()/memset() or bcopy()/bzero() */ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++#include ++#endif ++#include /* assert() */ ++//#include "sha2.h" ++#include "utilsha.h" ++ ++/* ++ * ASSERT NOTE: ++ * Some sanity checking code is included using assert(). On my FreeBSD ++ * system, this additional code can be removed by compiling with NDEBUG ++ * defined. Check your own systems manpage on assert() to see how to ++ * compile WITHOUT the sanity checking code on your system. ++ * ++ * UNROLLED TRANSFORM LOOP NOTE: ++ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform ++ * loop version for the hash transform rounds (defined using macros ++ * later in this file). Either define on the command line, for example: ++ * ++ * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c ++ * ++ * or define below: ++ * ++ * #define SHA2_UNROLL_TRANSFORM ++ * ++ */ ++ ++ ++/*** SHA-256/384/512 Machine Architecture Definitions *****************/ ++/* ++ * BYTE_ORDER NOTE: ++ * ++ * Please make sure that your system defines BYTE_ORDER. If your ++ * architecture is little-endian, make sure it also defines ++ * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are ++ * equivilent. ++ * ++ * If your system does not define the above, then you can do so by ++ * hand like this: ++ * ++ * #define LITTLE_ENDIAN 1234 ++ * #define BIG_ENDIAN 4321 ++ * ++ * And for little-endian machines, add: ++ * ++ * #define BYTE_ORDER LITTLE_ENDIAN ++ * ++ * Or for big-endian machines: ++ * ++ * #define BYTE_ORDER BIG_ENDIAN ++ * ++ * The FreeBSD machine this was written on defines BYTE_ORDER ++ * appropriately by including (which in turn includes ++ * where the appropriate definitions are actually ++ * made). ++ */ ++#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) ++#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN ++#endif ++ ++/* ++ * Define the followingsha2_* types to types of the correct length on ++ * the native archtecture. Most BSD systems and Linux define u_intXX_t ++ * types. Machines with very recent ANSI C headers, can use the ++ * uintXX_t definintions from inttypes.h by defining SHA2_USE_INTTYPES_H ++ * during compile or in the sha.h header file. ++ * ++ * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t ++ * will need to define these three typedefs below (and the appropriate ++ * ones in sha.h too) by hand according to their system architecture. ++ * ++ * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t ++ * types and pointing out recent ANSI C support for uintXX_t in inttypes.h. ++ * ++ * PJ: replace by uintX_t ++ */ ++ ++//typedef uint8_t sha2_byte; /* Exactly 1 byte */ ++//typedef uint32_t sha2_word32; /* Exactly 4 bytes */ ++//typedef uint64_t sha2_word64; /* Exactly 8 bytes */ ++ ++/*** SHA-256/384/512 Various Length Definitions ***********************/ ++/* NOTE: Most of these are in sha2.h */ ++#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) ++#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) ++#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) ++ ++ ++/*** ENDIAN REVERSAL MACROS *******************************************/ ++#if BYTE_ORDER == LITTLE_ENDIAN ++#define REVERSE32(w,x) { \ ++ uint32_t tmp = (w); \ ++ tmp = (tmp >> 16) | (tmp << 16); \ ++ (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ ++} ++#define REVERSE64(w,x) { \ ++ uint64_t tmp = (w); \ ++ tmp = (tmp >> 32) | (tmp << 32); \ ++ tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ ++ ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ ++ (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ ++ ((tmp & 0x0000ffff0000ffffULL) << 16); \ ++} ++#endif /* BYTE_ORDER == LITTLE_ENDIAN */ ++ ++/* ++ * Macro for incrementally adding the unsigned 64-bit integer n to the ++ * unsigned 128-bit integer (represented using a two-element array of ++ * 64-bit words): ++ */ ++#define ADDINC128(w,n) { \ ++ (w)[0] += (uint64_t)(n); \ ++ if ((w)[0] < (n)) { \ ++ (w)[1]++; \ ++ } \ ++} ++ ++/* ++ * Macros for copying blocks of memory and for zeroing out ranges ++ * of memory. Using these macros makes it easy to switch from ++ * using memset()/memcpy() and using bzero()/bcopy(). ++ * ++ * Please define either SHA2_USE_MEMSET_MEMCPY or define ++ * SHA2_USE_BZERO_BCOPY depending on which function set you ++ * choose to use: ++ */ ++#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY) ++/* Default to memset()/memcpy() if no option is specified */ ++#define SHA2_USE_MEMSET_MEMCPY 1 ++#endif ++#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY) ++/* Abort with an error if BOTH options are defined */ ++#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both! ++#endif ++ ++#ifdef SHA2_USE_MEMSET_MEMCPY ++#define MEMSET_BZERO(p,l) memset((p), 0, (l)) ++#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l)) ++#endif ++#ifdef SHA2_USE_BZERO_BCOPY ++#define MEMSET_BZERO(p,l) bzero((p), (l)) ++#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l)) ++#endif ++ ++ ++/*** THE SIX LOGICAL FUNCTIONS ****************************************/ ++/* ++ * Bit shifting and rotation (used by the six SHA-XYZ logical functions: ++ * ++ * NOTE: The naming of R and S appears backwards here (R is a SHIFT and ++ * S is a ROTATION) because the SHA-256/384/512 description document ++ * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this ++ * same "backwards" definition. ++ */ ++/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ ++#define R(b,x) ((x) >> (b)) ++/* 32-bit Rotate-right (used in SHA-256): */ ++#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) ++/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ ++#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) ++ ++/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ ++#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) ++#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) ++ ++/* Four of six logical functions used in SHA-256: */ ++#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) ++#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) ++#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) ++#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) ++ ++/* Four of six logical functions used in SHA-384 and SHA-512: */ ++#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) ++#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) ++#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) ++#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) ++ ++/*** INTERNAL FUNCTION PROTOTYPES *************************************/ ++/* NOTE: These should not be accessed directly from outside this ++ * library -- they are intended for private internal visibility/use ++ * only. ++ */ ++static void SHA512_Last(SHA512_CTX*); ++static void SHA256_Transform(SHA256_CTX*, const uint32_t*); ++static void SHA512_Transform(SHA512_CTX*, const uint64_t*); ++ ++ ++/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ ++/* Hash constant words K for SHA-256: */ ++static const uint32_t K256[64] = { ++ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, ++ 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, ++ 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, ++ 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, ++ 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, ++ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, ++ 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, ++ 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, ++ 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, ++ 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, ++ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, ++ 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, ++ 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, ++ 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, ++ 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, ++ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL ++}; ++ ++/* Initial hash value H for SHA-256: */ ++static const uint32_t sha256_initial_hash_value[8] = { ++ 0x6a09e667UL, ++ 0xbb67ae85UL, ++ 0x3c6ef372UL, ++ 0xa54ff53aUL, ++ 0x510e527fUL, ++ 0x9b05688cUL, ++ 0x1f83d9abUL, ++ 0x5be0cd19UL ++}; ++ ++/* Hash constant words K for SHA-384 and SHA-512: */ ++static const uint64_t K512[80] = { ++ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, ++ 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, ++ 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, ++ 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, ++ 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, ++ 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, ++ 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, ++ 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, ++ 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, ++ 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, ++ 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, ++ 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, ++ 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, ++ 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, ++ 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, ++ 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, ++ 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, ++ 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, ++ 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, ++ 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, ++ 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, ++ 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, ++ 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, ++ 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, ++ 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, ++ 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, ++ 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, ++ 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, ++ 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, ++ 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, ++ 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, ++ 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, ++ 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, ++ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, ++ 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, ++ 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, ++ 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, ++ 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, ++ 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, ++ 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL ++}; ++ ++/* Initial hash value H for SHA-384 */ ++static const uint64_t sha384_initial_hash_value[8] = { ++ 0xcbbb9d5dc1059ed8ULL, ++ 0x629a292a367cd507ULL, ++ 0x9159015a3070dd17ULL, ++ 0x152fecd8f70e5939ULL, ++ 0x67332667ffc00b31ULL, ++ 0x8eb44a8768581511ULL, ++ 0xdb0c2e0d64f98fa7ULL, ++ 0x47b5481dbefa4fa4ULL ++}; ++ ++/* Initial hash value H for SHA-512 */ ++static const uint64_t sha512_initial_hash_value[8] = { ++ 0x6a09e667f3bcc908ULL, ++ 0xbb67ae8584caa73bULL, ++ 0x3c6ef372fe94f82bULL, ++ 0xa54ff53a5f1d36f1ULL, ++ 0x510e527fade682d1ULL, ++ 0x9b05688c2b3e6c1fULL, ++ 0x1f83d9abfb41bd6bULL, ++ 0x5be0cd19137e2179ULL ++}; ++ ++/* ++ * Constant used by SHA256/384/512_End() functions for converting the ++ * digest to a readable hexadecimal character string: ++ */ ++ ++//static const char *sha2_hex_digits = "0123456789abcdef"; // PJ ++ ++ ++/*** SHA-256: *********************************************************/ ++static void SHA256_Init(SHA256_CTX* context) { ++ if (context == (SHA256_CTX*)0) { ++ return; ++ } ++ MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); ++ MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH); ++ context->bitcount = 0; ++} ++ ++#ifdef SHA2_UNROLL_TRANSFORM ++ ++/* Unrolled SHA-256 round macros: */ ++ ++#if BYTE_ORDER == LITTLE_ENDIAN ++ ++#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ ++ REVERSE32(*data++, W256[j]); \ ++ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ ++ K256[j] + W256[j]; \ ++ (d) += T1; \ ++ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ ++ j++ ++ ++ ++#else /* BYTE_ORDER == LITTLE_ENDIAN */ ++ ++#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ ++ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ ++ K256[j] + (W256[j] = *data++); \ ++ (d) += T1; \ ++ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ ++ j++ ++ ++#endif /* BYTE_ORDER == LITTLE_ENDIAN */ ++ ++#define ROUND256(a,b,c,d,e,f,g,h) \ ++ s0 = W256[(j+1)&0x0f]; \ ++ s0 = sigma0_256(s0); \ ++ s1 = W256[(j+14)&0x0f]; \ ++ s1 = sigma1_256(s1); \ ++ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ ++ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ ++ (d) += T1; \ ++ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ ++ j++ ++ ++void SHA256_Transform(SHA256_CTX* context, const uint32_t* data) { ++ uint32_t a, b, c, d, e, f, g, h, s0, s1; ++ uint32_t T1, *W256; ++ int j; ++ ++ W256 = (uint32_t*)context->buffer; ++ ++ /* Initialize registers with the prev. intermediate value */ ++ a = context->state[0]; ++ b = context->state[1]; ++ c = context->state[2]; ++ d = context->state[3]; ++ e = context->state[4]; ++ f = context->state[5]; ++ g = context->state[6]; ++ h = context->state[7]; ++ ++ j = 0; ++ do { ++ /* Rounds 0 to 15 (unrolled): */ ++ ROUND256_0_TO_15(a,b,c,d,e,f,g,h); ++ ROUND256_0_TO_15(h,a,b,c,d,e,f,g); ++ ROUND256_0_TO_15(g,h,a,b,c,d,e,f); ++ ROUND256_0_TO_15(f,g,h,a,b,c,d,e); ++ ROUND256_0_TO_15(e,f,g,h,a,b,c,d); ++ ROUND256_0_TO_15(d,e,f,g,h,a,b,c); ++ ROUND256_0_TO_15(c,d,e,f,g,h,a,b); ++ ROUND256_0_TO_15(b,c,d,e,f,g,h,a); ++ } while (j < 16); ++ ++ /* Now for the remaining rounds to 64: */ ++ do { ++ ROUND256(a,b,c,d,e,f,g,h); ++ ROUND256(h,a,b,c,d,e,f,g); ++ ROUND256(g,h,a,b,c,d,e,f); ++ ROUND256(f,g,h,a,b,c,d,e); ++ ROUND256(e,f,g,h,a,b,c,d); ++ ROUND256(d,e,f,g,h,a,b,c); ++ ROUND256(c,d,e,f,g,h,a,b); ++ ROUND256(b,c,d,e,f,g,h,a); ++ } while (j < 64); ++ ++ /* Compute the current intermediate hash value */ ++ context->state[0] += a; ++ context->state[1] += b; ++ context->state[2] += c; ++ context->state[3] += d; ++ context->state[4] += e; ++ context->state[5] += f; ++ context->state[6] += g; ++ context->state[7] += h; ++ ++ /* Clean up */ ++ a = b = c = d = e = f = g = h = T1 = 0; ++} ++ ++#else /* SHA2_UNROLL_TRANSFORM */ ++ ++static void SHA256_Transform(SHA256_CTX* context, const uint32_t* data) { ++ uint32_t a, b, c, d, e, f, g, h, s0, s1; ++ uint32_t T1, T2, *W256; ++ int j; ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ W256 = (uint32_t*)((void *)context->buffer); ++#else ++ W256 = (uint32_t*)context->buffer; ++#endif ++ ++ ++ /* Initialize registers with the prev. intermediate value */ ++ a = context->state[0]; ++ b = context->state[1]; ++ c = context->state[2]; ++ d = context->state[3]; ++ e = context->state[4]; ++ f = context->state[5]; ++ g = context->state[6]; ++ h = context->state[7]; ++ ++ j = 0; ++ do { ++#if BYTE_ORDER == LITTLE_ENDIAN ++ /* Copy data while converting to host byte order */ ++ REVERSE32(*data++,W256[j]); ++ /* Apply the SHA-256 compression function to update a..h */ ++ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; ++#else /* BYTE_ORDER == LITTLE_ENDIAN */ ++ /* Apply the SHA-256 compression function to update a..h with copy */ ++ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); ++#endif /* BYTE_ORDER == LITTLE_ENDIAN */ ++ T2 = Sigma0_256(a) + Maj(a, b, c); ++ h = g; ++ g = f; ++ f = e; ++ e = d + T1; ++ d = c; ++ c = b; ++ b = a; ++ a = T1 + T2; ++ ++ j++; ++ } while (j < 16); ++ ++ do { ++ /* Part of the message block expansion: */ ++ s0 = W256[(j+1)&0x0f]; ++ s0 = sigma0_256(s0); ++ s1 = W256[(j+14)&0x0f]; ++ s1 = sigma1_256(s1); ++ ++ /* Apply the SHA-256 compression function to update a..h */ ++ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + ++ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); ++ T2 = Sigma0_256(a) + Maj(a, b, c); ++ h = g; ++ g = f; ++ f = e; ++ e = d + T1; ++ d = c; ++ c = b; ++ b = a; ++ a = T1 + T2; ++ ++ j++; ++ } while (j < 64); ++ ++ /* Compute the current intermediate hash value */ ++ context->state[0] += a; ++ context->state[1] += b; ++ context->state[2] += c; ++ context->state[3] += d; ++ context->state[4] += e; ++ context->state[5] += f; ++ context->state[6] += g; ++ context->state[7] += h; ++ ++ /* Clean up */ ++ a = b = c = d = e = f = g = h = T1 = T2 = 0; ++} ++ ++#endif /* SHA2_UNROLL_TRANSFORM */ ++ ++static void SHA256_Update(SHA256_CTX* context, const uint8_t *data, size_t len) { ++ unsigned int freespace, usedspace; ++ ++ if (len == 0) { ++ /* Calling with no data is valid - we do nothing */ ++ return; ++ } ++ ++ /* Sanity check: */ ++ assert(context != (SHA256_CTX*)0 && data != (uint8_t*)0); ++ ++ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; ++ if (usedspace > 0) { ++ /* Calculate how much free space is available in the buffer */ ++ freespace = SHA256_BLOCK_LENGTH - usedspace; ++ ++ if (len >= freespace) { ++ /* Fill the buffer completely and process it */ ++ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace); ++ context->bitcount += freespace << 3; ++ len -= freespace; ++ data += freespace; ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ SHA256_Transform(context, (uint32_t*)((void *)context->buffer)); ++#else ++ SHA256_Transform(context, (uint32_t*)context->buffer); ++#endif ++ ++ } else { ++ /* The buffer is not yet full */ ++ MEMCPY_BCOPY(&context->buffer[usedspace], data, len); ++ context->bitcount += len << 3; ++ /* Clean up: */ ++ usedspace = freespace = 0; ++ return; ++ } ++ } ++ while (len >= SHA256_BLOCK_LENGTH) { ++ /* Process as many complete blocks as we can */ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ SHA256_Transform(context, (const uint32_t*)(const void *)(data)); ++#else ++ SHA256_Transform(context, (const uint32_t*)data); ++#endif ++ ++ context->bitcount += SHA256_BLOCK_LENGTH << 3; ++ len -= SHA256_BLOCK_LENGTH; ++ data += SHA256_BLOCK_LENGTH; ++ } ++ if (len > 0) { ++ /* There's left-overs, so save 'em */ ++ MEMCPY_BCOPY(context->buffer, data, len); ++ context->bitcount += len << 3; ++ } ++ /* Clean up: */ ++ usedspace = freespace = 0; ++} ++ ++static void SHA256_Final(uint8_t digest[], SHA256_CTX* context) { ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ uint32_t *d ; ++#else ++ uint32_t *d = (uint32_t*)digest; ++ ++#endif ++ unsigned int usedspace; ++ ++ /* Sanity check: */ ++ assert(context != (SHA256_CTX*)0); ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ d = malloc(sizeof(uint32_t)*8); /* why 8 ? see below for loop */ ++ assert(d); ++#endif ++ ++ /* If no digest buffer is passed, we don't bother doing this: */ ++ if (digest != (uint8_t*)0) { ++ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; ++#if BYTE_ORDER == LITTLE_ENDIAN ++ /* Convert FROM host byte order */ ++ REVERSE64(context->bitcount,context->bitcount); ++#endif ++ if (usedspace > 0) { ++ /* Begin padding with a 1 bit: */ ++ context->buffer[usedspace++] = 0x80; ++ ++ if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { ++ /* Set-up for the last transform: */ ++ MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace); ++ } else { ++ if (usedspace < SHA256_BLOCK_LENGTH) { ++ MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace); ++ } ++ /* Do second-to-last transform: */ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ SHA256_Transform(context, (uint32_t*)(void *)(context->buffer)); ++#else ++ SHA256_Transform(context, (uint32_t*)context->buffer); ++#endif ++ ++ ++ /* And set-up for the last transform: */ ++ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH); ++ } ++ } else { ++ /* Set-up for the last transform: */ ++ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH); ++ ++ /* Begin padding with a 1 bit: */ ++ *context->buffer = 0x80; ++ } ++ /* Set the bit count: */ ++ //*(uint64_t*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount; // aliasing violation warning ++ context->buffer64[SHA256_SHORT_BLOCK_LENGTH / sizeof(uint64_t)] = context->bitcount; ++ ++ /* Final transform: */ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ SHA256_Transform(context, (uint32_t*)((void *)(context->buffer))); ++ ++#else ++ SHA256_Transform(context, (uint32_t*)context->buffer); ++ ++#endif ++ ++#if BYTE_ORDER == LITTLE_ENDIAN ++ { ++ /* Convert TO host byte order */ ++ int j; ++ for (j = 0; j < 8; j++) { ++ REVERSE32(context->state[j],context->state[j]); ++ *d++ = context->state[j]; ++ } ++ } ++#else ++ MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH); ++#endif ++ } ++ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ memcpy(digest,d,SHA256_DIGEST_LENGTH); ++ free(d); ++#endif ++ ++ /* Clean up state data: */ ++ MEMSET_BZERO(context, sizeof(*context)); ++ usedspace = 0; ++} ++ ++/* ++static char *SHA256_End(SHA256_CTX* context, char buffer[]) { ++ uint8_t digest[SHA256_DIGEST_LENGTH], *d = digest; ++ int i; ++ ++ / * Sanity check: * / ++ assert(context != (SHA256_CTX*)0); ++ ++ if (buffer != (char*)0) { ++ SHA256_Final(digest, context); ++ ++ for (i = 0; i < SHA256_DIGEST_LENGTH; i++) { ++ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; ++ *buffer++ = sha2_hex_digits[*d & 0x0f]; ++ d++; ++ } ++ *buffer = (char)0; ++ } else { ++ MEMSET_BZERO(context, sizeof(context)); ++ } ++ MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH); ++ return buffer; ++} ++ ++static char* SHA256_Data(const uint8_t* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) { ++ SHA256_CTX context; ++ ++ SHA256_Init(&context); ++ SHA256_Update(&context, data, len); ++ return SHA256_End(&context, digest); ++} ++*/ ++ ++ ++/*** SHA-512: *********************************************************/ ++static void SHA512_Init(SHA512_CTX* context) { ++ if (context == (SHA512_CTX*)0) { ++ return; ++ } ++ MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH); ++ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH); ++ context->bitcount[0] = context->bitcount[1] = 0; ++} ++ ++#ifdef SHA2_UNROLL_TRANSFORM ++ ++/* Unrolled SHA-512 round macros: */ ++#if BYTE_ORDER == LITTLE_ENDIAN ++ ++#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ ++ REVERSE64(*data++, W512[j]); \ ++ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ ++ K512[j] + W512[j]; \ ++ (d) += T1, \ ++ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \ ++ j++ ++ ++ ++#else /* BYTE_ORDER == LITTLE_ENDIAN */ ++ ++#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ ++ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ ++ K512[j] + (W512[j] = *data++); \ ++ (d) += T1; \ ++ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ ++ j++ ++ ++#endif /* BYTE_ORDER == LITTLE_ENDIAN */ ++ ++#define ROUND512(a,b,c,d,e,f,g,h) \ ++ s0 = W512[(j+1)&0x0f]; \ ++ s0 = sigma0_512(s0); \ ++ s1 = W512[(j+14)&0x0f]; \ ++ s1 = sigma1_512(s1); \ ++ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \ ++ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ ++ (d) += T1; \ ++ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ ++ j++ ++ ++static void SHA512_Transform(SHA512_CTX* context, const uint64_t* data) { ++ uint64_t a, b, c, d, e, f, g, h, s0, s1; ++ uint64_t T1, *W512 = (uint64_t*)context->buffer; ++ int j; ++ ++ /* Initialize registers with the prev. intermediate value */ ++ a = context->state[0]; ++ b = context->state[1]; ++ c = context->state[2]; ++ d = context->state[3]; ++ e = context->state[4]; ++ f = context->state[5]; ++ g = context->state[6]; ++ h = context->state[7]; ++ ++ j = 0; ++ do { ++ ROUND512_0_TO_15(a,b,c,d,e,f,g,h); ++ ROUND512_0_TO_15(h,a,b,c,d,e,f,g); ++ ROUND512_0_TO_15(g,h,a,b,c,d,e,f); ++ ROUND512_0_TO_15(f,g,h,a,b,c,d,e); ++ ROUND512_0_TO_15(e,f,g,h,a,b,c,d); ++ ROUND512_0_TO_15(d,e,f,g,h,a,b,c); ++ ROUND512_0_TO_15(c,d,e,f,g,h,a,b); ++ ROUND512_0_TO_15(b,c,d,e,f,g,h,a); ++ } while (j < 16); ++ ++ /* Now for the remaining rounds up to 79: */ ++ do { ++ ROUND512(a,b,c,d,e,f,g,h); ++ ROUND512(h,a,b,c,d,e,f,g); ++ ROUND512(g,h,a,b,c,d,e,f); ++ ROUND512(f,g,h,a,b,c,d,e); ++ ROUND512(e,f,g,h,a,b,c,d); ++ ROUND512(d,e,f,g,h,a,b,c); ++ ROUND512(c,d,e,f,g,h,a,b); ++ ROUND512(b,c,d,e,f,g,h,a); ++ } while (j < 80); ++ ++ /* Compute the current intermediate hash value */ ++ context->state[0] += a; ++ context->state[1] += b; ++ context->state[2] += c; ++ context->state[3] += d; ++ context->state[4] += e; ++ context->state[5] += f; ++ context->state[6] += g; ++ context->state[7] += h; ++ ++ /* Clean up */ ++ a = b = c = d = e = f = g = h = T1 = 0; ++} ++ ++#else /* SHA2_UNROLL_TRANSFORM */ ++ ++static void SHA512_Transform(SHA512_CTX* context, const uint64_t* data) { ++ uint64_t a, b, c, d, e, f, g, h, s0, s1; ++ uint64_t T1, T2, *W512 = (uint64_t*)((void *)context->buffer); ++ int j; ++ ++ /* Initialize registers with the prev. intermediate value */ ++ a = context->state[0]; ++ b = context->state[1]; ++ c = context->state[2]; ++ d = context->state[3]; ++ e = context->state[4]; ++ f = context->state[5]; ++ g = context->state[6]; ++ h = context->state[7]; ++ ++ j = 0; ++ do { ++#if BYTE_ORDER == LITTLE_ENDIAN ++ /* Convert TO host byte order */ ++ REVERSE64(*data++, W512[j]); ++ /* Apply the SHA-512 compression function to update a..h */ ++ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; ++#else /* BYTE_ORDER == LITTLE_ENDIAN */ ++ /* Apply the SHA-512 compression function to update a..h with copy */ ++ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++); ++#endif /* BYTE_ORDER == LITTLE_ENDIAN */ ++ T2 = Sigma0_512(a) + Maj(a, b, c); ++ h = g; ++ g = f; ++ f = e; ++ e = d + T1; ++ d = c; ++ c = b; ++ b = a; ++ a = T1 + T2; ++ ++ j++; ++ } while (j < 16); ++ ++ do { ++ /* Part of the message block expansion: */ ++ s0 = W512[(j+1)&0x0f]; ++ s0 = sigma0_512(s0); ++ s1 = W512[(j+14)&0x0f]; ++ s1 = sigma1_512(s1); ++ ++ /* Apply the SHA-512 compression function to update a..h */ ++ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + ++ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); ++ T2 = Sigma0_512(a) + Maj(a, b, c); ++ h = g; ++ g = f; ++ f = e; ++ e = d + T1; ++ d = c; ++ c = b; ++ b = a; ++ a = T1 + T2; ++ ++ j++; ++ } while (j < 80); ++ ++ /* Compute the current intermediate hash value */ ++ context->state[0] += a; ++ context->state[1] += b; ++ context->state[2] += c; ++ context->state[3] += d; ++ context->state[4] += e; ++ context->state[5] += f; ++ context->state[6] += g; ++ context->state[7] += h; ++ ++ /* Clean up */ ++ a = b = c = d = e = f = g = h = T1 = T2 = 0; ++} ++ ++#endif /* SHA2_UNROLL_TRANSFORM */ ++ ++static void SHA512_Update(SHA512_CTX* context, const uint8_t *data, size_t len) { ++ unsigned int freespace, usedspace; ++ ++ if (len == 0) { ++ /* Calling with no data is valid - we do nothing */ ++ return; ++ } ++ ++ /* Sanity check: */ ++ assert(context != (SHA512_CTX*)0 && data != (uint8_t*)0); ++ ++ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; ++ if (usedspace > 0) { ++ /* Calculate how much free space is available in the buffer */ ++ freespace = SHA512_BLOCK_LENGTH - usedspace; ++ ++ if (len >= freespace) { ++ /* Fill the buffer completely and process it */ ++ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace); ++ ADDINC128(context->bitcount, freespace << 3); ++ len -= freespace; ++ data += freespace; ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ SHA512_Transform(context, (uint64_t*)((void *)context->buffer)); ++#else ++ SHA512_Transform(context, (uint64_t*)context->buffer); ++#endif ++ } else { ++ /* The buffer is not yet full */ ++ MEMCPY_BCOPY(&context->buffer[usedspace], data, len); ++ ADDINC128(context->bitcount, len << 3); ++ /* Clean up: */ ++ usedspace = freespace = 0; ++ return; ++ } ++ } ++ while (len >= SHA512_BLOCK_LENGTH) { ++ /* Process as many complete blocks as we can */ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ SHA512_Transform(context, (uint64_t*)((void *)context->buffer)); ++#else ++ SHA512_Transform(context, (uint64_t*)context->buffer); ++#endif ++ ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); ++ len -= SHA512_BLOCK_LENGTH; ++ data += SHA512_BLOCK_LENGTH; ++ } ++ if (len > 0) { ++ /* There's left-overs, so save 'em */ ++ MEMCPY_BCOPY(context->buffer, data, len); ++ ADDINC128(context->bitcount, len << 3); ++ } ++ /* Clean up: */ ++ usedspace = freespace = 0; ++} ++ ++static void SHA512_Last(SHA512_CTX* context) { ++ unsigned int usedspace; ++ ++ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; ++#if BYTE_ORDER == LITTLE_ENDIAN ++ /* Convert FROM host byte order */ ++ REVERSE64(context->bitcount[0],context->bitcount[0]); ++ REVERSE64(context->bitcount[1],context->bitcount[1]); ++#endif ++ if (usedspace > 0) { ++ /* Begin padding with a 1 bit: */ ++ context->buffer[usedspace++] = 0x80; ++ ++ if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) { ++ /* Set-up for the last transform: */ ++ MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace); ++ } else { ++ if (usedspace < SHA512_BLOCK_LENGTH) { ++ MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace); ++ } ++ /* Do second-to-last transform: */ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ SHA512_Transform(context, (uint64_t*)((void *)context->buffer)); ++#else ++ SHA512_Transform(context, (uint64_t*)context->buffer); ++#endif ++ ++ ++ /* And set-up for the last transform: */ ++ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2); ++ } ++ } else { ++ /* Prepare for final transform: */ ++ MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH); ++ ++ /* Begin padding with a 1 bit: */ ++ *context->buffer = 0x80; ++ } ++ /* Store the length of input data (in bits): */ ++ //*(uint64_t*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; // aliasing violation warning ++ //*(uint64_t*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0]; ++ context->buffer64[SHA512_SHORT_BLOCK_LENGTH / sizeof(uint64_t)] = context->bitcount[1]; ++ context->buffer64[SHA512_SHORT_BLOCK_LENGTH / sizeof(uint64_t) + 1] = context->bitcount[0]; ++ ++ /* Final transform: */ ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ SHA512_Transform(context, (uint64_t*)((void *)context->buffer)); ++#else ++ SHA512_Transform(context, (uint64_t*)context->buffer); ++#endif ++ ++} ++ ++static void SHA512_Final(uint8_t digest[], SHA512_CTX* context) { ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ uint64_t *d ; ++#else ++ uint64_t *d = (uint64_t*)digest; ++ ++#endif ++ ++ /* Sanity check: */ ++ assert(context != (SHA512_CTX*)0); ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ d = malloc(sizeof(uint64_t)*8); /* why 8 ? see below for loop */ ++ assert(d); ++#endif ++ ++ /* If no digest buffer is passed, we don't bother doing this: */ ++ if (digest != (uint8_t*)0) { ++ SHA512_Last(context); ++ ++ /* Save the hash data for output: */ ++#if BYTE_ORDER == LITTLE_ENDIAN ++ { ++ /* Convert TO host byte order */ ++ int j; ++ for (j = 0; j < 8; j++) { ++ REVERSE64(context->state[j],context->state[j]); ++ *d++ = context->state[j]; ++ } ++ } ++#else ++ MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH); ++#endif ++ } ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ memcpy(digest,d,SHA512_DIGEST_LENGTH); ++ free(d); ++#endif ++ ++ /* Zero out state data */ ++ MEMSET_BZERO(context, sizeof(*context)); ++} ++ ++/* ++static char *SHA512_End(SHA512_CTX* context, char buffer[]) { ++ uint8_t digest[SHA512_DIGEST_LENGTH], *d = digest; ++ int i; ++ ++ / * Sanity check: * / ++ assert(context != (SHA512_CTX*)0); ++ ++ if (buffer != (char*)0) { ++ SHA512_Final(digest, context); ++ ++ for (i = 0; i < SHA512_DIGEST_LENGTH; i++) { ++ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; ++ *buffer++ = sha2_hex_digits[*d & 0x0f]; ++ d++; ++ } ++ *buffer = (char)0; ++ } else { ++ MEMSET_BZERO(context, sizeof(context)); ++ } ++ MEMSET_BZERO(digest, SHA512_DIGEST_LENGTH); ++ return buffer; ++} ++ ++static char* SHA512_Data(const uint8_t* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) { ++ SHA512_CTX context; ++ ++ SHA512_Init(&context); ++ SHA512_Update(&context, data, len); ++ return SHA512_End(&context, digest); ++} ++*/ ++ ++/*** SHA-384: *********************************************************/ ++static void SHA384_Init(SHA384_CTX* context) { ++ if (context == (SHA384_CTX*)0) { ++ return; ++ } ++ MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH); ++ MEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH); ++ context->bitcount[0] = context->bitcount[1] = 0; ++} ++ ++static void SHA384_Update(SHA384_CTX* context, const uint8_t* data, size_t len) { ++ SHA512_Update((SHA512_CTX*)context, data, len); ++} ++ ++static void SHA384_Final(uint8_t digest[], SHA384_CTX* context) { ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ uint64_t *d; ++#else ++ uint64_t *d = (uint64_t*)digest; ++#endif ++ ++ ++ ++ /* Sanity check: */ ++ assert(context != (SHA384_CTX*)0); ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ d = malloc(sizeof(uint64_t)*6); /* why 6 ? see below for loop */ ++ assert(d); ++#endif ++ ++ /* If no digest buffer is passed, we don't bother doing this: */ ++ if (digest != (uint8_t*)0) { ++ SHA512_Last((SHA512_CTX*)context); ++ ++ /* Save the hash data for output: */ ++#if BYTE_ORDER == LITTLE_ENDIAN ++ { ++ /* Convert TO host byte order */ ++ int j; ++ for (j = 0; j < 6; j++) { ++ REVERSE64(context->state[j],context->state[j]); ++ *d++ = context->state[j]; ++ } ++ } ++#else ++ MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH); ++#endif ++ } ++#if defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __ARM_ARCH ||defined __aarch64__ ++ memcpy(digest,d,SHA384_DIGEST_LENGTH); ++ free(d); ++#endif ++ /* Zero out state data */ ++ MEMSET_BZERO(context, sizeof(*context)); ++} ++ ++/* ++static char *SHA384_End(SHA384_CTX* context, char buffer[]) { ++ uint8_t digest[SHA384_DIGEST_LENGTH], *d = digest; ++ int i; ++ ++ / * Sanity check: * / ++ assert(context != (SHA384_CTX*)0); ++ ++ if (buffer != (char*)0) { ++ SHA384_Final(digest, context); ++ ++ for (i = 0; i < SHA384_DIGEST_LENGTH; i++) { ++ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; ++ *buffer++ = sha2_hex_digits[*d & 0x0f]; ++ d++; ++ } ++ *buffer = (char)0; ++ } else { ++ MEMSET_BZERO(context, sizeof(context)); ++ } ++ MEMSET_BZERO(digest, SHA384_DIGEST_LENGTH); ++ return buffer; ++} ++ ++static char* SHA384_Data(const uint8_t* data, size_t len, char digest[SHA384_DIGEST_STRING_LENGTH]) { ++ SHA384_CTX context; ++ ++ SHA384_Init(&context); ++ SHA384_Update(&context, data, len); ++ return SHA384_End(&context, digest); ++} ++*/ ++ ++/* end of sha2.c */ ++ ++void sha256_init (sha256_state *state) ++{ ++ SHA256_Init(state); ++} ++ ++void sha384_init (sha384_state *state) ++{ ++ SHA384_Init(state); ++} ++ ++void sha512_init (sha512_state *state) ++{ ++ SHA512_Init(state); ++} ++ ++ ++void sha256_add (sha256_state *state, const void *data, size_t size) ++{ ++ SHA256_Update(state, (const uint8_t *)data, size); ++} ++ ++void sha384_add (sha384_state *state, const void *data, size_t size) ++{ ++ SHA384_Update(state, (const uint8_t *)data, size); ++} ++ ++void sha512_add (sha512_state *state, const void *data, size_t size) ++{ ++ SHA512_Update(state, (const uint8_t *)data, size); ++} ++ ++ ++void sha256_put (sha256_state *state, uint8_t digest[SHA256_DIGEST_LENGTH]) ++{ ++ SHA256_Final(digest, state); ++} ++ ++void sha384_put (sha384_state *state, uint8_t digest[SHA384_DIGEST_LENGTH]) ++{ ++ SHA384_Final(digest, state); ++} ++ ++void sha512_put (sha384_state *state, uint8_t digest[SHA512_DIGEST_LENGTH]) ++{ ++ SHA512_Final(digest, state); ++} ++ ++ ++void sha256 (const void *data, size_t size, uint8_t digest[SHA256_DIGEST_LENGTH]) ++{ ++ sha256_state state; ++ SHA256_Init(&state); ++ SHA256_Update(&state, (const uint8_t *)data, size); ++ SHA256_Final(digest, &state); ++} ++ ++void sha384 (const void *data, size_t size, uint8_t digest[SHA384_DIGEST_LENGTH]) ++{ ++ sha384_state state; ++ SHA384_Init(&state); ++ SHA384_Update(&state, (const uint8_t *)data, size); ++ SHA384_Final(digest, &state); ++} ++ ++void sha512 (const void *data, size_t size, uint8_t digest[SHA512_DIGEST_LENGTH]) ++{ ++ sha512_state state; ++ SHA512_Init(&state); ++ SHA512_Update(&state, (const uint8_t *)data, size); ++ SHA512_Final(digest, &state); ++} ++ ++static sha256_state sha256state; ++static sha384_state sha384state; ++static sha512_state sha512state; ++ ++ ++void sha256init (void) ++{ ++ SHA256_Init(&sha256state); ++} ++ ++void sha384init (void) ++{ ++ SHA384_Init(&sha384state); ++} ++ ++void sha512init (void) ++{ ++ SHA512_Init(&sha512state); ++} ++ ++ ++void sha256add (const void *data, size_t size) ++{ ++ SHA256_Update(&sha256state, (const uint8_t *)data, size); ++} ++ ++void sha384add (const void *data, size_t size) ++{ ++ SHA384_Update(&sha384state, (const uint8_t *)data, size); ++} ++ ++void sha512add (const void *data, size_t size) ++{ ++ SHA512_Update(&sha512state, (const uint8_t *)data, size); ++} ++ ++ ++void sha256put (uint8_t digest[SHA256_DIGEST_LENGTH]) ++{ ++ SHA256_Final(digest, &sha256state); ++} ++ ++void sha384put (uint8_t digest[SHA384_DIGEST_LENGTH]) ++{ ++ SHA384_Final(digest, &sha384state); ++} ++ ++void sha512put (uint8_t digest[SHA512_DIGEST_LENGTH]) ++{ ++ SHA512_Final(digest, &sha512state); ++} +diff --git a/texk/web2c/luatexdir/luapplib/util/utilsha.h b/texk/web2c/luatexdir/luapplib/util/utilsha.h +new file mode 100644 +index 000000000..53f354581 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/util/utilsha.h +@@ -0,0 +1,134 @@ ++/* sha2 implementation by Aaron D. Gifford (http://www.aarongifford.com) */ ++ ++#ifndef UTIL_SHA_H ++#define UTIL_SHA_H ++ ++#include ++#include ++#include "utildecl.h" ++ ++/* begin of sha2.h */ ++ ++/* ++ * FILE: sha2.h ++ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/ ++ * ++ * Copyright (c) 2000-2001, Aaron D. Gifford ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ ++ */ ++ ++/*** SHA-256/384/512 Various Length Definitions ***********************/ ++ ++#define SHA256_BLOCK_LENGTH 64 ++#define SHA256_DIGEST_LENGTH 32 ++#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) ++#define SHA384_BLOCK_LENGTH 128 ++#define SHA384_DIGEST_LENGTH 48 ++#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) ++#define SHA512_BLOCK_LENGTH 128 ++#define SHA512_DIGEST_LENGTH 64 ++#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) ++ ++typedef struct _SHA256_CTX { ++ uint32_t state[8]; ++ uint64_t bitcount; ++ union { ++ uint8_t buffer[SHA256_BLOCK_LENGTH]; ++ uint64_t buffer64[SHA256_BLOCK_LENGTH / sizeof(uint64_t)]; // to avoid warnings about violating aliasing rules ++ }; ++} SHA256_CTX; ++ ++typedef struct _SHA512_CTX { ++ uint64_t state[8]; ++ uint64_t bitcount[2]; ++ union { ++ uint8_t buffer[SHA512_BLOCK_LENGTH]; ++ uint64_t buffer64[SHA512_BLOCK_LENGTH / sizeof(uint64_t)]; ++ }; ++} SHA512_CTX; ++ ++typedef SHA512_CTX SHA384_CTX; ++ ++/*** SHA-256/384/512 Function Prototypes ******************************/ ++ ++/* ++void SHA256_Init(SHA256_CTX *); ++void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t); ++void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*); ++char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); ++char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); ++ ++void SHA384_Init(SHA384_CTX*); ++void SHA384_Update(SHA384_CTX*, const u_int8_t*, size_t); ++void SHA384_Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*); ++char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]); ++char* SHA384_Data(const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]); ++ ++void SHA512_Init(SHA512_CTX*); ++void SHA512_Update(SHA512_CTX*, const u_int8_t*, size_t); ++void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); ++char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); ++char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); ++*/ ++ ++/* end of sha2.h */ ++ ++#define sha256_state SHA256_CTX ++#define sha384_state SHA384_CTX ++#define sha512_state SHA512_CTX ++ ++UTILAPI void sha256_init (sha256_state *state); ++UTILAPI void sha384_init (sha384_state *state); ++UTILAPI void sha512_init (sha512_state *state); ++ ++UTILAPI void sha256_add (sha256_state *state, const void *data, size_t size); ++UTILAPI void sha384_add (sha384_state *state, const void *data, size_t size); ++UTILAPI void sha512_add (sha512_state *state, const void *data, size_t size); ++ ++UTILAPI void sha256_put (sha256_state *state, uint8_t digest[SHA256_DIGEST_LENGTH]); ++UTILAPI void sha384_put (sha384_state *state, uint8_t digest[SHA384_DIGEST_LENGTH]); ++UTILAPI void sha512_put (sha512_state *state, uint8_t digest[SHA512_DIGEST_LENGTH]); ++ ++UTILAPI void sha256 (const void *data, size_t size, uint8_t digest[SHA256_DIGEST_LENGTH]); ++UTILAPI void sha384 (const void *data, size_t size, uint8_t digest[SHA384_DIGEST_LENGTH]); ++UTILAPI void sha512 (const void *data, size_t size, uint8_t digest[SHA512_DIGEST_LENGTH]); ++ ++UTILAPI void sha256init (void); ++UTILAPI void sha384init (void); ++UTILAPI void sha512init (void); ++ ++UTILAPI void sha256add (const void *data, size_t size); ++UTILAPI void sha384add (const void *data, size_t size); ++UTILAPI void sha512add (const void *data, size_t size); ++ ++UTILAPI void sha256put (uint8_t digest[SHA256_DIGEST_LENGTH]); ++UTILAPI void sha384put (uint8_t digest[SHA384_DIGEST_LENGTH]); ++UTILAPI void sha512put (uint8_t digest[SHA512_DIGEST_LENGTH]); ++ ++#endif +\ No newline at end of file +diff --git a/texk/web2c/luatexdir/luapplib/zlib/zconf.h b/texk/web2c/luatexdir/luapplib/zlib/zconf.h +new file mode 100644 +index 000000000..02ce56c43 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/zlib/zconf.h +@@ -0,0 +1,428 @@ ++/* zconf.h -- configuration of the zlib compression library ++ * Copyright (C) 1995-2010 Jean-loup Gailly. ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* @(#) $Id$ */ ++ ++#ifndef ZCONF_H ++#define ZCONF_H ++ ++/* ++ * If you *really* need a unique prefix for all types and library functions, ++ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. ++ * Even better than compiling with -DZ_PREFIX would be to use configure to set ++ * this permanently in zconf.h using "./configure --zprefix". ++ */ ++#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ ++ ++/* all linked symbols */ ++# define _dist_code z__dist_code ++# define _length_code z__length_code ++# define _tr_align z__tr_align ++# define _tr_flush_block z__tr_flush_block ++# define _tr_init z__tr_init ++# define _tr_stored_block z__tr_stored_block ++# define _tr_tally z__tr_tally ++# define adler32 z_adler32 ++# define adler32_combine z_adler32_combine ++# define adler32_combine64 z_adler32_combine64 ++# define compress z_compress ++# define compress2 z_compress2 ++# define compressBound z_compressBound ++# define crc32 z_crc32 ++# define crc32_combine z_crc32_combine ++# define crc32_combine64 z_crc32_combine64 ++# define deflate z_deflate ++# define deflateBound z_deflateBound ++# define deflateCopy z_deflateCopy ++# define deflateEnd z_deflateEnd ++# define deflateInit2_ z_deflateInit2_ ++# define deflateInit_ z_deflateInit_ ++# define deflateParams z_deflateParams ++# define deflatePrime z_deflatePrime ++# define deflateReset z_deflateReset ++# define deflateSetDictionary z_deflateSetDictionary ++# define deflateSetHeader z_deflateSetHeader ++# define deflateTune z_deflateTune ++# define deflate_copyright z_deflate_copyright ++# define get_crc_table z_get_crc_table ++# define gz_error z_gz_error ++# define gz_intmax z_gz_intmax ++# define gz_strwinerror z_gz_strwinerror ++# define gzbuffer z_gzbuffer ++# define gzclearerr z_gzclearerr ++# define gzclose z_gzclose ++# define gzclose_r z_gzclose_r ++# define gzclose_w z_gzclose_w ++# define gzdirect z_gzdirect ++# define gzdopen z_gzdopen ++# define gzeof z_gzeof ++# define gzerror z_gzerror ++# define gzflush z_gzflush ++# define gzgetc z_gzgetc ++# define gzgets z_gzgets ++# define gzoffset z_gzoffset ++# define gzoffset64 z_gzoffset64 ++# define gzopen z_gzopen ++# define gzopen64 z_gzopen64 ++# define gzprintf z_gzprintf ++# define gzputc z_gzputc ++# define gzputs z_gzputs ++# define gzread z_gzread ++# define gzrewind z_gzrewind ++# define gzseek z_gzseek ++# define gzseek64 z_gzseek64 ++# define gzsetparams z_gzsetparams ++# define gztell z_gztell ++# define gztell64 z_gztell64 ++# define gzungetc z_gzungetc ++# define gzwrite z_gzwrite ++# define inflate z_inflate ++# define inflateBack z_inflateBack ++# define inflateBackEnd z_inflateBackEnd ++# define inflateBackInit_ z_inflateBackInit_ ++# define inflateCopy z_inflateCopy ++# define inflateEnd z_inflateEnd ++# define inflateGetHeader z_inflateGetHeader ++# define inflateInit2_ z_inflateInit2_ ++# define inflateInit_ z_inflateInit_ ++# define inflateMark z_inflateMark ++# define inflatePrime z_inflatePrime ++# define inflateReset z_inflateReset ++# define inflateReset2 z_inflateReset2 ++# define inflateSetDictionary z_inflateSetDictionary ++# define inflateSync z_inflateSync ++# define inflateSyncPoint z_inflateSyncPoint ++# define inflateUndermine z_inflateUndermine ++# define inflate_copyright z_inflate_copyright ++# define inflate_fast z_inflate_fast ++# define inflate_table z_inflate_table ++# define uncompress z_uncompress ++# define zError z_zError ++# define zcalloc z_zcalloc ++# define zcfree z_zcfree ++# define zlibCompileFlags z_zlibCompileFlags ++# define zlibVersion z_zlibVersion ++ ++/* all zlib typedefs in zlib.h and zconf.h */ ++# define Byte z_Byte ++# define Bytef z_Bytef ++# define alloc_func z_alloc_func ++# define charf z_charf ++# define free_func z_free_func ++# define gzFile z_gzFile ++# define gz_header z_gz_header ++# define gz_headerp z_gz_headerp ++# define in_func z_in_func ++# define intf z_intf ++# define out_func z_out_func ++# define uInt z_uInt ++# define uIntf z_uIntf ++# define uLong z_uLong ++# define uLongf z_uLongf ++# define voidp z_voidp ++# define voidpc z_voidpc ++# define voidpf z_voidpf ++ ++/* all zlib structs in zlib.h and zconf.h */ ++# define gz_header_s z_gz_header_s ++# define internal_state z_internal_state ++ ++#endif ++ ++#if defined(__MSDOS__) && !defined(MSDOS) ++# define MSDOS ++#endif ++#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) ++# define OS2 ++#endif ++#if defined(_WINDOWS) && !defined(WINDOWS) ++# define WINDOWS ++#endif ++#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) ++# ifndef WIN32 ++# define WIN32 ++# endif ++#endif ++#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) ++# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) ++# ifndef SYS16BIT ++# define SYS16BIT ++# endif ++# endif ++#endif ++ ++/* ++ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more ++ * than 64k bytes at a time (needed on systems with 16-bit int). ++ */ ++#ifdef SYS16BIT ++# define MAXSEG_64K ++#endif ++#ifdef MSDOS ++# define UNALIGNED_OK ++#endif ++ ++#ifdef __STDC_VERSION__ ++# ifndef STDC ++# define STDC ++# endif ++# if __STDC_VERSION__ >= 199901L ++# ifndef STDC99 ++# define STDC99 ++# endif ++# endif ++#endif ++#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) ++# define STDC ++#endif ++#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) ++# define STDC ++#endif ++#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) ++# define STDC ++#endif ++#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) ++# define STDC ++#endif ++ ++#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ ++# define STDC ++#endif ++ ++#ifndef STDC ++# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ ++# define const /* note: need a more gentle solution here */ ++# endif ++#endif ++ ++/* Some Mac compilers merge all .h files incorrectly: */ ++#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) ++# define NO_DUMMY_DECL ++#endif ++ ++/* Maximum value for memLevel in deflateInit2 */ ++#ifndef MAX_MEM_LEVEL ++# ifdef MAXSEG_64K ++# define MAX_MEM_LEVEL 8 ++# else ++# define MAX_MEM_LEVEL 9 ++# endif ++#endif ++ ++/* Maximum value for windowBits in deflateInit2 and inflateInit2. ++ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files ++ * created by gzip. (Files created by minigzip can still be extracted by ++ * gzip.) ++ */ ++#ifndef MAX_WBITS ++# define MAX_WBITS 15 /* 32K LZ77 window */ ++#endif ++ ++/* The memory requirements for deflate are (in bytes): ++ (1 << (windowBits+2)) + (1 << (memLevel+9)) ++ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) ++ plus a few kilobytes for small objects. For example, if you want to reduce ++ the default memory requirements from 256K to 128K, compile with ++ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" ++ Of course this will generally degrade compression (there's no free lunch). ++ ++ The memory requirements for inflate are (in bytes) 1 << windowBits ++ that is, 32K for windowBits=15 (default value) plus a few kilobytes ++ for small objects. ++*/ ++ ++ /* Type declarations */ ++ ++#ifndef OF /* function prototypes */ ++# ifdef STDC ++# define OF(args) args ++# else ++# define OF(args) () ++# endif ++#endif ++ ++/* The following definitions for FAR are needed only for MSDOS mixed ++ * model programming (small or medium model with some far allocations). ++ * This was tested only with MSC; for other MSDOS compilers you may have ++ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, ++ * just define FAR to be empty. ++ */ ++#ifdef SYS16BIT ++# if defined(M_I86SM) || defined(M_I86MM) ++ /* MSC small or medium model */ ++# define SMALL_MEDIUM ++# ifdef _MSC_VER ++# define FAR _far ++# else ++# define FAR far ++# endif ++# endif ++# if (defined(__SMALL__) || defined(__MEDIUM__)) ++ /* Turbo C small or medium model */ ++# define SMALL_MEDIUM ++# ifdef __BORLANDC__ ++# define FAR _far ++# else ++# define FAR far ++# endif ++# endif ++#endif ++ ++#if defined(WINDOWS) || defined(WIN32) ++ /* If building or using zlib as a DLL, define ZLIB_DLL. ++ * This is not mandatory, but it offers a little performance increase. ++ */ ++# ifdef ZLIB_DLL ++# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) ++# ifdef ZLIB_INTERNAL ++# define ZEXTERN extern __declspec(dllexport) ++# else ++# define ZEXTERN extern __declspec(dllimport) ++# endif ++# endif ++# endif /* ZLIB_DLL */ ++ /* If building or using zlib with the WINAPI/WINAPIV calling convention, ++ * define ZLIB_WINAPI. ++ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. ++ */ ++# ifdef ZLIB_WINAPI ++# ifdef FAR ++# undef FAR ++# endif ++# include ++ /* No need for _export, use ZLIB.DEF instead. */ ++ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ ++# define ZEXPORT WINAPI ++# ifdef WIN32 ++# define ZEXPORTVA WINAPIV ++# else ++# define ZEXPORTVA FAR CDECL ++# endif ++# endif ++#endif ++ ++#if defined (__BEOS__) ++# ifdef ZLIB_DLL ++# ifdef ZLIB_INTERNAL ++# define ZEXPORT __declspec(dllexport) ++# define ZEXPORTVA __declspec(dllexport) ++# else ++# define ZEXPORT __declspec(dllimport) ++# define ZEXPORTVA __declspec(dllimport) ++# endif ++# endif ++#endif ++ ++#ifndef ZEXTERN ++# define ZEXTERN extern ++#endif ++#ifndef ZEXPORT ++# define ZEXPORT ++#endif ++#ifndef ZEXPORTVA ++# define ZEXPORTVA ++#endif ++ ++#ifndef FAR ++# define FAR ++#endif ++ ++#if !defined(__MACTYPES__) ++typedef unsigned char Byte; /* 8 bits */ ++#endif ++typedef unsigned int uInt; /* 16 bits or more */ ++typedef unsigned long uLong; /* 32 bits or more */ ++ ++#ifdef SMALL_MEDIUM ++ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ ++# define Bytef Byte FAR ++#else ++ typedef Byte FAR Bytef; ++#endif ++typedef char FAR charf; ++typedef int FAR intf; ++typedef uInt FAR uIntf; ++typedef uLong FAR uLongf; ++ ++#ifdef STDC ++ typedef void const *voidpc; ++ typedef void FAR *voidpf; ++ typedef void *voidp; ++#else ++ typedef Byte const *voidpc; ++ typedef Byte FAR *voidpf; ++ typedef Byte *voidp; ++#endif ++ ++#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ ++# define Z_HAVE_UNISTD_H ++#endif ++ ++#ifdef STDC ++# include /* for off_t */ ++#endif ++ ++/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and ++ * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even ++ * though the former does not conform to the LFS document), but considering ++ * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as ++ * equivalently requesting no 64-bit operations ++ */ ++#if -_LARGEFILE64_SOURCE - -1 == 1 ++# undef _LARGEFILE64_SOURCE ++#endif ++ ++#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) ++# include /* for SEEK_* and off_t */ ++# ifdef VMS ++# include /* for off_t */ ++# endif ++# ifndef z_off_t ++# define z_off_t off_t ++# endif ++#endif ++ ++#ifndef SEEK_SET ++# define SEEK_SET 0 /* Seek from beginning of file. */ ++# define SEEK_CUR 1 /* Seek from current position. */ ++# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ ++#endif ++ ++#ifndef z_off_t ++# define z_off_t long ++#endif ++ ++#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 ++# define z_off64_t off64_t ++#else ++# define z_off64_t z_off_t ++#endif ++ ++#if defined(__OS400__) ++# define NO_vsnprintf ++#endif ++ ++#if defined(__MVS__) ++# define NO_vsnprintf ++#endif ++ ++/* MVS linker does not support external names larger than 8 bytes */ ++#if defined(__MVS__) ++ #pragma map(deflateInit_,"DEIN") ++ #pragma map(deflateInit2_,"DEIN2") ++ #pragma map(deflateEnd,"DEEND") ++ #pragma map(deflateBound,"DEBND") ++ #pragma map(inflateInit_,"ININ") ++ #pragma map(inflateInit2_,"ININ2") ++ #pragma map(inflateEnd,"INEND") ++ #pragma map(inflateSync,"INSY") ++ #pragma map(inflateSetDictionary,"INSEDI") ++ #pragma map(compressBound,"CMBND") ++ #pragma map(inflate_table,"INTABL") ++ #pragma map(inflate_fast,"INFA") ++ #pragma map(inflate_copyright,"INCOPY") ++#endif ++ ++#endif /* ZCONF_H */ +diff --git a/texk/web2c/luatexdir/luapplib/zlib/zlib.h b/texk/web2c/luatexdir/luapplib/zlib/zlib.h +new file mode 100644 +index 000000000..bfbba83e8 +--- /dev/null ++++ b/texk/web2c/luatexdir/luapplib/zlib/zlib.h +@@ -0,0 +1,1613 @@ ++/* zlib.h -- interface of the 'zlib' general purpose compression library ++ version 1.2.5, April 19th, 2010 ++ ++ Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++ ++ Jean-loup Gailly Mark Adler ++ jloup@gzip.org madler@alumni.caltech.edu ++ ++ ++ The data format used by the zlib library is described by RFCs (Request for ++ Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt ++ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). ++*/ ++ ++#ifndef ZLIB_H ++#define ZLIB_H ++ ++#include "zconf.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define ZLIB_VERSION "1.2.5" ++#define ZLIB_VERNUM 0x1250 ++#define ZLIB_VER_MAJOR 1 ++#define ZLIB_VER_MINOR 2 ++#define ZLIB_VER_REVISION 5 ++#define ZLIB_VER_SUBREVISION 0 ++ ++/* ++ The 'zlib' compression library provides in-memory compression and ++ decompression functions, including integrity checks of the uncompressed data. ++ This version of the library supports only one compression method (deflation) ++ but other algorithms will be added later and will have the same stream ++ interface. ++ ++ Compression can be done in a single step if the buffers are large enough, ++ or can be done by repeated calls of the compression function. In the latter ++ case, the application must provide more input and/or consume the output ++ (providing more output space) before each call. ++ ++ The compressed data format used by default by the in-memory functions is ++ the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped ++ around a deflate stream, which is itself documented in RFC 1951. ++ ++ The library also supports reading and writing files in gzip (.gz) format ++ with an interface similar to that of stdio using the functions that start ++ with "gz". The gzip format is different from the zlib format. gzip is a ++ gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. ++ ++ This library can optionally read and write gzip streams in memory as well. ++ ++ The zlib format was designed to be compact and fast for use in memory ++ and on communications channels. The gzip format was designed for single- ++ file compression on file systems, has a larger header than zlib to maintain ++ directory information, and uses a different, slower check method than zlib. ++ ++ The library does not install any signal handler. The decoder checks ++ the consistency of the compressed data, so the library should never crash ++ even in case of corrupted input. ++*/ ++ ++typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); ++typedef void (*free_func) OF((voidpf opaque, voidpf address)); ++ ++struct internal_state; ++ ++typedef struct z_stream_s { ++ Bytef *next_in; /* next input byte */ ++ uInt avail_in; /* number of bytes available at next_in */ ++ uLong total_in; /* total nb of input bytes read so far */ ++ ++ Bytef *next_out; /* next output byte should be put there */ ++ uInt avail_out; /* remaining free space at next_out */ ++ uLong total_out; /* total nb of bytes output so far */ ++ ++ char *msg; /* last error message, NULL if no error */ ++ struct internal_state FAR *state; /* not visible by applications */ ++ ++ alloc_func zalloc; /* used to allocate the internal state */ ++ free_func zfree; /* used to free the internal state */ ++ voidpf opaque; /* private data object passed to zalloc and zfree */ ++ ++ int data_type; /* best guess about the data type: binary or text */ ++ uLong adler; /* adler32 value of the uncompressed data */ ++ uLong reserved; /* reserved for future use */ ++} z_stream; ++ ++typedef z_stream FAR *z_streamp; ++ ++/* ++ gzip header information passed to and from zlib routines. See RFC 1952 ++ for more details on the meanings of these fields. ++*/ ++typedef struct gz_header_s { ++ int text; /* true if compressed data believed to be text */ ++ uLong time; /* modification time */ ++ int xflags; /* extra flags (not used when writing a gzip file) */ ++ int os; /* operating system */ ++ Bytef *extra; /* pointer to extra field or Z_NULL if none */ ++ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ ++ uInt extra_max; /* space at extra (only when reading header) */ ++ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ ++ uInt name_max; /* space at name (only when reading header) */ ++ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ ++ uInt comm_max; /* space at comment (only when reading header) */ ++ int hcrc; /* true if there was or will be a header crc */ ++ int done; /* true when done reading gzip header (not used ++ when writing a gzip file) */ ++} gz_header; ++ ++typedef gz_header FAR *gz_headerp; ++ ++/* ++ The application must update next_in and avail_in when avail_in has dropped ++ to zero. It must update next_out and avail_out when avail_out has dropped ++ to zero. The application must initialize zalloc, zfree and opaque before ++ calling the init function. All other fields are set by the compression ++ library and must not be updated by the application. ++ ++ The opaque value provided by the application will be passed as the first ++ parameter for calls of zalloc and zfree. This can be useful for custom ++ memory management. The compression library attaches no meaning to the ++ opaque value. ++ ++ zalloc must return Z_NULL if there is not enough memory for the object. ++ If zlib is used in a multi-threaded application, zalloc and zfree must be ++ thread safe. ++ ++ On 16-bit systems, the functions zalloc and zfree must be able to allocate ++ exactly 65536 bytes, but will not be required to allocate more than this if ++ the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers ++ returned by zalloc for objects of exactly 65536 bytes *must* have their ++ offset normalized to zero. The default allocation function provided by this ++ library ensures this (see zutil.c). To reduce memory requirements and avoid ++ any allocation of 64K objects, at the expense of compression ratio, compile ++ the library with -DMAX_WBITS=14 (see zconf.h). ++ ++ The fields total_in and total_out can be used for statistics or progress ++ reports. After compression, total_in holds the total size of the ++ uncompressed data and may be saved for use in the decompressor (particularly ++ if the decompressor wants to decompress everything in a single step). ++*/ ++ ++ /* constants */ ++ ++#define Z_NO_FLUSH 0 ++#define Z_PARTIAL_FLUSH 1 ++#define Z_SYNC_FLUSH 2 ++#define Z_FULL_FLUSH 3 ++#define Z_FINISH 4 ++#define Z_BLOCK 5 ++#define Z_TREES 6 ++/* Allowed flush values; see deflate() and inflate() below for details */ ++ ++#define Z_OK 0 ++#define Z_STREAM_END 1 ++#define Z_NEED_DICT 2 ++#define Z_ERRNO (-1) ++#define Z_STREAM_ERROR (-2) ++#define Z_DATA_ERROR (-3) ++#define Z_MEM_ERROR (-4) ++#define Z_BUF_ERROR (-5) ++#define Z_VERSION_ERROR (-6) ++/* Return codes for the compression/decompression functions. Negative values ++ * are errors, positive values are used for special but normal events. ++ */ ++ ++#define Z_NO_COMPRESSION 0 ++#define Z_BEST_SPEED 1 ++#define Z_BEST_COMPRESSION 9 ++#define Z_DEFAULT_COMPRESSION (-1) ++/* compression levels */ ++ ++#define Z_FILTERED 1 ++#define Z_HUFFMAN_ONLY 2 ++#define Z_RLE 3 ++#define Z_FIXED 4 ++#define Z_DEFAULT_STRATEGY 0 ++/* compression strategy; see deflateInit2() below for details */ ++ ++#define Z_BINARY 0 ++#define Z_TEXT 1 ++#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ ++#define Z_UNKNOWN 2 ++/* Possible values of the data_type field (though see inflate()) */ ++ ++#define Z_DEFLATED 8 ++/* The deflate compression method (the only one supported in this version) */ ++ ++#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ ++ ++#define zlib_version zlibVersion() ++/* for compatibility with versions < 1.0.2 */ ++ ++ ++ /* basic functions */ ++ ++ZEXTERN const char * ZEXPORT zlibVersion OF((void)); ++/* The application can compare zlibVersion and ZLIB_VERSION for consistency. ++ If the first character differs, the library code actually used is not ++ compatible with the zlib.h header file used by the application. This check ++ is automatically made by deflateInit and inflateInit. ++ */ ++ ++/* ++ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); ++ ++ Initializes the internal stream state for compression. The fields ++ zalloc, zfree and opaque must be initialized before by the caller. If ++ zalloc and zfree are set to Z_NULL, deflateInit updates them to use default ++ allocation functions. ++ ++ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: ++ 1 gives best speed, 9 gives best compression, 0 gives no compression at all ++ (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION ++ requests a default compromise between speed and compression (currently ++ equivalent to level 6). ++ ++ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_STREAM_ERROR if level is not a valid compression level, or ++ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible ++ with the version assumed by the caller (ZLIB_VERSION). msg is set to null ++ if there is no error message. deflateInit does not perform any compression: ++ this will be done by deflate(). ++*/ ++ ++ ++ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); ++/* ++ deflate compresses as much data as possible, and stops when the input ++ buffer becomes empty or the output buffer becomes full. It may introduce ++ some output latency (reading input without producing any output) except when ++ forced to flush. ++ ++ The detailed semantics are as follows. deflate performs one or both of the ++ following actions: ++ ++ - Compress more input starting at next_in and update next_in and avail_in ++ accordingly. If not all input can be processed (because there is not ++ enough room in the output buffer), next_in and avail_in are updated and ++ processing will resume at this point for the next call of deflate(). ++ ++ - Provide more output starting at next_out and update next_out and avail_out ++ accordingly. This action is forced if the parameter flush is non zero. ++ Forcing flush frequently degrades the compression ratio, so this parameter ++ should be set only when necessary (in interactive applications). Some ++ output may be provided even if flush is not set. ++ ++ Before the call of deflate(), the application should ensure that at least ++ one of the actions is possible, by providing more input and/or consuming more ++ output, and updating avail_in or avail_out accordingly; avail_out should ++ never be zero before the call. The application can consume the compressed ++ output when it wants, for example when the output buffer is full (avail_out ++ == 0), or after each call of deflate(). If deflate returns Z_OK and with ++ zero avail_out, it must be called again after making room in the output ++ buffer because there might be more output pending. ++ ++ Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to ++ decide how much data to accumulate before producing output, in order to ++ maximize compression. ++ ++ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is ++ flushed to the output buffer and the output is aligned on a byte boundary, so ++ that the decompressor can get all input data available so far. (In ++ particular avail_in is zero after the call if enough output space has been ++ provided before the call.) Flushing may degrade compression for some ++ compression algorithms and so it should be used only when necessary. This ++ completes the current deflate block and follows it with an empty stored block ++ that is three bits plus filler bits to the next byte, followed by four bytes ++ (00 00 ff ff). ++ ++ If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the ++ output buffer, but the output is not aligned to a byte boundary. All of the ++ input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. ++ This completes the current deflate block and follows it with an empty fixed ++ codes block that is 10 bits long. This assures that enough bytes are output ++ in order for the decompressor to finish the block before the empty fixed code ++ block. ++ ++ If flush is set to Z_BLOCK, a deflate block is completed and emitted, as ++ for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to ++ seven bits of the current block are held to be written as the next byte after ++ the next deflate block is completed. In this case, the decompressor may not ++ be provided enough bits at this point in order to complete decompression of ++ the data provided so far to the compressor. It may need to wait for the next ++ block to be emitted. This is for advanced applications that need to control ++ the emission of deflate blocks. ++ ++ If flush is set to Z_FULL_FLUSH, all output is flushed as with ++ Z_SYNC_FLUSH, and the compression state is reset so that decompression can ++ restart from this point if previous compressed data has been damaged or if ++ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade ++ compression. ++ ++ If deflate returns with avail_out == 0, this function must be called again ++ with the same value of the flush parameter and more output space (updated ++ avail_out), until the flush is complete (deflate returns with non-zero ++ avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that ++ avail_out is greater than six to avoid repeated flush markers due to ++ avail_out == 0 on return. ++ ++ If the parameter flush is set to Z_FINISH, pending input is processed, ++ pending output is flushed and deflate returns with Z_STREAM_END if there was ++ enough output space; if deflate returns with Z_OK, this function must be ++ called again with Z_FINISH and more output space (updated avail_out) but no ++ more input data, until it returns with Z_STREAM_END or an error. After ++ deflate has returned Z_STREAM_END, the only possible operations on the stream ++ are deflateReset or deflateEnd. ++ ++ Z_FINISH can be used immediately after deflateInit if all the compression ++ is to be done in a single step. In this case, avail_out must be at least the ++ value returned by deflateBound (see below). If deflate does not return ++ Z_STREAM_END, then it must be called again as described above. ++ ++ deflate() sets strm->adler to the adler32 checksum of all input read ++ so far (that is, total_in bytes). ++ ++ deflate() may update strm->data_type if it can make a good guess about ++ the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered ++ binary. This field is only for information purposes and does not affect the ++ compression algorithm in any manner. ++ ++ deflate() returns Z_OK if some progress has been made (more input ++ processed or more output produced), Z_STREAM_END if all input has been ++ consumed and all output has been produced (only when flush is set to ++ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example ++ if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible ++ (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not ++ fatal, and deflate() can be called again with more input and more output ++ space to continue compressing. ++*/ ++ ++ ++ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); ++/* ++ All dynamically allocated data structures for this stream are freed. ++ This function discards any unprocessed input and does not flush any pending ++ output. ++ ++ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the ++ stream state was inconsistent, Z_DATA_ERROR if the stream was freed ++ prematurely (some input or output was discarded). In the error case, msg ++ may be set but then points to a static string (which must not be ++ deallocated). ++*/ ++ ++ ++/* ++ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); ++ ++ Initializes the internal stream state for decompression. The fields ++ next_in, avail_in, zalloc, zfree and opaque must be initialized before by ++ the caller. If next_in is not Z_NULL and avail_in is large enough (the ++ exact value depends on the compression method), inflateInit determines the ++ compression method from the zlib header and allocates all data structures ++ accordingly; otherwise the allocation will be deferred to the first call of ++ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to ++ use default allocation functions. ++ ++ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the ++ version assumed by the caller, or Z_STREAM_ERROR if the parameters are ++ invalid, such as a null pointer to the structure. msg is set to null if ++ there is no error message. inflateInit does not perform any decompression ++ apart from possibly reading the zlib header if present: actual decompression ++ will be done by inflate(). (So next_in and avail_in may be modified, but ++ next_out and avail_out are unused and unchanged.) The current implementation ++ of inflateInit() does not process any header information -- that is deferred ++ until inflate() is called. ++*/ ++ ++ ++ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); ++/* ++ inflate decompresses as much data as possible, and stops when the input ++ buffer becomes empty or the output buffer becomes full. It may introduce ++ some output latency (reading input without producing any output) except when ++ forced to flush. ++ ++ The detailed semantics are as follows. inflate performs one or both of the ++ following actions: ++ ++ - Decompress more input starting at next_in and update next_in and avail_in ++ accordingly. If not all input can be processed (because there is not ++ enough room in the output buffer), next_in is updated and processing will ++ resume at this point for the next call of inflate(). ++ ++ - Provide more output starting at next_out and update next_out and avail_out ++ accordingly. inflate() provides as much output as possible, until there is ++ no more input data or no more space in the output buffer (see below about ++ the flush parameter). ++ ++ Before the call of inflate(), the application should ensure that at least ++ one of the actions is possible, by providing more input and/or consuming more ++ output, and updating the next_* and avail_* values accordingly. The ++ application can consume the uncompressed output when it wants, for example ++ when the output buffer is full (avail_out == 0), or after each call of ++ inflate(). If inflate returns Z_OK and with zero avail_out, it must be ++ called again after making room in the output buffer because there might be ++ more output pending. ++ ++ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, ++ Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much ++ output as possible to the output buffer. Z_BLOCK requests that inflate() ++ stop if and when it gets to the next deflate block boundary. When decoding ++ the zlib or gzip format, this will cause inflate() to return immediately ++ after the header and before the first block. When doing a raw inflate, ++ inflate() will go ahead and process the first block, and will return when it ++ gets to the end of that block, or when it runs out of data. ++ ++ The Z_BLOCK option assists in appending to or combining deflate streams. ++ Also to assist in this, on return inflate() will set strm->data_type to the ++ number of unused bits in the last byte taken from strm->next_in, plus 64 if ++ inflate() is currently decoding the last block in the deflate stream, plus ++ 128 if inflate() returned immediately after decoding an end-of-block code or ++ decoding the complete header up to just before the first byte of the deflate ++ stream. The end-of-block will not be indicated until all of the uncompressed ++ data from that block has been written to strm->next_out. The number of ++ unused bits may in general be greater than seven, except when bit 7 of ++ data_type is set, in which case the number of unused bits will be less than ++ eight. data_type is set as noted here every time inflate() returns for all ++ flush options, and so can be used to determine the amount of currently ++ consumed input in bits. ++ ++ The Z_TREES option behaves as Z_BLOCK does, but it also returns when the ++ end of each deflate block header is reached, before any actual data in that ++ block is decoded. This allows the caller to determine the length of the ++ deflate block header for later use in random access within a deflate block. ++ 256 is added to the value of strm->data_type when inflate() returns ++ immediately after reaching the end of the deflate block header. ++ ++ inflate() should normally be called until it returns Z_STREAM_END or an ++ error. However if all decompression is to be performed in a single step (a ++ single call of inflate), the parameter flush should be set to Z_FINISH. In ++ this case all pending input is processed and all pending output is flushed; ++ avail_out must be large enough to hold all the uncompressed data. (The size ++ of the uncompressed data may have been saved by the compressor for this ++ purpose.) The next operation on this stream must be inflateEnd to deallocate ++ the decompression state. The use of Z_FINISH is never required, but can be ++ used to inform inflate that a faster approach may be used for the single ++ inflate() call. ++ ++ In this implementation, inflate() always flushes as much output as ++ possible to the output buffer, and always uses the faster approach on the ++ first call. So the only effect of the flush parameter in this implementation ++ is on the return value of inflate(), as noted below, or when it returns early ++ because Z_BLOCK or Z_TREES is used. ++ ++ If a preset dictionary is needed after this call (see inflateSetDictionary ++ below), inflate sets strm->adler to the adler32 checksum of the dictionary ++ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets ++ strm->adler to the adler32 checksum of all output produced so far (that is, ++ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described ++ below. At the end of the stream, inflate() checks that its computed adler32 ++ checksum is equal to that saved by the compressor and returns Z_STREAM_END ++ only if the checksum is correct. ++ ++ inflate() can decompress and check either zlib-wrapped or gzip-wrapped ++ deflate data. The header type is detected automatically, if requested when ++ initializing with inflateInit2(). Any information contained in the gzip ++ header is not retained, so applications that need that information should ++ instead use raw inflate, see inflateInit2() below, or inflateBack() and ++ perform their own processing of the gzip header and trailer. ++ ++ inflate() returns Z_OK if some progress has been made (more input processed ++ or more output produced), Z_STREAM_END if the end of the compressed data has ++ been reached and all uncompressed output has been produced, Z_NEED_DICT if a ++ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was ++ corrupted (input stream not conforming to the zlib format or incorrect check ++ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example ++ next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, ++ Z_BUF_ERROR if no progress is possible or if there was not enough room in the ++ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and ++ inflate() can be called again with more input and more output space to ++ continue decompressing. If Z_DATA_ERROR is returned, the application may ++ then call inflateSync() to look for a good compression block if a partial ++ recovery of the data is desired. ++*/ ++ ++ ++ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); ++/* ++ All dynamically allocated data structures for this stream are freed. ++ This function discards any unprocessed input and does not flush any pending ++ output. ++ ++ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state ++ was inconsistent. In the error case, msg may be set but then points to a ++ static string (which must not be deallocated). ++*/ ++ ++ ++ /* Advanced functions */ ++ ++/* ++ The following functions are needed only in some special applications. ++*/ ++ ++/* ++ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, ++ int level, ++ int method, ++ int windowBits, ++ int memLevel, ++ int strategy)); ++ ++ This is another version of deflateInit with more compression options. The ++ fields next_in, zalloc, zfree and opaque must be initialized before by the ++ caller. ++ ++ The method parameter is the compression method. It must be Z_DEFLATED in ++ this version of the library. ++ ++ The windowBits parameter is the base two logarithm of the window size ++ (the size of the history buffer). It should be in the range 8..15 for this ++ version of the library. Larger values of this parameter result in better ++ compression at the expense of memory usage. The default value is 15 if ++ deflateInit is used instead. ++ ++ windowBits can also be -8..-15 for raw deflate. In this case, -windowBits ++ determines the window size. deflate() will then generate raw deflate data ++ with no zlib header or trailer, and will not compute an adler32 check value. ++ ++ windowBits can also be greater than 15 for optional gzip encoding. Add ++ 16 to windowBits to write a simple gzip header and trailer around the ++ compressed data instead of a zlib wrapper. The gzip header will have no ++ file name, no extra data, no comment, no modification time (set to zero), no ++ header crc, and the operating system will be set to 255 (unknown). If a ++ gzip stream is being written, strm->adler is a crc32 instead of an adler32. ++ ++ The memLevel parameter specifies how much memory should be allocated ++ for the internal compression state. memLevel=1 uses minimum memory but is ++ slow and reduces compression ratio; memLevel=9 uses maximum memory for ++ optimal speed. The default value is 8. See zconf.h for total memory usage ++ as a function of windowBits and memLevel. ++ ++ The strategy parameter is used to tune the compression algorithm. Use the ++ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a ++ filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no ++ string match), or Z_RLE to limit match distances to one (run-length ++ encoding). Filtered data consists mostly of small values with a somewhat ++ random distribution. In this case, the compression algorithm is tuned to ++ compress them better. The effect of Z_FILTERED is to force more Huffman ++ coding and less string matching; it is somewhat intermediate between ++ Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as ++ fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The ++ strategy parameter only affects the compression ratio but not the ++ correctness of the compressed output even if it is not set appropriately. ++ Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler ++ decoder for special applications. ++ ++ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid ++ method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is ++ incompatible with the version assumed by the caller (ZLIB_VERSION). msg is ++ set to null if there is no error message. deflateInit2 does not perform any ++ compression: this will be done by deflate(). ++*/ ++ ++ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, ++ const Bytef *dictionary, ++ uInt dictLength)); ++/* ++ Initializes the compression dictionary from the given byte sequence ++ without producing any compressed output. This function must be called ++ immediately after deflateInit, deflateInit2 or deflateReset, before any call ++ of deflate. The compressor and decompressor must use exactly the same ++ dictionary (see inflateSetDictionary). ++ ++ The dictionary should consist of strings (byte sequences) that are likely ++ to be encountered later in the data to be compressed, with the most commonly ++ used strings preferably put towards the end of the dictionary. Using a ++ dictionary is most useful when the data to be compressed is short and can be ++ predicted with good accuracy; the data can then be compressed better than ++ with the default empty dictionary. ++ ++ Depending on the size of the compression data structures selected by ++ deflateInit or deflateInit2, a part of the dictionary may in effect be ++ discarded, for example if the dictionary is larger than the window size ++ provided in deflateInit or deflateInit2. Thus the strings most likely to be ++ useful should be put at the end of the dictionary, not at the front. In ++ addition, the current implementation of deflate will use at most the window ++ size minus 262 bytes of the provided dictionary. ++ ++ Upon return of this function, strm->adler is set to the adler32 value ++ of the dictionary; the decompressor may later use this value to determine ++ which dictionary has been used by the compressor. (The adler32 value ++ applies to the whole dictionary even if only a subset of the dictionary is ++ actually used by the compressor.) If a raw deflate was requested, then the ++ adler32 value is not computed and strm->adler is not set. ++ ++ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a ++ parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is ++ inconsistent (for example if deflate has already been called for this stream ++ or if the compression method is bsort). deflateSetDictionary does not ++ perform any compression: this will be done by deflate(). ++*/ ++ ++ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, ++ z_streamp source)); ++/* ++ Sets the destination stream as a complete copy of the source stream. ++ ++ This function can be useful when several compression strategies will be ++ tried, for example when there are several ways of pre-processing the input ++ data with a filter. The streams that will be discarded should then be freed ++ by calling deflateEnd. Note that deflateCopy duplicates the internal ++ compression state which can be quite large, so this strategy is slow and can ++ consume lots of memory. ++ ++ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not ++ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent ++ (such as zalloc being Z_NULL). msg is left unchanged in both source and ++ destination. ++*/ ++ ++ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); ++/* ++ This function is equivalent to deflateEnd followed by deflateInit, ++ but does not free and reallocate all the internal compression state. The ++ stream will keep the same compression level and any other attributes that ++ may have been set by deflateInit2. ++ ++ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent (such as zalloc or state being Z_NULL). ++*/ ++ ++ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, ++ int level, ++ int strategy)); ++/* ++ Dynamically update the compression level and compression strategy. The ++ interpretation of level and strategy is as in deflateInit2. This can be ++ used to switch between compression and straight copy of the input data, or ++ to switch to a different kind of input data requiring a different strategy. ++ If the compression level is changed, the input available so far is ++ compressed with the old level (and may be flushed); the new level will take ++ effect only at the next call of deflate(). ++ ++ Before the call of deflateParams, the stream state must be set as for ++ a call of deflate(), since the currently available input may have to be ++ compressed and flushed. In particular, strm->avail_out must be non-zero. ++ ++ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source ++ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if ++ strm->avail_out was zero. ++*/ ++ ++ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, ++ int good_length, ++ int max_lazy, ++ int nice_length, ++ int max_chain)); ++/* ++ Fine tune deflate's internal compression parameters. This should only be ++ used by someone who understands the algorithm used by zlib's deflate for ++ searching for the best matching string, and even then only by the most ++ fanatic optimizer trying to squeeze out the last compressed bit for their ++ specific input data. Read the deflate.c source code for the meaning of the ++ max_lazy, good_length, nice_length, and max_chain parameters. ++ ++ deflateTune() can be called after deflateInit() or deflateInit2(), and ++ returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. ++ */ ++ ++ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, ++ uLong sourceLen)); ++/* ++ deflateBound() returns an upper bound on the compressed size after ++ deflation of sourceLen bytes. It must be called after deflateInit() or ++ deflateInit2(), and after deflateSetHeader(), if used. This would be used ++ to allocate an output buffer for deflation in a single pass, and so would be ++ called before deflate(). ++*/ ++ ++ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, ++ int bits, ++ int value)); ++/* ++ deflatePrime() inserts bits in the deflate output stream. The intent ++ is that this function is used to start off the deflate output with the bits ++ leftover from a previous deflate stream when appending to it. As such, this ++ function can only be used for raw deflate, and must be used before the first ++ deflate() call after a deflateInit2() or deflateReset(). bits must be less ++ than or equal to 16, and that many of the least significant bits of value ++ will be inserted in the output. ++ ++ deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent. ++*/ ++ ++ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, ++ gz_headerp head)); ++/* ++ deflateSetHeader() provides gzip header information for when a gzip ++ stream is requested by deflateInit2(). deflateSetHeader() may be called ++ after deflateInit2() or deflateReset() and before the first call of ++ deflate(). The text, time, os, extra field, name, and comment information ++ in the provided gz_header structure are written to the gzip header (xflag is ++ ignored -- the extra flags are set according to the compression level). The ++ caller must assure that, if not Z_NULL, name and comment are terminated with ++ a zero byte, and that if extra is not Z_NULL, that extra_len bytes are ++ available there. If hcrc is true, a gzip header crc is included. Note that ++ the current versions of the command-line version of gzip (up through version ++ 1.3.x) do not support header crc's, and will report that it is a "multi-part ++ gzip file" and give up. ++ ++ If deflateSetHeader is not used, the default gzip header has text false, ++ the time set to zero, and os set to 255, with no extra, name, or comment ++ fields. The gzip header is returned to the default state by deflateReset(). ++ ++ deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent. ++*/ ++ ++/* ++ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, ++ int windowBits)); ++ ++ This is another version of inflateInit with an extra parameter. The ++ fields next_in, avail_in, zalloc, zfree and opaque must be initialized ++ before by the caller. ++ ++ The windowBits parameter is the base two logarithm of the maximum window ++ size (the size of the history buffer). It should be in the range 8..15 for ++ this version of the library. The default value is 15 if inflateInit is used ++ instead. windowBits must be greater than or equal to the windowBits value ++ provided to deflateInit2() while compressing, or it must be equal to 15 if ++ deflateInit2() was not used. If a compressed stream with a larger window ++ size is given as input, inflate() will return with the error code ++ Z_DATA_ERROR instead of trying to allocate a larger window. ++ ++ windowBits can also be zero to request that inflate use the window size in ++ the zlib header of the compressed stream. ++ ++ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits ++ determines the window size. inflate() will then process raw deflate data, ++ not looking for a zlib or gzip header, not generating a check value, and not ++ looking for any check values for comparison at the end of the stream. This ++ is for use with other formats that use the deflate compressed data format ++ such as zip. Those formats provide their own check values. If a custom ++ format is developed using the raw deflate format for compressed data, it is ++ recommended that a check value such as an adler32 or a crc32 be applied to ++ the uncompressed data as is done in the zlib, gzip, and zip formats. For ++ most applications, the zlib format should be used as is. Note that comments ++ above on the use in deflateInit2() applies to the magnitude of windowBits. ++ ++ windowBits can also be greater than 15 for optional gzip decoding. Add ++ 32 to windowBits to enable zlib and gzip decoding with automatic header ++ detection, or add 16 to decode only the gzip format (the zlib format will ++ return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a ++ crc32 instead of an adler32. ++ ++ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the ++ version assumed by the caller, or Z_STREAM_ERROR if the parameters are ++ invalid, such as a null pointer to the structure. msg is set to null if ++ there is no error message. inflateInit2 does not perform any decompression ++ apart from possibly reading the zlib header if present: actual decompression ++ will be done by inflate(). (So next_in and avail_in may be modified, but ++ next_out and avail_out are unused and unchanged.) The current implementation ++ of inflateInit2() does not process any header information -- that is ++ deferred until inflate() is called. ++*/ ++ ++ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, ++ const Bytef *dictionary, ++ uInt dictLength)); ++/* ++ Initializes the decompression dictionary from the given uncompressed byte ++ sequence. This function must be called immediately after a call of inflate, ++ if that call returned Z_NEED_DICT. The dictionary chosen by the compressor ++ can be determined from the adler32 value returned by that call of inflate. ++ The compressor and decompressor must use exactly the same dictionary (see ++ deflateSetDictionary). For raw inflate, this function can be called ++ immediately after inflateInit2() or inflateReset() and before any call of ++ inflate() to set the dictionary. The application must insure that the ++ dictionary that was used for compression is provided. ++ ++ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a ++ parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is ++ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the ++ expected one (incorrect adler32 value). inflateSetDictionary does not ++ perform any decompression: this will be done by subsequent calls of ++ inflate(). ++*/ ++ ++ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); ++/* ++ Skips invalid compressed data until a full flush point (see above the ++ description of deflate with Z_FULL_FLUSH) can be found, or until all ++ available input is skipped. No output is provided. ++ ++ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR ++ if no more input was provided, Z_DATA_ERROR if no flush point has been ++ found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the ++ success case, the application may save the current current value of total_in ++ which indicates where valid compressed data was found. In the error case, ++ the application may repeatedly call inflateSync, providing more input each ++ time, until success or end of the input data. ++*/ ++ ++ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, ++ z_streamp source)); ++/* ++ Sets the destination stream as a complete copy of the source stream. ++ ++ This function can be useful when randomly accessing a large stream. The ++ first pass through the stream can periodically record the inflate state, ++ allowing restarting inflate at those points when randomly accessing the ++ stream. ++ ++ inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not ++ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent ++ (such as zalloc being Z_NULL). msg is left unchanged in both source and ++ destination. ++*/ ++ ++ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); ++/* ++ This function is equivalent to inflateEnd followed by inflateInit, ++ but does not free and reallocate all the internal decompression state. The ++ stream will keep attributes that may have been set by inflateInit2. ++ ++ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent (such as zalloc or state being Z_NULL). ++*/ ++ ++ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, ++ int windowBits)); ++/* ++ This function is the same as inflateReset, but it also permits changing ++ the wrap and window size requests. The windowBits parameter is interpreted ++ the same as it is for inflateInit2. ++ ++ inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent (such as zalloc or state being Z_NULL), or if ++ the windowBits parameter is invalid. ++*/ ++ ++ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, ++ int bits, ++ int value)); ++/* ++ This function inserts bits in the inflate input stream. The intent is ++ that this function is used to start inflating at a bit position in the ++ middle of a byte. The provided bits will be used before any bytes are used ++ from next_in. This function should only be used with raw inflate, and ++ should be used before the first inflate() call after inflateInit2() or ++ inflateReset(). bits must be less than or equal to 16, and that many of the ++ least significant bits of value will be inserted in the input. ++ ++ If bits is negative, then the input stream bit buffer is emptied. Then ++ inflatePrime() can be called again to put bits in the buffer. This is used ++ to clear out bits leftover after feeding inflate a block description prior ++ to feeding inflate codes. ++ ++ inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent. ++*/ ++ ++ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); ++/* ++ This function returns two values, one in the lower 16 bits of the return ++ value, and the other in the remaining upper bits, obtained by shifting the ++ return value down 16 bits. If the upper value is -1 and the lower value is ++ zero, then inflate() is currently decoding information outside of a block. ++ If the upper value is -1 and the lower value is non-zero, then inflate is in ++ the middle of a stored block, with the lower value equaling the number of ++ bytes from the input remaining to copy. If the upper value is not -1, then ++ it is the number of bits back from the current bit position in the input of ++ the code (literal or length/distance pair) currently being processed. In ++ that case the lower value is the number of bytes already emitted for that ++ code. ++ ++ A code is being processed if inflate is waiting for more input to complete ++ decoding of the code, or if it has completed decoding but is waiting for ++ more output space to write the literal or match data. ++ ++ inflateMark() is used to mark locations in the input data for random ++ access, which may be at bit positions, and to note those cases where the ++ output of a code may span boundaries of random access blocks. The current ++ location in the input stream can be determined from avail_in and data_type ++ as noted in the description for the Z_BLOCK flush parameter for inflate. ++ ++ inflateMark returns the value noted above or -1 << 16 if the provided ++ source stream state was inconsistent. ++*/ ++ ++ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, ++ gz_headerp head)); ++/* ++ inflateGetHeader() requests that gzip header information be stored in the ++ provided gz_header structure. inflateGetHeader() may be called after ++ inflateInit2() or inflateReset(), and before the first call of inflate(). ++ As inflate() processes the gzip stream, head->done is zero until the header ++ is completed, at which time head->done is set to one. If a zlib stream is ++ being decoded, then head->done is set to -1 to indicate that there will be ++ no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be ++ used to force inflate() to return immediately after header processing is ++ complete and before any actual data is decompressed. ++ ++ The text, time, xflags, and os fields are filled in with the gzip header ++ contents. hcrc is set to true if there is a header CRC. (The header CRC ++ was valid if done is set to one.) If extra is not Z_NULL, then extra_max ++ contains the maximum number of bytes to write to extra. Once done is true, ++ extra_len contains the actual extra field length, and extra contains the ++ extra field, or that field truncated if extra_max is less than extra_len. ++ If name is not Z_NULL, then up to name_max characters are written there, ++ terminated with a zero unless the length is greater than name_max. If ++ comment is not Z_NULL, then up to comm_max characters are written there, ++ terminated with a zero unless the length is greater than comm_max. When any ++ of extra, name, or comment are not Z_NULL and the respective field is not ++ present in the header, then that field is set to Z_NULL to signal its ++ absence. This allows the use of deflateSetHeader() with the returned ++ structure to duplicate the header. However if those fields are set to ++ allocated memory, then the application will need to save those pointers ++ elsewhere so that they can be eventually freed. ++ ++ If inflateGetHeader is not used, then the header information is simply ++ discarded. The header is always checked for validity, including the header ++ CRC if present. inflateReset() will reset the process to discard the header ++ information. The application would need to call inflateGetHeader() again to ++ retrieve the header from the next gzip stream. ++ ++ inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent. ++*/ ++ ++/* ++ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, ++ unsigned char FAR *window)); ++ ++ Initialize the internal stream state for decompression using inflateBack() ++ calls. The fields zalloc, zfree and opaque in strm must be initialized ++ before the call. If zalloc and zfree are Z_NULL, then the default library- ++ derived memory allocation routines are used. windowBits is the base two ++ logarithm of the window size, in the range 8..15. window is a caller ++ supplied buffer of that size. Except for special applications where it is ++ assured that deflate was used with small window sizes, windowBits must be 15 ++ and a 32K byte window must be supplied to be able to decompress general ++ deflate streams. ++ ++ See inflateBack() for the usage of these routines. ++ ++ inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of ++ the paramaters are invalid, Z_MEM_ERROR if the internal state could not be ++ allocated, or Z_VERSION_ERROR if the version of the library does not match ++ the version of the header file. ++*/ ++ ++typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); ++typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); ++ ++ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, ++ in_func in, void FAR *in_desc, ++ out_func out, void FAR *out_desc)); ++/* ++ inflateBack() does a raw inflate with a single call using a call-back ++ interface for input and output. This is more efficient than inflate() for ++ file i/o applications in that it avoids copying between the output and the ++ sliding window by simply making the window itself the output buffer. This ++ function trusts the application to not change the output buffer passed by ++ the output function, at least until inflateBack() returns. ++ ++ inflateBackInit() must be called first to allocate the internal state ++ and to initialize the state with the user-provided window buffer. ++ inflateBack() may then be used multiple times to inflate a complete, raw ++ deflate stream with each call. inflateBackEnd() is then called to free the ++ allocated state. ++ ++ A raw deflate stream is one with no zlib or gzip header or trailer. ++ This routine would normally be used in a utility that reads zip or gzip ++ files and writes out uncompressed files. The utility would decode the ++ header and process the trailer on its own, hence this routine expects only ++ the raw deflate stream to decompress. This is different from the normal ++ behavior of inflate(), which expects either a zlib or gzip header and ++ trailer around the deflate stream. ++ ++ inflateBack() uses two subroutines supplied by the caller that are then ++ called by inflateBack() for input and output. inflateBack() calls those ++ routines until it reads a complete deflate stream and writes out all of the ++ uncompressed data, or until it encounters an error. The function's ++ parameters and return types are defined above in the in_func and out_func ++ typedefs. inflateBack() will call in(in_desc, &buf) which should return the ++ number of bytes of provided input, and a pointer to that input in buf. If ++ there is no input available, in() must return zero--buf is ignored in that ++ case--and inflateBack() will return a buffer error. inflateBack() will call ++ out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() ++ should return zero on success, or non-zero on failure. If out() returns ++ non-zero, inflateBack() will return with an error. Neither in() nor out() ++ are permitted to change the contents of the window provided to ++ inflateBackInit(), which is also the buffer that out() uses to write from. ++ The length written by out() will be at most the window size. Any non-zero ++ amount of input may be provided by in(). ++ ++ For convenience, inflateBack() can be provided input on the first call by ++ setting strm->next_in and strm->avail_in. If that input is exhausted, then ++ in() will be called. Therefore strm->next_in must be initialized before ++ calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called ++ immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in ++ must also be initialized, and then if strm->avail_in is not zero, input will ++ initially be taken from strm->next_in[0 .. strm->avail_in - 1]. ++ ++ The in_desc and out_desc parameters of inflateBack() is passed as the ++ first parameter of in() and out() respectively when they are called. These ++ descriptors can be optionally used to pass any information that the caller- ++ supplied in() and out() functions need to do their job. ++ ++ On return, inflateBack() will set strm->next_in and strm->avail_in to ++ pass back any unused input that was provided by the last in() call. The ++ return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR ++ if in() or out() returned an error, Z_DATA_ERROR if there was a format error ++ in the deflate stream (in which case strm->msg is set to indicate the nature ++ of the error), or Z_STREAM_ERROR if the stream was not properly initialized. ++ In the case of Z_BUF_ERROR, an input or output error can be distinguished ++ using strm->next_in which will be Z_NULL only if in() returned an error. If ++ strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning ++ non-zero. (in() will always be called before out(), so strm->next_in is ++ assured to be defined if out() returns non-zero.) Note that inflateBack() ++ cannot return Z_OK. ++*/ ++ ++ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); ++/* ++ All memory allocated by inflateBackInit() is freed. ++ ++ inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream ++ state was inconsistent. ++*/ ++ ++ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); ++/* Return flags indicating compile-time options. ++ ++ Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: ++ 1.0: size of uInt ++ 3.2: size of uLong ++ 5.4: size of voidpf (pointer) ++ 7.6: size of z_off_t ++ ++ Compiler, assembler, and debug options: ++ 8: DEBUG ++ 9: ASMV or ASMINF -- use ASM code ++ 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention ++ 11: 0 (reserved) ++ ++ One-time table building (smaller code, but not thread-safe if true): ++ 12: BUILDFIXED -- build static block decoding tables when needed ++ 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed ++ 14,15: 0 (reserved) ++ ++ Library content (indicates missing functionality): ++ 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking ++ deflate code when not needed) ++ 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect ++ and decode gzip streams (to avoid linking crc code) ++ 18-19: 0 (reserved) ++ ++ Operation variations (changes in library functionality): ++ 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate ++ 21: FASTEST -- deflate algorithm with only one, lowest compression level ++ 22,23: 0 (reserved) ++ ++ The sprintf variant used by gzprintf (zero is best): ++ 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format ++ 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! ++ 26: 0 = returns value, 1 = void -- 1 means inferred string length returned ++ ++ Remainder: ++ 27-31: 0 (reserved) ++ */ ++ ++ ++ /* utility functions */ ++ ++/* ++ The following utility functions are implemented on top of the basic ++ stream-oriented functions. To simplify the interface, some default options ++ are assumed (compression level and memory usage, standard memory allocation ++ functions). The source code of these utility functions can be modified if ++ you need special options. ++*/ ++ ++ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, ++ const Bytef *source, uLong sourceLen)); ++/* ++ Compresses the source buffer into the destination buffer. sourceLen is ++ the byte length of the source buffer. Upon entry, destLen is the total size ++ of the destination buffer, which must be at least the value returned by ++ compressBound(sourceLen). Upon exit, destLen is the actual size of the ++ compressed buffer. ++ ++ compress returns Z_OK if success, Z_MEM_ERROR if there was not ++ enough memory, Z_BUF_ERROR if there was not enough room in the output ++ buffer. ++*/ ++ ++ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, ++ const Bytef *source, uLong sourceLen, ++ int level)); ++/* ++ Compresses the source buffer into the destination buffer. The level ++ parameter has the same meaning as in deflateInit. sourceLen is the byte ++ length of the source buffer. Upon entry, destLen is the total size of the ++ destination buffer, which must be at least the value returned by ++ compressBound(sourceLen). Upon exit, destLen is the actual size of the ++ compressed buffer. ++ ++ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_BUF_ERROR if there was not enough room in the output buffer, ++ Z_STREAM_ERROR if the level parameter is invalid. ++*/ ++ ++ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); ++/* ++ compressBound() returns an upper bound on the compressed size after ++ compress() or compress2() on sourceLen bytes. It would be used before a ++ compress() or compress2() call to allocate the destination buffer. ++*/ ++ ++ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, ++ const Bytef *source, uLong sourceLen)); ++/* ++ Decompresses the source buffer into the destination buffer. sourceLen is ++ the byte length of the source buffer. Upon entry, destLen is the total size ++ of the destination buffer, which must be large enough to hold the entire ++ uncompressed data. (The size of the uncompressed data must have been saved ++ previously by the compressor and transmitted to the decompressor by some ++ mechanism outside the scope of this compression library.) Upon exit, destLen ++ is the actual size of the uncompressed buffer. ++ ++ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not ++ enough memory, Z_BUF_ERROR if there was not enough room in the output ++ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. ++*/ ++ ++ ++ /* gzip file access functions */ ++ ++/* ++ This library supports reading and writing files in gzip (.gz) format with ++ an interface similar to that of stdio, using the functions that start with ++ "gz". The gzip format is different from the zlib format. gzip is a gzip ++ wrapper, documented in RFC 1952, wrapped around a deflate stream. ++*/ ++ ++typedef voidp gzFile; /* opaque gzip file descriptor */ ++ ++/* ++ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); ++ ++ Opens a gzip (.gz) file for reading or writing. The mode parameter is as ++ in fopen ("rb" or "wb") but can also include a compression level ("wb9") or ++ a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only ++ compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' ++ for fixed code compression as in "wb9F". (See the description of ++ deflateInit2 for more information about the strategy parameter.) Also "a" ++ can be used instead of "w" to request that the gzip stream that will be ++ written be appended to the file. "+" will result in an error, since reading ++ and writing to the same gzip file is not supported. ++ ++ gzopen can be used to read a file which is not in gzip format; in this ++ case gzread will directly read from the file without decompression. ++ ++ gzopen returns NULL if the file could not be opened, if there was ++ insufficient memory to allocate the gzFile state, or if an invalid mode was ++ specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). ++ errno can be checked to determine if the reason gzopen failed was that the ++ file could not be opened. ++*/ ++ ++ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); ++/* ++ gzdopen associates a gzFile with the file descriptor fd. File descriptors ++ are obtained from calls like open, dup, creat, pipe or fileno (if the file ++ has been previously opened with fopen). The mode parameter is as in gzopen. ++ ++ The next call of gzclose on the returned gzFile will also close the file ++ descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor ++ fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, ++ mode);. The duplicated descriptor should be saved to avoid a leak, since ++ gzdopen does not close fd if it fails. ++ ++ gzdopen returns NULL if there was insufficient memory to allocate the ++ gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not ++ provided, or '+' was provided), or if fd is -1. The file descriptor is not ++ used until the next gz* read, write, seek, or close operation, so gzdopen ++ will not detect if fd is invalid (unless fd is -1). ++*/ ++ ++ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); ++/* ++ Set the internal buffer size used by this library's functions. The ++ default buffer size is 8192 bytes. This function must be called after ++ gzopen() or gzdopen(), and before any other calls that read or write the ++ file. The buffer memory allocation is always deferred to the first read or ++ write. Two buffers are allocated, either both of the specified size when ++ writing, or one of the specified size and the other twice that size when ++ reading. A larger buffer size of, for example, 64K or 128K bytes will ++ noticeably increase the speed of decompression (reading). ++ ++ The new buffer size also affects the maximum length for gzprintf(). ++ ++ gzbuffer() returns 0 on success, or -1 on failure, such as being called ++ too late. ++*/ ++ ++ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); ++/* ++ Dynamically update the compression level or strategy. See the description ++ of deflateInit2 for the meaning of these parameters. ++ ++ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not ++ opened for writing. ++*/ ++ ++ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); ++/* ++ Reads the given number of uncompressed bytes from the compressed file. If ++ the input file was not in gzip format, gzread copies the given number of ++ bytes into the buffer. ++ ++ After reaching the end of a gzip stream in the input, gzread will continue ++ to read, looking for another gzip stream, or failing that, reading the rest ++ of the input file directly without decompression. The entire input file ++ will be read if gzread is called until it returns less than the requested ++ len. ++ ++ gzread returns the number of uncompressed bytes actually read, less than ++ len for end of file, or -1 for error. ++*/ ++ ++ZEXTERN int ZEXPORT gzwrite OF((gzFile file, ++ voidpc buf, unsigned len)); ++/* ++ Writes the given number of uncompressed bytes into the compressed file. ++ gzwrite returns the number of uncompressed bytes written or 0 in case of ++ error. ++*/ ++ ++ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); ++/* ++ Converts, formats, and writes the arguments to the compressed file under ++ control of the format string, as in fprintf. gzprintf returns the number of ++ uncompressed bytes actually written, or 0 in case of error. The number of ++ uncompressed bytes written is limited to 8191, or one less than the buffer ++ size given to gzbuffer(). The caller should assure that this limit is not ++ exceeded. If it is exceeded, then gzprintf() will return an error (0) with ++ nothing written. In this case, there may also be a buffer overflow with ++ unpredictable consequences, which is possible only if zlib was compiled with ++ the insecure functions sprintf() or vsprintf() because the secure snprintf() ++ or vsnprintf() functions were not available. This can be determined using ++ zlibCompileFlags(). ++*/ ++ ++ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); ++/* ++ Writes the given null-terminated string to the compressed file, excluding ++ the terminating null character. ++ ++ gzputs returns the number of characters written, or -1 in case of error. ++*/ ++ ++ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); ++/* ++ Reads bytes from the compressed file until len-1 characters are read, or a ++ newline character is read and transferred to buf, or an end-of-file ++ condition is encountered. If any characters are read or if len == 1, the ++ string is terminated with a null character. If no characters are read due ++ to an end-of-file or len < 1, then the buffer is left untouched. ++ ++ gzgets returns buf which is a null-terminated string, or it returns NULL ++ for end-of-file or in case of error. If there was an error, the contents at ++ buf are indeterminate. ++*/ ++ ++ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); ++/* ++ Writes c, converted to an unsigned char, into the compressed file. gzputc ++ returns the value that was written, or -1 in case of error. ++*/ ++ ++ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); ++/* ++ Reads one byte from the compressed file. gzgetc returns this byte or -1 ++ in case of end of file or error. ++*/ ++ ++ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); ++/* ++ Push one character back onto the stream to be read as the first character ++ on the next read. At least one character of push-back is allowed. ++ gzungetc() returns the character pushed, or -1 on failure. gzungetc() will ++ fail if c is -1, and may fail if a character has been pushed but not read ++ yet. If gzungetc is used immediately after gzopen or gzdopen, at least the ++ output buffer size of pushed characters is allowed. (See gzbuffer above.) ++ The pushed character will be discarded if the stream is repositioned with ++ gzseek() or gzrewind(). ++*/ ++ ++ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); ++/* ++ Flushes all pending output into the compressed file. The parameter flush ++ is as in the deflate() function. The return value is the zlib error number ++ (see function gzerror below). gzflush is only permitted when writing. ++ ++ If the flush parameter is Z_FINISH, the remaining data is written and the ++ gzip stream is completed in the output. If gzwrite() is called again, a new ++ gzip stream will be started in the output. gzread() is able to read such ++ concatented gzip streams. ++ ++ gzflush should be called only when strictly necessary because it will ++ degrade compression if called too often. ++*/ ++ ++/* ++ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, ++ z_off_t offset, int whence)); ++ ++ Sets the starting position for the next gzread or gzwrite on the given ++ compressed file. The offset represents a number of bytes in the ++ uncompressed data stream. The whence parameter is defined as in lseek(2); ++ the value SEEK_END is not supported. ++ ++ If the file is opened for reading, this function is emulated but can be ++ extremely slow. If the file is opened for writing, only forward seeks are ++ supported; gzseek then compresses a sequence of zeroes up to the new ++ starting position. ++ ++ gzseek returns the resulting offset location as measured in bytes from ++ the beginning of the uncompressed stream, or -1 in case of error, in ++ particular if the file is opened for writing and the new starting position ++ would be before the current position. ++*/ ++ ++ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); ++/* ++ Rewinds the given file. This function is supported only for reading. ++ ++ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) ++*/ ++ ++/* ++ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); ++ ++ Returns the starting position for the next gzread or gzwrite on the given ++ compressed file. This position represents a number of bytes in the ++ uncompressed data stream, and is zero when starting, even if appending or ++ reading a gzip stream from the middle of a file using gzdopen(). ++ ++ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) ++*/ ++ ++/* ++ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); ++ ++ Returns the current offset in the file being read or written. This offset ++ includes the count of bytes that precede the gzip stream, for example when ++ appending or when using gzdopen() for reading. When reading, the offset ++ does not include as yet unused buffered input. This information can be used ++ for a progress indicator. On error, gzoffset() returns -1. ++*/ ++ ++ZEXTERN int ZEXPORT gzeof OF((gzFile file)); ++/* ++ Returns true (1) if the end-of-file indicator has been set while reading, ++ false (0) otherwise. Note that the end-of-file indicator is set only if the ++ read tried to go past the end of the input, but came up short. Therefore, ++ just like feof(), gzeof() may return false even if there is no more data to ++ read, in the event that the last read request was for the exact number of ++ bytes remaining in the input file. This will happen if the input file size ++ is an exact multiple of the buffer size. ++ ++ If gzeof() returns true, then the read functions will return no more data, ++ unless the end-of-file indicator is reset by gzclearerr() and the input file ++ has grown since the previous end of file was detected. ++*/ ++ ++ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); ++/* ++ Returns true (1) if file is being copied directly while reading, or false ++ (0) if file is a gzip stream being decompressed. This state can change from ++ false to true while reading the input file if the end of a gzip stream is ++ reached, but is followed by data that is not another gzip stream. ++ ++ If the input file is empty, gzdirect() will return true, since the input ++ does not contain a gzip stream. ++ ++ If gzdirect() is used immediately after gzopen() or gzdopen() it will ++ cause buffers to be allocated to allow reading the file to determine if it ++ is a gzip file. Therefore if gzbuffer() is used, it should be called before ++ gzdirect(). ++*/ ++ ++ZEXTERN int ZEXPORT gzclose OF((gzFile file)); ++/* ++ Flushes all pending output if necessary, closes the compressed file and ++ deallocates the (de)compression state. Note that once file is closed, you ++ cannot call gzerror with file, since its structures have been deallocated. ++ gzclose must not be called more than once on the same file, just as free ++ must not be called more than once on the same allocation. ++ ++ gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a ++ file operation error, or Z_OK on success. ++*/ ++ ++ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); ++ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); ++/* ++ Same as gzclose(), but gzclose_r() is only for use when reading, and ++ gzclose_w() is only for use when writing or appending. The advantage to ++ using these instead of gzclose() is that they avoid linking in zlib ++ compression or decompression code that is not used when only reading or only ++ writing respectively. If gzclose() is used, then both compression and ++ decompression code will be included the application when linking to a static ++ zlib library. ++*/ ++ ++ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); ++/* ++ Returns the error message for the last error which occurred on the given ++ compressed file. errnum is set to zlib error number. If an error occurred ++ in the file system and not in the compression library, errnum is set to ++ Z_ERRNO and the application may consult errno to get the exact error code. ++ ++ The application must not modify the returned string. Future calls to ++ this function may invalidate the previously returned string. If file is ++ closed, then the string previously returned by gzerror will no longer be ++ available. ++ ++ gzerror() should be used to distinguish errors from end-of-file for those ++ functions above that do not distinguish those cases in their return values. ++*/ ++ ++ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); ++/* ++ Clears the error and end-of-file flags for file. This is analogous to the ++ clearerr() function in stdio. This is useful for continuing to read a gzip ++ file that is being written concurrently. ++*/ ++ ++ ++ /* checksum functions */ ++ ++/* ++ These functions are not related to compression but are exported ++ anyway because they might be useful in applications using the compression ++ library. ++*/ ++ ++ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); ++/* ++ Update a running Adler-32 checksum with the bytes buf[0..len-1] and ++ return the updated checksum. If buf is Z_NULL, this function returns the ++ required initial value for the checksum. ++ ++ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed ++ much faster. ++ ++ Usage example: ++ ++ uLong adler = adler32(0L, Z_NULL, 0); ++ ++ while (read_buffer(buffer, length) != EOF) { ++ adler = adler32(adler, buffer, length); ++ } ++ if (adler != original_adler) error(); ++*/ ++ ++/* ++ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, ++ z_off_t len2)); ++ ++ Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 ++ and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for ++ each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of ++ seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. ++*/ ++ ++ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); ++/* ++ Update a running CRC-32 with the bytes buf[0..len-1] and return the ++ updated CRC-32. If buf is Z_NULL, this function returns the required ++ initial value for the for the crc. Pre- and post-conditioning (one's ++ complement) is performed within this function so it shouldn't be done by the ++ application. ++ ++ Usage example: ++ ++ uLong crc = crc32(0L, Z_NULL, 0); ++ ++ while (read_buffer(buffer, length) != EOF) { ++ crc = crc32(crc, buffer, length); ++ } ++ if (crc != original_crc) error(); ++*/ ++ ++/* ++ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); ++ ++ Combine two CRC-32 check values into one. For two sequences of bytes, ++ seq1 and seq2 with lengths len1 and len2, CRC-32 check values were ++ calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 ++ check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and ++ len2. ++*/ ++ ++ ++ /* various hacks, don't look :) */ ++ ++/* deflateInit and inflateInit are macros to allow checking the zlib version ++ * and the compiler's view of z_stream: ++ */ ++ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, ++ const char *version, int stream_size)); ++ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, ++ const char *version, int stream_size)); ++ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, ++ int windowBits, int memLevel, ++ int strategy, const char *version, ++ int stream_size)); ++ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, ++ const char *version, int stream_size)); ++ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, ++ unsigned char FAR *window, ++ const char *version, ++ int stream_size)); ++#define deflateInit(strm, level) \ ++ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) ++#define inflateInit(strm) \ ++ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) ++#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ ++ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ ++ (strategy), ZLIB_VERSION, sizeof(z_stream)) ++#define inflateInit2(strm, windowBits) \ ++ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) ++#define inflateBackInit(strm, windowBits, window) \ ++ inflateBackInit_((strm), (windowBits), (window), \ ++ ZLIB_VERSION, sizeof(z_stream)) ++ ++/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or ++ * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if ++ * both are true, the application gets the *64 functions, and the regular ++ * functions are changed to 64 bits) -- in case these are set on systems ++ * without large file support, _LFS64_LARGEFILE must also be true ++ */ ++#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 ++ ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ++ ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ++ ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ++ ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); ++ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); ++ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); ++#endif ++ ++#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0 ++# define gzopen gzopen64 ++# define gzseek gzseek64 ++# define gztell gztell64 ++# define gzoffset gzoffset64 ++# define adler32_combine adler32_combine64 ++# define crc32_combine crc32_combine64 ++# ifdef _LARGEFILE64_SOURCE ++ ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ++ ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); ++ ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); ++ ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); ++ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ++ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); ++# endif ++#else ++ ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); ++ ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); ++ ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); ++ ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); ++ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ++ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); ++#endif ++ ++/* hack for buggy compilers */ ++#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) ++ struct internal_state {int dummy;}; ++#endif ++ ++/* undocumented functions */ ++ZEXTERN const char * ZEXPORT zError OF((int)); ++ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ++ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); ++ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* ZLIB_H */ +commit d35e544671d94a9e6ebf87ff5f7d747fda616f27 +Author: Luigi Scarso +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/Build/source@48594 c570f23f-e606-0410-a88d-b1316a301751 + +diff --git a/texk/web2c/luatexdir/am/luapplib.am b/texk/web2c/luatexdir/am/luapplib.am +new file mode 100644 +index 000000000..513062325 +--- /dev/null ++++ b/texk/web2c/luatexdir/am/luapplib.am +@@ -0,0 +1,86 @@ ++## texk/web2c/luatexdir/am/luapplib.am: Makefile fragment for libluapplib. ++## ++## Copyright (C) 2018 Luigi Scarso ++## 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) +diff --git a/texk/web2c/luatexdir/dvi/dvigen.c b/texk/web2c/luatexdir/dvi/dvigen.c +new file mode 100644 +index 000000000..8e285917a +--- /dev/null ++++ b/texk/web2c/luatexdir/dvi/dvigen.c +@@ -0,0 +1,1558 @@ ++/* ++ ++dvigen.w ++ ++Copyright 2009-2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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; ++} +diff --git a/texk/web2c/luatexdir/dvi/dvigen.w b/texk/web2c/luatexdir/dvi/dvigen.w +deleted file mode 100644 +index d991445a5..000000000 +--- a/texk/web2c/luatexdir/dvi/dvigen.w ++++ /dev/null +@@ -1,1258 +0,0 @@ +-% dvigen.w +-% +-% Copyright 2009-2013 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-\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); +- } +-} +diff --git a/texk/web2c/luatexdir/font/dofont.w b/texk/web2c/luatexdir/font/dofont.c +similarity index 63% +rename from texk/web2c/luatexdir/font/dofont.w +rename to texk/web2c/luatexdir/font/dofont.c +index b91bec106..b23368d83 100644 +--- a/texk/web2c/luatexdir/font/dofont.w ++++ b/texk/web2c/luatexdir/font/dofont.c +@@ -1,42 +1,42 @@ +-% dofont.w +-% +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include "lua/luatex-api.h" + +-@ a bit more interfacing is needed for proper error reporting ++/*tex ++ ++ 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); ++ 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); ++ 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); + } +@@ -46,26 +46,22 @@ static char *font_error_message(pointer u, char *nom, scaled s) + + static int do_define_font(int f, const char *cnom, scaled s, int natural_dir) + { +- +- boolean res; /* was the callback successful? */ +- int callback_id; ++ boolean res = 0; + char *cnam; + int r, t; +- res = 0; +- +- callback_id = callback_defined(define_font_callback); ++ 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) { /* success */ ++ 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); +- /* |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); +@@ -86,12 +82,16 @@ static int do_define_font(int f, const char *cnom, scaled s, int natural_dir) + } + } + 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 */ ++ /*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) { /* implies lua */ ++ if (font_type(f) != virtual_font_type) { ++ /*tex This implies \LUA. */ + do_vf(f); + set_font_natural_dir(f, natural_dir); + } +@@ -112,8 +112,8 @@ int read_font_info(pointer u, char *cnom, scaled s, int natural_dir) + 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,", ++ 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;", +@@ -129,10 +129,6 @@ int read_font_info(pointer u, char *cnom, scaled s, int natural_dir) + } + } + +-@ 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; +diff --git a/texk/web2c/luatexdir/font/mapfile.c b/texk/web2c/luatexdir/font/mapfile.c +new file mode 100644 +index 000000000..5ea1e7ca0 +--- /dev/null ++++ b/texk/web2c/luatexdir/font/mapfile.c +@@ -0,0 +1,770 @@ ++/* ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#include "ptexlib.h" ++#include ++#include ++#include ++#include ++ ++#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 {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; ++ } ++} +diff --git a/texk/web2c/luatexdir/font/mapfile.w b/texk/web2c/luatexdir/font/mapfile.w +deleted file mode 100644 +index f47c5b504..000000000 +--- a/texk/web2c/luatexdir/font/mapfile.w ++++ /dev/null +@@ -1,715 +0,0 @@ +-% mapfile.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +- +-#include "ptexlib.h" +-#include +-#include +-#include +-#include +- +-#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: ' 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; +- } +-} +diff --git a/texk/web2c/luatexdir/font/pkin.w b/texk/web2c/luatexdir/font/pkin.c +similarity index 55% +rename from texk/web2c/luatexdir/font/pkin.w +rename to texk/web2c/luatexdir/font/pkin.c +index a5d3eb4bc..afebdef33 100644 +--- a/texk/web2c/luatexdir/font/pkin.w ++++ b/texk/web2c/luatexdir/font/pkin.c +@@ -1,63 +1,56 @@ +-% pkin.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2008 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ +-NAME ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2008 Taco Hoekwater + +-pkin.c - implementation of readchar() ++This file is part of LuaTeX. + +-DESCRIPTION ++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. + +-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. ++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. + +-BUGFIXES ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . + +-May 1997: Eric Delaunay 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 ++/*tex + +-Piet Tutelaers ++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. + +-Modified for use with pdftex by Han The Thanh . ++May 1997: Eric Delaunay 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. + +-@c ++At some point Piet Tutelaers redesigned the code and later Han ++The Thanh again modified the code to suite \PDFTEX. Of course ++in \LUATEX\ again we adapted the code. + ++*/ + + #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. ++/*tex ++ ++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; +@@ -70,7 +63,6 @@ static shalfword pkbyte(void) + static int pkduo(void) + { + register int i; +- + i = pkbyte(); + if (i > 127) + i -= 256; +@@ -81,7 +73,6 @@ static int pkduo(void) + static int pktrio(void) + { + register int i; +- + i = pkbyte(); + if (i > 127) + i -= 256; +@@ -93,7 +84,6 @@ static int pktrio(void) + static int pkquad(void) + { + register int i; +- + i = pkbyte(); + if (i > 127) + i -= 256; +@@ -103,14 +93,13 @@ static int pkquad(void) + 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. + +-@ 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; +@@ -154,10 +143,10 @@ static halfword pkpackednum(void) + i++; + } while (!(j != 0)); + if (i > 3) { +-/* +- Damn, we got a huge count! We {\it fake} it by giving an artificially +- large repeat count. +- */ ++ /*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) { +@@ -175,9 +164,6 @@ static halfword pkpackednum(void) + repeatcount = pkpackednum(); + else + repeatcount = 1; +-#ifdef DEBUG +- printf("[%d]", (int) repeatcount); +-#endif + return ((*realfunc) ()); + } + } +@@ -185,7 +171,6 @@ static halfword pkpackednum(void) + static halfword rest(void) + { + halfword i; +- + if (pk_remainder < 0) { + pk_remainder = -pk_remainder; + return (0); +@@ -202,13 +187,12 @@ static halfword rest(void) + } 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--; +@@ -219,11 +203,16 @@ static halfword handlehuge(halfword i, halfword k) + } + + +-@ And now we have our unpacking routine. ++/*tex ++ ++ 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 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) +@@ -236,7 +225,6 @@ static void unpack(chardesc * cd) + shalfword hbit; + halfword count; + shalfword wordwidth; +- + wordwidth = (shalfword) ((cd->cwidth + 15) / 16); + i = (int) (2 * cd->cheight * (long) wordwidth); + if (i <= 0) +@@ -317,80 +305,89 @@ static void unpack(chardesc * cd) + } + } + +-@ +-|readchar()|: the main routine +-check pk preamble if necessary, +-Reads the character definition of character `c' into `cd' if available, +-return FALSE (0) otherwise. ++/*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. ++ ++*/ + +-@c + int readchar(boolean check_preamble, chardesc * cd) + { + register shalfword i; + register int k; + register int length = 0; +- +-/* +- Check the preamble of the pkfile +- */ ++ /*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"); +- for (i = pkbyte(); i > 0; i--) /* creator of pkfile */ ++ /*tex The creator of the file: */ ++ for (i = pkbyte(); i > 0; i--) + (void) pkbyte(); +- (void) pkquad(); /* design size */ +- k = pkquad(); /* checksum */ +- k = pkquad(); /* hppp */ +- k = pkquad(); /* vppp */ ++ /*tex The design size: */ ++ (void) pkquad(); ++ /*tex The checksum: */ ++ k = pkquad(); ++ /*tex The hppp: */ ++ k = pkquad(); ++ /*tex The vppp: */ ++ k = pkquad(); + } +-/* +- Now we skip to the desired character definition +- */ ++ /*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(); +- (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(); ++ 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); +@@ -399,28 +396,29 @@ int readchar(boolean check_preamble, chardesc * cd) + } 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); ++ 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 */ ++ /*tex Character not found: */ ++ return 0; + } +diff --git a/texk/web2c/luatexdir/font/sfnt.w b/texk/web2c/luatexdir/font/sfnt.c +similarity index 79% +rename from texk/web2c/luatexdir/font/sfnt.w +rename to texk/web2c/luatexdir/font/sfnt.c +index 83e38990d..c7f4723c4 100644 +--- a/texk/web2c/luatexdir/font/sfnt.w ++++ b/texk/web2c/luatexdir/font/sfnt.c +@@ -1,53 +1,58 @@ +-% sfnt.w +-% +-% Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata, +-% the dvipdfmx project team +-% Copyright 2006-2008 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ Based on dvipdfmx-0.13.2c +-@c ++/* + ++Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata, ++the dvipdfmx project team ++Copyright 2006-2008 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++/*tex ++ ++ The code below is originally based on |dvipdfmx-0.13.2c|: ++ ++*/ + + #include "ptexlib.h" + +-#if HAVE_CONFIG_H ++#if HAVE_CONFIG_H + # include +-#endif /* |HAVE_CONFIG_H_| */ ++#endif + + #include + + #include "font/sfnt.h" + +-@ type: +- +- `true' (0x74727565): TrueType (Mac) +- +- `typ1' (0x74797031) (Mac): PostScript font housed in a sfnt wrapper ++/*tex + +- 0x00010000: TrueType (Win)/OpenType ++ Types: + +- `OTTO': PostScript CFF font with OpenType wrapper ++ \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 + +- `ttcf': TrueType Collection ++*/ + +-@c + #define SFNT_TRUETYPE 0x00010000UL +-#define SFNT_MAC_TRUE 0x74727565UL ++#define SFNT_MAC_TRUE 0x74727565UL + #define SFNT_OPENTYPE 0x00010000UL + #define SFNT_POSTSCRIPT 0x4f54544fUL + #define SFNT_TTC 0x74746366UL +@@ -56,14 +61,11 @@ 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) { +@@ -73,7 +75,6 @@ sfnt *sfnt_open(unsigned char *buff, int buflen) + } else if (type == SFNT_TTC) { + sfont->type = SFNT_TYPE_TTC; + } +- + sfont->loc = 0; + sfont->directory = NULL; + return sfont; +@@ -82,7 +83,6 @@ sfnt *sfnt_open(unsigned char *buff, int buflen) + static void release_directory(struct sfnt_table_directory *td) + { + long i; +- + if (td) { + if (td->tables) { + for (i = 0; i < td->num_tables; i++) { +@@ -95,19 +95,16 @@ static void release_directory(struct sfnt_table_directory *td) + RELEASE(td->flags); + RELEASE(td); + } +- + return; + } + + void sfnt_close(sfnt * sfont) + { +- + if (sfont) { + if (sfont->directory) + release_directory(sfont->directory); + RELEASE(sfont); + } +- + return; + } + +@@ -115,70 +112,67 @@ 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. ++/*tex ++ ++ 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; + } + ++/*tex + +-@ Computes the max power of 2 <= n ++ 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; + } + ++/*tex + +-@ Computes the log2 of the max power of 2 <= n ++ 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) { +@@ -186,61 +180,45 @@ static ULONG sfnt_calc_checksum(void *data, ULONG length) + 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) +@@ -248,19 +226,14 @@ ULONG sfnt_find_table_len(sfnt * sfont, const char *tag) + 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) +@@ -268,75 +241,51 @@ ULONG sfnt_find_table_pos(sfnt * sfont, const char *tag) + 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) { +@@ -346,22 +295,19 @@ int sfnt_require_table(sfnt * sfont, const char *tag, int must_exist) + 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. + +-@ 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)) +@@ -376,14 +322,8 @@ pdf_obj *sfnt_create_FontFile_stream(sfnt * sfont) + 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); +@@ -391,20 +331,15 @@ pdf_obj *sfnt_create_FontFile_stream(sfnt * sfont) + 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). +- */ ++ /*tex 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 */ ++ /*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; +@@ -412,11 +347,9 @@ pdf_obj *sfnt_create_FontFile_stream(sfnt * sfont) + 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) { +@@ -426,13 +359,11 @@ pdf_obj *sfnt_create_FontFile_stream(sfnt * sfont) + offset += length; + } + if (!td->tables[i].data) { +- if (!sfont->buffer) +- { ++ 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) { +@@ -453,10 +384,9 @@ pdf_obj *sfnt_create_FontFile_stream(sfnt * sfont) + RELEASE(td->tables[i].data); + td->tables[i].data = NULL; + } +- /* Set offset for next table */ ++ /*tex Set offset for next table. */ + offset = (long) (offset + (long) td->tables[i].length); + } + } +- + return stream; + } +diff --git a/texk/web2c/luatexdir/font/subfont.w b/texk/web2c/luatexdir/font/subfont.c +similarity index 69% +rename from texk/web2c/luatexdir/font/subfont.w +rename to texk/web2c/luatexdir/font/subfont.c +index 0616bdad4..d2f5f8d2f 100644 +--- a/texk/web2c/luatexdir/font/subfont.w ++++ b/texk/web2c/luatexdir/font/subfont.c +@@ -1,30 +1,28 @@ +-% subfont.w +-% +-% Copyright 2005-2006 Han The Thanh +-% Copyright 2006-2008 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2005-2006 Han The Thanh ++Copyright 2006-2008 Taco Hoekwater + ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include + +-@ @c + static struct avl_table *sfd_tree = NULL; + + static unsigned char *sfd_buffer = NULL; +@@ -34,12 +32,11 @@ 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_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) +- ++#define sfd_eof() (sfd_curbyte>=sfd_size) + + static FILE *sfd_file; + static char sfd_line[SFD_BUF_SIZE]; +@@ -50,8 +47,10 @@ static subfont_entry *new_subfont_entry(void) + subfont_entry *subfont; + subfont = xtalloc(1, subfont_entry); + subfont->infix = NULL; +- for (i = 0; i < 256; ++i) +- subfont->charcodes[i] = -1; /* unassigned */ ++ for (i = 0; i < 256; ++i) { ++ /*tex Unassigned: */ ++ subfont->charcodes[i] = -1; ++ } + subfont->next = NULL; + return subfont; + } +@@ -84,8 +83,7 @@ static void destroy_sfd_entry(void *pa, void *pb) + 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); ++ return strcmp(((const sfd_entry *) pa)->name, ((const sfd_entry *) pb)->name); + } + + void sfd_free(void) +@@ -117,19 +115,17 @@ static void sfd_getline(boolean expect_eof) + 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 */ ++ /*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); +@@ -141,7 +137,6 @@ static sfd_entry *read_sfd(char *sfd_name) + 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); +@@ -168,30 +163,38 @@ static sfd_entry *read_sfd(char *sfd_name) + sfd->name = xstrdup(sfd_name); + while (!sfd_eof()) { + sfd_getline(true); +- if (*sfd_line == 10) /* empty line indicating eof */ ++ 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); +- p = sfd_line + n; /* skip to the next word */ ++ /*tex Skip to the next word: */ ++ p = sfd_line + n; + k = 0; + read_ranges: + for (;;) { +- if (*p == '\\') { /* continue on next line */ ++ if (*p == '\\') { ++ /*tex Continue on the next line: */ + sfd_getline(false); + p = sfd_line; + goto read_ranges; +- } else if (*p == 0) /* end of subfont */ ++ } 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 == ':') { /* offset */ ++ if (*p == ':') { ++ /*tex Variant: offset */ + k = i; + p++; +- } else if (*p == '_') { /* range */ ++ } 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) +@@ -199,8 +202,10 @@ static sfd_entry *read_sfd(char *sfd_name) + while (i <= j) + sf->charcodes[k++] = i++; + p += n + 1; +- } else /* codepoint */ ++ } else { ++ /*tex Variant: codepoint */ + sf->charcodes[k++] = i; ++ } + } + } + tex_printf("}"); +@@ -209,7 +214,6 @@ static sfd_entry *read_sfd(char *sfd_name) + return sfd; + } + +-@ @c + boolean handle_subfont_fm(fm_entry * fm, int mode) + { + size_t l; +@@ -220,16 +224,20 @@ boolean handle_subfont_fm(fm_entry * fm, int mode) + char buf[SMALL_BUF_SIZE]; + assert(fm->tfm_name != NULL); + p = fm->tfm_name; +- q = strchr(p, '@@'); /* search for the first '@@' */ ++ /*tex Search for the first |@|. */ ++ q = strchr(p, '@'); + if (q == NULL) + return false; +- r = strchr(q + 1, '@@'); /* search for the second '@@' */ ++ /*tex Search for the second |@|: */ ++ r = strchr(q + 1, '@'); + 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 */ ++ /*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; +- l = (size_t) (r - (q + 1)); /* length of sfd name */ ++ } ++ /*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); +@@ -237,15 +245,16 @@ boolean handle_subfont_fm(fm_entry * fm, int mode) + sfd = read_sfd(buf); + if (sfd == NULL) + return false; +- /* at this point we know fm is a subfont */ ++ /*tex At this point we know fm is a subfont. */ + set_subfont(fm); + xfree(fm->ps_name); +- /* set default values for PidEid */ ++ /*tex Set default values for |Pid| and |Eid|: */ + if (fm->pid == -1) { + fm->pid = 3; + fm->eid = 1; + } +- l = (size_t) (q - p); /* length of base tfm name (prefix) */ ++ /*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; +@@ -257,8 +266,11 @@ boolean handle_subfont_fm(fm_entry * fm, int mode) + 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 */ ++ /*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; +diff --git a/texk/web2c/luatexdir/font/texfont.w b/texk/web2c/luatexdir/font/texfont.c +similarity index 89% +rename from texk/web2c/luatexdir/font/texfont.w +rename to texk/web2c/luatexdir/font/texfont.c +index ce463326a..332a8a344 100644 +--- a/texk/web2c/luatexdir/font/texfont.w ++++ b/texk/web2c/luatexdir/font/texfont.c +@@ -1,41 +1,52 @@ +-% texfont.w +-% +-% Copyright 2006-2013 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@* 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 ++/* ++ ++Copyright 2006-2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++/*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" + +-@ @c + #define noDEBUG + + #define proper_char_index(c) (c<=font_ec(f) && c>=font_bc(f)) +@@ -46,7 +57,6 @@ 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; +@@ -89,7 +99,6 @@ void set_max_font_id(int i) + font_id_maxval = i; + } + +-@ @c + int new_font(void) + { + int k; +@@ -98,7 +107,7 @@ int new_font(void) + sa_tree_item sa_value = { 0 }; + id = new_font_id(); + font_bytes += (int) sizeof(texfont); +- /* most stuff is zero */ ++ /*tex Most stuff is zero */ + font_tables[id] = xcalloc(1, sizeof(texfont)); + font_tables[id]->_font_name = NULL; + font_tables[id]->_font_area = NULL; +@@ -112,43 +121,44 @@ int new_font(void) + 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 */ ++ /*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); +- font_slant(id) = 0; /* vertical */ +- font_extend(id) = 1000; /* normal width */ +- +- /* allocate eight values including 0 */ ++ /*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); + } +- /* 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 */ ++ /*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; + } + +-@ @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))); ++ 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) +@@ -163,9 +173,10 @@ charinfo *get_charinfo(internal_font_number f, int c) + if (tglyph >= font_tables[f]->charinfo_size) { + font_malloc_charinfo(f, 256); + } +- font_tables[f]->charinfo[tglyph].ef = 1000; /* init */ ++ font_tables[f]->charinfo[tglyph].ef = 1000; + sa_value.int_value = tglyph; +- set_sa_item(font_tables[f]->characters, c, sa_value, 1); /* 1 = global */ ++ /*tex 1 means global */ ++ set_sa_item(font_tables[f]->characters, c, sa_value, 1); + glyph = tglyph; + } + return &(font_tables[f]->charinfo[glyph]); +@@ -187,7 +198,6 @@ charinfo *get_charinfo(internal_font_number f, int c) + return &(font_tables[f]->charinfo[0]); + } + +-@ @c + static void set_charinfo(internal_font_number f, int c, charinfo * ci) + { + int glyph; +@@ -205,7 +215,6 @@ static void set_charinfo(internal_font_number f, int c, charinfo * ci) + } + } + +-@ @c + charinfo *copy_charinfo(charinfo * ci) + { + int x, k; +@@ -231,7 +240,7 @@ charinfo *copy_charinfo(charinfo * ci) + if (ci->tounicode != NULL) { + co->tounicode = xstrdup(ci->tounicode); + } +- /* kerns */ ++ /*tex Kerns */ + if ((kern = get_charinfo_kerns(ci)) != NULL) { + x = 0; + while (!kern_end(kern[x])) { +@@ -241,7 +250,7 @@ charinfo *copy_charinfo(charinfo * ci) + co->kerns = xmalloc((unsigned) (x * (int) sizeof(kerninfo))); + memcpy(co->kerns, ci->kerns, (size_t) (x * (int) sizeof(kerninfo))); + } +- /* ligs */ ++ /*tex Ligatures */ + if ((lig = get_charinfo_ligatures(ci)) != NULL) { + x = 0; + while (!lig_end(lig[x])) { +@@ -252,14 +261,13 @@ charinfo *copy_charinfo(charinfo * ci) + memcpy(co->ligatures, ci->ligatures, + (size_t) (x * (int) sizeof(liginfo))); + } +- /* packets */ ++ /*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); + } +- +- /* horizontal and vertical extenders */ ++ /*tex Horizontal and vertical extenders */ + if (get_charinfo_vert_variants(ci) != NULL) { + set_charinfo_vert_variants(co, copy_variants(get_charinfo_vert_variants(ci))); + } +@@ -320,7 +328,6 @@ charinfo *char_info(internal_font_number f, int c) + return &(font_tables[f]->charinfo[0]); + } + +-@ @c + scaled_whd get_charinfo_whd(internal_font_number f, int c) + { + scaled_whd s; +@@ -332,7 +339,6 @@ scaled_whd get_charinfo_whd(internal_font_number f, int c) + return s; + } + +-@ @c + int char_exists(internal_font_number f, int c) + { + if (f > font_id_maxval) +@@ -347,35 +353,35 @@ int char_exists(internal_font_number f, int c) + return 0; + } + +-@ @c + 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_pop(Luas, 2); ++ lua_settop(Luas, top); + 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(); ++ 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; + } + +-@ @c +-extinfo *new_variant(int glyph, int startconnect, int endconnect, +- int advance, int repeater) ++extinfo *new_variant(int glyph, int startconnect, int endconnect, int advance, int repeater) + { + extinfo *ext; + ext = xmalloc(sizeof(extinfo)); +@@ -388,8 +394,6 @@ extinfo *new_variant(int glyph, int startconnect, int endconnect, + return ext; + } + +- +-@ @c + static extinfo *copy_variant(extinfo * old) + { + extinfo *ext; +@@ -403,7 +407,6 @@ static extinfo *copy_variant(extinfo * old) + return ext; + } + +-@ @c + static void dump_variant(extinfo * ext) + { + dump_int(ext->glyph); +@@ -414,8 +417,6 @@ static void dump_variant(extinfo * ext) + return; + } + +- +-@ @c + static extinfo *undump_variant(void) + { + int x; +@@ -437,7 +438,6 @@ static extinfo *undump_variant(void) + return ext; + } + +-@ @c + void add_charinfo_vert_variant(charinfo * ci, extinfo * ext) + { + if (ci->vert_variants == NULL) { +@@ -451,7 +451,6 @@ void add_charinfo_vert_variant(charinfo * ci, extinfo * ext) + + } + +-@ @c + void add_charinfo_hor_variant(charinfo * ci, extinfo * ext) + { + if (ci->hor_variants == NULL) { +@@ -465,7 +464,6 @@ void add_charinfo_hor_variant(charinfo * ci, extinfo * ext) + + } + +-@ @c + extinfo *copy_variants(extinfo * o) + { + extinfo *c, *t = NULL, *h = NULL; +@@ -482,7 +480,6 @@ extinfo *copy_variants(extinfo * o) + return h; + } + +-@ @c + static void dump_charinfo_variants(extinfo * o) + { + while (o != NULL) { +@@ -493,7 +490,6 @@ static void dump_charinfo_variants(extinfo * o) + return; + } + +-@ @c + static extinfo *undump_charinfo_variants(void) + { + extinfo *c, *t = NULL, *h = NULL; +@@ -510,9 +506,12 @@ static extinfo *undump_charinfo_variants(void) + } + + +-@ Note that mant more small things like this are implemented +-as macros in the header file. +-@c ++/*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; +@@ -610,7 +609,6 @@ void set_charinfo_rp(charinfo * ci, scaled val) + ci->rp = val; + } + +-@ @c + void set_charinfo_vert_variants(charinfo * ci, extinfo * ext) + { + extinfo *c, *lst; +@@ -625,7 +623,6 @@ void set_charinfo_vert_variants(charinfo * ci, extinfo * ext) + ci->vert_variants = ext; + } + +-@ @c + void set_charinfo_hor_variants(charinfo * ci, extinfo * ext) + { + extinfo *c, *lst; +@@ -641,11 +638,10 @@ void set_charinfo_hor_variants(charinfo * ci, extinfo * ext) + + } + +-@ @c + int get_charinfo_math_kerns(charinfo * ci, int id) + { +- +- int k = 0; /* all callers check for |result>0| */ ++ /*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) { +@@ -660,7 +656,6 @@ int get_charinfo_math_kerns(charinfo * ci, int id) + return k; + } + +-@ @c + void add_charinfo_math_kern(charinfo * ci, int id, scaled ht, scaled krn) + { + int k; +@@ -693,8 +688,6 @@ void add_charinfo_math_kern(charinfo * ci, int id, scaled ht, scaled krn) + } + } + +- +-@ @c + static void dump_math_kerns(charinfo * ci) + { + int k, l; +@@ -724,11 +717,9 @@ static void dump_math_kerns(charinfo * ci) + } + } + +-@ @c + static void undump_math_kerns(charinfo * ci) + { +- int k; +- int x; ++ int k, x; + undump_int(x); + ci->top_left_math_kerns = x; + if (x > 0) +@@ -771,21 +762,23 @@ static void undump_math_kerns(charinfo * ci) + } + } + ++/*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 ++ 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. + +- 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 */ ++ /*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); +@@ -815,10 +808,13 @@ void set_charinfo_extensible(charinfo * ci, int top, int bot, int mid, int rep) + } + } + +-@ Note that many more simple things like this are implemented as macros +-in the header file. ++/*tex ++ ++ 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; +@@ -987,7 +983,6 @@ scaled char_bot_accent(internal_font_number f, int c) + return get_charinfo_bot_accent(ci); + } + +- + int char_remainder(internal_font_number f, int c) + { + charinfo *ci = char_info(f, c); +@@ -1036,7 +1031,6 @@ eight_bits *char_packets(internal_font_number f, int c) + return get_charinfo_packets(ci); + } + +-@ @c + void set_font_params(internal_font_number f, int b) + { + int i; +@@ -1055,7 +1049,6 @@ void set_font_params(internal_font_number f, int b) + } + } + +-@ @c + void set_font_math_params(internal_font_number f, int b) + { + int i; +@@ -1074,13 +1067,11 @@ void set_font_math_params(internal_font_number f, int b) + } + } + +-@ @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; +@@ -1090,12 +1081,10 @@ int copy_font(int f) + 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; +@@ -1106,7 +1095,6 @@ int copy_font(int f) + 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))); +@@ -1122,12 +1110,10 @@ int copy_font(int f) + 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)); +@@ -1135,27 +1121,23 @@ int copy_font(int f) + 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: */ ++ /*tex Not updated yet: */ + font_tables[k]->charinfo_count = font_tables[f]->charinfo_count; + return k; + } + +-@ @c + void delete_font(int f) + { + int i; +@@ -1172,7 +1154,6 @@ void delete_font(int f) + 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); +@@ -1185,24 +1166,21 @@ void delete_font(int f) + set_charinfo_hor_variants(co, NULL); + } + } +- /* free .notdef */ ++ /*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--; + } + } + } + +-@ @c + void create_null_font(void) + { + int i = new_font(); +@@ -1212,7 +1190,6 @@ void create_null_font(void) + set_font_touched(i, 1); + } + +-@ @c + boolean is_valid_font(int id) + { + int ret = 0; +@@ -1221,7 +1198,6 @@ boolean is_valid_font(int id) + return ret; + } + +-@ @c + boolean cmp_font_area(int id, str_number t) + { + char *tt = NULL; +@@ -1241,9 +1217,13 @@ boolean cmp_font_area(int id, str_number t) + return 1; + } + +-@ Here come some subroutines to deal with expanded fonts for HZ-algorithm. +-return 1 == identical +-@c ++/*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; +@@ -1257,18 +1237,16 @@ static boolean cmp_font_name(int id, char *tt) + 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)| */ ++ if (has_lig(f, c)) + return 0; + } + return 1; + } + +-@ @c + int get_tag_code(internal_font_number f, int c) + { + small_number i; +@@ -1286,7 +1264,6 @@ int get_tag_code(internal_font_number f, int c) + return -1; + } + +-@ @c + int get_lp_code(internal_font_number f, int c) + { + charinfo *ci = char_info(f, c); +@@ -1305,13 +1282,11 @@ int get_ef_code(internal_font_number f, int 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) { +@@ -1333,7 +1308,6 @@ void set_tag_code(internal_font_number f, int c, int i) + } + } + +-@ @c + void set_lp_code(internal_font_number f, int c, int i) + { + charinfo *co; +@@ -1361,7 +1335,6 @@ void set_ef_code(internal_font_number f, int c, int i) + } + } + +-@ @c + void set_no_ligatures(internal_font_number f) + { + int c; +@@ -1370,7 +1343,7 @@ void set_no_ligatures(internal_font_number f) + return; + co = char_info(f, left_boundarychar); + set_charinfo_ligatures(co, NULL); +- co = char_info(f, right_boundarychar); /* this is weird */ ++ 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; +@@ -1379,7 +1352,6 @@ void set_no_ligatures(internal_font_number f) + font_tables[f]->ligatures_disabled = 1; + } + +-@ @c + liginfo get_ligature(internal_font_number f, int lc, int rc) + { + int k = 0; +@@ -1407,7 +1379,6 @@ liginfo get_ligature(internal_font_number f, int lc, int rc) + return t; + } + +-@ @c + scaled raw_get_kern(internal_font_number f, int lc, int rc) + { + int k = 0; +@@ -1431,7 +1402,6 @@ scaled raw_get_kern(internal_font_number f, int lc, int rc) + 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))) +@@ -1440,15 +1410,14 @@ scaled get_kern(internal_font_number f, int lc, int rc) + } + + +-@ dumping and undumping fonts ++/*tex 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); \ ++#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) +@@ -1476,8 +1445,7 @@ static void dump_charinfo(int f, int c) + dump_int(get_charinfo_index(co)); + dump_string(get_charinfo_name(co)); + dump_string(get_charinfo_tounicode(co)); +- +- /* ligatures */ ++ /*tex Ligatures */ + x = 0; + if ((lig = get_charinfo_ligatures(co)) != NULL) { + while (!lig_end(lig[x])) { +@@ -1489,7 +1457,7 @@ static void dump_charinfo(int f, int c) + } else { + dump_int(x); + } +- /* kerns */ ++ /*tex Kerns */ + x = 0; + if ((kern = get_charinfo_kerns(co)) != NULL) { + while (!kern_end(kern[x])) { +@@ -1501,13 +1469,12 @@ static void dump_charinfo(int f, int c) + } else { + dump_int(x); + } +- /* packets */ ++ /*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)); +@@ -1532,6 +1499,9 @@ static void dump_font_entry(texfont * f) + 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); +@@ -1556,7 +1526,6 @@ static void dump_font_entry(texfont * f) + 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]); +@@ -1568,9 +1537,7 @@ void dump_font(int 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 )); + } +@@ -1586,7 +1553,6 @@ void dump_font(int f) + } else { + dump_int(0); + } +- + for (i = font_bc(f); i <= font_ec(f); i++) { + if (quick_char_exists(f, i)) { + dump_charinfo(f, i); +@@ -1594,7 +1560,6 @@ void dump_font(int f) + } + } + +-@ @c + static int undump_charinfo(int f) + { + charinfo *co; +@@ -1603,7 +1568,6 @@ static int undump_charinfo(int f) + liginfo *lig = NULL; + kerninfo *kern = NULL; + eight_bits *packet = NULL; +- + undump_int(i); + co = get_charinfo(f, i); + undump_int(x); +@@ -1634,8 +1598,7 @@ static int undump_charinfo(int f) + set_charinfo_used(co, x); + undump_int(x); + set_charinfo_index(co, x); +- +- /* name */ ++ /*tex Name */ + undump_int(x); + if (x > 0) { + font_bytes += x; +@@ -1643,7 +1606,7 @@ static int undump_charinfo(int f) + undump_things(*s, x); + } + set_charinfo_name(co, s); +- /* tounicode */ ++ /*tex Tounicode */ + undump_int(x); + if (x > 0) { + font_bytes += x; +@@ -1651,7 +1614,7 @@ static int undump_charinfo(int f) + undump_things(*s, x); + } + set_charinfo_tounicode(co, s); +- /* ligatures */ ++ /*tex Ligatures */ + undump_int(x); + if (x > 0) { + font_bytes += (int) ((unsigned) x * sizeof(liginfo)); +@@ -1659,7 +1622,7 @@ static int undump_charinfo(int f) + undump_things(*lig, x); + } + set_charinfo_ligatures(co, lig); +- /* kerns */ ++ /*tex Kerns */ + undump_int(x); + if (x > 0) { + font_bytes += (int) ((unsigned) x * sizeof(kerninfo)); +@@ -1668,7 +1631,7 @@ static int undump_charinfo(int f) + } + set_charinfo_kerns(co, kern); + +- /* packets */ ++ /*tex Packets */ + undump_int(x); + if (x > 0) { + font_bytes += x; +@@ -1676,7 +1639,6 @@ static int undump_charinfo(int f) + 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()); +@@ -1685,19 +1647,18 @@ static int undump_charinfo(int f) + return i; + } + +-#define undump_font_string(a) \ +- undump_int (x); \ +- if (x>0) { \ +- font_bytes += x; \ ++#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); \ ++ 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; +@@ -1711,6 +1672,9 @@ static void undump_font_entry(texfont * f) + 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; +@@ -1730,7 +1694,6 @@ static void undump_font_entry(texfont * f) + 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) +@@ -1746,7 +1709,6 @@ void undump_font(int f) + 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); +@@ -1755,12 +1717,10 @@ void undump_font(int f) + 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)); +@@ -1768,35 +1728,40 @@ void undump_font(int f) + 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 */ ++ /*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) { +- i = undump_charinfo(f); /* left boundary */ ++ /*tex left boundary */ ++ i = undump_charinfo(f); + } + undump_int(x); + if (x) { +- i = undump_charinfo(f); /* right boundary */ ++ /*tex right boundary */ ++ i = undump_charinfo(f); + } +- + i = font_bc(f); + while (i < font_ec(f)) { + i = undump_charinfo(f); + } + } + +-/* moved from pdffont.w */ ++/* The \PK\ pixel density value from |texmf.cnf| */ ++ ++int pk_dpi; + +-@ @c +-int pk_dpi; /* PK pixel density value from \.{texmf.cnf} */ ++/*tex ++ ++ This one looks up the font for a \TFM\ with name |s| loaded at |fs| size and ++ if found then flushes |s|. ++ ++*/ + +-@ @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++) { +@@ -1814,9 +1779,14 @@ internal_font_number tfm_lookup(char *s, scaled fs) + return null_font; + } + +-@ @c ++/*tex ++ ++ This returns the multiple of |font_step(f)| that is nearest to |e|. ++ ++*/ ++ + 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; +@@ -1842,7 +1812,6 @@ int fix_expand_value(internal_font_number f, int 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); +@@ -1850,19 +1819,20 @@ void set_expand_params(internal_font_number f, int stretch_limit, int shrink_lim + set_font_max_stretch(f, stretch_limit); + } + +-@ @c ++/*tex ++ ++ This reads font expansion spec and load expanded font. ++ ++*/ ++ + 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); +@@ -1882,24 +1852,21 @@ void read_expand_font(void) + normal_error("font expansion", "invalid limit(s)"); + if (scan_keyword("autoexpand")) { + normal_warning("font expansion", "autoexpand not supported"); +- /* Scan an optional space */ ++ /*tex 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 */ ++ /*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))) ++ ((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))) ++ ((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)) +@@ -1908,11 +1875,17 @@ void read_expand_font(void) + } + } + +-@ @c ++/*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) +-{ /* letter-space a font by creating a virtual font */ +- pointer u; /* user's font identifier */ +- str_number t; /* name for the frozen font identifier */ ++{ ++ pointer u; ++ str_number t; + internal_font_number f, k; + boolean nolig = false; + get_r_token(); +@@ -1934,11 +1907,17 @@ void new_letterspaced_font(small_number a) + font_id_text(f) = t; + } + +-@ @c ++/*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) +-{ /* 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 */ ++{ ++ pointer u; ++ str_number t; + internal_font_number f, k; + get_r_token(); + u = cur_cs; +@@ -1956,7 +1935,6 @@ void make_font_copy(small_number a) + font_id_text(f) = t; + } + +-@ @c + void glyph_to_unicode(void) + { + str_number s1, s2; +diff --git a/texk/web2c/luatexdir/font/tfmofm.c b/texk/web2c/luatexdir/font/tfmofm.c +new file mode 100644 +index 000000000..f5d8c2e92 +--- /dev/null ++++ b/texk/web2c/luatexdir/font/tfmofm.c +@@ -0,0 +1,1186 @@ ++/* ++ ++Copyright 2006-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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=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 ((zec)) 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; ++} +diff --git a/texk/web2c/luatexdir/font/tounicode.w b/texk/web2c/luatexdir/font/tounicode.c +similarity index 64% +rename from texk/web2c/luatexdir/font/tounicode.w +rename to texk/web2c/luatexdir/font/tounicode.c +index 441a10dd5..f0449aff9 100644 +--- a/texk/web2c/luatexdir/font/tounicode.w ++++ b/texk/web2c/luatexdir/font/tounicode.c +@@ -1,41 +1,46 @@ +-% tounicode.w +-% +-% Copyright 2006 Han The Thanh, +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2006 Han The Thanh, ++Copyright 2006-2010 Taco Hoekwater + ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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()| */ ++ ++/*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); ++ return strcmp(((const glyph_unicode_entry *) pa)->name, ((const glyph_unicode_entry *) pb)->name); + } + + static glyph_unicode_entry *new_glyph_unicode_entry(void) +@@ -54,7 +59,6 @@ static void destroy_glyph_unicode_entry(void *pa, void *pb) + (void) pb; + xfree(e->name); + if (e->code == UNI_STRING) { +- assert(e->unicode_seq != NULL); + xfree(e->unicode_seq); + } + } +@@ -65,31 +69,34 @@ void glyph_unicode_free(void) + 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 */ ++ /*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++; /* ignore leading spaces */ ++ p++; + l = (int) strlen(p); ++ /*tex Ignore traling spaces. */ + while (l > 0 && p[l - 1] == ' ') +- l--; /* ignore traling spaces */ +- valid_unistr = 1; /* a unicode value is the most common case */ ++ 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; /* if a space occurs we treat this entry as a string */ ++ valid_unistr = 2; + else if (!isXdigit((unsigned char)p[i])) { + valid_unistr = 0; + break; +@@ -100,23 +107,21 @@ void def_tounicode(str_number glyph, str_number unistr) + return; + } + if (glyph_unicode_tree == NULL) { +- glyph_unicode_tree = +- avl_create(comp_glyph_unicode_entry, NULL, &avl_xallocator); +- assert(glyph_unicode_tree != NULL); ++ glyph_unicode_tree = avl_create(comp_glyph_unicode_entry, NULL, &avl_xallocator); + } + t.name = buf; +- /* allow overriding existing entries */ ++ /*tex 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 */ ++ } else { ++ /*tex Make a 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 */ ++ 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; +@@ -125,28 +130,24 @@ void def_tounicode(str_number glyph, str_number unistr) + 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 */ +- ++ /*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; +@@ -171,33 +172,33 @@ static long check_unicode_value(char *s, boolean multiple_value) + 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 ++/* ++ ++ 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; +- +- /* skip dummy entries */ ++ /*tex Skip dummy entries. */ + if (s == NULL || s == notdef) + return; +- +- /* strip everything after the first dot */ ++ /*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; +- +- /* check for case of multiple components separated by |'_'| */ ++ /*tex Check for case of multiple components separated by |_|. */ + p = strchr(s, '_'); + if (p != NULL) { + assert(strlen(s) < sizeof(buf)); +@@ -213,20 +214,26 @@ static void set_glyph_unicode(char *s, glyph_unicode_entry * gp) + tmp.code = UNI_UNDEF; + set_glyph_unicode(s, &tmp); + switch (tmp.code) { +- case UNI_UNDEF: /* not found, do nothing */ ++ case UNI_UNDEF: ++ /*tex Not found, do nothing. */ + break; +- case UNI_STRING: /* s matched an entry with string value in the database */ ++ 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: /* s is a multiple value of form "uniXXXX" */ ++ 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: /* s matched an entry with numeric value in the +- database, or a value derived from "uXXXX" */ ++ 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)); + } +@@ -243,8 +250,7 @@ static void set_glyph_unicode(char *s, glyph_unicode_entry * gp) + gp->unicode_seq = xstrdup(buf2); + return; + } +- +- /* lookup for glyph name in the database */ ++ /*tex Lookup glyph name in the database. */ + tmp.name = s; + tmp.code = UNI_UNDEF; + ptmp = (glyph_unicode_entry *) avl_find(glyph_unicode_tree, &tmp); +@@ -253,23 +259,24 @@ static void set_glyph_unicode(char *s, glyph_unicode_entry * gp) + gp->unicode_seq = ptmp->unicode_seq; + return; + } +- +- /* check for case of "uniXXXX" (multiple 4-hex-digit values allowed) */ ++ /*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) /* single value */ ++ if (strlen(p) == 4) { ++ /*tex Single value: */ + gp->code = code; +- else { /* multiple value */ ++ } else { ++ /*tex Multiple value: */ + gp->code = UNI_EXTRA_STRING; + gp->unicode_seq = xstrdup(p); + } + } +- return; /* since the last case cannot happen */ ++ /*tex Since the last case cannot happen: */ ++ return; + } +- +- /* check for case of "uXXXX" (single value up to 6 hex digits) */ ++ /*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); +@@ -280,9 +287,7 @@ static void set_glyph_unicode(char *s, glyph_unicode_entry * gp) + } + } + +-@ @c +-static void set_cid_glyph_unicode(long index, glyph_unicode_entry * gp, +- internal_font_number f) ++static void set_cid_glyph_unicode(long index, glyph_unicode_entry * gp, internal_font_number f) + { + char *s; + if (font_tounicode(f)) { +@@ -290,15 +295,14 @@ static void set_cid_glyph_unicode(long index, glyph_unicode_entry * gp, + gp->code = UNI_EXTRA_STRING; + gp->unicode_seq = xstrdup(s); + } else { +- /* no fallback as we're providing them ourselves */ ++ /*tex No fall back as we're providing them ourselves. */ + } + } else { +- /* fallback */ ++ /*tex Fall back. */ + gp->code = index; + } + } + +-@ @c + int write_tounicode(PDF pdf, char **glyph_names, char *name) + { + char buf[SMALL_BUF_SIZE], *p; +@@ -314,64 +318,75 @@ int write_tounicode(PDF pdf, char **glyph_names, char *name) + 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" */ ++ 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> \n" "endcodespacerange\n", buf, buf, buf, buf, buf); +- +- /* set gtab */ ++ 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> \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], >ab[i]); + } + gtab[256].code = UNI_UNDEF; +- +- /* set |range_size| */ ++ /*tex 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 */ ++ /*tex Single entry: */ ++ range_size[i] = 1; + i++; + } else if (gtab[i].code == UNI_UNDEF) { +- range_size[i] = 0; /* no entry */ ++ /*tex No entry: */ ++ range_size[i] = 0; + i++; +- } else { /* gtab[i].code >= 0 */ ++ } else { ++ /*tex |gtab[i].code >= 0| */ + j = i; +- while (i < 256 && gtab[i + 1].code >= 0 && +- gtab[i].code + 1 == gtab[i + 1].code) ++ 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 */ ++ /*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); + } + } +- +- /* calculate |bfrange_count| and |bfchar_count| */ ++ /*tex Calculate |bfrange_count| and |bfchar_count|. */ + bfrange_count = 0; + bfchar_count = 0; + for (i = 0; i < 256;) { +@@ -384,8 +399,7 @@ int write_tounicode(PDF pdf, char **glyph_names, char *name) + } else + i++; + } +- +- /* write out bfrange */ ++ /*tex Write out |bfrange|. */ + i = 0; + write_bfrange: + if (bfrange_count > 100) +@@ -405,8 +419,7 @@ int write_tounicode(PDF pdf, char **glyph_names, char *name) + pdf_printf(pdf, "endbfrange\n"); + if (bfrange_count > 0) + goto write_bfrange; +- +- /* write out bfchar */ ++ /*tex Write out |bfchar|. */ + i = 0; + write_bfchar: + if (bfchar_count > 100) +@@ -421,7 +434,7 @@ int write_tounicode(PDF pdf, char **glyph_names, char *name) + i += range_size[i]; + else if (range_size[i] == 0) + i++; +- else /* |range_size[i] == 1| */ ++ else + break; + } + assert(i < 256 && gtab[i].code != UNI_UNDEF); +@@ -435,66 +448,63 @@ int write_tounicode(PDF pdf, char **glyph_names, char *name) + pdf_printf(pdf, "endbfchar\n"); + if (bfchar_count > 0) + goto write_bfchar; +- +- /* free strings allocated by |set_glyph_unicode()| */ ++ /*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_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); +- ++ 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> \n"@/ +- "endcodespacerange\n", buf, buf, buf, buf, buf); ++ 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> \n" ++ "endcodespacerange\n", ++ buf, buf, buf, buf, buf); + xfree(buf); +- /* set up gtab */ ++ /*tex Set up |gtab|: */ + for (i = 0; i < 65537; ++i) { + gtab[i].code = UNI_UNDEF; + } +@@ -510,29 +520,33 @@ int write_cid_tounicode(PDF pdf, fo_entry * fo, internal_font_number f) + } + } + } +- +- /* set |range_size| */ ++ /*tex 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 */ ++ /*tex Single entry. */ ++ range_size[i] = 1; + i++; + } else if (gtab[i].code == UNI_UNDEF) { +- range_size[i] = 0; /* no entry */ ++ /*tex No entry. */ ++ range_size[i] = 0; + i++; +- } else { /* |gtab[i].code >= 0| */ ++ } 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++; + } +- /* at this point i is the last entry of the subrange */ +- i++; /* move i to the next entry */ ++ /* 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; + } + } +- +- /* calculate |bfrange_count| and |bfchar_count| */ ++ /*tex Calculate |bfrange_count| and |bfchar_count|. */ + bfrange_count = 0; + bfchar_count = 0; + for (i = 0; i < 65536;) { +@@ -545,8 +559,7 @@ int write_cid_tounicode(PDF pdf, fo_entry * fo, internal_font_number f) + } else + i++; + } +- +- /* write out bfrange */ ++ /*tex Write out |bfrange|. */ + i = 0; + write_bfrange: + if (bfrange_count > 100) +@@ -558,16 +571,13 @@ int write_cid_tounicode(PDF pdf, fo_entry * fo, internal_font_number f) + 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)); ++ 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 */ ++ /*tex Write out |bfchar| */ + i = 0; + write_bfchar: + if (bfchar_count > 100) +@@ -582,12 +592,12 @@ int write_cid_tounicode(PDF pdf, fo_entry * fo, internal_font_number f) + i += range_size[i]; + else if (range_size[i] == 0) + i++; +- else /* |range_size[i] == 1| */ ++ 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)); +@@ -596,16 +606,18 @@ int write_cid_tounicode(PDF pdf, fo_entry * fo, internal_font_number f) + pdf_printf(pdf, "endbfchar\n"); + if (bfchar_count > 0) + goto write_bfchar; +- +- /* free strings allocated by |set_glyph_unicode()| */ ++ /*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_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; +diff --git a/texk/web2c/luatexdir/font/tt_glyf.w b/texk/web2c/luatexdir/font/tt_glyf.c +similarity index 76% +rename from texk/web2c/luatexdir/font/tt_glyf.w +rename to texk/web2c/luatexdir/font/tt_glyf.c +index c0f17e5ab..750e126aa 100644 +--- a/texk/web2c/luatexdir/font/tt_glyf.w ++++ b/texk/web2c/luatexdir/font/tt_glyf.c +@@ -1,28 +1,25 @@ +-% tt_glyf.w +-% +-% Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata, +-% the dvipdfmx project team +-% Copyright 2006-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@* Subsetting glyf, updating loca, hmtx, etc. ++Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata, ++the dvipdfmx project team ++Copyright 2006-2012 Taco Hoekwater + +-@ @c ++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 . ++ ++*/ + + #include "ptexlib.h" + +@@ -31,7 +28,6 @@ + #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 +@@ -39,55 +35,41 @@ + 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 { +@@ -102,28 +84,21 @@ USHORT tt_add_glyph(struct tt_glyphs * g, USHORT gid, USHORT 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->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 */ + +-@ 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; +@@ -134,7 +109,6 @@ struct tt_glyphs *tt_build_init(void) + g->used_slot = NEW(8192, unsigned char); + memset(g->used_slot, 0, 8192); + tt_add_glyph(g, 0, 0); +- + return g; + } + +@@ -159,37 +133,33 @@ 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 */ ++ /*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; +- /* temp */ ++ /*tex Something temporary: */ + ULONG *location, offset; + long i; +- USHORT *w_stat; /* Estimate most frequently appeared width */ +- ++ /*tex Estimate the most frequently appeared width. */ ++ USHORT *w_stat; + int tex_font = fd->tex_font; + int streamprovider = 0; + int callback_id = 0 ; +@@ -197,60 +167,35 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + 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! */ ++ /*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); ++ 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) { +@@ -262,34 +207,27 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + } 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. +- */ ++ memset(w_stat, 0, (size_t) (sizeof(USHORT) * ((long unsigned) g->emsize + 2))); + 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. +- */ ++ /*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; /* old gid */ ++ USHORT gid; + ULONG loc, len; + BYTE *p, *endptr; + SHORT number_of_contours; +- +- if (i >= g->num_glyphs) /* finished */ ++ 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; +@@ -306,52 +244,43 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + if (g->gd[i].advw <= g->emsize) { + w_stat[g->gd[i].advw]++; + } else { +- w_stat[g->emsize + 1]++; /* larger than em */ ++ /*tex It's larger than em. */ ++ w_stat[g->emsize + 1]++; + } + +- if (len == 0) { /* Does not contains any data. */ ++ if (len == 0) { ++ /*tex Does not contain any data. */ + continue; + } else if (len < 10) { + formatted_error("ttf","invalid glyph data (gid %u)", gid); + } +- +-/* todo: no need for this */ ++ /*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); +- /* |_FIXME_| */ +-#if 1 +- if (!vmtx) /* |vertOriginY == sTypeAscender| */ +- g->gd[i].tsb = +- (SHORT) (g->default_advh - g->default_tsb - g->gd[i].ury); +-#endif ++ 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); +- +- /* Read evrything else. */ ++ /*tex Read evrything else. */ + sfnt_read(p, (int) len - 10, sfont); +- /* +- Fix GIDs of composite glyphs. +- */ ++ /*tex Fix GIDs of composite glyphs. */ + if (number_of_contours < 0) { +- USHORT flags, cgid, new_gid; /* flag, gid of a component */ ++ USHORT flags, cgid, new_gid; + 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)); +@@ -363,34 +292,38 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + new_gid = tt_add_glyph(g, cgid, find_empty_slot(g)); + } + p += sfnt_put_ushort(p, new_gid); +- /* +- * Just skip remaining part. +- */ ++ /*tex Just skip remaining part. */ + p += (flags & ARG_1_AND_2_ARE_WORDS) ? 4 : 2; +- if (flags & WE_HAVE_A_SCALE) /* F2Dot14 */ ++ if (flags & WE_HAVE_A_SCALE) { ++ /*tex |F2Dot14| */ + p += 2; +- else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) /* F2Dot14 x 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) /* F2Dot14 x 4 */ ++ } else if (flags & WE_HAVE_A_TWO_BY_TWO) { ++ /*tex four times |F2Dot14| */ + p += 8; ++ } + } while (flags & MORE_COMPONENTS); +- /* +- TrueType instructions comes here: ++ /*tex ++ ++ TrueType instructions comes here. The call pattern is: + +- |length_of_instruction| (|ushort|) ++ \starttyping ++ |length_of_instruction| (|ushort|) ++ instruction (|byte * length_of_instruction|) ++ \stoptyping + +- instruction (|byte * length_of_instruction|) + */ + } + } + RELEASE(location); + RELEASE(hmtx); +- if (vmtx) ++ 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) { +@@ -400,13 +333,11 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + } + } + 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; +@@ -419,17 +350,17 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + num_hm_known = 1; + } + } +- /* All advance widths are same. */ ++ /*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); ++ 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. + +- /* +- 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); +@@ -437,12 +368,10 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + 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++) { +@@ -470,12 +399,10 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + } 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 */ ++ 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) { +@@ -487,18 +414,15 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + 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 */ ++ /*tex We free data here since it consume much memory. */ + g->gd[i].length = 0; + g->gd[i].data = NULL; + } +@@ -507,7 +431,6 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + } 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) { +@@ -515,20 +438,17 @@ int tt_build_tables(sfnt * sfont, struct tt_glyphs *g, fd_entry * fd) + } + 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) ++ if (os2) { + RELEASE(os2); +- ++ } + return 0; + } + +@@ -539,66 +459,33 @@ int tt_get_metrics(sfnt * sfont, struct tt_glyphs *g) + 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 +- ) ++ 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"); +- +- /* +- 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); ++ 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) { +@@ -610,22 +497,16 @@ int tt_get_metrics(sfnt * sfont, struct tt_glyphs *g) + } 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. +- */ ++ /*tex Read glyf table. */ + offset = sfnt_locate_table(sfont, "glyf"); + for (i = 0; i < g->num_glyphs; i++) { +- USHORT gid; /* old gid */ ++ USHORT 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; +@@ -639,33 +520,30 @@ int tt_get_metrics(sfnt * sfont, struct tt_glyphs *g) + } + 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 */ ++ /*tex It's larger than em: */ ++ w_stat[g->emsize + 1]++; + } +- +- if (len == 0) { /* Does not contains any data. */ ++ 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)); +- /*number_of_contours = */(void)sfnt_get_short(sfont); +- +- /* BoundingBox: FWord x 4 */ ++ /*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); +- /* |_FIXME_| */ +-#if 1 +- if (!vmtx) /* |vertOriginY == sTypeAscender| */ +- g->gd[i].tsb = +- (SHORT) (g->default_advh - g->default_tsb - g->gd[i].ury); +-#endif ++ 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); +@@ -673,13 +551,11 @@ int tt_get_metrics(sfnt * sfont, struct tt_glyphs *g) + RELEASE(hhea); + RELEASE(head); + RELEASE(os2); +- +- if (vmtx) ++ 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) { +@@ -689,7 +565,5 @@ int tt_get_metrics(sfnt * sfont, struct tt_glyphs *g) + } + } + RELEASE(w_stat); +- +- + return 0; + } +diff --git a/texk/web2c/luatexdir/font/tt_table.w b/texk/web2c/luatexdir/font/tt_table.c +similarity index 86% +rename from texk/web2c/luatexdir/font/tt_table.w +rename to texk/web2c/luatexdir/font/tt_table.c +index a4164f39d..dfcd4d650 100644 +--- a/texk/web2c/luatexdir/font/tt_table.w ++++ b/texk/web2c/luatexdir/font/tt_table.c +@@ -1,52 +1,41 @@ +-% tt_table.w +-% +-% Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata, +-% the dvipdfmx project team +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2002 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project ++ team ++Copyright 2006-2010 Taco Hoekwater + ++This file is part of LuaTeX. + +-#include "ptexlib.h" ++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 . ++ ++*/ + ++#include "ptexlib.h" + #include + #include "font/sfnt.h" + #include "font/tt_table.h" + +-@ tables contains information refered by other tables +- +- |maxp->numGlyphs, etc --> loca, etc| +- +- |hhea->numberOfHMetrics --> hmtx| ++/*tex + +- |head->indexToLocFormat --> loca| ++ Tables contain information referred by other tables. + +- |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); +@@ -69,7 +58,6 @@ char *tt_pack_head_table(struct tt_head_table *table) + p += sfnt_put_short(p, table->fontDirectionHint); + p += sfnt_put_short(p, table->indexToLocFormat); + p += sfnt_put_short(p, table->glyphDataFormat); +- + return data; + } + +@@ -77,11 +65,8 @@ 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); +@@ -103,14 +88,12 @@ struct tt_head_table *tt_read_head_table(sfnt * 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); +@@ -127,16 +110,13 @@ char *tt_pack_maxp_table(struct tt_maxp_table *table) + 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); +@@ -153,7 +133,6 @@ struct tt_maxp_table *tt_read_maxp_table(sfnt * sfont) + table->maxSizeOfInstructions = sfnt_get_ushort(sfont); + table->maxComponentElements = sfnt_get_ushort(sfont); + table->maxComponentDepth = sfnt_get_ushort(sfont); +- + return table; + } + +@@ -161,7 +140,6 @@ 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); +@@ -178,7 +156,6 @@ char *tt_pack_hhea_table(struct tt_hhea_table *table) + } + p += sfnt_put_short(p, table->metricDataFormat); + p += sfnt_put_ushort(p, table->numberOfHMetrics); +- + return data; + } + +@@ -186,9 +163,7 @@ 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); +@@ -207,23 +182,19 @@ struct tt_hhea_table *tt_read_hhea_table(sfnt * 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->advanceHeightMax); + p += sfnt_put_short(p, table->minTopSideBearing); + p += sfnt_put_short(p, table->minBottomSideBearing); + p += sfnt_put_short(p, table->yMaxExtent); +@@ -234,7 +205,6 @@ char *tt_pack_vhea_table(struct tt_vhea_table *table) + p += sfnt_put_short(p, table->reserved[i]); + } + p += sfnt_put_ushort(p, table->numOfLongVerMetrics); +- + return data; + } + +@@ -242,15 +212,13 @@ 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->advanceHeightMax = sfnt_get_short(sfont); + table->minTopSideBearing = sfnt_get_short(sfont); + table->minBottomSideBearing = sfnt_get_short(sfont); + table->yMaxExtent = sfnt_get_short(sfont); +@@ -261,34 +229,26 @@ struct tt_vhea_table *tt_read_vhea_table(sfnt * sfont) + (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. +- */ ++ 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); +@@ -296,22 +256,20 @@ struct tt_VORG_table *tt_read_VORG_table(sfnt * sfont) + } else { + vorg = NULL; + } +- + return vorg; + } + ++/*tex + +-@ hmtx and vmtx ++ Reading and writing |hmtx| and |vmtx| depends on other tables, like |maxp|, ++ |hhea| and |vhea|. + +-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 *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) +@@ -319,26 +277,23 @@ struct tt_longMetrics *tt_read_longMetrics(sfnt * sfont, USHORT numGlyphs, + m[gid].sideBearing = sfnt_get_short(sfont); + m[gid].advance = last_adv; + } +- + return m; + } + +-@ OS/2 table ++/*tex ++ ++ The |OS/2| table may not exist. ++ ++*/ + +-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); +@@ -382,37 +337,31 @@ struct tt_os2__table *tt_read_os2__table(sfnt * 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 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' */ ++ /*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) { +@@ -428,33 +377,28 @@ tt_get_name(sfnt * sfont, char *dest, USHORT destlen, + 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 */ ++ /*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"); +- /* +- Wrokaround for some bad TTfonts: +- Language ID value 0xffffu for `accept any language ID' +- */ ++ /*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) { +- /* +- Finally falling back to Mac Roman name field. +- Warning: Some bad Japanese TTfonts using SJIS encoded string in the +- Mac Roman name field. +- */ ++ /*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; + } +diff --git a/texk/web2c/luatexdir/font/vfovf.c b/texk/web2c/luatexdir/font/vfovf.c +new file mode 100644 +index 000000000..e1fdb903e +--- /dev/null ++++ b/texk/web2c/luatexdir/font/vfovf.c +@@ -0,0 +1,1444 @@ ++/* ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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); ++} +diff --git a/texk/web2c/luatexdir/font/vfpacket.c b/texk/web2c/luatexdir/font/vfpacket.c +new file mode 100644 +index 000000000..41132c54a +--- /dev/null ++++ b/texk/web2c/luatexdir/font/vfpacket.c +@@ -0,0 +1,445 @@ ++/* ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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)"); ++ } ++ } ++ } ++ } ++} +diff --git a/texk/web2c/luatexdir/font/vfpacket.w b/texk/web2c/luatexdir/font/vfpacket.w +deleted file mode 100644 +index d2329916a..000000000 +--- a/texk/web2c/luatexdir/font/vfpacket.w ++++ /dev/null +@@ -1,424 +0,0 @@ +-% vfpacket.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2013 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +- +-#include "ptexlib.h" +- +-@ 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)"); +- } +- } +- } +- } +-} +diff --git a/texk/web2c/luatexdir/font/writeenc.w b/texk/web2c/luatexdir/font/writeenc.c +similarity index 71% +rename from texk/web2c/luatexdir/font/writeenc.w +rename to texk/web2c/luatexdir/font/writeenc.c +index 1a7ed3891..554c246a5 100644 +--- a/texk/web2c/luatexdir/font/writeenc.w ++++ b/texk/web2c/luatexdir/font/writeenc.c +@@ -1,34 +1,37 @@ +-% writeenc.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ All encoding entries go into AVL tree for fast search by name. +-@c ++/*tex ++ ++ All encoding entries go into AVL tree for fast search by name. ++ ++*/ ++ + struct avl_table *fe_tree = NULL; + +-@ AVL sort |fe_entry| into |fe_tree| by name +-@c ++/* 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; +@@ -41,7 +44,8 @@ static fe_entry *new_fe_entry(void) + fe = xtalloc(1, fe_entry); + fe->name = NULL; + fe->fe_objnum = 0; +- fe->glyph_names = NULL; /* encoding file not yet read in */ ++ /*tex The encoding file is not yet read in. */ ++ fe->glyph_names = NULL; + fe->tx_tree = NULL; + return fe; + } +@@ -67,7 +71,8 @@ static void register_fe_entry(fe_entry * fe) + } + assert(fe != NULL); + assert(fe->name != NULL); +- assert(lookup_fe_entry(fe->name) == NULL); /* encoding not yet registered */ ++ /*tex The encoding is not yet registered. */ ++ assert(lookup_fe_entry(fe->name) == NULL); + aa = avl_probe(fe_tree, fe); + assert(aa != NULL); + } +@@ -85,9 +90,7 @@ fe_entry *get_fe_entry(char *s) + return fe; + } + +-@ @c +-static void write_enc(PDF pdf, char **glyph_names, struct avl_table *tx_tree, +- int fe_objnum) ++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; +@@ -102,9 +105,9 @@ static void write_enc(PDF pdf, char **glyph_names, struct avl_table *tx_tree, + 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 */ ++ if (*p == i_old + 1) { + pdf_add_name(pdf, glyph_names[*p]); +- else { ++ } else { + pdf_add_int(pdf, *p); + pdf_add_name(pdf, glyph_names[*p]); + } +@@ -134,8 +137,7 @@ void write_fontencodings(PDF pdf) + write_fontencoding(pdf, fe); + } + +-@ cleaning up... +-@c ++/*tex Cleaning up \unknown */ + + static void destroy_fe_entry(void *pa, void *pb) + { +diff --git a/texk/web2c/luatexdir/font/writefont.w b/texk/web2c/luatexdir/font/writefont.c +similarity index 65% +rename from texk/web2c/luatexdir/font/writefont.w +rename to texk/web2c/luatexdir/font/writefont.c +index 2ba81d617..5d75ef80c 100644 +--- a/texk/web2c/luatexdir/font/writefont.w ++++ b/texk/web2c/luatexdir/font/writefont.c +@@ -1,69 +1,69 @@ +-% writefont.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. + ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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 }, ++ { "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 } ++ { "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 */ ++/*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); ++ 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 ++/*tex We initialize data structure for |/Type| |/Font|: */ ++ + static fo_entry *new_fo_entry(void) + { + fo_entry *fo; +@@ -81,8 +81,8 @@ static fo_entry *new_fo_entry(void) + return fo; + } + +-@ initialize data structure for /Type /FontDescriptor +-@c ++/*tex We initialize data structure for |/Type| |/FontDescriptor|: */ ++ + fd_entry *new_fd_entry(internal_font_number f) + { + fd_entry *fd; +@@ -108,12 +108,14 @@ fd_entry *new_fd_entry(internal_font_number 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. ++/*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. ++ ++*/ + +-@c + static void preset_fontmetrics(fd_entry * fd, internal_font_number f) + { + int i; +@@ -143,9 +145,7 @@ 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 */ ++ /*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; +@@ -178,34 +178,41 @@ static void write_fontmetrics(PDF pdf, fd_entry * fd) + 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_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); + } + +-@ +-@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) ++ 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 ++ } 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); +@@ -218,27 +225,20 @@ static void pdf_dict_add_fontname(PDF pdf, const char *key, fd_entry * fd) + 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); + } + +@@ -247,66 +247,72 @@ 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); ++ /*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); +- assert(aa != NULL); ++ if (aa == NULL) { ++ /*tex Is this a problem? */ ++ } + } + + 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: */ ++ /*tex An encoding is needed for \TRUETYPE\ writing: */ + fo->fd->fe = fo->fe; +- /* map entry needed by TrueType writing: */ ++ /*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); +- 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|. ++/*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|. ++ ++*/ + +-@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) */ ++ /*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])); +- assert(aa != NULL); ++ if (aa == NULL) { ++ /*tex Is this a problem? */ ++ } + } + } + } + } + +-@ +-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. ++/*tex ++ ++ Function |mark_chars| has 2 uses: + +-@c +-static struct avl_table *mark_chars(fo_entry * fo, struct avl_table *tx_tree, +- internal_font_number f) ++ \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; +@@ -319,19 +325,19 @@ static struct avl_table *mark_chars(fo_entry * fo, struct avl_table *tx_tree, + j = xtalloc(1, int); + *j = i; + aa = avl_probe(tx_tree, j); +- assert(aa != NULL); ++ if (aa == NULL) { ++ /*tex Is this a problem? */ ++ } + } + } + 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| */ ++ /*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; +@@ -341,7 +347,7 @@ static void get_char_range(fo_entry * fo, internal_font_number f) + 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 */ ++ /*tex No character has been used from this font. */ + fo->last_char = 0; + fo->first_char = fo->last_char + 1; + } +@@ -350,7 +356,7 @@ static void get_char_range(fo_entry * fo, internal_font_number f) + static int font_has_subset(internal_font_number f) + { + int i, s; +- /* search for |first_char| and |last_char| */ ++ /*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; +@@ -364,19 +370,14 @@ static int font_has_subset(internal_font_number f) + 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) +@@ -393,19 +394,21 @@ static void write_charwidth_array(PDF pdf, fo_entry * fo, internal_font_number f + pdf_end_obj(pdf); + } + +-@ Remark: Font objects from embedded PDF files are never registered +-into |fo_tree|; they are individually written out. +-@c ++/*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; +- 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); + } +@@ -415,60 +418,63 @@ 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); ++ 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); +- assert(aa != NULL); ++ if (aa == NULL) { ++ /*tex Is this a problem? */ ++ } + } + +-@ +-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 ++/*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) + { +- assert(is_included(fd->fm)); + if (is_cidkeyed(fd->fm)) { + if (is_opentype(fd->fm)) { + writetype0(pdf, fd); +- } else if (is_truetype(fd->fm)) { ++ } else if (is_truetype(fd->fm)) { + if (!writetype2(pdf, fd)) { + writetype0(pdf,fd); +- fd->fm->type |= F_OTF; fd->fm->type ^= F_TRUETYPE; ++ 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)) { ++ 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)) ++ if (is_type1(fd->fm)) { ++ writet1(pdf, fd); ++ } else if (is_truetype(fd->fm)) { + writettf(pdf, fd); +- else if (is_opentype(fd->fm)) ++ } else if (is_opentype(fd->fm)) { + writeotf(pdf, fd); +- else +- assert(0); ++ } else { ++ normal_error("fonts","there is a problem writing the font file (2)"); ++ } + } + 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 */ ++ /*tex The font file stream: */ ++ pdf_begin_obj(pdf, fd->ff_objnum, OBJSTM_NEVER); + 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)) ++ /*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"); +-#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); +@@ -478,7 +484,7 @@ static void write_fontfile(PDF pdf, fd_entry * fd) + } else if (is_opentype(fd->fm)) { + pdf_dict_add_name(pdf, "Subtype", "Type1C"); + } else { +- assert(0); /* todo: error messages */ ++ normal_error("fonts","there is a problem writing the font file (3)"); + } + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); +@@ -488,17 +494,11 @@ static void write_fontfile(PDF pdf, fd_entry * fd) + 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 */ +@@ -513,31 +513,37 @@ static void write_fontdescriptor(PDF pdf, fd_entry * fd) + 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| */ ++ /*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)) { +- /* this will set |fd->ff_found| if font file is found */ ++ /*tex 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) ++ if (fd->fm->fd_flags != FD_FLAGS_NOT_SET_IN_MAPLINE) { + fd_flags = (int) fd->fm->fd_flags; +- else if (fd->ff_found) ++ } 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; ++ } 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", +@@ -554,19 +560,20 @@ static void write_fontdescriptor(PDF pdf, fd_entry * fd) + else if (is_opentype(fd->fm)) + pdf_dict_add_ref(pdf, "FontFile3", (int) fd->ff_objnum); + else +- assert(0); ++ normal_error("fonts","there is a problem writing the font file (4)"); + } 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; ++ /*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); +@@ -575,16 +582,15 @@ static void write_fontdescriptor(PDF pdf, fd_entry * fd) + else if (is_opentype(fd->fm)) + pdf_dict_add_ref(pdf, "FontFile3", (int) fd->ff_objnum); + else +- assert(0); ++ 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); + } +- /* +- Currently we don't export the optional keys for CID fonts like +- \.{/Style << /Panose <12-byte string> >>} and we probably never +- will. ++ /*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); +@@ -601,21 +607,13 @@ static void write_fontdescriptors(PDF pdf) + 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 */ ++ /*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)) { +- assert(fo->fd->builtin_glyph_names != NULL); + fo->tounicode_objnum = write_tounicode(pdf, fo->fd->builtin_glyph_names, fo->fm->tfm_name); + } + } +@@ -629,11 +627,9 @@ static void write_fontdictionary(PDF pdf, fo_entry * fo) + 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); ++ 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); +- 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); +@@ -642,8 +638,9 @@ static void write_fontdictionary(PDF pdf, fo_entry * fo) + 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_out(pdf, '\n'); ++ pdf_set_space(pdf); + } + pdf_end_dict(pdf); + pdf_end_obj(pdf); +@@ -656,49 +653,53 @@ static void write_fontdictionaries(PDF pdf) + 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)) ++ 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 ++/*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); /* see \.{writeenc.w} */ ++ write_fontencodings(pdf); + 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| */ ++ /*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; +- 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)) { +- /* ++ /*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))) { +- /* 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. */ ++ /*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); + } + } +- /* for |write_charwidth_array|: */ ++ /*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)) { +@@ -713,7 +714,7 @@ static void create_fontdictionary(PDF pdf, internal_font_number f) + if (fo->fe != NULL) { + mark_reenc_glyphs(fo, f); + if (!is_type1(fo->fm)) { +- /* mark reencoded characters as chars on TeX level */ ++ /*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)) { +@@ -721,16 +722,17 @@ static void create_fontdictionary(PDF pdf, internal_font_number f) + } + } + } else { +- /* mark non-reencoded characters as chars on TeX level */ ++ /*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 { +- /* +- Builtin fonts still need the /Widths array and /FontDescriptor +- (to avoid error 'font FOO contains bad /BBox'). ++ /*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); +@@ -745,8 +747,6 @@ static void create_fontdictionary(PDF pdf, internal_font_number f) + } + } + +-@ +-@c + static int has_ttf_outlines(fm_entry * fm) + { + FILE *f = fopen(fm->ff_name, "rb"); +@@ -767,38 +767,39 @@ 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. ++ /*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) { +- /* +- Create a virtual font map entry, as this is needed by the +- rest of the font inclusion mechanism. ++ /*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(); +- 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 */ ++ /*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)) { +- /* +- 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. +- */ ++ /*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; +@@ -807,35 +808,34 @@ void do_pdf_font(PDF pdf, internal_font_number f) + formatted_error("font","file '%s' does not contain font '%s'",fm->ff_name, fm->ps_name); + } + } +- /* Needed for the CIDSystemInfo: */ ++ /*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); +- /* Flags can perhaps be done better. */ ++ /*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)) { ++ case opentype_format: ++ if (has_ttf_outlines(fm)) { ++ set_truetype(fm); ++ } else { ++ set_opentype(fm); ++ } ++ break; ++ case truetype_format: + 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)); ++ 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 */ ++ /*tex This makes \quotation {unknown} default to subsetted inclusion. */ + if (font_embedding(f) != no_embedding) { + set_included(fm); + if (font_embedding(f) != full_embedding) { +@@ -844,37 +844,33 @@ void do_pdf_font(PDF pdf, internal_font_number f) + } + 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)) ++ /*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); + } + } + +-@ 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. ++/*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. + +-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. ++ 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))) ++ __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); +@@ -883,59 +879,24 @@ static int comp_glw_entry(const void *pa, const void *pb, void *p + + 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 */ ++ /*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); + } + + +-@ 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. ++/*tex + +-@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). +-*/ ++ 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) +-{ +- 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) +@@ -951,7 +912,9 @@ static void mark_cid_subset_glyphs(fo_entry * fo, internal_font_number f) + 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); ++ if (aa == NULL) { ++ /*tex Is this a problem? */ ++ } + } else { + xfree(j); + } +@@ -959,30 +922,29 @@ static void mark_cid_subset_glyphs(fo_entry * fo, internal_font_number f) + } + } + +-@ 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. ++/*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. + +-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 + +-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); +@@ -995,19 +957,16 @@ static void write_cid_charwidth_array(PDF pdf, fo_entry * fo) + pdf_begin_array(pdf); + j = glyph->wd; + } +- if (glyph->id == (unsigned) (i + 1)) +- pdf_out(pdf, ' '); +- ++ 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); +@@ -1026,15 +985,15 @@ 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); ++ /*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. +@@ -1043,24 +1002,20 @@ static void create_cid_fontdictionary(PDF pdf, internal_font_number f) + } + 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); ++ 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"); +@@ -1076,12 +1031,10 @@ void write_cid_fontdictionary(PDF pdf, fo_entry * fo, internal_font_number f) + 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"); +@@ -1100,17 +1053,17 @@ void write_cid_fontdictionary(PDF pdf, fo_entry * fo, internal_font_number f) + 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 +- ++ /*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); + } +diff --git a/texk/web2c/luatexdir/font/writet3.w b/texk/web2c/luatexdir/font/writet3.c +similarity index 90% +rename from texk/web2c/luatexdir/font/writet3.w +rename to texk/web2c/luatexdir/font/writet3.c +index 879cef8d5..6121c24a1 100644 +--- a/texk/web2c/luatexdir/font/writet3.w ++++ b/texk/web2c/luatexdir/font/writet3.c +@@ -1,25 +1,24 @@ +-% writet3.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2011 Taco Hoekwater + ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include +@@ -41,7 +40,8 @@ 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 */ ++/*tex Not static because used elsewhere. */ ++ + unsigned char *t3_buffer = NULL; + int t3_size = 0; + int t3_curbyte = 0; +@@ -50,8 +50,6 @@ int t3_curbyte = 0; + 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) { +@@ -71,7 +69,7 @@ static void update_bbox(int llx, int lly, int urx, int ury, boolean is_first_gly + } + } + +-/* fixed precision 3 (+5 in pdfgen.w)*/ ++/*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) +@@ -79,8 +77,6 @@ static void update_bbox(int llx, int lly, int urx, int ury, boolean is_first_gly + #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; +@@ -98,17 +94,14 @@ static boolean writepk(PDF pdf, internal_font_number f) + 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) { +- /* .dpi/.pk */ ++ /*tex |.dpi/.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); +@@ -213,8 +206,6 @@ static boolean writepk(PDF pdf, internal_font_number f) + return true; + } + +-@ +-@c + void writet3(PDF pdf, internal_font_number f) + { + int i; +@@ -224,7 +215,6 @@ void writet3(PDF pdf, internal_font_number f) + int pk_font_scale; + pdffloat pf; + boolean is_notdef; +- + t3_glyph_num = 0; + t3_image_used = false; + for (i = 0; i < 256; i++) { +@@ -232,7 +222,6 @@ void writet3(PDF pdf, internal_font_number f) + t3_char_widths[i] = 0; + } + is_pk_font = false; +- + xfree(t3_buffer); + t3_curbyte = 0; + t3_size = 0; +@@ -246,8 +235,7 @@ void writet3(PDF pdf, internal_font_number f) + if (pdf_char_marked(f, i)) + break; + last_char = i; +- +- /* Type 3 font dictionary */ ++ /*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"); +@@ -302,8 +290,7 @@ void writet3(PDF pdf, internal_font_number f) + pdf_dict_add_ref(pdf, "CharProcs", (int) cptr); + pdf_end_dict(pdf); + pdf_end_obj(pdf); +- +- /* chars width array */ ++ /*tex The |Widths| array: */ + pdf_begin_obj(pdf, wptr, OBJSTM_ALWAYS); + pdf_begin_array(pdf); + if (is_pk_font) { +@@ -319,8 +306,7 @@ void writet3(PDF pdf, internal_font_number f) + } + pdf_end_array(pdf); + pdf_end_obj(pdf); +- +- /* encoding dictionary */ ++ /*tex The |Encoding| dictionary: */ + pdf_begin_obj(pdf, eptr, OBJSTM_ALWAYS); + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "Encoding"); +@@ -354,8 +340,7 @@ void writet3(PDF pdf, internal_font_number f) + pdf_end_array(pdf); + pdf_end_dict(pdf); + pdf_end_obj(pdf); +- +- /* CharProcs dictionary */ ++ /*tex The |CharProcs| dictionary: */ + pdf_begin_obj(pdf, cptr, OBJSTM_ALWAYS); + pdf_begin_dict(pdf); + for (i = first_char; i <= last_char; i++) { +diff --git a/texk/web2c/luatexdir/font/writettf.w b/texk/web2c/luatexdir/font/writettf.c +similarity index 65% +rename from texk/web2c/luatexdir/font/writettf.w +rename to texk/web2c/luatexdir/font/writettf.c +index 4a016cbe4..cf034e98d 100644 +--- a/texk/web2c/luatexdir/font/writettf.w ++++ b/texk/web2c/luatexdir/font/writettf.c +@@ -1,32 +1,31 @@ +-% writettf.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include "font/writettf.h" + #include + + #define DEFAULT_NTABS 14 +-#define NEW_CMAP_SIZE 2 ++#define NEW_CMAP_SIZE 2 + + #define ttf_putchar(A) strbuf_putchar(pdf->fb, (A)) + #define ttf_offset() strbuf_offset(pdf->fb) +@@ -37,8 +36,10 @@ int ttf_size = 0; + int ttf_curbyte = 0; + + typedef struct { +- char *name; /* name of glyph */ +- long newindex; /* new index of glyph in output file */ ++ /*tex the name of glyph */ ++ char *name; ++ /*tex the new index of glyph in output file */ ++ long newindex; + + } ttfenc_entry; + +@@ -87,303 +88,88 @@ static TTF_ULONG checkSumAdjustment_offset; + FILE *ttf_file; + static ttfenc_entry ttfenc_tab[256]; + +-fd_entry *fd_cur; /* pointer to the current font descriptor */ ++/*tex A pointer to the current font descriptor: */ ++ ++fd_entry *fd_cur; + + 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", ++ /* 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" ++ /* 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 */ ++ "Delta", /* increment */ ++ "Omega", /* Ohm */ ++ "Pi", /* product */ ++ "Sigma", /* summation */ ++ "dmacron", /* dslash */ ++ "macron", /* overscore */ ++ "periodcentered", /* middot */ + NULL + }; + +@@ -404,9 +190,8 @@ static const char *newtabnames[] = { + "prep" + }; + +-@ Back to code. Low-level helpers first. ++/* Back to code. Low-level helpers first. */ + +-@c + static ttf_cmap_entry *new_ttf_cmap_entry(void) + { + ttf_cmap_entry *e; +@@ -438,7 +223,6 @@ static int comp_ttf_cmap_entry(const void *pa, const void *pb, void *p) + *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); +@@ -459,8 +243,10 @@ static unsigned char ttf_addchksm(unsigned char b) + + static TTF_ULONG ttf_getchksm(PDF pdf) + { +- while (tab_length % 4 != 0) +- ttf_putchar(ttf_addchksm(0)); /* |ttf_addchksm| updates |tab_length| */ ++ while (tab_length % 4 != 0) { ++ /*tex |ttf_addchksm| updates |tab_length| */ ++ ttf_putchar(ttf_addchksm(0)); ++ } + return checksum; + } + +@@ -539,48 +325,34 @@ 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 */ ++ /*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); +- assert(aa != NULL); ++ if (aa == NULL) { ++ /*tex Is this a problem? */ ++ } + } +- /* end of workaround */ +- +- /* take over collected characters from \TeX, reencode them */ ++ /*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)) { +- assert(*q >= 0 && *q < 256); ++ 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); +- } else +- assert(0); ++ } + } + +-@ +-@c +-#define ttf_append_byte(B)\ +-do {\ +- if (name_tab[i].platform_id == 3)\ +- *q++ = 0;\ +- *q++ = B;\ ++#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) +@@ -588,9 +360,6 @@ 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 == '}' || +@@ -609,9 +378,7 @@ static void ttf_read_name(void) + 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_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++) { +@@ -624,14 +391,12 @@ static void ttf_read_name(void) + } + for (p = name_buf; p - name_buf < name_buf_size; p++) + *p = get_char(); +- /* look for PS font name */ ++ /*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->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; + } +@@ -639,15 +404,14 @@ static void ttf_read_name(void) + 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) { ++ (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->fontname = xstrdup(strip_spaces_and_delims(buf, (int) strlen(buf))); + fd_cur->font_dim[FONTNAME_CODE].set = true; + break; + } +@@ -659,8 +423,7 @@ 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); ++ 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; +@@ -668,14 +431,15 @@ static void ttf_read_mapx(void) + 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 */ ++ /*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); ++ 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()); +@@ -697,8 +461,7 @@ void ttf_read_hhea(void) + 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); ++ ttf_skip(TTF_FWORD_SIZE + TTF_UFWORD_SIZE + 3 * TTF_FWORD_SIZE + 8 * TTF_SHORT_SIZE); + nhmtxs = get_ushort(); + } + +@@ -745,55 +508,57 @@ void ttf_read_post(void) + post_format = get_fixed(); + italic_angle = get_fixed(); + int_part = (long) (italic_angle >> 16); +- if (int_part > 0x7FFF) { /* a negative number */ ++ 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].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() */ ++ 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: +- 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; ++ 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); + } +- } +- 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); +- } + } + } + +@@ -813,14 +578,17 @@ 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 */ ++ /*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); +- ttf_skip(TTF_FIXED_SIZE); /* ignore the sfnt number */ ++ /*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++) { +@@ -836,7 +604,8 @@ void ttf_read_tabdir(void) + { + int i; + dirtab_entry *tab; +- ttf_skip(TTF_FIXED_SIZE); /* ignore the sfnt number */ ++ /*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++) { +@@ -848,8 +617,7 @@ void ttf_read_tabdir(void) + } + } + +-static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid, +- boolean warn) ++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; +@@ -858,8 +626,7 @@ static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid, + long n, i, k, length, index; + ttf_cmap_entry tmp_e, *p; + void **aa; +- +- /* look up in |ttf_cmap_tree| first, return if found */ ++ /*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; +@@ -870,9 +637,8 @@ static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid, + 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) */ ++ /*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); +@@ -886,36 +652,46 @@ static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid, + if (format == 4) + goto read_cmap_format_4; + else { +- if (warn) ++ if (warn) { + formatted_warning("ttf font", "cmap format %i unsupported", format); ++ } + return NULL; + } + } + } +- if (warn) ++ 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: +- /* initialize the new entry */ ++ /*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) +- p->table[i] = -1; /* unassigned yet */ +- +- /* read the subtable */ +- length = get_ushort(); /* length of subtable */ +- (void) get_ushort(); /* skip the version number */ ++ 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; +- (void) get_ushort(); /* skip searchRange */ +- (void) get_ushort(); /* skip entrySelector */ +- (void) get_ushort(); /* skip rangeShift */ ++ /*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(); +- (void) get_ushort(); /* skip reversedPad */ ++ /*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++) +@@ -923,7 +699,8 @@ static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid, + 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 */ ++ /*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(); +@@ -935,9 +712,7 @@ static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid, + 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); ++ k = (i - s->startCode) + s->idRangeOffset / 2 + (s - seg_tab) - segCount; + index = glyphId[k]; + if (index != 0) + index = (index + s->idDelta) & 0xFFFF; +@@ -958,12 +733,12 @@ static ttf_cmap_entry *ttf_read_cmap(char *ttf_name, int pid, int eid, + xfree(seg_tab); + xfree(glyphId); + aa = avl_probe(ttf_cmap_tree, p); +- assert(aa != NULL); ++ if (aa == NULL) { ++ /*tex Is this a problem? */ ++ } + return p; + } + +-@ +-@c + static void ttf_read_font(void) + { + ttf_read_tabdir(); +@@ -995,7 +770,6 @@ static void ttf_reset_chksm(PDF pdf, dirtab_entry * tab) + 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; +@@ -1012,17 +786,17 @@ static void ttf_copytab(PDF pdf, const char *name) + ttf_set_chksm(pdf, tab); + } + +-@ +-@c +-#define BYTE_ENCODING_LENGTH \ +- ((256)*TTF_BYTE_SIZE + 3*TTF_USHORT_SIZE) ++#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 */ ++ /*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); +@@ -1031,67 +805,87 @@ static void ttf_byte_encoding(PDF pdf) + formatted_warning("ttf font", + "glyph '%s' has been mapped to '%s' in 'ttf_byte_encoding' cmap table", + e->name, notdef); +- put_byte(0); /* notdef */ ++ /*tex |.notdef|: */ ++ put_byte(0); + } + } + +-@ +-@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 */ ++ /*tex format number 6: trimmed table mapping */ ++ (void) put_ushort(6); + (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++) ++ /*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); ++ } + } + +-@ +-@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) */ ++ /*tex format number (4: segment mapping to delta values) */ ++ (void) put_ushort(4); + (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++) ++ /*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); ++ } + } + +-@ +-@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 */ ++ /*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) +@@ -1101,23 +895,25 @@ static void ttf_write_cmap(PDF pdf) + 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 */ ++ /*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)"); ++ 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); +@@ -1125,27 +921,24 @@ static void ttf_write_cmap(PDF pdf) + } + 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; +- } ++ 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) { +@@ -1174,10 +967,12 @@ static void ttf_write_name(PDF pdf) + 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 */ ++ 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); +- /* additional space for subset tags */ ++ /*tex Additional space for subset tags. */ + p = new_name_buf; + for (i = 0; i < name_record_num; i++) { + n = name_tab + i; +@@ -1201,10 +996,10 @@ static void ttf_write_name(PDF pdf) + new_name_buf_size = name_buf_size; + } + ttf_reset_chksm(pdf, tab); +- (void) put_ushort(0); /* Format selector */ ++ (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); ++ (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); +@@ -1220,8 +1015,6 @@ static void ttf_write_name(PDF pdf) + xfree(new_name_buf); + } + +-@ +-@c + static void ttf_write_dirtab(PDF pdf) + { + dirtab_entry *tab; +@@ -1249,7 +1042,7 @@ static void ttf_write_dirtab(PDF pdf) + put_ulong((long) tab->length); + } + } +- /* adjust checkSumAdjustment */ ++ /*tex adjust |checkSumAdjustment| */ + tmp_ulong = 0; + checksum = 0; + for (p = (char *) pdf->fb->data, i = 0; i < (unsigned) save_offset;) { +@@ -1270,8 +1063,6 @@ static void ttf_write_dirtab(PDF pdf) + ttf_seek_outbuf(save_offset); + } + +-@ +-@c + static void ttf_write_glyf(PDF pdf) + { + long *id, k; +@@ -1294,9 +1085,9 @@ static void ttf_write_glyf(PDF pdf) + 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 ++ /*tex ++ Here we change |new_glyphs_count|, which appears in ++ the condition of the |for| loop. + */ + } + (void) put_ushort(glyph_tab[idx].newindex); +@@ -1314,21 +1105,22 @@ static void ttf_write_glyf(PDF pdf) + 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)); ++ 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. ++/*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. ++ ++*/ + +-@c + static void ttf_reindex_glyphs(void) + { + ttfenc_entry *e; +@@ -1337,14 +1129,13 @@ static void ttf_reindex_glyphs(void) + 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 */ ++ /*tex The index of the |.notdef| glyph */ ++ e->newindex = 0; ++ /*tex Handle the case of reencoded fonts. */ + if (e->name == notdef) + continue; +- /* scan form `index123' */ ++ /*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); +@@ -1353,22 +1144,22 @@ static void ttf_reindex_glyphs(void) + glyph = glyph_tab + index; + goto append_new_glyph; + } +- /* scan form `uniABCD' */ ++ /*tex 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) */ ++ /*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; /* once only */ ++ cmap_not_found = true; + } + } + 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)", +@@ -1382,7 +1173,7 @@ static void ttf_reindex_glyphs(void) + continue; + } + } +- /* look up by name */ ++ /*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; +@@ -1391,7 +1182,6 @@ static void ttf_reindex_glyphs(void) + 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; +@@ -1401,28 +1191,42 @@ static void ttf_reindex_glyphs(void) + } + } + +-@ To calculate the checkSum for the 'head' table which itself includes the +- checkSumAdjustment entry for the entire font, do the following: ++/*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. ++ ++*/ + +- \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; +@@ -1431,9 +1235,9 @@ static void ttf_write_head(PDF pdf) + 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); ++ /*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); +@@ -1442,21 +1246,16 @@ static void ttf_write_head(PDF pdf) + 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); ++ 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; +@@ -1469,8 +1268,6 @@ static void ttf_write_htmx(PDF pdf) + ttf_set_chksm(pdf, tab); + } + +-@ +-@c + static void ttf_write_loca(PDF pdf) + { + long *id; +@@ -1497,8 +1294,6 @@ static void ttf_write_loca(PDF pdf) + 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); +@@ -1509,37 +1304,41 @@ static void ttf_write_mapx(PDF pdf) + 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) ++ 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) */ ++ } ++ /*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); +- ttf_ncopy(pdf, 4 * TTF_CHAR_SIZE + TTF_USHORT_SIZE); /* achVendID + fsSelection */ ++ /*tex |achVendID| + |fsSelection| */ ++ ttf_ncopy(pdf, 4 * TTF_CHAR_SIZE + TTF_USHORT_SIZE); + ttf_skip(2 * TTF_USHORT_SIZE); +- (void) put_ushort(0x0000); /* usFirstCharIndex */ +- (void) put_ushort(0xF0FF); /* usLastCharIndex */ ++ /*tex |usFirstCharIndex| */ ++ (void) put_ushort(0x0000); ++ /*tex |usLastCharIndex| */ ++ (void) put_ushort(0xF0FF); + 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 */ ++ /*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); + } + +-@ +-@c + static boolean unsafe_name(const char *s) + { + const char **p; +@@ -1559,12 +1358,10 @@ static void ttf_write_post(PDF pdf) + 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); ++ 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); ++ 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++) { +@@ -1587,22 +1384,23 @@ static void ttf_write_post(PDF pdf) + 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 */ ++ /*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); + } + +-@ +-@c + static void ttf_subset_font(PDF pdf) + { + ttf_init_font(pdf, new_ntabs); +@@ -1628,8 +1426,6 @@ static void ttf_subset_font(PDF pdf) + ttf_write_dirtab(pdf); + } + +-@ +-@c + static void ttf_copy_font(PDF pdf) + { + dirtab_entry *tab; +@@ -1643,33 +1439,25 @@ static void ttf_copy_font(PDF pdf) + 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)); +- ++ /* 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); ++ 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) { ++ 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); + } +@@ -1696,7 +1484,6 @@ void writettf(PDF pdf, fd_entry * fd) + name_tab = NULL; + name_buf = NULL; + ttf_read_font(); +- + pdf_save_offset(pdf); + pdf_flush(pdf); + if (is_subsetted(fd_cur->fm)) { +@@ -1705,7 +1492,6 @@ void writettf(PDF pdf, fd_entry * fd) + } else + ttf_copy_font(pdf); + ttf_length = ttf_offset(); +- + xfree(dir_tab); + xfree(glyph_tab); + xfree(glyph_index); +@@ -1732,7 +1518,7 @@ static void do_writeotf(PDF pdf, fd_entry * fd) + if (tracefilenames) + tex_printf("<<%s", cur_file_name); + ttf_read_tabdir(); +- /* read font parameters */ ++ /*tex Read teh font parameters. */ + if (ttf_name_lookup("head", false) != NULL) + ttf_read_head(); + if (ttf_name_lookup("hhea", false) != NULL) +@@ -1741,10 +1527,10 @@ static void do_writeotf(PDF pdf, fd_entry * fd) + 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 */ ++ /*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(); +@@ -1754,30 +1540,21 @@ static void do_writeotf(PDF pdf, fd_entry * fd) + 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); ++ 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) { ++ 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); + } +@@ -1788,7 +1565,6 @@ void writeotf(PDF pdf, fd_entry * fd) + ttf_read_file(); + ttf_close(); + } +- + fd_cur->ff_found = true; + do_writeotf(pdf, fd); + xfree(ttf_buffer); +diff --git a/texk/web2c/luatexdir/font/writetype0.w b/texk/web2c/luatexdir/font/writetype0.c +similarity index 69% +rename from texk/web2c/luatexdir/font/writetype0.w +rename to texk/web2c/luatexdir/font/writetype0.c +index ebf4be667..7a81d58c9 100644 +--- a/texk/web2c/luatexdir/font/writetype0.w ++++ b/texk/web2c/luatexdir/font/writetype0.c +@@ -1,29 +1,34 @@ +-% writetype0.w +-% +-% Copyright 2006-2008 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2006-2008 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include "font/writettf.h" + #include "font/writecff.h" + +-@ @c ++/*tex ++ ++ Here we also support the newer CFF2 specification, ++ ++*/ ++ + extern unsigned char *ttf_buffer; + + void writetype0(PDF pdf, fd_entry * fd) +@@ -34,15 +39,13 @@ void writetype0(PDF pdf, fd_entry * fd) + dirtab_entry *tab; + cff_font *cff; + sfnt *sfont; +- + dir_tab = NULL; + glyph_tab = NULL; +- +- fd_cur = fd; /* |fd_cur| is global inside \.{writettf.w} */ ++ /*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 = +@@ -69,9 +72,7 @@ void writetype0(PDF pdf, fd_entry * fd) + 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); +@@ -86,7 +87,7 @@ void writetype0(PDF pdf, fd_entry * fd) + else ttf_read_tabdir(); + sfnt_close(sfont); + +- /* read font parameters */ ++ /*tex Read font parameters: */ + if (ttf_name_lookup("head", false) != NULL) + ttf_read_head(); + if (ttf_name_lookup("hhea", false) != NULL) +@@ -95,32 +96,25 @@ void writetype0(PDF pdf, fd_entry * fd) + 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 */ ++ /*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); +- +- /* 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 */ ++ /*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); +-#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 */ ++ /*tex Just copy: */ + for (i = (long) tab->length; i > 0; i--) + strbuf_putchar(pdf->fb, (unsigned char) ttf_getnum(1)); + } +diff --git a/texk/web2c/luatexdir/font/writetype2.w b/texk/web2c/luatexdir/font/writetype2.c +similarity index 65% +rename from texk/web2c/luatexdir/font/writetype2.w +rename to texk/web2c/luatexdir/font/writetype2.c +index 81aefb1fb..b8a746090 100644 +--- a/texk/web2c/luatexdir/font/writetype2.w ++++ b/texk/web2c/luatexdir/font/writetype2.c +@@ -1,24 +1,23 @@ +-% writetype2.w +-% +-% Copyright 2006-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + ++Copyright 2006-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include "font/writettf.h" +@@ -28,15 +27,22 @@ + #include "font/sfnt.h" + #include "font/tt_glyf.h" + +-@ forward declaration +-@c ++/*tex ++ ++ Forward declarations ++ ++*/ ++ + boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen); + +-@ @c + unsigned long cidtogid_obj = 0; + +-@ low-level helpers +-@c ++/*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"); \ +@@ -96,55 +102,23 @@ int do_sfnt_read(unsigned char *dest, int len, sfnt * f) + return len; + } + +-pdf_obj *pdf_new_stream(void) +-{ +- pdf_obj *stream = xmalloc(sizeof(pdf_obj)); +- stream->length = 0; +- stream->data = NULL; +- return stream; +-} ++/*tex + +-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; +-} ++ The main function: + +-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} */ ++ /*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 = +@@ -167,20 +141,13 @@ boolean writetype2(PDF pdf, fd_entry * fd) + 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 */ +- ++ /*tex Here is the real work done: */ + 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); +@@ -190,72 +157,69 @@ boolean writetype2(PDF pdf, fd_entry * fd) + 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.} ++/*tex + +- 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 \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|. + +- \.{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} +-}; ++} + ++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); ++ /*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", ++ 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 ++/*tex ++ ++ Creating the subset. ++*/ ++ + extern int cidset; ++ + boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen) + { + +@@ -270,48 +234,38 @@ boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen) + 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; ++ return false; + } +- + if (is_subsetted(fd->fm)) { +- /* rebuild the glyph tables and create a fresh cidmap */ ++ /*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 +- +- /* fill |used_chars| */ ++ /*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); +@@ -319,56 +273,44 @@ boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen) + found != NULL; found = (glw_entry *) avl_t_next(&t)) { + used_chars[found->id] = 1; + } +- +- /* Map CIDs to GIDs. */ +- +- num_glyphs = 1; /* \.{.notdef} */ ++ /*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 /* |!NO_GHOSTSCRIPT_BUG| */ +- ++#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); + } +- +- /* Create font file */ +- ++ /*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); +- +- /* squeeze in the cidgidmap */ ++ /*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); +- 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++) { +@@ -377,15 +319,14 @@ boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen) + pdf_end_stream(pdf); + pdf_end_obj(pdf); + } +- +- /* the tff subset */ ++ /*tex 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. */ ++ /*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); +@@ -409,23 +350,6 @@ boolean make_tt_subset(PDF pdf, fd_entry * fd, unsigned char *buff, int buflen) + } + } + } +- +- /* 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; +diff --git a/texk/web2c/luatexdir/image/pdftoepdf.c b/texk/web2c/luatexdir/image/pdftoepdf.c +new file mode 100644 +index 000000000..b29493b07 +--- /dev/null ++++ b/texk/web2c/luatexdir/image/pdftoepdf.c +@@ -0,0 +1,1049 @@ ++/* ++pdftoepdf.w ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2015 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++*/ ++ ++#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; isize; ++ pdf_begin_dict(pdf); ++ for (i=0; idict; /* 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; itype) { ++ 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; iarray,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; ++} +diff --git a/texk/web2c/luatexdir/image/pdftoepdf.w b/texk/web2c/luatexdir/image/pdftoepdf.w +deleted file mode 100644 +index d69795926..000000000 +--- a/texk/web2c/luatexdir/image/pdftoepdf.w ++++ /dev/null +@@ -1,972 +0,0 @@ +-% pdftoepdf.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2015 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +-#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 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; +-} +diff --git a/texk/web2c/luatexdir/image/writeimg.w b/texk/web2c/luatexdir/image/writeimg.c +similarity index 70% +rename from texk/web2c/luatexdir/image/writeimg.w +rename to texk/web2c/luatexdir/image/writeimg.c +index ef414d5d4..e3e3f608f 100644 +--- a/texk/web2c/luatexdir/image/writeimg.w ++++ b/texk/web2c/luatexdir/image/writeimg.c +@@ -1,109 +1,82 @@ +-% writeimg.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@* Image inclusion. +- +-@ @c ++/* ++ ++writeimg.c ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ + #include "ptexlib.h" + #include + #include + +-@ @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 "lua.h" + #include "lauxlib.h" + +-@* Patch ImageTypeDetection 2003/02/08 by Heiko Oberdiek. ++/*tex + +-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 ++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|. + +-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. ++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. + +-@c +-#define HEADER_JPG "\xFF\xD8" +-#define HEADER_PNG "\x89PNG\r\n\x1A\n" ++*/ ++ ++#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 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 */ ++ ++#define HEADER_PDF_MEMSTREAM "data:application/pdf," ++#define LEN_PDF_MEMSTREAM 21 + + static void check_type_by_header(image_dict * idict) + { +@@ -115,13 +88,13 @@ static void check_type_by_header(image_dict * idict) + return; + if (img_type(idict) != IMG_TYPE_NONE) + return; +- /* here we read the and also check for a memstream object */ ++ /*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) { +- /* check the prefix of img_filepath(idict) */ ++ /*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]); + } +@@ -133,7 +106,7 @@ static void check_type_by_header(image_dict * idict) + formatted_error("pdf backend","reading image file '%s' failed",img_filepath(idict)); + } + } +- /* a valid file, but perhaps unsupported */ ++ /*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)) { +@@ -141,7 +114,7 @@ static void check_type_by_header(image_dict * idict) + } + } + xfclose(file, img_filepath(idict)); +- /* tests */ ++ /*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) +@@ -154,15 +127,13 @@ static void check_type_by_header(image_dict * idict) + 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 */ ++ if (img_type(idict) != IMG_TYPE_NONE) + return; +- /* tests */ + if ((image_suffix = strrchr(img_filename(idict), '.')) == 0) + img_type(idict) = IMG_TYPE_NONE; + else if (strcasecmp(image_suffix, ".png") == 0) +@@ -179,14 +150,13 @@ static void check_type_by_extension(image_dict * idict) + 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; ++ img_pdfstream_size(p) = 0; + } + +-@ @c + image *new_image(void) + { + image *p = xtalloc(1, image); +@@ -199,7 +169,6 @@ image *new_image(void) + return p; + } + +-@ @c + image_dict *new_image_dict(void) + { + image_dict *p = xtalloc(1, image_dict); +@@ -214,7 +183,8 @@ image_dict *new_image_dict(void) + img_unset_bbox(p); + img_unset_group(p); + img_state(p) = DICT_NEW; +- img_index(p) = -1; /* -1 = unused, used count from 0 */ ++ /*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); +@@ -223,7 +193,6 @@ image_dict *new_image_dict(void) + return p; + } + +-@ @c + static void free_dict_strings(image_dict * p) + { + xfree(img_filename(p)); +@@ -232,12 +201,13 @@ static void free_dict_strings(image_dict * 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 */ ++ 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: +@@ -256,7 +226,6 @@ void free_image_dict(image_dict * p) + 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)); +@@ -271,7 +240,6 @@ void free_image_dict(image_dict * p) + xfree(p); + } + +-@ @c + void read_img(image_dict * idict) + { + char *filepath = NULL; +@@ -282,7 +250,7 @@ void read_img(image_dict * idict) + 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 */ ++ /*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); +@@ -290,22 +258,22 @@ void read_img(image_dict * idict) + } + } + if (img_filepath(idict) == NULL && (strstr(img_filename(idict),"data:application/pdf,") != NULL)) { +- /* we need to check here for a pdf memstream */ ++ /*tex 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 */ ++ /*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) { +- /* in any case we need a name */ ++ /*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)); +- /* type checks */ ++ /*tex A few type checks. */ + check_type_by_header(idict); + check_type_by_extension(idict); +- /* read image */ ++ /*tex Now we're ready to read the image. */ + switch (img_type(idict)) { + case IMG_TYPE_PDFMEMSTREAM: + case IMG_TYPE_PDF: +@@ -340,8 +308,7 @@ void read_img(image_dict * idict) + } + } + +-@ @c +-static image_dict *read_image(char *file_name, int page_num, char *page_name, int colorspace, int page_box) ++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(); +@@ -353,6 +320,9 @@ static image_dict *read_image(char *file_name, int page_num, char *page_name, in + 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"); + } +@@ -363,8 +333,12 @@ static image_dict *read_image(char *file_name, int page_num, char *page_name, in + return idict; + } + +-@ scans PDF pagebox specification +-@c ++/*tex ++ ++ There can be several page boxes. Normally the cropbox is used. ++ ++*/ ++ + static pdfboxspec_e scan_pdf_box_spec(void) + { + if (scan_keyword("mediabox")) +@@ -381,14 +355,13 @@ static pdfboxspec_e scan_pdf_box_spec(void) + return PDF_BOX_SPEC_NONE; + } + +-@ @c +-void scan_pdfximage(PDF pdf) /* static_pdf */ ++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; +- alt_rule = scan_alt_rule(); /* scans || to |alt_rule| */ ++ 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); +@@ -396,13 +369,33 @@ void scan_pdfximage(PDF pdf) /* static_pdf */ + } + if (scan_keyword("named")) { + scan_toks(false, true); +- named = tokenlist_to_cstring(def_ref, true, NULL); ++ 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); +- page = 0; + } 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; +@@ -419,7 +412,7 @@ void scan_pdfximage(PDF pdf) /* static_pdf */ + normal_error("pdf backend","no image filename given"); + } + delete_token_ref(def_ref); +- idict = read_image(file_name, page, named, colorspace, pagebox); ++ 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; +@@ -427,33 +420,32 @@ void scan_pdfximage(PDF pdf) /* static_pdf */ + last_saved_image_pages = img_totalpages(idict); + } + +-@ @c + void scan_pdfrefximage(PDF pdf) + { +- /* one could scan transform as well */ ++ /*tex One could scan transform as well. */ + int transform = 0; +- /* begin of experiment */ ++ /*tex Begin of experiment. */ + int open = 0; +- /* end of experiment */ ++ /*tex End of experiment. */ + image_dict *idict; +- /* scans || to |alt_rule| */ ++ /*tex This scans || to |alt_rule|. */ + scaled_whd alt_rule, dim; + alt_rule = scan_alt_rule(); +- /* begin of experiment */ ++ /*tex Begin of experiment. */ + if (scan_keyword("keepopen")) { + open = 1; + } +- /* end of experiment */ ++ /*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)]; +- /* begin of experiment */ ++ /*tex Begin of experiment, */ + if (open) { +- /* so we keep the original value when no close is given */ ++ /*tex So we keep the original value when no close is given. */ + idict->keepopen = 1; + } +- /* end of experiment */ ++ /*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; +@@ -473,38 +465,41 @@ void scan_pdfrefximage(PDF pdf) + } + } + +-@ |tex_scale()| sequence of decisions: +- +-{\obeylines\obeyspaces\tt +-wd ht dp : res = tex; +-wd ht -- +-wd -- dp +-wd -- -- +--- ht dp +--- ht -- +--- -- dp +--- -- -- : res = nat; +-} ++/* ++ 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 ++ ++*/ + +-@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 */ ++ /*tex width, height, and depth specified */ + res = tex; +- } else /* max. 2 dimensions are specified */ if (!is_running(tex.wd)) { ++ } 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; +- /* width and height specified */ ++ /*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; +- /* width and depth specified */ ++ /*tex width and depth specified */ + res.ht = ext_xn_over_d(tex.wd, nat.ht + nat.dp, nat.wd) - tex.dp; + } else { +- /* only width specified */ ++ /*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); + } +@@ -512,44 +507,50 @@ scaled_whd tex_scale(scaled_whd nat, scaled_whd tex) + res.ht = tex.ht; + if (!is_running(tex.dp)) { + res.dp = tex.dp; +- /* height and depth specified */ ++ /*tex height and depth specified */ + res.wd = ext_xn_over_d(tex.ht + tex.dp, nat.wd, nat.ht + nat.dp); + } else { +- /* only height specified */ ++ /*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; +- /* only depth specified */ ++ /*tex only depth specified */ + res.ht = nat.ht - (tex.dp - nat.dp); + res.wd = nat.wd; + } else { +- /* nothing specified */ ++ /*tex 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. ++/*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. ++ ++*/ + +-@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 */ ++ /*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)) { +- x = img_xsize(idict) = img_bbox(idict)[2] - img_bbox(idict)[0]; /* dimensions from image.bbox */ ++ /*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 { +- x = img_xsize(idict); /* dimensions, resolutions from image file */ ++ /*tex dimensions, resolutions from image file */ ++ x = img_xsize(idict); + y = img_ysize(idict); + } + xr = img_xres(idict); +@@ -569,7 +570,8 @@ scaled_whd scale_img(image_dict * idict, scaled_whd alt_rule, int transform) + xr = yr; + yr = tmp; + } +- nat.dp = 0; /* always for images */ ++ /*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; +@@ -591,7 +593,6 @@ scaled_whd scale_img(image_dict * idict, scaled_whd alt_rule, int transform) + return tex_scale(nat, alt_rule); + } + +-@ @c + void write_img(PDF pdf, image_dict * idict) + { + if (img_state(idict) < DICT_WRITTEN) { +@@ -628,15 +629,17 @@ void write_img(PDF pdf, image_dict * idict) + img_state(idict) = DICT_WRITTEN; + } + +-@ write an image +-@c ++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)]); + } + +-@ @c + void check_pdfstream_dict(image_dict * idict) + { + if (!img_is_bbox(idict)) +@@ -645,40 +648,43 @@ void check_pdfstream_dict(image_dict * idict) + 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_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); +- pdf_dict_add_streaminfo(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_puts(pdf, img_pdfstream_stream(idict)); ++ 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); + } + +-@ @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 */ ++ 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); +@@ -690,53 +696,60 @@ 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, ... */ ++ /*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) { +- return; /* empty string blocks PTEX.FileName output */ ++ /*tex empty string blocks PTEX.FileName output */ ++ return; + } else { + p = img_visiblefilename(idict); + } + } else { +- /* unset so let's use the default */ ++ /*tex unset so let's use the default */ + p = img_filepath(idict); + } +- // write additional information ++ /*tex 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 */ ++/*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. ++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. ++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. ++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. ++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. ++ ++*/ + +-@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. ++/*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|. ++ ++*/ + +-@ scan rule spec to |alt_rule| +-@c + scaled_whd scan_alt_rule(void) + { + boolean loop; +@@ -763,8 +776,12 @@ scaled_whd scan_alt_rule(void) + return alt_rule; + } + +-@ copy file of arbitrary size to PDF buffer and flush as needed +-@c ++/*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; +diff --git a/texk/web2c/luatexdir/image/writejbig2.w b/texk/web2c/luatexdir/image/writejbig2.c +similarity index 80% +rename from texk/web2c/luatexdir/image/writejbig2.w +rename to texk/web2c/luatexdir/image/writejbig2.c +index a58313ce8..77a1ddb07 100644 +--- a/texk/web2c/luatexdir/image/writejbig2.w ++++ b/texk/web2c/luatexdir/image/writejbig2.c +@@ -1,27 +1,30 @@ +-% writejbig2.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2013 Taco Hoekwater +-% Copyright 2003-2013 Hartmut Henkel +-% +-% 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 . +- +-@ +-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. ++/* ++ ++writejbig2.c ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2013 Taco Hoekwater ++Copyright 2003-2013 Hartmut Henkel ++ ++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 . ++ ++*/ ++ ++/*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 + ========== +@@ -78,7 +81,8 @@ 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" +@@ -87,8 +91,8 @@ object exists, reference it. Else create fresh one. + #include + #include "image/image.h" + +-@ @c +-/* 7.3 Segment types */ ++/*tex Table 7.3: Segment types */ ++ + #define M_SymbolDictionary 0 + #define M_IntermediateTextRegion 4 + #define M_ImmediateTextRegion 6 +@@ -111,13 +115,12 @@ object exists, reference it. Else create fresh one. + #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 */ ++ void *d; + } LITEM; + + typedef struct _LIST { +@@ -130,26 +133,28 @@ 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() */ ++ /*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; +- boolean endofstripeflag; /* set by checkseghdrflags() */ +- boolean endofpageflag; /* set by checkseghdrflags() */ +- boolean pageinfoflag; /* set by checkseghdrflags() */ +- boolean endoffileflag; /* set by checkseghdrflags() */ ++ /*tex Set by |checkseghdrflags|: */ ++ boolean endofstripeflag; ++ boolean endofpageflag; ++ boolean pageinfoflag; ++ boolean endoffileflag; + } SEGINFO; + + typedef struct _PAGEINFO { +- LIST segments; /* segments associated with page */ ++ LIST segments; + unsigned long pagenum; + unsigned int width; + unsigned int height; +@@ -164,17 +169,19 @@ typedef struct _FILEINFO { + FILE *file; + char *filepath; + long filesize; +- LIST pages; /* not including page0 */ ++ /*tex Not including |page0|: */ ++ LIST pages; + 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()| */ ++ /*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; + +-@ @c + static struct avl_table *file_tree = NULL; + + static int comp_file_entry(const void *pa, const void *pb, void *p) +@@ -195,7 +202,6 @@ static int comp_segment_entry(const void *pa, const void *pb, void *p) + return (int) (((const SEGINFO *) pa)->segnum - ((const SEGINFO *) pb)->segnum); + } + +-@ @c + static int ygetc(FILE * stream) + { + int c = getc(stream); +@@ -208,7 +214,6 @@ static int ygetc(FILE * stream) + return c; + } + +-@ @c + static void initlinkedlist(LIST * lp) + { + lp->first = NULL; +@@ -233,7 +238,6 @@ static LIST *litem_append(LIST * lp) + return lp; + } + +-@ @c + static FILEINFO *new_fileinfo(void) + { + FILEINFO *fip; +@@ -252,7 +256,6 @@ static FILEINFO *new_fileinfo(void) + return fip; + } + +-@ @c + static PAGEINFO *new_pageinfo(void) + { + PAGEINFO *pip; +@@ -269,7 +272,6 @@ static PAGEINFO *new_pageinfo(void) + return pip; + } + +-@ @c + static void init_seginfo(SEGINFO * sip) + { + sip->segnum = 0; +@@ -293,7 +295,6 @@ static void init_seginfo(SEGINFO * sip) + sip->endoffileflag = false; + } + +-@ @c + static void pages_maketree(LIST * plp) + { + LITEM *ip; +@@ -307,7 +308,6 @@ static void pages_maketree(LIST * plp) + } + } + +-@ @c + static void segments_maketree(LIST * slp) + { + LITEM *ip; +@@ -321,7 +321,6 @@ static void segments_maketree(LIST * slp) + } + } + +-@ @c + static PAGEINFO *find_pageinfo(LIST * plp, unsigned long pagenum) + { + PAGEINFO tmp; +@@ -330,7 +329,6 @@ static PAGEINFO *find_pageinfo(LIST * plp, unsigned long pagenum) + return (PAGEINFO *) avl_find(plp->tree, &tmp); + } + +-@ @c + static SEGINFO *find_seginfo(LIST * slp, unsigned long segnum) + { + SEGINFO tmp; +@@ -339,21 +337,18 @@ static SEGINFO *find_seginfo(LIST * slp, unsigned long segnum) + 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; +@@ -366,39 +361,40 @@ static unsigned long getstreamlen(LITEM * slip, boolean refer) + return len; + } + +-@ @c + static void readfilehdr(FILEINFO * fip) + { + unsigned int i; +- /* Annex D.4 File header syntax */ +- /* Annex D.4.1 ID string */ ++ /*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"); +- /* Annex D.4.2 File header flags */ ++ /*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) { /* Annex D.1 vs. Annex D.2 */ ++ 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); + } +- /* Annex D.4.3 Number of pages */ +- if (!(fip->filehdrflags >> 1) & 0x01) /* known number of pages */ ++ /*tex Annex D.4.3: Number of pages */ ++ if (( !(fip->filehdrflags >> 1)) & 0x01) { ++ /*tex The known number of pages: */ + fip->numofpages = read4bytes(fip->file); +- /* --- at end of file header --- */ ++ } ++ /*tex 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 */ ++ /*tex Table 7.3: Segment types */ + switch (sip->seghdrflags & 0x3f) { + case M_SymbolDictionary: + case M_IntermediateTextRegion: +@@ -437,24 +433,34 @@ static void checkseghdrflags(SEGINFO * sip) + } + } + +-@ for first reading of file; return value tells if header been read ++/*tex ++ ++ 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 */ ++ 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); +- /* 7.2.3 Segment header flags */ ++ /*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, */ +- return true; /* makes it compliant with Example 3.4 of PDFRef. 5th ed. */ ++ 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; +- /* 7.2.4 Referred-to segment count and retention flags */ ++ /*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) +@@ -463,7 +469,7 @@ static boolean readseghdr(FILEINFO * fip, SEGINFO * sip) + sip->fieldlen = 5 + sip->countofrefered / 8; + xfseek(fip->file, sip->fieldlen - 1, SEEK_CUR, fip->filepath); + } +- /* 7.2.5 Referred-to segment numbers */ ++ /*tex Table 7.2.5: Referred-to segment numbers */ + if (sip->segnum <= 256) + sip->segnumwidth = 1; + else if (sip->segnum <= 65536) +@@ -483,19 +489,18 @@ static boolean readseghdr(FILEINFO * fip, SEGINFO * sip) + break; + } + } +- /* 7.2.6 Segment page association */ ++ /*tex Table 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 */ ++ /*tex Table 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 ---- */ ++ /*tex End of segment header. */ + return true; + } + +-@ @c + static void checkseghdr(FILEINFO * fip, SEGINFO * sip); + + static void markpage0seg(FILEINFO * fip, unsigned long referedseg) +@@ -511,19 +516,23 @@ static void markpage0seg(FILEINFO * fip, unsigned long referedseg) + } + } + +-@ for writing, marks refered page0 segments, sets segpage > 0 to 1 ++/*tex ++ ++ For writing, marks refered page0 segments, sets segpage larger than ++ zero to one. ++ ++*/ + +-@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 */ ++ /*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)); +- /* 7.2.5 Referred-to segment numbers */ ++ /*tex Table 7.2.5: Referred-to segment numbers */ + for (i = 0; i < sip->countofrefered; i++) { + switch (sip->segnumwidth) { + case 1: +@@ -548,7 +557,7 @@ static void writeseghdr(PDF pdf, FILEINFO * fip, SEGINFO * sip) + } + if (sip->countofrefered > 0) + sip->refers = true; +- /* 7.2.6 Segment page association */ ++ /*tex Table 7.2.6: Segment page association */ + if (sip->pageassocsizeflag) + for (i = 0; i < 3; i++) { + (void) ygetc(fip->file); +@@ -556,23 +565,27 @@ static void writeseghdr(PDF pdf, FILEINFO * fip, SEGINFO * sip) + } + (void) ygetc(fip->file); + pdf_out(pdf, (unsigned char) ((sip->segpage > 0) ? 1 : 0)); +- /* 7.2.7 Segment data length */ ++ /*tex Table 7.2.7: Segment data length */ + for (i = 0; i < 4; i++) + pdf_out(pdf, ygetc(fip->file)); +- /* ---- at end of segment header ---- */ ++ /* End of segment header. */ + } + +-@ for recursive marking of refered page0 segments +-@c ++/*tex ++ ++ For recursive marking of refered page0 segments: ++ ++*/ ++ + 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 */ ++ /*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); +- /* 7.2.5 Referred-to segment numbers */ ++ /*tex Table 7.2.5: Referred-to segment numbers */ + for (i = 0; i < sip->countofrefered; i++) { + switch (sip->segnumwidth) { + case 1: +@@ -590,33 +603,34 @@ static void checkseghdr(FILEINFO * fip, SEGINFO * sip) + } + if (sip->countofrefered > 0) + sip->refers = true; +- /* 7.2.6 Segment page association */ +- /* 7.2.7 Segment data length */ ++ /*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); +- /* ---- at end of segment header ---- */ ++ /*tex 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 */ ++ /*tex Table D.2: Random-access organisation */ ++ do { ++ /*tex Find random-access stream start. */ + (void) readseghdr(fip, &tmp); +- while (!tmp.endoffileflag); ++ } 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 */ ++ /*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; +@@ -624,9 +638,12 @@ static void rd_jbig2_info(FILEINFO * fip) + LIST *plp, *slp; + fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE); + readfilehdr(fip); +- if (!fip->sequentialaccess) /* D.2 Random-access organisation */ ++ if (!fip->sequentialaccess) { ++ /*tex Table D.2: Random-access organisation */ + streampos = findstreamstart(fip); +- while (true) { /* loop over segments */ ++ } ++ while (true) { ++ /*tex Loop over segments: */ + if (!sipavail) { + sip = xtalloc(1, SEGINFO); + sipavail = true; +@@ -658,11 +675,10 @@ static void rd_jbig2_info(FILEINFO * fip) + else + sip->datastart = sip->hdrend; + sip->dataend = sip->datastart + sip->segdatalen; +- if (!fip->sequentialaccess +- && (sip->pageinfoflag || sip->endofstripeflag)) ++ 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 */ ++ /*tex Table 7.4.8: Page information segment syntax */ + if (sip->pageinfoflag) { + pip->pagenum = (unsigned long) sip->segpage; + pip->width = read4bytes(fip->file); +@@ -670,7 +686,7 @@ static void rd_jbig2_info(FILEINFO * fip) + pip->xres = read4bytes(fip->file); + pip->yres = read4bytes(fip->file); + pip->pagesegmentflags = (unsigned) ygetc(fip->file); +- /* 7.4.8.6 Page striping information */ ++ /*tex Table 7.4.8.6: Page striping information */ + pip->stripinginfo = read2bytes(fip->file); + seekdist -= 19; + } +@@ -694,9 +710,7 @@ static void rd_jbig2_info(FILEINFO * fip) + xfclose(fip->file, fip->filepath); + } + +-@ @c +-static void wr_jbig2(PDF pdf, image_dict * idict, FILEINFO * fip, +- unsigned long page) ++static void wr_jbig2(PDF pdf, image_dict * idict, FILEINFO * fip, unsigned long page) + { + LITEM *slip; + PAGEINFO *pip; +@@ -743,11 +757,12 @@ static void wr_jbig2(PDF pdf, image_dict * idict, FILEINFO * fip, + } + 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 */ ++ 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); +- /* mark refered-to page 0 segments, change segpages > 1 to 1 */ ++ /*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++) +@@ -759,7 +774,6 @@ static void wr_jbig2(PDF pdf, image_dict * idict, FILEINFO * fip, + xfclose(fip->file, fip->filepath); + } + +-@ @c + boolean supported_jbig2(image_dict * idict) + { + if (img_pdfmajorversion(idict) < 2 && img_pdfminorversion(idict) < 4) { +@@ -770,20 +784,19 @@ boolean supported_jbig2(image_dict * idict) + } + } + +-@ @c + void flush_jbig2_info(image_dict * idict) + { +- /* todo */ ++ /*tex Todo (or not). */ + } + +-@ @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_... */ ++ /*tex Already set probably, see other |read_|. */ ++ img_type(idict) = IMG_TYPE_JBIG2; + if (! supported_jbig2(idict)) { +- /* already an error done */ ++ /*tex Already an error seen? */ + } + if (img_pagenum(idict) < 1) { + normal_error("readjbig2","page must be > 0"); +@@ -819,24 +832,19 @@ void read_jbig2_info(image_dict * idict) + 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 */ ++ /*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; + } + +-@ @c + void flush_jbig2_page0_objects(PDF pdf) + { + FILEINFO *fip; +@@ -846,7 +854,8 @@ void flush_jbig2_page0_objects(PDF pdf) + 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 */ ++ /*tex |NULL|: page0 */ ++ wr_jbig2(pdf, NULL, fip, 0); + } + } + } +diff --git a/texk/web2c/luatexdir/image/writejp2.w b/texk/web2c/luatexdir/image/writejp2.c +similarity index 82% +rename from texk/web2c/luatexdir/image/writejp2.w +rename to texk/web2c/luatexdir/image/writejp2.c +index cb317e119..0ea001708 100644 +--- a/texk/web2c/luatexdir/image/writejp2.w ++++ b/texk/web2c/luatexdir/image/writejp2.c +@@ -1,38 +1,42 @@ +-% writejp2.w +-% +-% Copyright 2011-2013 Taco Hoekwater +-% Copyright 2011-2013 Hartmut Henkel +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++writejp2.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|. ++Copyright 2011-2013 Taco Hoekwater ++Copyright 2011-2013 Hartmut Henkel ++ ++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 . ++ ++*/ ++ ++/*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|. ++ ++*/ + +-@c + #include "ptexlib.h" + #include + #include + #include "image/image.h" + #include "image/writejp2.h" +-#include "image/writejbig2.h" /* read2bytes(), read4bytes() */ ++#include "image/writejbig2.h" ++ ++/*tex Table 1.2: Defined boxes */ + +-/* Table 1.2 -- Defined boxes */ + #define BOX_JP 0x6A502020 + #define BOX_FTYP 0x66747970 + #define BOX_JP2H 0x6a703268 +@@ -45,7 +49,8 @@ ISO/IEC 15444-1, Second edition, 2004-09-15, file |15444-1annexi.pdf|. + #define BOX_RESD 0x72657364 + #define BOX_JP2C 0x6A703263 + +-/* 1.4 Box definition */ ++/*tex Table 1.4: Box definition */ ++ + typedef struct { + uint64_t lbox; + unsigned int tbox; +@@ -71,7 +76,8 @@ static hdr_struct read_boxhdr(image_dict * idict) + return hdr; + } + +-/* 1.5.3.1 Image Header box */ ++/*tex Table 1.5.3.1: Image Header box */ ++ + static void scan_ihdr(image_dict * idict) + { + unsigned int height, width; +@@ -88,8 +94,9 @@ static void scan_ihdr(image_dict * idict) + (void) xgetc(img_file(idict)); /* ipr */ + } + +-/* 1.5.3.7.1 Capture Resolution box */ +-/* 1.5.3.7.2 Default Display Resolution box */ ++/*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) + { +@@ -108,7 +115,7 @@ static void scan_resc_resd(image_dict * idict) + img_yres(idict) = (int) (vr_ + 0.5); + } + +-/* 1.5.3.7 Resolution box (superbox) */ ++/*tex Table 1.5.3.7: Resolution box (superbox) */ + + static void scan_res(image_dict * idict, uint64_t epos_s) + { +@@ -121,7 +128,7 @@ static void scan_res(image_dict * idict, uint64_t epos_s) + epos = spos + hdr.lbox; + switch (hdr.tbox) { + case (BOX_RESC): +- /* arbitrarily: let BOX_RESD have precedence */ ++ /*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) +@@ -143,7 +150,7 @@ static void scan_res(image_dict * idict, uint64_t epos_s) + } + } + +-/* 1.5.3 JP2 Header box (superbox) */ ++/*tex Table 1.5.3: JP2 Header box (superbox) */ + + static boolean scan_jp2h(image_dict * idict, uint64_t epos_s) + { +@@ -178,7 +185,7 @@ static boolean scan_jp2h(image_dict * idict, uint64_t epos_s) + + static void close_and_cleanup_jp2(image_dict * idict) + { +- /* if one of then is not NULL we already cleaned up */ ++ /*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; +@@ -216,11 +223,11 @@ void read_jp2_info(image_dict * idict) + normal_error("readjp2","size problem"); + } + spos = epos = 0; +- /* 1.5.1 JPEG 2000 Signature box */ ++ /*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)); +- /* 1.5.2 File Type box */ ++ /*tex Table 1.5.2: File Type box */ + spos = epos; + hdr = read_boxhdr(idict); + if (hdr.tbox != BOX_FTYP) { +@@ -256,9 +263,7 @@ static void reopen_jp2(image_dict * idict) + height = img_ysize(idict); + xres = img_xres(idict); + yres = img_yres(idict); +- /* +- we need to make sure that the file kept open +- */ ++ /*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) +@@ -292,6 +297,6 @@ void write_jp2(PDF pdf, image_dict * idict) + normal_error("writejp2","fread failed"); + pdf_end_stream(pdf); + pdf_end_obj(pdf); +- /* always */ ++ /*tex We always:*/ + close_and_cleanup_jp2(idict); + } +diff --git a/texk/web2c/luatexdir/image/writejpg.w b/texk/web2c/luatexdir/image/writejpg.c +similarity index 76% +rename from texk/web2c/luatexdir/image/writejpg.w +rename to texk/web2c/luatexdir/image/writejpg.c +index 54ed47f0f..f58dc2aa6 100644 +--- a/texk/web2c/luatexdir/image/writejpg.w ++++ b/texk/web2c/luatexdir/image/writejpg.c +@@ -1,31 +1,30 @@ +-% writejpg.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++writejpg.w ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include + #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 */ +@@ -97,7 +96,6 @@ typedef enum { + 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; +@@ -128,28 +126,31 @@ static unsigned int read_exif_bytes(unsigned char **p, int n, int b) + 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. ++/*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}. ++ ++*/ + +-@ @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 */ ++ /*tex silence uninitialized warnings */ ++ int value = 0; + unsigned int num = 0; + unsigned int den = 0; + boolean found_x = false; +@@ -165,7 +166,6 @@ static void read_APP1_Exif (FILE *fp, unsigned short length, int *xx, int *yy, i + 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); +@@ -193,52 +193,65 @@ static void read_APP1_Exif (FILE *fp, unsigned short length, int *xx, int *yy, i + type = read_exif_bytes(&p, 2, bigendian); + read_exif_bytes(&p, 4, bigendian); + switch (type) { +- case 1: /* byte */ ++ case 1: ++ /*tex byte */ + value = *p++; + p += 3; + break; +- case 3: /* unsigned short */ +- case 8: /* signed short */ ++ case 3: ++ /*tex unsigned short */ ++ case 8: ++ /*tex signed short */ + value = read_exif_bytes(&p, 2, bigendian); + p += 2; + break; +- case 4: /* unsigned long */ +- case 9: /* signed long */ ++ case 4: ++ /*tex unsigned long */ ++ case 9: ++ /*tex signed long */ + value = read_exif_bytes(&p, 4, bigendian); + break; +- case 5: /* rational */ +- case 10: /* srational */ ++ 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: /* undefined */ ++ case 7: ++ /*tex undefined */ + value = *p++; + p += 3; + break; +- case 2: /* ascii */ ++ case 2: ++ /*tex ascii */ + default: + p += 4; + break; + } + switch (tag) { +- case 274: /* orientation */ ++ case 274: ++ /*tex orientation */ + orientation = value; + break; +- case 282: /* x res */ ++ case 282: ++ /*tex x res */ + if (den != 0) { + xres = num / den; + found_x = true; +- } ++ } + break; +- case 283: /* y res */ ++ case 283: ++ /*tex y res */ + if (den != 0) { + yres = num / den; + found_y = true ; +- } ++ } + break; +- case 296: /* res unit */ ++ case 296: ++ /*tex res unit */ + switch (value) { + case 2: + res_unit = 1.0; +@@ -250,69 +263,69 @@ static void read_APP1_Exif (FILE *fp, unsigned short length, int *xx, int *yy, i + res_unit = 0; + break; + } +- case 0x5110: /* PixelUnit */ +- switch (value) { ++ break; ++ case 0x5110: ++ /*tex PixelUnit */ ++ switch (value) { + case 1: +- res_unit_ms = 0.0254; /* Unit is meter */ +- break; +- default: +- res_unit_ms = 0; +- } +- case 0x5111: /* PixelPerUnitX */ ++ 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: /* PixelPerUnitY */ ++ xres_ms = value; ++ break; ++ case 0x5112: ++ /*tex PixelPerUnitY */ + found_y_ms = true ; +- yres_ms = value ; +- break; +- } +- +- ++ 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); ++ 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); ++ 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); +- } ++ 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 (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. ++ 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. + + */ + +-@ @c + static void close_and_cleanup_jpg(image_dict * idict) + { +- /* if one of then is not NULL we already cleaned up */ ++ /*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; +@@ -322,19 +335,21 @@ static void close_and_cleanup_jpg(image_dict * 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. ++/*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. ++ ++*/ + +-@ @c + void read_jpg_info(image_dict * idict) + { + int i, position, units = 0; +@@ -381,16 +396,20 @@ void read_jpg_info(image_dict * idict) + position = ftell(fp); + length = 0 ; + switch (i) { +- case M_SOF3: /* lossless */ ++ case M_SOF3: ++ /*tex lossless */ + case M_SOF5: + case M_SOF6: +- case M_SOF7: /* lossless */ ++ case M_SOF7: ++ /*tex lossless */ + case M_SOF9: + case M_SOF10: +- case M_SOF11: /* lossless */ ++ case M_SOF11: ++ /*tex lossless */ + case M_SOF13: + case M_SOF14: +- case M_SOF15: /* lossless */ ++ case M_SOF15: ++ /*tex lossless */ + formatted_error("readjpg","unsupported compression SOF_%d", i - M_SOF0); + break; + case M_SOF2: +@@ -399,7 +418,8 @@ void read_jpg_info(image_dict * idict) + } + case M_SOF0: + case M_SOF1: +- length = (int) read2bytes(fp); /* read segment length */ ++ /*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); +@@ -427,19 +447,20 @@ void read_jpg_info(image_dict * idict) + 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*/ ++ /*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: +- /* pixels per inch */ ++ /*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: +- /* pixels per cm */ ++ /*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; +@@ -448,7 +469,10 @@ void read_jpg_info(image_dict * idict) + break; + } + } +- /* if either xres or yres is 0 but the other isn't, set it to the value of the other */ ++ /*tex ++ If either xres or yres is 0 but the other isn't, set ++ it to the value of the other. ++ */ + } + } + break; +@@ -471,7 +495,7 @@ void read_jpg_info(image_dict * idict) + } + } + break; +- /* ignore markers without parameters */ ++ /*tex ignore markers without parameters */ + case M_SOI: + case M_EOI: + case M_TEM: +@@ -485,23 +509,18 @@ void read_jpg_info(image_dict * idict) + case M_RST7: + break; + default: +- /* skip variable length markers */ ++ /*tex 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); +@@ -514,14 +533,13 @@ void read_jpg_info(image_dict * idict) + } + } + +-@ @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); +- /* ++ /*tex + we need to make sure that the file kept open + */ + img_keepopen(idict) = 1; +@@ -531,7 +549,6 @@ static void reopen_jpg(image_dict * idict) + } + } + +-@ @c + void write_jpg(PDF pdf, image_dict * idict) + { + size_t l; +@@ -588,6 +605,5 @@ void write_jpg(PDF pdf, image_dict * idict) + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); +- /* always */ + close_and_cleanup_jpg(idict); + } +diff --git a/texk/web2c/luatexdir/image/writepng.w b/texk/web2c/luatexdir/image/writepng.c +similarity index 85% +rename from texk/web2c/luatexdir/image/writepng.w +rename to texk/web2c/luatexdir/image/writepng.c +index 412000c42..165e202f4 100644 +--- a/texk/web2c/luatexdir/image/writepng.w ++++ b/texk/web2c/luatexdir/image/writepng.c +@@ -1,36 +1,32 @@ +-% writepng.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2013 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++writepng.c ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include + #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; +@@ -41,16 +37,16 @@ static void close_and_cleanup_png(image_dict * idict) + } + } + +-@ @c + void flush_png_info(image_dict * idict) + { + close_and_cleanup_png(idict); + } + +-@ @c ++/*tex A dummy function: */ ++ + static void warn(png_structp png_ptr, png_const_charp msg) + { +- (void)png_ptr; (void)msg; /* Make compiler happy */ ++ (void)png_ptr; (void)msg; + } + + void read_png_info(image_dict * idict) +@@ -80,12 +76,12 @@ void read_png_info(image_dict * idict) + normal_error("readpng","internal error"); + } + #if PNG_LIBPNG_VER >= 10603 +- /* ignore possibly incorrect CMF bytes */ ++ /*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); +- /* resolution support */ ++ /*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)) { +@@ -108,15 +104,12 @@ void read_png_info(image_dict * idict) + 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. +- */ ++ /*tex 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++); \ +@@ -173,7 +166,6 @@ void read_png_info(image_dict * idict) + xfree(rows[i]); \ + } + +-@ @c + static void write_palette_streamobj(PDF pdf, int palette_objnum, png_colorp palette, int num_palette) + { + int i; +@@ -195,7 +187,6 @@ static void write_palette_streamobj(PDF pdf, int palette_objnum, png_colorp pale + 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; +@@ -227,7 +218,6 @@ static void write_smask_streamobj(PDF pdf, image_dict * idict, int smask_objnum, + pdf_end_obj(pdf); + } + +-@ @c + static void write_png_gray(PDF pdf, image_dict * idict) + { + int i; +@@ -258,7 +248,6 @@ static void write_png_gray(PDF pdf, image_dict * idict) + pdf_end_obj(pdf); + } + +-@ @c + static void write_png_gray_alpha(PDF pdf, image_dict * idict) + { + int i; +@@ -307,7 +296,6 @@ static void write_png_gray_alpha(PDF pdf, image_dict * idict) + xfree(smask); + } + +-@ @c + static void write_png_rgb_alpha(PDF pdf, image_dict * idict) + { + int i; +@@ -356,20 +344,21 @@ static void write_png_rgb_alpha(PDF pdf, image_dict * idict) + 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. ++/*tex + +-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. ++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]; +@@ -394,7 +383,7 @@ static void copy_png(PDF pdf, image_dict * idict) + 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 */ ++ /*tex 1st pass to find overall stream /Length */ + if (fseek(f, 8, SEEK_SET) != 0) + normal_error("writepng", "fseek in file failed"); + do { +@@ -423,8 +412,7 @@ static void copy_png(PDF pdf, image_dict * idict) + 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 */ ++ /*tex 2nd pass to copy data */ + endflag = false; + if (fseek(f, 8, SEEK_SET) != 0) + normal_error("writepng", "fseek in file failed"); +@@ -433,7 +421,7 @@ static void copy_png(PDF pdf, image_dict * idict) + type = spng_getint(f); + switch (type) { + case SPNG_CHUNK_IDAT: +- /* do copy */ ++ /*tex do copy */ + if (idat == 2) { + normal_error("writepng", "IDAT chunk sequence broken"); + } +@@ -445,7 +433,7 @@ static void copy_png(PDF pdf, image_dict * idict) + } + break; + case SPNG_CHUNK_IEND: +- /* done */ ++ /*tex done */ + endflag = true; + break; + default: +@@ -459,17 +447,15 @@ static void copy_png(PDF pdf, image_dict * idict) + pdf_end_obj(pdf); + } + +-@ @c + static void reopen_png(image_dict * idict) + { + int width, height, xres, yres; +- width = img_xsize(idict); /* do consistency check */ ++ /*tex A consistency check: */ ++ width = img_xsize(idict); + height = img_ysize(idict); + xres = img_xres(idict); + yres = img_yres(idict); +- /* +- we need to ake sure that the file kept open +- */ ++ /*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)) { +@@ -477,13 +463,10 @@ static void reopen_png(image_dict * idict) + } + } + +-@ @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 */ ++ /*tex for libpng < 1.5.0 */ + # define PNG_FP_1 100000 + #endif + int num_palette, palette_objnum = 0; +@@ -494,31 +477,30 @@ void write_png(PDF pdf, image_dict * idict) + 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 */ ++ /*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; + } +- /* alpha channel support */ ++ /*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; + } +- /* 16 bit depth support */ ++ /*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; + } +- /* gamma support */ ++ /*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); +@@ -527,11 +509,10 @@ void write_png(PDF pdf, image_dict * idict) + 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_set_gamma(png_p, (pdf->gamma / 1000.0), (1000.0 / pdf->image_gamma)); + png_copy = false; + } +- /* reset structure */ ++ /*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); +@@ -555,9 +536,11 @@ void write_png(PDF pdf, image_dict * idict) + 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_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: +@@ -629,14 +612,12 @@ void write_png(PDF pdf, image_dict * idict) + 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; +@@ -645,42 +626,20 @@ void write_png(PDF pdf, image_dict * idict) + } + } + write_palette_streamobj(pdf, palette_objnum, palette, num_palette); +- /* always */ ++ /*tex 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 ++/*tex ++ ++ 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 + } +diff --git a/texk/web2c/luatexdir/lang/hnjalloc.c b/texk/web2c/luatexdir/lang/hnjalloc.c +new file mode 100644 +index 000000000..3ebeee9e4 +--- /dev/null ++++ b/texk/web2c/luatexdir/lang/hnjalloc.c +@@ -0,0 +1,65 @@ ++/* ++ ++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 ++#include ++#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); ++} +diff --git a/texk/web2c/luatexdir/lang/hnjalloc.w b/texk/web2c/luatexdir/lang/hnjalloc.w +deleted file mode 100644 +index 91fefec9e..000000000 +--- a/texk/web2c/luatexdir/lang/hnjalloc.w ++++ /dev/null +@@ -1,69 +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. +- +-@ wrappers for malloc +-@c +- +-#include +-#include +-#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); +-} +diff --git a/texk/web2c/luatexdir/lang/hyphen.w b/texk/web2c/luatexdir/lang/hyphen.c +similarity index 61% +rename from texk/web2c/luatexdir/lang/hyphen.w +rename to texk/web2c/luatexdir/lang/hyphen.c +index 35befb743..3a96f195b 100644 +--- a/texk/web2c/luatexdir/lang/hyphen.w ++++ b/texk/web2c/luatexdir/lang/hyphen.c +@@ -1,57 +1,51 @@ +-% 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 ++/* + ++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 /* for NULL, malloc */ +-#include /* for fprintf */ +-#include /* for strdup */ +-#include /* for malloc used by substring inclusion */ ++#include /* for NULL, malloc */ ++#include /* for fprintf */ ++#include /* for strdup */ ++#include /* for malloc used by substring inclusion */ + + #define MAXPATHS 40960 + + #ifdef UNX +-# include /* for exit */ ++# include /* for exit */ + #endif + + #include +@@ -60,9 +54,8 @@ + + #include "lang/hnjalloc.h" + +-@ TODO: should be moved to separate library ++/*tex This could be moved to separate library. */ + +-@c + static unsigned char *hnj_strdup(const unsigned char *s) + { + unsigned char *new; +@@ -75,18 +68,20 @@ static unsigned char *hnj_strdup(const unsigned char *s) + return new; + } + +-@* Type definitions. ++/*tex + +-@ a little bit of a hash table implementation. This simply maps strings +- to state numbers ++ First some type definitions and a little bit of a hash table implementation. ++ This simply maps strings to state numbers + +-@c +-typedef struct _HashTab HashTab; ++*/ ++ ++typedef struct _HashTab HashTab; + typedef struct _HashEntry HashEntry; +-typedef struct _HashIter HashIter; +-typedef union _HashVal HashVal; ++typedef struct _HashIter HashIter; ++typedef union _HashVal HashVal; ++ ++/*tex A cheap, but effective, hack. */ + +-/* A cheap, but effective, hack. */ + #define HASH_SIZE 31627 + + struct _HashTab { +@@ -110,13 +105,13 @@ struct _HashIter { + int ndx; + }; + +-@ State machine ++/* The state state machine. */ + +-@c + typedef struct _HyphenState HyphenState; + typedef struct _HyphenTrans HyphenTrans; ++ + #define MAX_CHARS 256 +-#define MAX_NAME 20 ++#define MAX_NAME 20 + + struct _HyphenDict { + int num_states; +@@ -130,9 +125,14 @@ struct _HyphenDict { + + struct _HyphenState { + char *match; +- /*char *repl; */ +- /*signed char replindex; */ +- /*signed char replcut; */ ++ /*tex Removed: ++ ++ \starttyping ++ char *repl; ++ signed char replindex; ++ signed char replcut; ++ \stoptyping ++ */ + int fallback_state; + int num_trans; + HyphenTrans *trans; +@@ -143,20 +143,21 @@ struct _HyphenTrans { + 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 + +-@ 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]; +@@ -164,9 +165,8 @@ static char *combine(char *expr, const char *subexpr) + return expr; + } + ++/*tex Some original code: */ + +-@ ORIGINAL CODE +-@c + static HashIter *new_HashIter(HashTab * h) + { + HashIter *i = hnj_malloc(sizeof(HashIter)); +@@ -176,7 +176,6 @@ static HashIter *new_HashIter(HashTab * h) + return i; + } + +- + static int nextHashStealPattern(HashIter * i, unsigned char **word, char **pattern) + { + while (i->cur == NULL) { +@@ -204,7 +203,6 @@ static int nextHash(HashIter * i, unsigned char **word) + return 1; + } + +- + static int eachHash(HashIter * i, unsigned char **word, char **pattern) + { + while (i->cur == NULL) { +@@ -218,16 +216,17 @@ static int eachHash(HashIter * i, unsigned char **word, char **pattern) + return 1; + } + +- + static void delete_HashIter(HashIter * i) + { + hnj_free(i); + } + ++/*tex + +-@ a |char*| hash function from ASU - adapted from Gtk+ ++A |char*| hash function from ASU, adapted from |Gtk+|: ++ ++*/ + +-@c + static unsigned int hnj_string_hash(const unsigned char *s) + { + const unsigned char *p; +@@ -240,18 +239,15 @@ static unsigned int hnj_string_hash(const unsigned char *s) + h = h ^ g; + } + } +- return h /* \% M */ ; ++ return h; + } + ++/*tex This assumes that key is not already present! */ + +-@ 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]; +@@ -260,23 +256,18 @@ static void state_insert(HashTab * hashtab, unsigned char *key, int state) + hashtab->entries[i] = e; + } + ++/*tex This also assumes that key is not already present! */ + +-@ 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(); ++ if (hyppat && strcmp((char *) e->u.hyppat, (char *) hyppat) != 0) { ++ normal_warning("hyphenation","a conflicting pattern has been ignored"); + } + hnj_free(e->u.hyppat); + } +@@ -292,15 +283,12 @@ static void hyppat_insert(HashTab * hashtab, unsigned char *key, char *hyppat) + hashtab->entries[i] = e; + } + ++/*tex We return |state| if found, otherwise |-1|. */ + +-@ 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)) { +@@ -310,15 +298,14 @@ static int state_lookup(HashTab * hashtab, const unsigned char *key) + return -1; + } + ++/*tex We return |state| if found, otherwise |-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 */ ++ /*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); +@@ -330,24 +317,18 @@ static char *hyppat_lookup(HashTab * hashtab, const unsigned char *chars, int l) + return NULL; + } + ++/*tex Get the state number, allocating a new state if necessary. */ + +-@ 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) ++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 */ ++ /*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))); ++ (int) ((dict->num_states << 1) * (int) sizeof(HyphenState))); + } + dict->states[dict->num_states].match = NULL; + dict->states[dict->num_states].fallback_state = -1; +@@ -356,70 +337,59 @@ static int hnj_get_state(HyphenDict * dict, + return dict->num_states++; + } + ++/*tex + +-@ Add a transition from state1 to state2 through ch - assumes that the +- transition does not already exist ++ 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). +- */ ++ /* ++ 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) { +- 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 */ ++ 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 +- } 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 ;) +- */ ++ /* ++ 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))); ++ (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 + +-#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 ++ 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. + ++*/ + +-@ 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) ++static const unsigned char *next_pattern(size_t * length, const unsigned char **buf) + { + const unsigned char *here, *rover = *buf; + while (*rover && isspace(*rover)) +@@ -435,25 +405,28 @@ static const unsigned char *next_pattern(size_t * length, + } + *length = (size_t) (rover - here); + *buf = rover; +- return *length ? here : NULL; /* zero sensed */ ++ /*tex Zero sensed: */ ++ return *length ? here : NULL; + } + + static void init_hash(HashTab ** h) + { + int i; +- if (*h) ++ if (*h) { + return; ++ } + *h = hnj_malloc(sizeof(HashTab)); +- for (i = 0; i < HASH_SIZE; i++) ++ for (i = 0; i < HASH_SIZE; i++) { + (*h)->entries[i] = NULL; ++ } + } + +- + static void clear_state_hash(HashTab ** h) + { + int i; +- if (*h == NULL) ++ if (*h == NULL) { + return; ++ } + for (i = 0; i < HASH_SIZE; i++) { + HashEntry *e, *next; + for (e = (*h)->entries[i]; e; e = next) { +@@ -466,19 +439,20 @@ static void clear_state_hash(HashTab ** h) + *h = NULL; + } + +- + static void clear_hyppat_hash(HashTab ** h) + { + int i; +- if (*h == NULL) ++ 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) ++ if (e->u.hyppat) { + hnj_free(e->u.hyppat); ++ } + hnj_free(e); + } + } +@@ -486,7 +460,6 @@ static void clear_hyppat_hash(HashTab ** h) + *h = NULL; + } + +- + static void init_dict(HyphenDict * dict) + { + dict->num_states = 1; +@@ -502,7 +475,6 @@ static void init_dict(HyphenDict * dict) + init_hash(&dict->patterns); + } + +- + static void clear_dict(HyphenDict * dict) + { + int state_num; +@@ -519,8 +491,6 @@ static void clear_dict(HyphenDict * dict) + clear_state_hash(&dict->state_num); + } + +- +- + HyphenDict *hnj_hyphen_new(void) + { + HyphenDict *dict = hnj_malloc(sizeof(HyphenDict)); +@@ -528,14 +498,12 @@ HyphenDict *hnj_hyphen_new(void) + return dict; + } + +- + void hnj_hyphen_clear(HyphenDict * dict) + { + clear_dict(dict); + init_dict(dict); + } + +- + void hnj_hyphen_free(HyphenDict * dict) + { + clear_dict(dict); +@@ -568,29 +536,30 @@ unsigned char *hnj_serialize(HyphenDict * dict) + return buf; + } + +- + void hnj_free_serialize(unsigned char *c) + { + hnj_free(c); + } + ++/*tex + +-@ hyphenation pattern: +- +-signed bytes +- +-0 indicates end (actually any negative number) ++ In hyphenation patterns we use signed bytes where |0|, or actually any ++ negative number, indicates end: + +-: prio(1+),startpos,length,len1,[replace],len2,[replace] ++ \starttyping ++ prio(1+),startpos,length,len1,[replace],len2,[replace] ++ \starttyping + +-most basic example is: ++ A basic example is: + +-p n 0 0 0 ++ \starttyping ++ p n 0 0 0 ++ \starttyping + +-for a hyphenation point between characters ++ for a hyphenation point between characters. + ++*/ + +-@c + void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f) + { + int state_num, last_state; +@@ -601,7 +570,6 @@ void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f) + unsigned char *word; + char *pattern; + size_t l = 0; +- + const unsigned char *format; + const unsigned char *begin = f; + unsigned char *pat; +@@ -609,46 +577,23 @@ void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f) + 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(); ++ normal_warning("hyphenation","a pattern of more than 254 bytes ignored"); + 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 */ ++ /*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)); +- /* remove hyphenation encoders (digits) from pat */ ++ /*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]; +@@ -665,20 +610,25 @@ void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f) + org[j + 1] = 0; + hyppat_insert(dict->patterns, pat, org); + } +- dict->pat_length += (int) ((f - begin) + 2); /* 2 for spurious spaces */ ++ /*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])) +- continue; /* Do not clip an utf8 sequence */ ++ 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])) +- continue; /* Do not start halfway an utf8 sequence */ ++ 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; +@@ -694,7 +644,8 @@ void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f) + 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' */ ++ /*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); +@@ -704,20 +655,15 @@ void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f) + } + } + 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 */ ++ /*tex Now, put in the prefix transitions. */ + while (found < 0) { + j1--; + last_state = state_num; +@@ -741,71 +687,48 @@ void hnj_hyphen_load(HyphenDict * dict, const unsigned char *f) + } + delete_HashIter(v); + clear_hyppat_hash(&dict->merged); +- +- /* put in the fallback states */ ++ /*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) { +- /* 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; ++ 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; + } +- 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) ++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 */ ++ /*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); +- +- /* Add a '.' to beginning and end to facilitate matching */ ++ /*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; +- +- /* now, run the finite state machine */ +- for (char_num = 0, here = begin_point; here != vlink(end_point); +- here = vlink(here)) { +- ++ /*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 = '.'; +@@ -816,28 +739,20 @@ void hnj_hyphen_hyphenate(HyphenDict * dict, + } + } + 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 +- */ ++ /*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)); +-#if 0 +- printf("%*s%s\n", offset,"", match); +-#endif + int m; + for (m = 0; match[m]; m++) { + if (hyphens[offset + m] < match[m]) +@@ -848,21 +763,19 @@ void hnj_hyphen_hyphenate(HyphenDict * dict, + } + } + state = hstate->fallback_state; +-#if 0 +- printf(" back to %d\n", state); +-#endif + } +- /* nothing worked, let's go to the next character */ ++ /*tex Nothing worked, let's go to the next character. */ + state = 0; +- try_next_letter:; ++ try_next_letter:; + char_num++; + } +- +- /* restore the correct pointers */ ++ /*tex Restore the correct pointers. */ + vlink(last1) = vlink(end_point); ++ /*tex + +- /* 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| ++ 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++; +diff --git a/texk/web2c/luatexdir/lang/texlang.w b/texk/web2c/luatexdir/lang/texlang.c +similarity index 59% +rename from texk/web2c/luatexdir/lang/texlang.w +rename to texk/web2c/luatexdir/lang/texlang.c +index 337d2eca5..04f96660f 100644 +--- a/texk/web2c/luatexdir/lang/texlang.w ++++ b/texk/web2c/luatexdir/lang/texlang.c +@@ -1,31 +1,32 @@ +-% texlang.w +-% +-% Copyright 2006-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++texlang.w ++ ++Copyright 2006-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include + #include "lua/luatex-api.h" + +-@ Low-level helpers ++/*tex Low-level helpers */ + +-@ @c + #define unVERBOSE + + #define MAX_TEX_LANGUAGES 16384 +@@ -60,7 +61,9 @@ struct tex_language *new_language(int n) + 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 */ ++ /*tex ++ For now, we might just use specific value for whatever task. ++ */ + hj_codes_from_lc_codes(l); + } + return lang; +@@ -82,7 +85,6 @@ struct tex_language *get_language(int n) + } + } + +-@ @c + void set_pre_hyphen_char(int n, int v) + { + struct tex_language *l = get_language((int) n); +@@ -183,7 +185,6 @@ void load_tex_patterns(int curlang, halfword head) + 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) { \ +@@ -192,17 +193,24 @@ void load_tex_patterns(int curlang, halfword head) + uindex = uni2string(uindex, xx); \ + } while (0) + +-@ Cleans one word which is returned in |cleaned|, returns the new offset into +-|buffer| ++/* ++ ++ This 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 */ ++ /*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; + +@@ -210,21 +218,21 @@ const char *clean_hyphenation(int id, const char *buff, char **cleaned) + word[i++] = (unsigned)*s; + s++; + if ((s-buff)>MAX_WORD_LEN) { +- /* todo: this is too strict, should count unicode, not bytes */ ++ /*tex 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 */ ++ /*tex Now convert the input to \UNICODE. */ + word[i] = '\0'; + utf2uni_strcpy(uword, (const char *)word); +- /* build the new word string */ ++ /*tex Build the new word string. */ + i = 0; + while (uword[i]>0) { + u = uword[i++]; + if (u == '-') { +- /* skip */ ++ /*tex Skip. */ + } else if (u == '=') { + STORE_CHAR(id,'-'); + } else if (u == '{') { +@@ -259,6 +267,9 @@ const char *clean_hyphenation(int id, const char *buff, char **cleaned) + 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); + } +@@ -268,7 +279,6 @@ const char *clean_hyphenation(int id, const char *buff, char **cleaned) + return s; + } + +-@ @c + void load_hyphenation(struct tex_language *lang, const unsigned char *buff) + { + const char *s; +@@ -322,7 +332,6 @@ void load_tex_hyphenation(int curlang, halfword head) + 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; +@@ -331,21 +340,21 @@ static halfword insert_discretionary(halfword t, halfword pre, halfword post, ha + int attr = node_attr(t) ; + disc_penalty(d) = penalty; + if (t == replace) { +- /* prev disc next-next */ ++ /*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 { +- /* prev disc next */ ++ /*tex We have |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. */ ++ /*tex For compound words following explicit hyphens. */ + f = get_cur_font(); + } + for (g = pre; g != null; g = vlink(g)) { +@@ -406,7 +415,6 @@ halfword insert_syllable_discretionary(halfword t, lang_variables * lan) + } + set_disc_field(pre_break(n), g); + } +- + if (lan->post_hyphen_char > 0) { + t = vlink(n); + g = raw_glyph_node(); +@@ -423,7 +431,6 @@ halfword insert_syllable_discretionary(halfword t, lang_variables * lan) + return n; + } + +-@ @c + static halfword insert_character(halfword t, int c) + { + halfword p; +@@ -456,12 +463,12 @@ static halfword compound_word_break(halfword t, int clang) + 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 ++ /*tex ++ No |couple_nodes(f, t);| as we can better not expose |f| as |prev| ++ pointer. + */ + vlink(f) = t ; + alink(t) = null ; +@@ -472,14 +479,14 @@ void set_disc_field(halfword f, halfword t) + } + } + +-@ @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 */ ++ 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)); +@@ -491,7 +498,6 @@ static char *hyphenation_exception(int exceptions, char *w) + return ret; + } + +-@ @c + char *exception_strings(struct tex_language *lang) + { + const char *value; +@@ -503,8 +509,8 @@ char *exception_strings(struct tex_language *lang) + lua_checkstack(Luas, 2); + lua_rawgeti(Luas, LUA_REGISTRYINDEX, lang->exceptions); + if (lua_istable(Luas, -1)) { +- /* iterate and join */ +- lua_pushnil(Luas); /* first key */ ++ /*tex Iterate and join. */ ++ lua_pushnil(Luas); + while (lua_next(Luas, -2) != 0) { + value = lua_tolstring(Luas, -1, &l); + if (current + 2 + l > size) { +@@ -520,15 +526,19 @@ char *exception_strings(struct tex_language *lang) + 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 ++/*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 ++ ++*/ + +-@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 |{| */ ++ /*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]); +@@ -548,7 +558,8 @@ 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 |{| */ ++ /*tex This puts uword[i] on the |{|. */ ++ i++; + while (i < (unsigned) len && uword[i + 1] != '}') { + ret++; + i++; +@@ -557,22 +568,23 @@ static int count_exception_part(unsigned int *j, unsigned int *uword, int len) + return ret; + } + +-@ @c + 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; ++ halfword t, pen; + unsigned len; + int clang; + lang_variables langdata; +@@ -584,126 +596,191 @@ static void do_exception(halfword wordstart, halfword r, char *replacement) + 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 (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] == '=') { +- /* do nothing ? */ ++ /*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); + } +- /*i++; *//* jump over the last right brace */ ++ /*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) { ++ 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--; + } +- t = insert_discretionary(t, gg, hh, replace, hyphen_penalty_par); +- t = vlink(t); /* skip the new disc */ + } else { + t = vlink(t); + } ++ /*tex Again we play safe. */ ++ if (t == null || vlink(t) == r) { ++ break; ++ } + } + } + +-@ 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 +- ++/*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 +@@ -716,20 +793,24 @@ there was not the best idea ever. + 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 + +-/* +- 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 ------. ++ \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 ---- 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. ++ 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}. + + */ + +@@ -742,79 +823,78 @@ static halfword find_next_wordstart(halfword r, halfword first_language, halfwor + halfword t ; + while (r != null) { + switch (type(r)) { +- case boundary_node: +- if (subtype(r) == word_boundary) { ++ 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 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 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) ; ++ 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; ++ } + } +- if (t == null) { +- /* 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; + } +- } +- /* 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; ++ /*tex We go on. */ + } +- } else { +- /* go on */ + } +- } +- break; +- default: +- start_ok = 0; +- break; ++ 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; +@@ -831,16 +911,16 @@ static int valid_wordend(halfword s, halfword strict_bound) + 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 ? */ ++ || (type(r) == kern_node && (subtype(r) == explicit_kern || + 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) == hlist_node || ++ type(r) == vlist_node || ++ type(r) == rule_node || ++ type(r) == dir_node || + type(r) == whatsit_node || +- type(r) == ins_node || /* yes or no strict test */ +- type(r) == adjust_node /* yes or no strict test */ ++ type(r) == ins_node || ++ type(r) == adjust_node + ) && ! (strict_bound == 2 || strict_bound == 3)) + || type(r) == boundary_node + ) +@@ -848,7 +928,6 @@ static int valid_wordend(halfword s, halfword strict_bound) + return 0; + } + +-@ @c + void hnj_hyphenation(halfword head, halfword tail) + { + int lchar, i; +@@ -859,37 +938,43 @@ void hnj_hyphenation(halfword head, halfword tail) + 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: + +- \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. ++ \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) { /* could be while(1), but let's be paranoid */ ++ 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; +@@ -897,7 +982,7 @@ void hnj_hyphenation(halfword head, halfword tail) + assert(is_simple_character(wordstart)); + hyf_font = font(wordstart); + if (hyphen_char(hyf_font) < 0) { +- /* For backward compatibility: */ ++ /*tex For backward compatibility we set: */ + hyf_font = 0; + } + clang = char_lang(wordstart); +@@ -947,19 +1032,28 @@ void hnj_hyphenation(halfword head, halfword tail) + r = vlink(r); + } + if (explicit_hyphen == true) { +- /* we are not at the start, so we only need to look ahead */ ++ /*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))) { +- /* we have a word already but the next character may not be a hyphen too */ ++ /*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 { +- /* we jump over the sequence of hyphens */ +- while ((t != null) && (type(t) == glyph_node) && (character(t) == ex_hyphen_char_par)) { ++ /*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) { +- /* we reached the end of the list and will quit the loop later */ ++ /*tex we reached the end of the list and will quit the loop later */ + r = null; + } + } +@@ -971,32 +1065,67 @@ void hnj_hyphenation(halfword head, halfword tail) + && (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) { +- /* handle the exception and go on to the next word */ +- do_exception(wordstart, r, replacement); ++ /*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 (!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)) { ++ 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 */ + } +- /* if (!right) break; */ +- /* what is right overruns left .. a bit messy */ + } +- (void) hnj_hyphen_hyphenate(lang->patterns, wordstart, end_word, wordlen, left, right, &langdata); + } + } ++ expstart = null ; + explicit_hyphen = false; + wordlen = 0; + hy = utf8word; +@@ -1008,36 +1137,40 @@ void hnj_hyphenation(halfword head, halfword tail) + vlink(tail) = save_tail1; + } + +-@ @c + 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_pop(Luas, 2); ++ lua_settop(Luas, top); + return; + } + nodelist_to_lua(Luas, head); + nodelist_to_lua(Luas, tail); +- if (lua_pcall(Luas, 2, 0, 0) != 0) { ++ if ((i=lua_pcall(Luas, 2, 0, 0)) != 0) { + formatted_warning("hyphenation","bad specification: %s",lua_tostring(Luas, -1)); +- lua_pop(Luas, 2); +- lua_error(Luas); ++ lua_settop(Luas, top); ++ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return; + } +- lua_pop(Luas, 1); ++ lua_settop(Luas, top); + } else if (callback_id == 0) { + hnj_hyphenation(head, tail); + } + } + +-@ Dumping and undumping languages. ++/*tex ++ ++ Dumping and undumping languages: ++ ++*/ + +-@c + #define dump_string(a) \ + if (a!=NULL) { \ + x = (int)strlen(a)+1; \ +@@ -1106,7 +1239,7 @@ static void undump_one_language(int i) + lang->post_exhyphen_char = x; + undump_int(x); + lang->hyphenation_min = x; +- /* patterns */ ++ /*tex patterns */ + undump_int(x); + if (x > 0) { + s = xmalloc((unsigned) x); +@@ -1114,7 +1247,7 @@ static void undump_one_language(int i) + load_patterns(lang, (unsigned char *) s); + free(s); + } +- /* exceptions */ ++ /*tex exceptions */ + undump_int(x); + if (x > 0) { + s = xmalloc((unsigned) x); +@@ -1137,21 +1270,26 @@ void undump_language_data(void) + } + } + +-@ When \TeX\ has scanned `\.{\\hyphenation}', it calls on a procedure named +-|new_hyph_exceptions| to do the right thing. ++/*tex ++ ++ 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|. ++/* ++ Similarly, when \TeX\ has scanned `\.{\\patterns}', it calls on a procedure ++ named |new_patterns|. ++ ++*/ + +-@c + void new_patterns(void) + { + (void) scan_toks(false, true); +@@ -1159,10 +1297,14 @@ void new_patterns(void) + 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). ++/*tex ++ ++ `\.{\\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(); +@@ -1177,10 +1319,14 @@ void new_post_hyphen_char(void) + 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). ++/*tex ++ ++ `\.{\\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(); +diff --git a/texk/web2c/luatexdir/lua/helpers.c b/texk/web2c/luatexdir/lua/helpers.c +new file mode 100644 +index 000000000..e7be7a2c2 +--- /dev/null ++++ b/texk/web2c/luatexdir/lua/helpers.c +@@ -0,0 +1,29 @@ ++/* ++ ++helpers.w ++ ++Copyright 2017 LuaTeX team ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++/*tex ++ ++We might move here some helpers common to code that are now in weird places, ++probably organized in texhelpers, luahelpers, pdfhelpers. ++ ++*/ +diff --git a/texk/web2c/luatexdir/lua/helpers.w b/texk/web2c/luatexdir/lua/helpers.w +deleted file mode 100644 +index d12e47690..000000000 +--- a/texk/web2c/luatexdir/lua/helpers.w ++++ /dev/null +@@ -1,26 +0,0 @@ +-% helpers.w +-% +-% Copyright 2017 LuaTeX team +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ We will move here some helpers common to code that are now in weird places, +-probably organized in texhelpers, luahelpers, pdfhelpers. +- +- +- +-@c +-/* to fill... */ +diff --git a/texk/web2c/luatexdir/lua/llfslibext.c b/texk/web2c/luatexdir/lua/llfslibext.c +deleted file mode 100644 +index eba5778df..000000000 +--- a/texk/web2c/luatexdir/lua/llfslibext.c ++++ /dev/null +@@ -1,171 +0,0 @@ +-/* llfslibext.c +- +- Copyright 2010-2011 Taco Hoekwater +- +- This file is part of LuaTeX. +- +- LuaTeX is free software; you can redistribute it and/or modify it under +- the terms of the GNU General Public License as published by the Free +- Software Foundation; either version 2 of the License, or (at your +- option) any later version. +- +- LuaTeX is distributed in the hope that it will be useful, but WITHOUT +- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +- License for more details. +- +- You should have received a copy of the GNU General Public License along +- with LuaTeX; if not, see . */ +- +-#include "ptexlib.h" +-#include "lua/luatex-api.h" +- +-#include +-#include +-#include +- +- +-#ifdef _WIN32 +-# include +-#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 */ +-} +diff --git a/texk/web2c/luatexdir/lua/lpdfelib.c b/texk/web2c/luatexdir/lua/lpdfelib.c +new file mode 100644 +index 000000000..31fad9f63 +--- /dev/null ++++ b/texk/web2c/luatexdir/lua/lpdfelib.c +@@ -0,0 +1,1666 @@ ++/*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 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() ++ \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;iarray->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;idictionary->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 argument: string or lightuserdata expected"); ++ break; ++ } ++ if (docstream == NULL) { ++ luaL_error(L, "bad 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 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, " stream has an invalid id"); ++ } ++ if (strlen(file_id) > STREAM_FILE_ID_LEN ) { ++ /* a limit to the length of the string */ ++ luaL_error(L, " 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 ++ = getstring (array|dict|ref,index|key) ++ = getinteger (array|dict|ref,index|key) ++ = getnumber (array|dict|ref,index|key) ++ = getboolean (array|dict|ref,index|key) ++ = getname (array|dict|ref,index|key) ++ = getdictionary(array|dict|ref,index|key) ++ = getarray (array|dict|ref,index|key) ++ , = 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 expected"); \ ++ } else if (t == LUA_TNUMBER) { \ ++ normal_warning("pdfe lib","lua 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 or "); \ ++ } \ ++} 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; ++} +diff --git a/texk/web2c/luatexdir/lua/lpdfscannerlib.c b/texk/web2c/luatexdir/lua/lpdfscannerlib.c +new file mode 100644 +index 000000000..6c3bc8876 +--- /dev/null ++++ b/texk/web2c/luatexdir/lua/lpdfscannerlib.c +@@ -0,0 +1,1110 @@ ++/* lpdfscannerlib.c ++ ++ Copyright 2013 Taco Hoekwater ++ ++ This file is part of LuaTeX. ++ ++ LuaTeX is free software; you can redistribute it and/or modify it under ++ the terms of the GNU General Public License as published by the Free ++ Software Foundation; either version 2 of the License, or (at your ++ option) any later version. ++ ++ LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++ License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with LuaTeX; if not, see . ++ ++*/ ++ ++/*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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "luapplib/pplib.h" ++ ++#include ++ ++#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 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 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; ++} +diff --git a/texk/web2c/luatexdir/lua/luainit.w b/texk/web2c/luatexdir/lua/luainit.c +similarity index 76% +rename from texk/web2c/luatexdir/lua/luainit.w +rename to texk/web2c/luatexdir/lua/luainit.c +index dec76b021..60c654aa0 100644 +--- a/texk/web2c/luatexdir/lua/luainit.w ++++ b/texk/web2c/luatexdir/lua/luainit.c +@@ -1,23 +1,25 @@ +-% luainit.w +-% +-% Copyright 2006-2018 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++luainit.w ++ ++Copyright 2006-2018 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +@@ -29,27 +31,23 @@ + + extern int load_luatex_core_lua (lua_State * L); + +-/* internalized strings: see luatex-api.h */ +-set_make_keys; ++/*tex internalized strings: see luatex-api.h */ + +-@ +-This file is getting a bit messy, but it is not simple to fix unilaterally. ++set_make_keys; + +-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. ++/*tex + +-In fact, it sets three C variables: ++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| ++ |kpse_invocation_name| |kpse_invocation_short_name| |kpse->program_name| + + and five environment variables: + +- SELFAUTOLOC SELFAUTODIR SELFAUTOPARENT SELFAUTOGRANDPARENT progname ++ |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", +@@ -109,9 +107,13 @@ const_string LUATEX_IHELP[] = { + 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 ++/*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; +@@ -126,26 +128,28 @@ const char *lc_numeric; + " --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 ++/*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; +- +- /* SearchPath() always gives back an absolute directory */ ++ /*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); +- /* slashify the dirname */ ++ /*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; +- +- /* SearchPath() always gives back an absolute directory */ ++ /*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) { +@@ -158,7 +162,6 @@ static char *ex_selfdir(char *argv0) + #endif + } + +-@ @c + static void prepare_cmdline(lua_State * L, char **av, int ac, int zero_offset) + { + int i; +@@ -178,11 +181,8 @@ static void prepare_cmdline(lua_State * L, char **av, int ac, int zero_offset) + return; + } + +- +-@ @c + int kpse_init = -1; + +-@ @c + string input_name = NULL; + + static string user_progname = NULL; +@@ -201,22 +201,20 @@ int safer_option = 0; + int nosocket_option = 0; + int utc_option = 0; + +-@ Reading the options. ++/*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|. ++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. + */ + +-/* ++#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[] = { +@@ -253,9 +251,7 @@ static struct option long_options[] = { + {"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. */ +- ++ /*tex Shorter option names for the above. */ + {"file-line-error", 0, &filelineerrorstylep, 1}, + {"no-file-line-error", 0, &filelineerrorstylep, -1}, + {"jobname", 1, 0, 0}, +@@ -266,18 +262,16 @@ static struct option long_options[] = { + {"8bit", 0, 0, 0}, + {"mktex", 1, 0, 0}, + {"no-mktex", 1, 0, 0}, +- +- /* Synchronization: just like "interaction" above */ +- ++ /*tex 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 */ ++ /*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); +@@ -286,11 +280,11 @@ int lua_numeric_field_by_index(lua_State * L, int name_index, int dflt) + 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 */ ++ /*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); +@@ -299,20 +293,21 @@ unsigned int lua_unsigned_numeric_field_by_index(lua_State * L, int name_index, + return i; + } + +-@ @c + static int recorderoption = 0; + + static void parse_options(int ac, char **av) + { + #ifdef WIN32 +-/* save argc and argv */ ++ /*tex We save |argc| and |argv|. */ + int sargc = argc; + char **sargv = argv; + #endif +- int g; /* `getopt' return code. */ ++ /*tex The `getopt' return code. */ ++ int g; + int option_index; + char *firstfile = NULL; +- opterr = 0; /* dont whine */ ++ /*tex Dont whine. */ ++ opterr = 0; + #ifdef LuajitTeX + if ((strstr(argv[0], "luajittexlua") != NULL) || + (strstr(argv[0], "texluajit") != NULL)) { +@@ -326,37 +321,39 @@ static void parse_options(int ac, char **av) + + for (;;) { + g = getopt_long_only(ac, av, "+", long_options, &option_index); +- if (g == -1) /* End of arguments, exit the loop. */ ++ if (g == -1) { ++ /*tex 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 (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 = xstrdup(optarg); ++ 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; +- } ++ 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; +@@ -414,7 +411,7 @@ static void parse_options(int ac, char **av) + WARNING1("Ignoring unknown argument `%s' to --interaction", optarg); + } + } else if (ARGUMENT_IS("synctex")) { +- /* Synchronize TeXnology: catching the command line option as a long */ ++ /*tex Synchronize TeXnology: catching the command line option as a long */ + synctexoption = (int) strtol(optarg, NULL, 0); + } else if (ARGUMENT_IS("recorder")) { + recorderoption = 1 ; +@@ -445,8 +442,8 @@ static void parse_options(int ac, char **av) + "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" ++ "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* */ +@@ -454,7 +451,7 @@ static void parse_options(int ac, char **av) + uexit(0); + } + } +- /* attempt to find |input_name| / |dump_name| */ ++ /*tex attempt to find |input_name| and |dump_name| */ + if (lua_only) { + if (argv[optind]) { + startup_filename = xstrdup(argv[optind]); +@@ -502,20 +499,23 @@ static void parse_options(int ac, char **av) + return; + #endif + } +- if (safer_option) /* --safer implies --nosocket */ ++ /*tex |--safer| implies |--nosocket| */ ++ if (safer_option) + nosocket_option = 1; +- /* Finalize the input filename. */ ++ /*tex 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) ++/*tex ++ Test for readability. ++*/ ++ ++#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; +@@ -543,8 +543,6 @@ static char *find_filename(char *name, const char *envkey) + return NULL; + } + +-@ @c +- + static void init_kpse(void) + { + if (!user_progname) { +@@ -576,11 +574,10 @@ static void init_kpse(void) + user_progname = dump_name; + } + } +- kpse_set_program_enabled(kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT, +- kpse_src_compile); +- ++ 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' */ ++ /*tex set up 'restrictedshell' */ ++ init_shell_escape(); + init_start_time(); + program_name_set = 1 ; + if (recorderoption) { +@@ -588,19 +585,18 @@ static void init_kpse(void) + } + } + +-@ @c + static void fix_dumpname(void) + { + int dist; + if (dump_name) { +- /* adjust array for Pascal and provide extension, if needed */ ++ /*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 { +- /* For |dump_name| to be NULL is a bug. */ ++ /*tex For |dump_name| to be NULL is a bug. */ + if (!ini_version) { + fprintf(stdout, "no format given, quitting\n"); + exit(1); +@@ -608,17 +604,17 @@ static void fix_dumpname(void) + } + } + +-@ lua require patch +- +-@ Auxiliary function for kpse search ++/*tex ++ 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) ++ kpse_file_format_type format, const char *errname) + { + const char *filename; + const char *altname; +- altname = luaL_gsub(L, name, ".", "/"); /* Lua convention */ ++ /*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); +@@ -629,16 +625,17 @@ static const char *luatex_kpse_find_aux(lua_State *L, const char *name, + return filename; + } + +-@ The lua search function. ++/*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. + +-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 . + +-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) +@@ -653,16 +650,18 @@ static int luatex_kpse_lua_find(lua_State * L) + 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 (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)); ++ lua_tostring(L, 1), filename, lua_tostring(L, -1)); + } +- return 1; /* library loaded successfully */ ++ /*tex library loaded successfully */ ++ return 1; + } + +-@ @c + static int clua_loader_function = 0; + extern int searcher_C_luatex (lua_State *L, const char *name, const char *filename); + +@@ -671,8 +670,9 @@ 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; /* library not found in this path */ ++ return 1; + } + name = luaL_checkstring(L, 1); + if (program_name_set == 0) { +@@ -687,12 +687,16 @@ static int luatex_kpse_clua_find(lua_State * L) + 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 */ ++ if (filename == NULL) { ++ /*tex library not found in this path */ ++ return 1; ++ } + extensionless = strdup(filename); +- if (!extensionless) +- return 1; /* allocation failure */ +- /* Fix Issue 850: replace '.' with LUA_DIRSEP */ ++ 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') { +@@ -703,33 +707,44 @@ static int luatex_kpse_clua_find(lua_State * L) + } + } + p = strstr(extensionless, temp_name); +- if (!p) return 1; /* this would be exceedingly weird */ ++ if (!p) { ++ /*tex this would be exceedingly weird */ ++ return 1; ++ } + *p = '\0'; + prefix = strdup(extensionless); +- if (!prefix) return 1; /* allocation failure */ ++ if (!prefix) { ++ /*tex allocation failure */ ++ return 1; ++ } + postfix = strdup(p+strlen(name)); +- if (!postfix) return 1; /* allocation failure */ ++ 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); +- /* save package.path */ ++ /*tex 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 = "?" */ ++ /*tex set package.path = "?" */ + lua_pushstring(L,total); + lua_setfield(L,-2,"cpath"); +- lua_pop(L,1); /* pop "package" */ +- /* run function */ ++ /*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); +- /* restore package.path */ ++ /*tex restore package.path */ + lua_getglobal(L,"package"); + lua_pushstring(L,path_saved); + lua_setfield(L,-2,"cpath"); +- lua_pop(L,1); /* pop "package" */ ++ /*tex pop ``package'' */ ++ lua_pop(L,1); + free(extensionless); + free(total); + free(temp_name); +@@ -737,12 +752,13 @@ static int luatex_kpse_clua_find(lua_State * L) + } + } + +-@ Setting up the new search functions. ++/*tex ++ ++ Setting up the new search functions. This replaces package.searchers[2] and ++ package.searchers[3] with the functions defined above. + +-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"); +@@ -751,46 +767,45 @@ static void setup_lua_path(lua_State * L) + #else + lua_getfield(L, -1, "searchers"); + #endif +- lua_rawgeti(L, -1, 2); /* package.searchers[2] */ ++ /*tex package.searchers[2] */ ++ lua_rawgeti(L, -1, 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] */ ++ /*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); +- lua_rawseti(L, -2, 3); /* replace the normal lua lib loader */ +- +- lua_pop(L, 2); /* pop the array and table */ ++ /*tex replace the normal lua lib loader */ ++ lua_rawseti(L, -2, 3); ++ /*tex pop the array and table */ ++ lua_pop(L, 2); + } + +-@ helper variables for the safe keeping of table ids ++/*tex ++ ++ 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 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 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+1); ++ lua_pushinteger(L,i+offset); + lua_pushstring(L, list[i]); + lua_settable(L, -3); + } +@@ -812,28 +827,31 @@ int lua_show_valid_keys(lua_State *L, int *list, int max) + #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" ++/* 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" + +-@ @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 */ ++ /*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++; +@@ -862,12 +880,10 @@ static void mk_suffixlist(void) + } + #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; +@@ -878,7 +894,7 @@ void lua_initialize(int ac, char **av) + char *old_locale = NULL; + char *env_locale = NULL; + char *tmp = NULL; +- /* Save to pass along to topenin. */ ++ /*tex Save to pass along to topenin. */ + const char *fmt = "This is " MyName ", Version %s" WEB2CVERSION; + argc = ac; + argv = av; +@@ -887,8 +903,7 @@ void lua_initialize(int ac, char **av) + sprintf(banner, fmt, luatex_version_string); + luatex_banner = banner; + kpse_invocation_name = kpse_program_basename(argv[0]); +- +- /* be 'luac' */ ++ /*tex be `luac' */ + if (argc >1) { + #ifdef LuajitTeX + if (FILESTRCASEEQ(kpse_invocation_name, "texluajitc")) +@@ -911,106 +926,104 @@ void lua_initialize(int ac, char **av) + #if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) + mk_suffixlist(); + #endif +- +- /* Must be initialized before options are parsed. */ ++ /*tex 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. */ ++ /*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; +- +- /* parse commandline */ ++ /*tex parse commandline */ + parse_options(ac, av); + if (lua_only) { +- /* Shell has no restrictions. */ ++ /*tex 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. */ ++ /*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) { +- /* If setlocale fails here, then the state */ +- /* could be compromised, and we exit. */ ++ /*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); +- } ++ 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); ++ if (tmp) { ++ lc_ctype = xstrdup(tmp); + } +- tmp = setlocale (LC_COLLATE, NULL); +- if (tmp){ +- lc_collate = xstrdup(tmp); ++ tmp = setlocale (LC_COLLATE, NULL); ++ if (tmp) { ++ lc_collate = xstrdup(tmp); + } +- tmp = setlocale (LC_NUMERIC, NULL); +- if (tmp){ +- lc_numeric = 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); + } +- /* 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) */ ++ /*tex 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 */ ++ /*tex this is sometimes needed */ + putenv(engine_luatex); +- + luainterpreter(); +- +- /* init internalized strings */ ++ /*tex init internalized strings */ + set_init_keys; +- + lua_pushstring(Luas,"lua.functions"); + lua_newtable(Luas); + lua_settable(Luas,LUA_REGISTRYINDEX); +- +- /* here start the key definitions */ ++ /*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; +- +- prepare_cmdline(Luas, argv, argc, lua_offset); /* collect arguments */ ++ /*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); ++ xfree(startup_filename); + } + startup_filename = find_filename(given_file, "LUATEXDIR"); + } +- /* now run the file */ ++ /*tex now run the file */ + if (startup_filename != NULL) { + char *v1; + int tex_table_id = hide_lua_table(Luas, "tex"); +@@ -1018,7 +1031,7 @@ void lua_initialize(int ac, char **av) + 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 */ ++ /*tex hide the 'tex' and 'pdf' table */ + if (load_luatex_core_lua(Luas)) { + fprintf(stderr, "Error in execution of luatex-core.lua .\n"); + } +@@ -1026,20 +1039,20 @@ void lua_initialize(int ac, char **av) + fprintf(stdout, "%s\n", lua_tostring(Luas, -1)); + exit(1); + } +- init_tex_table(Luas); /* needed ? */ ++ init_tex_table(Luas); + if (lua_pcall(Luas, 0, 0, 0)) { + fprintf(stdout, "%s\n", lua_tostring(Luas, -1)); + lua_traceback(Luas); +- /* lua_close(Luas); */ ++ /*tex lua_close(Luas); */ + exit(1); + } else { + if (given_file) + free(given_file); +- /* lua_close(Luas); */ ++ /*tex lua_close(Luas); */ + exit(0); + } + } +- /* a normal tex run */ ++ /*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); +@@ -1060,28 +1073,25 @@ void lua_initialize(int ac, char **av) + 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 */ ++ /*tex re-enable loading of texmf.cnf values, see luatex.ch */ ++ luainit = 0; + init_kpse(); + kpse_init = 1; + } +- /* |prohibit_file_trace| (boolean) */ ++ /*tex |prohibit_file_trace| (boolean) */ + tracefilenames = 1; + get_lua_boolean("texconfig", "trace_file_names", &tracefilenames); +- +- /* |file_line_error| */ ++ /*tex |file_line_error| */ + filelineerrorstylep = false; + get_lua_boolean("texconfig", "file_line_error", &filelineerrorstylep); +- +- /* |halt_on_error| */ ++ /*tex |halt_on_error| */ + haltonerrorp = false; + get_lua_boolean("texconfig", "halt_on_error", &haltonerrorp); +- +- /* |restrictedshell| */ ++ /*tex |restrictedshell| */ + v1 = NULL; + get_lua_string("texconfig", "shell_escape", &v1); + if (v1) { +@@ -1093,7 +1103,7 @@ void lua_initialize(int ac, char **av) + } + free(v1); + } +- /* If shell escapes are restricted, get allowed cmds from cnf. */ ++ /*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); +@@ -1102,11 +1112,10 @@ void lua_initialize(int ac, char **av) + 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. + */ +@@ -1115,38 +1124,32 @@ void lua_initialize(int ac, char **av) + 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 if (luainit) { ++ if (given_file) { ++ fprintf(stdout, "%s file %s not found\n", (lua_only ? "Script" : "Configuration"), given_file); ++ free(given_file); + } else { +- /* init */ +- init_kpse(); +- kpse_init = 1; +- fix_dumpname(); ++ 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"); + } +- +- /* 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) { +@@ -1156,7 +1159,10 @@ void check_texconfig_init(void) + 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 */ ++ /*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(); + } +diff --git a/texk/web2c/luatexdir/lua/luanode.w b/texk/web2c/luatexdir/lua/luanode.c +similarity index 64% +rename from texk/web2c/luatexdir/lua/luanode.w +rename to texk/web2c/luatexdir/lua/luanode.c +index ef2f0b926..3182b6097 100644 +--- a/texk/web2c/luatexdir/lua/luanode.w ++++ b/texk/web2c/luatexdir/lua/luanode.c +@@ -1,33 +1,32 @@ +-% luanode.w +-% +-% Copyright 2006-2008 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-/* 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 */ ++luanode.w + +-@ @c ++Copyright 2006-2008 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include "lua/luatex-api.h" + +-@ @c + 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) { +@@ -38,20 +37,20 @@ void lua_node_filter_s(int filterid, int extrainfo) + 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_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); +- error(); ++ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return; + } + lua_settop(Luas, s_top); + return; + } + +-@ @c + 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); +@@ -59,45 +58,45 @@ void lua_node_filter(int filterid, int extrainfo, halfword head_node, halfword * + lua_settop(Luas, s_top); + return; + } +- /* we start after head */ ++ /*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; + } +- /* we make sure we have no prev */ ++ /*tex We make sure we have no prev */ + alink(start_node) = null ; +- /* the action */ ++ /*tex 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)); ++ 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); +- error(); ++ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return; + } +- /* the result */ ++ /*tex the result */ + if (lua_isboolean(Luas, -1)) { + if (lua_toboolean(Luas, -1) != 1) { +- /* discard */ ++ /*tex discard */ + flush_node_list(start_node); + vlink(head_node) = null; + } else { +- /* keep */ ++ /*tex keep */ + } + } else { +- /* append to old head */ +- start_done = nodelist_from_lua(Luas); ++ /*tex append to old head */ ++ start_done = nodelist_from_lua(Luas,-1); + try_couple_nodes(head_node,start_done); + } +- /* redundant as we set top anyway */ ++ /*tex redundant as we set top anyway */ + lua_pop(Luas, 2); +- /* find tail in order to update tail */ ++ /*tex find tail in order to update tail */ + start_node = vlink(head_node); + if (start_node != null) { +- /* maybe just always slide (harmless and fast) */ ++ /*tex maybe just always slide (harmless and fast) */ + if (fix_node_lists) { +- /* slides and returns last node */ ++ /*tex slides and returns last node */ + *tail_node = fix_node_list(start_node); + } else { + last_node = vlink(start_node); +@@ -105,24 +104,23 @@ void lua_node_filter(int filterid, int extrainfo, halfword head_node, halfword * + start_node = last_node; + last_node = vlink(start_node); + } +- /* we're at the end now */ ++ /*tex we're at the end now */ + *tail_node = start_node; + } + } else { +- /* we're already at the end */ ++ /*tex we're already at the end */ + *tail_node = head_node; + } +- /* clean up */ ++ /*tex clean up */ + lua_settop(Luas, s_top); + return; + } + +-@ @c + int lua_linebreak_callback(int is_broken, halfword head_node, halfword * new_head) + { +- int a; ++ int a, i; + register halfword *p; +- int ret = 0; /* failure */ ++ 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) { +@@ -130,32 +128,33 @@ int lua_linebreak_callback(int is_broken, halfword head_node, halfword * new_hea + return ret; + } + if (!get_callback(Luas, callback_id)) { +- lua_settop(Luas, s_top); ++ 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)); ++ 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); +- error(); ++ 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); ++ a = nodelist_from_lua(Luas,-1); + 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) ++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) { +@@ -170,10 +169,10 @@ int lua_appendtovlist_callback(halfword box, int location, halfword prev_depth, + 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)); ++ 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); +- error(); ++ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return 0; + } + if (lua_type(Luas,-1) == LUA_TNUMBER) { +@@ -187,13 +186,13 @@ int lua_appendtovlist_callback(halfword box, int location, halfword prev_depth, + 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 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); +@@ -205,7 +204,7 @@ halfword lua_hpack_filter(halfword head_node, scaled size, int pack_type, int ex + lua_settop(Luas, s_top); + return head_node; + } +- alink(head_node) = null ; /* hh-ls */ ++ alink(head_node) = null ; + nodelist_to_lua(Luas, head_node); + lua_push_group_code(Luas,extrainfo); + lua_pushinteger(Luas, size); +@@ -220,10 +219,10 @@ halfword lua_hpack_filter(halfword head_node, scaled size, int pack_type, int ex + } else { + lua_pushnil(Luas); + } +- if (lua_pcall(Luas, 6, 1, 0) != 0) { +- fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1)); ++ 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); +- error(); ++ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return head_node; + } + ret = head_node; +@@ -233,29 +232,26 @@ halfword lua_hpack_filter(halfword head_node, scaled size, int pack_type, int ex + ret = null; + } + } else { +- ret = nodelist_from_lua(Luas); ++ ret = nodelist_from_lua(Luas,-1); + } + 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) ++ 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) { /* output */ ++ if (extrainfo == 8) { + callback_id = callback_defined(pre_output_filter_callback); + } else { + callback_id = callback_defined(vpack_filter_callback); +@@ -268,7 +264,7 @@ halfword lua_vpack_filter(halfword head_node, scaled size, int pack_type, scaled + lua_settop(Luas, s_top); + return head_node; + } +- alink(head_node) = null ; /* hh-ls */ ++ alink(head_node) = null ; + nodelist_to_lua(Luas, head_node); + lua_push_group_code(Luas, extrainfo); + lua_pushinteger(Luas, size); +@@ -284,10 +280,10 @@ halfword lua_vpack_filter(halfword head_node, scaled size, int pack_type, scaled + } else { + lua_pushnil(Luas); + } +- if (lua_pcall(Luas, 7, 1, 0) != 0) { +- fprintf(stdout, "error: %s\n", lua_tostring(Luas, -1)); ++ if ((i=lua_pcall(Luas, 7, 1, 0)) != 0) { ++ formatted_warning("vpack filter", "error: %s", lua_tostring(Luas, -1)); + lua_settop(Luas, s_top); +- error(); ++ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return head_node; + } + ret = head_node; +@@ -297,76 +293,84 @@ halfword lua_vpack_filter(halfword head_node, scaled size, int pack_type, scaled + ret = null; + } + } else { +- ret = nodelist_from_lua(Luas); ++ ret = nodelist_from_lua(Luas,-1); + } + 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. ++/*tex ++ ++ This is a quick hack to fix \ETEX's \.{\\lastnodetype} now that there are many ++ more visible node types. ++ ++*/ + +-@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 */ ++ /*tex old ligature value */ ++ return 7; + } else { +- return 0; /* old character value */ ++ /*tex old character value */ ++ return 0; + } + } + +-@ @c +-void lua_pdf_literal(PDF pdf, int i) ++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); +- pdf_out_block(pdf, s, l); +- pdf_out(pdf, 10); /* |pdf_print_nl| */ ++ 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); + } + +-@ @c + void copy_pdf_literal(pointer r, pointer p) + { +- pdf_literal_type(r) = pdf_literal_type(p); ++ int t = pdf_literal_type(p); ++ pdf_literal_type(r) = t; + pdf_literal_mode(r) = pdf_literal_mode(p); +- if (pdf_literal_type(p) == normal) { ++ if (t == normal) { + pdf_literal_data(r) = pdf_literal_data(p); + add_token_ref(pdf_literal_data(p)); +- } else { ++ } 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); + } + } + +-@ @c + void copy_late_lua(pointer r, pointer p) + { +- late_lua_type(r) = late_lua_type(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 (late_lua_type(p) == normal) { ++ if (t == normal) { + late_lua_data(r) = late_lua_data(p); + add_token_ref(late_lua_data(p)); +- } else { ++ } else if (t == lua_refid_literal) { + 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) { +@@ -375,28 +379,28 @@ void copy_user_lua(pointer r, pointer p) + } + } + +-@ @c + void free_pdf_literal(pointer p) + { +- if (pdf_literal_type(p) == normal) { ++ int t = pdf_literal_type(p); ++ if (t == normal) { + delete_token_ref(pdf_literal_data(p)); +- } else { ++ } 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 (late_lua_type(p) == normal) { ++ if (t == normal) { + delete_token_ref(late_lua_data(p)); +- } else { ++ } else if (t == lua_refid_literal) { + luaL_unref(Luas, LUA_REGISTRYINDEX, late_lua_data(p)); + } + } + +-@ @c + void free_user_lua(pointer p) + { + if (user_node_value(p) != 0) { +@@ -404,9 +408,9 @@ void free_user_lua(pointer p) + } + } + +-@ @c + void show_pdf_literal(pointer p) + { ++ int t = pdf_literal_type(p); + tprint_esc("pdfliteral"); + switch (pdf_literal_mode(p)) { + case set_origin: +@@ -422,30 +426,36 @@ void show_pdf_literal(pointer p) + tprint(" raw"); + break; + default: +- confusion("literal2"); ++ tprint(" "); + break; + } +- if (pdf_literal_type(p) == normal) { ++ if (t == normal) { + print_mark(pdf_literal_data(p)); ++ } else if (t == lua_refid_literal) { ++ tprint(" "); + } else { +- lua_rawgeti(Luas, LUA_REGISTRYINDEX, pdf_literal_data(p)); +- tprint("\""); +- tprint(lua_tostring(Luas, -1)); +- tprint("\""); +- lua_pop(Luas, 1); ++ tprint(" "); + } + } + +-@ @c + void show_late_lua(pointer p) + { ++ int t = late_lua_type(p); + tprint_esc("latelua"); + print_int(late_lua_reg(p)); +- if (late_lua_type(p) == normal) { ++ if (t == normal) { + print_mark(late_lua_data(p)); +- } else { +- tprint(" "); ++ } else if (t == lua_refid_call) { ++ tprint(" "); ++ } else { ++ tprint(" "); + } + } +diff --git a/texk/web2c/luatexdir/lua/luastuff.w b/texk/web2c/luatexdir/lua/luastuff.c +similarity index 56% +rename from texk/web2c/luatexdir/lua/luastuff.w +rename to texk/web2c/luatexdir/lua/luastuff.c +index a2b1dd143..0d9342223 100644 +--- a/texk/web2c/luatexdir/lua/luastuff.w ++++ b/texk/web2c/luatexdir/lua/luastuff.c +@@ -1,30 +1,32 @@ +-% luastuff.w +-% +-% Copyright 2006-2013 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++luastuff.w ++ ++Copyright 2006-2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ + #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; +@@ -45,31 +47,44 @@ int lua_active = 0; + lua_pop(L, 1); + #endif + +-@ @c + void make_table(lua_State * L, const char *tab, const char *mttab, const char *getfunc, const char *setfunc) + { +- /* make the table *//* |[{}]| */ +- lua_pushstring(L, tab); /* |[{},"dimen"]| */ +- lua_newtable(L); /* |[{},"dimen",{}]| */ +- lua_settable(L, -3); /* |[{}]| */ +- /* fetch it back */ +- lua_pushstring(L, tab); /* |[{},"dimen"]| */ +- lua_gettable(L, -2); /* |[{},{}]| */ +- /* make the meta entries */ +- luaL_newmetatable(L, mttab); /* |[{},{},{}]| */ +- lua_pushstring(L, "__index"); /* |[{},{},{},"__index"]| */ +- lua_pushstring(L, getfunc); /* |[{},{},{},"__index","getdimen"]| */ +- lua_gettable(L, -5); /* |[{},{},{},"__index",]| */ +- lua_settable(L, -3); /* |[{},{},{}]| */ +- lua_pushstring(L, "__newindex"); /* |[{},{},{},"__newindex"]| */ +- lua_pushstring(L, setfunc); /* |[{},{},{},"__newindex","setdimen"]| */ +- lua_gettable(L, -5); /* |[{},{},{},"__newindex",]| */ +- lua_settable(L, -3); /* |[{},{},{}]| */ +- lua_setmetatable(L, -2); /* |[{},{}]| : assign the metatable */ +- lua_pop(L, 1); /* |[{}]| : clean the stack */ ++ /*tex make the table *//* |[{}]| */ ++ /*tex |[{},"dimen"]| */ ++ lua_pushstring(L, tab); ++ /*tex |[{},"dimen",{}]| */ ++ lua_newtable(L); ++ /*tex |[{}]| */ ++ lua_settable(L, -3); ++ /*tex fetch it back */ ++ /*tex |[{},"dimen"]| */ ++ lua_pushstring(L, tab); ++ /*tex |[{},{}]| */ ++ lua_gettable(L, -2); ++ /*tex make the meta entries */ ++ /*tex |[{},{},{}]| */ ++ luaL_newmetatable(L, mttab); ++ /*tex |[{},{},{},"__index"]| */ ++ lua_pushstring(L, "__index"); ++ /*tex |[{},{},{},"__index","getdimen"]| */ ++ lua_pushstring(L, getfunc); ++ /*tex |[{},{},{},"__index",]| */ ++ lua_gettable(L, -5); ++ /*tex |[{},{},{}]| */ ++ lua_settable(L, -3); ++ lua_pushstring(L, "__newindex"); /*tex |[{},{},{},"__newindex"]| */ ++ /*tex |[{},{},{},"__newindex","setdimen"]| */ ++ lua_pushstring(L, setfunc); ++ /*tex |[{},{},{},"__newindex",]| */ ++ lua_gettable(L, -5); ++ /*tex |[{},{},{}]| */ ++ lua_settable(L, -3); ++ /*tex |[{},{}]| : assign the metatable */ ++ lua_setmetatable(L, -2); ++ /*tex |[{}]| : clean the stack */ ++ lua_pop(L, 1); + } + +-@ @c + static const char *getS(lua_State * L, void *ud, size_t * size) + { + LoadS *ls = (LoadS *) ud; +@@ -81,18 +96,19 @@ static const char *getS(lua_State * L, void *ud, size_t * size) + 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. */ ++ /* ++ \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; +- (void) ud; /* for -Wunused */ ++ /*tex define |ud| for -Wunused */ ++ (void) ud; + if (nsize == 0) + free(ptr); + else +@@ -102,15 +118,14 @@ static void *my_luaalloc(void *ud, void *ptr, size_t osize, size_t nsize) + } + #endif + +-@ @c + static int my_luapanic(lua_State * L) + { +- (void) L; /* to avoid warnings */ ++ /*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; + } + +-@ @c + void luafunctioncall(int slot) + { + int i ; +@@ -120,12 +135,17 @@ void luafunctioncall(int slot) + lua_gettable(Luas, LUA_REGISTRYINDEX); + lua_rawgeti(Luas, -1,slot); + if (lua_isfunction(Luas,-1)) { +- int base = lua_gettop(Luas); /* function index */ ++ /*tex function index */ ++ int base = lua_gettop(Luas); + lua_pushinteger(Luas, slot); +- lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ +- lua_insert(Luas, base); /* put it under chunk */ ++ /* 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); +- lua_remove(Luas, base); /* remove traceback function */ ++ /*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)); +@@ -135,9 +155,8 @@ void luafunctioncall(int slot) + lua_active--; + } + +-@ @c + static const luaL_Reg lualibs[] = { +- /* standard lua libraries */ ++ /*tex standard \LUA\ libraries */ + { "_G", luaopen_base }, + { "package", luaopen_package }, + { "table", luaopen_table }, +@@ -149,32 +168,30 @@ static const luaL_Reg lualibs[] = { + { "lpeg", luaopen_lpeg }, + { "bit32", luaopen_bit32 }, + #ifdef LuajitTeX +- /* bit is only in luajit, */ +- /* coroutine is loaded in a special way */ +- { "bit", luaopen_bit }, ++ /*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 +- /* additional (public) libraries */ ++ /*tex additional (public) libraries */ + { "unicode", luaopen_unicode }, + { "zip", luaopen_zip }, + { "md5", luaopen_md5 }, ++ { "sha2", luaopen_sha2 }, + { "lfs", luaopen_lfs }, +- /* extra standard lua libraries */ ++ /*tex 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 */ ++ /*tex more libraries will be loaded later */ + { NULL, NULL } + }; + +-@ @c + static void do_openlibs(lua_State * L) + { + const luaL_Reg *lib = lualibs; +@@ -183,22 +200,23 @@ static void do_openlibs(lua_State * L) + } + } + +-@ @c + #ifdef LuajitTeX +- /* in luajit load_aux is not used.*/ ++ /*tex in \LUAJIT\ |load_aux| is not used.*/ + #else + static int load_aux (lua_State *L, int status) { +- if (status == 0) /* OK? */ ++ if (status == 0) ++ /*tex okay */ + return 1; + else { ++ /*tex return nil plus error message */ + lua_pushnil(L); +- lua_insert(L, -2); /* put before error message */ +- return 2; /* return nil plus error message */ ++ /*tex put before error message */ ++ lua_insert(L, -2); ++ return 2; + } + } + #endif + +-@ @c + static int luatex_loadfile (lua_State *L) { + int status = 0; + const char *fname = luaL_optstring(L, 1, NULL); +@@ -206,12 +224,14 @@ static int luatex_loadfile (lua_State *L) { + #ifdef LuajitTeX + /* 5.1 */ + #else +- int env = !lua_isnone(L, 3); /* 'env' parameter? */ ++ /*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; /* return nil plus error message */ ++ return 2; + } + status = luaL_loadfilex(L, fname, mode); + if (status == LUA_OK) { +@@ -219,9 +239,11 @@ static int luatex_loadfile (lua_State *L) { + #ifdef LuajitTeX + /* 5.1 */ + #else +- if (env) { /* 'env' parameter? */ ++ if (env) { ++ /*tex the |env| parameter */ + lua_pushvalue(L, 3); +- lua_setupvalue(L, -2, 1); /* set it as 1st upvalue of loaded chunk */ ++ /*tex set it as first upvalue of loaded chunk */ ++ lua_setupvalue(L, -2, 1); + } + #endif + } +@@ -232,15 +254,15 @@ static int luatex_loadfile (lua_State *L) { + #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) { ++ /*tex return |nil| plus error message */ + lua_pushnil(L); + lua_pushstring(L, "reading from stdin is disabled in batch mode"); +- return 2; /* return nil plus error message */ ++ return 2; + } else { + tprint_nl("lua> "); + } +@@ -252,13 +274,12 @@ static int luatex_dofile (lua_State *L) { + return lua_gettop(L) - n; + } + +-@ @c + void luainterpreter(void) + { + lua_State *L; + #ifdef LuajitTeX + if (jithash_hashname == NULL) { +- /* default lua51 */ ++ /*tex default lua51 */ + luajittex_choose_hash_function = 0; + jithash_hashname = (char *) xmalloc(strlen("lua51") + 1); + jithash_hashname = strcpy ( jithash_hashname, "lua51"); +@@ -267,7 +288,7 @@ void luainterpreter(void) + } else if (strcmp((const char*)jithash_hashname,"luajit20") == 0) { + luajittex_choose_hash_function = 1; + } else { +- /* default lua51 */ ++ /*tex default lua51 */ + luajittex_choose_hash_function = 0; + jithash_hashname = strcpy ( jithash_hashname, "lua51"); + } +@@ -280,8 +301,8 @@ void luainterpreter(void) + return; + } + lua_atpanic(L, &my_luapanic); +- +- do_openlibs(L); /* does all the 'simple' libraries */ ++ /*tex This initializes all the `simple' libraries: */ ++ do_openlibs(L); + #ifdef LuajitTeX + if (luajiton){ + luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE|LUAJIT_MODE_ON); +@@ -290,22 +311,17 @@ void luainterpreter(void) + 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. ++ /*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"); +@@ -315,28 +331,26 @@ void luainterpreter(void) + 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"); /* package.loaded.socket = nil */ +- ++ 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"); /* package.loaded.mime = nil */ +- lua_pop(L, 2); /* pop the tables */ +- +- luatex_socketlua_open(L); /* preload the pure lua modules */ ++ lua_setfield(L, -2, "mime"); ++ /*tex pop the tables */ ++ lua_pop(L, 2); ++ /*tex preload the pure \LUA\ modules */ ++ luatex_socketlua_open(L); + } +- +- /* zlib. slightly odd calling convention */ ++ /*tex |zlib|'s slightly odd calling convention */ + luaopen_zlib(L); + lua_setglobal(L, "zlib"); +- + luaopen_gzip(L); +- +- /* our own libraries register themselves */ +- ++ /*tex our own libraries register themselves */ + luaopen_fio(L); + luaopen_ff(L); + luaopen_tex(L); +@@ -345,29 +359,25 @@ void luainterpreter(void) + 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_epdf(L); ++ luaopen_pdfe(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; +@@ -380,7 +390,6 @@ int hide_lua_table(lua_State * L, const char *name) + return r; + } + +-@ @c + void unhide_lua_table(lua_State * L, const char *name, int r) + { + lua_rawgeti(L, LUA_REGISTRYINDEX, r); +@@ -388,7 +397,6 @@ void unhide_lua_table(lua_State * L, const char *name, int r) + luaL_unref(L, LUA_REGISTRYINDEX, r); + } + +-@ @c + int hide_lua_value(lua_State * L, const char *name, const char *item) + { + int r = 0; +@@ -402,7 +410,6 @@ int hide_lua_value(lua_State * L, const char *name, const char *item) + return r; + } + +-@ @c + void unhide_lua_value(lua_State * L, const char *name, const char *item, int r) + { + lua_getglobal(L, name); +@@ -413,7 +420,6 @@ void unhide_lua_value(lua_State * L, const char *name, const char *item, int r) + } + } + +-@ @c + int lua_traceback(lua_State * L) + { + lua_getglobal(L, "debug"); +@@ -426,21 +432,23 @@ int lua_traceback(lua_State * L) + 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 */ ++ /*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; + } + +-@ @c +-static void luacall(int p, int nameptr, boolean is_string) /* hh-ls: optimized lua_id resolving */ ++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(); + } +@@ -449,16 +457,22 @@ static void luacall(int p, int nameptr, boolean is_string) /* hh-ls: optimized l + const char *ss = NULL; + lua_rawgeti(Luas, LUA_REGISTRYINDEX, p); + if (lua_isfunction(Luas,-1)) { +- int base = lua_gettop(Luas); /* function index */ ++ /*tex function index */ ++ int base = lua_gettop(Luas); + lua_checkstack(Luas, 1); +- lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ +- lua_insert(Luas, base); /* put it under chunk */ ++ /*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); +- lua_remove(Luas, base); /* remove traceback function */ ++ /*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 ; + } +@@ -475,7 +489,8 @@ static void luacall(int p, int nameptr, boolean is_string) /* hh-ls: optimized l + ls.size = ll; + if (ls.size > 0) { + if (nameptr > 0) { +- int l = 0; /* not used */ ++ /*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); +@@ -492,12 +507,17 @@ static void luacall(int p, int nameptr, boolean is_string) /* hh-ls: optimized l + if (i != 0) { + Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1)); + } else { +- int base = lua_gettop(Luas); /* function index */ ++ /*tex function index */ ++ int base = lua_gettop(Luas); + lua_checkstack(Luas, 1); +- lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ +- lua_insert(Luas, base); /* put it under chunk */ ++ /*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); +- lua_remove(Luas, base); /* remove traceback function */ ++ /*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)); +@@ -505,31 +525,99 @@ static void luacall(int p, int nameptr, boolean is_string) /* hh-ls: optimized l + } + 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--; + } + +-@ @c + void late_lua(PDF pdf, halfword p) + { ++ halfword t; + (void) pdf; +- if (late_lua_type(p)==normal) { +- expand_macros_in_tokenlist(p); /* sets |def_ref| */ ++ 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 { ++ } 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. */ + } + } + +-@ @c +-void luatokencall(int p, int nameptr) /* hh-ls: optimized lua_id resolving */ ++void luatokencall(int p, int nameptr) + { + LoadS ls; +- int i, l; ++ int i; ++ int l = 0; + char *s = NULL; + char *lua_id; +- assert(Luas); +- l = 0; ++ int stacktop = lua_gettop(Luas); + lua_active++; + s = tokenlist_to_cstring(p, 1, &l); + ls.s = s; +@@ -538,7 +626,7 @@ void luatokencall(int p, int nameptr) /* hh-ls: optimized lua_id resolving */ + if (nameptr > 0) { + lua_id = tokenlist_to_cstring(nameptr, 1, &l); + i = Luas_load(Luas, getS, &ls, lua_id); +- xfree(lua_id); ++ xfree(lua_id); + } else if (nameptr < 0) { + lua_id = get_lua_name((nameptr + 65536)); + if (lua_id != NULL) { +@@ -553,40 +641,53 @@ void luatokencall(int p, int nameptr) /* hh-ls: optimized lua_id resolving */ + if (i != 0) { + Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1)); + } else { +- int base = lua_gettop(Luas); /* function index */ ++ /*tex function index */ ++ int base = lua_gettop(Luas); + lua_checkstack(Luas, 1); +- lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ +- lua_insert(Luas, base); /* put it under chunk */ ++ /*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); +- lua_remove(Luas, base); /* remove traceback function */ ++ /*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--; + } + +-@ @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 ? */ ++ /*tex ++ Free the 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 */ ++ /*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 */ ++ /* ++ 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 */ ++ /*tex ++ This is never reached. ++ */ + lua_close(L); + return (lua_State *) NULL; + } else { +@@ -595,35 +696,41 @@ lua_State *luatex_error(lua_State * L, int is_fatal) + } + } + +-@ @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 */ ++ /*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); /* - */ +- lua_pushstring(L, s); /* s */ +- lua_newtable(L); /* t s */ ++ 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); /* k t s */ +- lua_pushinteger(L, p->idx); /* v k t s */ +- lua_settable(L, -3); /* t s */ ++ 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); /* - */ ++ lua_settable(L, LUA_REGISTRYINDEX); ++ /* tex state: - */ + } + +- +-@ @c +-/* +- luajit compatibility layer for luatex lua5.2 ++/*tex ++ Here comes a \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 ) +@@ -631,24 +738,22 @@ LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { + 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)) {*/ ++ /* if (isvalid(o1) && isvalid(o2)) {*/ + switch (op) { + case LUA_OPEQ: i = lua_equal(L, o1, o2); break; + case LUA_OPLT: i = lua_lessthan(L, o1, o2); break; + case LUA_OPLE: i = (lua_lessthan(L, o1, o2) || lua_equal(L, o1, o2)) ; break; + default: luaL_error(L, "invalid option"); + } +- /*}*/ ++ /* } */ + lua_unlock(L); + return i; + } + +-@ @c + #endif +diff --git a/texk/web2c/luatexdir/lua/luatoken.c b/texk/web2c/luatexdir/lua/luatoken.c +new file mode 100644 +index 000000000..fb197b68f +--- /dev/null ++++ b/texk/web2c/luatexdir/lua/luatoken.c +@@ -0,0 +1,585 @@ ++/* ++ ++luatoken.w ++ ++Copyright 2006-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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; ++} ++*/ +diff --git a/texk/web2c/luatexdir/lua/luatoken.w b/texk/web2c/luatexdir/lua/luatoken.w +deleted file mode 100644 +index e9740ffc6..000000000 +--- a/texk/web2c/luatexdir/lua/luatoken.w ++++ /dev/null +@@ -1,424 +0,0 @@ +-% luatoken.w +-% +-% Copyright 2006-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +-#include "ptexlib.h" +-#include "lua/luatex-api.h" +- +-@ @c +-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; +-} +- +-*/ +diff --git a/texk/web2c/luatexdir/lua/mplibstuff.c b/texk/web2c/luatexdir/lua/mplibstuff.c +new file mode 100644 +index 000000000..f1713ae0e +--- /dev/null ++++ b/texk/web2c/luatexdir/lua/mplibstuff.c +@@ -0,0 +1,115 @@ ++/* ++ ++mplibstuff.w ++ ++Copyright 2017 LuaTeX team ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++/*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 ++ ++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"; ++ ++ ++ +diff --git a/texk/web2c/luatexdir/lua/mplibstuff.w b/texk/web2c/luatexdir/lua/mplibstuff.w +deleted file mode 100644 +index 3104a2870..000000000 +--- a/texk/web2c/luatexdir/lua/mplibstuff.w ++++ /dev/null +@@ -1,93 +0,0 @@ +-% mplibstuff.w +-% +-% Copyright 2017 LuaTeX team +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ PNG and SVG backends are not available in \LuaTeX, because it's complex +-to manage the math formulas at run time. In this respect |PostScript| and the highlevel |objects| +-are better, and they are the standard way. Another problem is how to emit the warning: +-the |normal\_warning| function is not available when \LuaTeX\ is called as LUA only. +- +- +- +-@c +-#include +- +-extern void normal_warning(const char *t, const char *p); +-extern int lua_only; +-#define mplibstuff_message(BACKEND) do { \ +- if (lua_only) { \ +- fprintf(stdout,"mplib: " #BACKEND " backend not available.\n"); \ +- } else { \ +- normal_warning("mplib", #BACKEND " backend not available."); \ +- } \ +-} while (0) +- +- +-@ @c +-void mp_png_backend_initialize (void *mp); +-void mp_png_backend_free (void *mp); +-int mp_png_gr_ship_out (void *hh, void *options, int standalone); +-int mp_png_ship_out (void *hh, const char *options); +- +-@ @c +-void mp_svg_backend_initialize (void *mp); +-void mp_svg_backend_free (void *mp); +-int mp_svg_ship_out (void *hh, int prologues); +-int mp_svg_gr_ship_out (void *hh, int qprologues, int standalone); +- +-@ @c +-void mp_png_backend_initialize (void *mp) {return; } /*{mplibstuff_message(1png);return;}*/ +-void mp_png_backend_free (void *mp) {return; } /*{mplibstuff_message(png);return;}*/ +-int mp_png_gr_ship_out (void *hh, void *options, int standalone) {mplibstuff_message(png);return 1;} +-int mp_png_ship_out (void *hh, const char *options) {mplibstuff_message(png);return 1;} +- +-@ @c +-void mp_svg_backend_initialize (void *mp) {return;} /*{mplibstuff_message(svg);return;}*/ +-void mp_svg_backend_free (void *mp) {return;} /*{mplibstuff_message(svg);return;}*/ +-int mp_svg_ship_out (void *hh, int prologues) {mplibstuff_message(svg);return 1;} +-int mp_svg_gr_ship_out (void *hh, int qprologues, int standalone) {mplibstuff_message(svg);return 1;} +- +-@ @c +-const char* +-cairo_version_string (void); +-const char* +-pixman_version_string (void); +-#define CAIRO_VERSION_STRING "CAIRO NOT AVAILABLE" +-const char *COMPILED_CAIRO_VERSION_STRING = CAIRO_VERSION_STRING; +-#define PIXMAN_VERSION_STRING "PIXMAN NOT AVAILABLE" +-const char *COMPILED_PIXMAN_VERSION_STRING = PIXMAN_VERSION_STRING; +- +-const char* +-cairo_version_string (void) +-{ +- return CAIRO_VERSION_STRING; +-} +- +-const char* +-pixman_version_string (void) +-{ +- return PIXMAN_VERSION_STRING; +-} +- +- +- +- +- +-@ @c +-char png_libpng_ver[] = "PNG NOT AVAILABLE"; +- +diff --git a/texk/web2c/luatexdir/lua/texluac.c b/texk/web2c/luatexdir/lua/texluac.c +new file mode 100644 +index 000000000..61016cfdb +--- /dev/null ++++ b/texk/web2c/luatexdir/lua/texluac.c +@@ -0,0 +1,532 @@ ++/* ++ ++texluac.w ++ ++Copyright (C) 1994-2007 Lua.org, PUC-Rio. All rights reserved. ++Copyright 2006-2013 Taco Hoekwater ++ ++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 ++#include ++#include ++#include ++#include ++ ++#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; itop+(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; ip[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; i1); ++ 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; ik[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; pc0) ++ 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; isizelocvars; ++ printf("locals (%d) for %p:\n",n,VOID(f)); ++ for (i=0; ilocvars[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; iupvalues[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; ip[i],full); ++} +diff --git a/texk/web2c/luatexdir/lua/texluac.w b/texk/web2c/luatexdir/lua/texluac.w +deleted file mode 100644 +index 424c74c77..000000000 +--- a/texk/web2c/luatexdir/lua/texluac.w ++++ /dev/null +@@ -1,495 +0,0 @@ +-% texluac.w +-% +-% Copyright (C) 1994-2007 Lua.org, PUC-Rio. All rights reserved. +-% Copyright 2006-2013 Taco Hoekwater +-% +-% 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 +-#include +-#include +-#include +-#include +- +-#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; itop+(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; ip[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; i1); +- 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; ik[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; pc0) 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; isizelocvars; +- printf("locals (%d) for %p:\n",n,VOID(f)); +- for (i=0; ilocvars[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; iupvalues[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; ip[i],full); +-} +diff --git a/texk/web2c/luatexdir/lua/texluajitc.c b/texk/web2c/luatexdir/lua/texluajitc.c +new file mode 100644 +index 000000000..5cac5d65a +--- /dev/null ++++ b/texk/web2c/luatexdir/lua/texluajitc.c +@@ -0,0 +1,650 @@ ++/* ++ ++ 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 ++#include ++#include ++ ++#define luajit_c ++ ++#include "lua.h" ++#include "lauxlib.h" ++#include "lualib.h" ++#include "luajit.h" ++ ++#include "lj_arch.h" ++ ++#if LJ_TARGET_POSIX ++#include ++#define lua_stdin_is_tty() isatty(0) ++#elif LJ_TARGET_WINDOWS ++#include ++#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 ++#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("")) - 1); ++ if (strstr(msg, LUA_QL("")) == 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; ++} ++ +diff --git a/texk/web2c/luatexdir/lua/texluajitc.w b/texk/web2c/luatexdir/lua/texluajitc.w +deleted file mode 100644 +index f54f9c8d4..000000000 +--- a/texk/web2c/luatexdir/lua/texluajitc.w ++++ /dev/null +@@ -1,575 +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 +-*/ +- +-@ @c +-#include +-#include +-#include +- +-#define luajit_c +- +-#include "lua.h" +-#include "lauxlib.h" +-#include "lualib.h" +-#include "luajit.h" +- +-#include "lj_arch.h" +- +-#if LJ_TARGET_POSIX +-#include +-#define lua_stdin_is_tty() isatty(0) +-#elif LJ_TARGET_WINDOWS +-#include +-#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 +-#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("")) - 1); +- if (strstr(msg, LUA_QL("")) == 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; +-} +- +diff --git a/texk/web2c/luatexdir/pdf/pdfaction.w b/texk/web2c/luatexdir/pdf/pdfaction.c +similarity index 85% +rename from texk/web2c/luatexdir/pdf/pdfaction.w +rename to texk/web2c/luatexdir/pdf/pdfaction.c +index bb0d6e06d..2909ac6ad 100644 +--- a/texk/web2c/luatexdir/pdf/pdfaction.w ++++ b/texk/web2c/luatexdir/pdf/pdfaction.c +@@ -1,29 +1,29 @@ +-% pdfaction.w +-% +-% Copyright 2009-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2009-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ read an action specification ++/*tex Read an action specification. */ + +-@c halfword scan_action(PDF pdf) ++halfword scan_action(PDF pdf) + { + int p = new_node(whatsit_node, pdf_action_node); + (void) pdf; +@@ -73,13 +73,13 @@ + } + if (scan_keyword("newwindow")) { + set_pdf_action_new_window(p, pdf_window_new); +- /* Scan an optional space */ ++ /*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); +- /* Scan an optional space */ ++ /*tex Scan an optional space. */ + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); +@@ -94,9 +94,9 @@ + return p; + } + +-@ write an action specification ++/*tex Write an action specification. */ + +-@c void write_action(PDF pdf, halfword p) ++void write_action(PDF pdf, halfword p) + { + char *s; + int d = 0; +diff --git a/texk/web2c/luatexdir/pdf/pdfannot.w b/texk/web2c/luatexdir/pdf/pdfannot.c +similarity index 66% +rename from texk/web2c/luatexdir/pdf/pdfannot.w +rename to texk/web2c/luatexdir/pdf/pdfannot.c +index 43ee3692b..e501947c7 100644 +--- a/texk/web2c/luatexdir/pdf/pdfannot.w ++++ b/texk/web2c/luatexdir/pdf/pdfannot.c +@@ -1,27 +1,26 @@ +-% pdfannot.w +-% +-% Copyright 2009-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2009-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ @c + void do_annot(PDF pdf, halfword p, halfword parent_box, scaledpos cur) + { + scaled_whd alt_rule; +@@ -43,13 +42,13 @@ void do_annot(PDF pdf, halfword p, halfword parent_box, scaledpos cur) + addto_page_resources(pdf, obj_type_annot, pdf_annot_objnum(p)); + } + +-@ create a new whatsit node for annotation ++/*tex Create a new whatsit node for annotation. */ + +-@c void new_annot_whatsit(small_number w) ++void new_annot_whatsit(small_number w) + { + scaled_whd alt_rule; + new_whatsit(w); +- alt_rule = scan_alt_rule(); /* scans || to |alt_rule| */ ++ 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); +@@ -63,14 +62,14 @@ void do_annot(PDF pdf, halfword p, halfword parent_box, scaledpos cur) + } + } + +-@ scanning at the \TeX\ end ++/*tex Scanning at the \TEX\ end: */ + +-@c void scan_annot(PDF pdf) ++void scan_annot(PDF pdf) + { + int k; + if (scan_keyword("reserveobjnum")) { + k = pdf_create_obj(pdf, obj_type_annot, 0); +- /* Scan an optional space */ ++ /*tex Scan an optional space. */ + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); +diff --git a/texk/web2c/luatexdir/pdf/pdfcolorstack.w b/texk/web2c/luatexdir/pdf/pdfcolorstack.c +similarity index 79% +rename from texk/web2c/luatexdir/pdf/pdfcolorstack.w +rename to texk/web2c/luatexdir/pdf/pdfcolorstack.c +index ac117d6ba..fbb88692f 100644 +--- a/texk/web2c/luatexdir/pdf/pdfcolorstack.w ++++ b/texk/web2c/luatexdir/pdf/pdfcolorstack.c +@@ -1,43 +1,53 @@ +-% pdfcolorstack.w +-% +-% Copyright 2009-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + +-#include "ptexlib.h" ++Copyright 2009-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. + +-@* Color Stack and Matrix Transformation Support. ++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. + +-@ In the following array and especially stack data structures are used. ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . + +-They have the following properties: ++*/ ++ ++#include "ptexlib.h" + +-\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. ++/*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 + +-@ Color Stack +-@c ++*/ + + #define MAX_COLORSTACKS 32768 + +@@ -66,10 +76,13 @@ 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. ++/*tex ++ ++ 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) +@@ -90,38 +103,40 @@ static void colstacks_first_init(void) + 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). ++/*tex ++ ++ 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 */ ++ /*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); + } +- /* claim new color stack */ ++ /*tex Claim new color stack. */ + colstack_num = colstacks_used++; + colstack = &colstacks[colstack_num]; +- /* configure the new color stack */ ++ /*tex Configure the new color stack. */ + colstack->page_stack = NULL; + colstack->form_stack = NULL; + colstack->page_size = 0; +@@ -141,12 +156,11 @@ int newcolorstack(const char *str, int literal_mode, boolean page_start) + return colstack_num; + } + +-@ @c + #define get_colstack(n) (&colstacks[n]) + +-@ Puts a string on top of the string pool and updates |pool_ptr|. ++/*tex Put a string on top of the string pool and updates |pool_ptr|. */ + +-@c static void put_cstring_on_str_pool(char *str) ++static void put_cstring_on_str_pool(char *str) + { + int save_selector = selector; + selector = new_string; +@@ -157,7 +171,6 @@ int newcolorstack(const char *str, int literal_mode, boolean page_start) + selector = save_selector; + } + +-@ @c + static int colorstackset(int colstack_no, str_number s) + { + colstack_type *colstack = get_colstack(colstack_no); +@@ -172,7 +185,6 @@ static int colorstackset(int colstack_no, str_number s) + return colstack->literal_mode; + } + +-@ @c + int colorstackcurrent(int colstack_no) + { + colstack_type *colstack = get_colstack(colstack_no); +@@ -185,7 +197,6 @@ int colorstackcurrent(int colstack_no) + return colstack->literal_mode; + } + +-@ @c + static int colorstackpush(int colstack_no, str_number s) + { + colstack_type *colstack = get_colstack(colstack_no); +@@ -220,7 +231,6 @@ static int colorstackpush(int colstack_no, str_number s) + return colstack->literal_mode; + } + +-@ @c + int colorstackpop(int colstack_no) + { + colstack_type *colstack = get_colstack(colstack_no); +@@ -244,16 +254,13 @@ int colorstackpop(int colstack_no) + 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++) { +@@ -269,7 +276,6 @@ void colorstackpagestart(void) + } + } + +-@ @c + int colorstackskippagestart(int colstack_no) + { + colstack_type *colstack = get_colstack(colstack_no); +@@ -285,8 +291,6 @@ int colorstackskippagestart(int colstack_no) + return 0; + } + +- +-@ @c + void pdf_out_colorstack(PDF pdf, halfword p) + { + int old_setting; +@@ -295,11 +299,7 @@ void pdf_out_colorstack(PDF pdf, halfword 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(""); ++ formatted_warning("pdf backend","color stack %u is not initialized",(unsigned int) stack_no); + return; + } + switch (cmd) { +@@ -335,7 +335,6 @@ void pdf_out_colorstack(PDF pdf, halfword p) + } + } + +-@ @c + void pdf_out_colorstack_startpage(PDF pdf) + { + int start_status; +diff --git a/texk/web2c/luatexdir/pdf/pdfdest.w b/texk/web2c/luatexdir/pdf/pdfdest.c +similarity index 83% +rename from texk/web2c/luatexdir/pdf/pdfdest.w +rename to texk/web2c/luatexdir/pdf/pdfdest.c +index 74e8e4b1f..346cd26e0 100644 +--- a/texk/web2c/luatexdir/pdf/pdfdest.w ++++ b/texk/web2c/luatexdir/pdf/pdfdest.c +@@ -1,37 +1,40 @@ +-% pdfdest.w +-% +-% Copyright 2009-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2009-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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. ++/*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. ++ ++*/ + +-@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 */ ++ pdf->dest_names = xmallocarray(dest_name_entry, inf_dest_names_size); + } + +-@ @c + void append_dest_name(PDF pdf, char *s, int n) + { + int a; +@@ -50,10 +53,13 @@ void append_dest_name(PDF pdf, char *s, int 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. ++/*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. ++ ++*/ + +-@c + static void warn_dest_dup(int id, small_number byname) + { + if (byname > 0) { +@@ -62,10 +68,8 @@ static void warn_dest_dup(int id, small_number byname) + } 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; +@@ -85,7 +89,10 @@ void do_dest(PDF pdf, halfword p, halfword parent_box, scaledpos cur) + 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 */ ++ /*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()) +@@ -117,7 +124,6 @@ void do_dest(PDF pdf, halfword p, halfword parent_box, scaledpos cur) + } + } + +-@ @c + void write_out_pdf_mark_destinations(PDF pdf) + { + pdf_object_list *k; +@@ -143,12 +149,11 @@ void write_out_pdf_mark_destinations(PDF pdf) + if (pdf_dest_xyz_zoom(i) == null) { + pdf_add_null(pdf); + } else { +- if (pdf->cave == 1) +- pdf_out(pdf, ' '); ++ 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->cave = 1; ++ pdf_set_space(pdf); + } + break; + case pdf_dest_fit: +@@ -191,7 +196,6 @@ void write_out_pdf_mark_destinations(PDF pdf) + } + } + +-@ @c + void scan_pdfdest(PDF pdf) + { + halfword q; +@@ -242,13 +246,12 @@ void scan_pdfdest(PDF pdf) + } else { + normal_error("pdf backend", "destination type missing"); + } +- /* Scan an optional space */ ++ /*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(); /* scans || to |alt_rule| */ ++ 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); +@@ -268,8 +271,8 @@ void scan_pdfdest(PDF pdf) + } + } + +-@ sorts |dest_names| by names +-@c ++/*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; +@@ -277,24 +280,30 @@ static int dest_cmp(const void *a, const void *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. ++/*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. ++ ++*/ + +-@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) */ ++ /*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; +@@ -306,9 +315,12 @@ int output_name_tree(PDF pdf) + 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 */ ++ /*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; +@@ -317,7 +329,7 @@ int output_name_tree(PDF pdf) + names_tail = l; + } + set_obj_link(pdf, names_tail, 0); +- /* Output the current node in this level */ ++ /*tex Output the current node in this level. */ + pdf_begin_obj(pdf, l, OBJSTM_ALWAYS); + pdf_begin_dict(pdf); + j = 0; +@@ -332,13 +344,13 @@ int output_name_tree(PDF pdf) + 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 */ ++ /*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"); +@@ -362,7 +374,6 @@ int output_name_tree(PDF pdf) + pdf_end_dict(pdf); + pdf_end_obj(pdf); + } while (b != 0); +- + if (k == l) { + dests = l; + goto DONE; +diff --git a/texk/web2c/luatexdir/pdf/pdffont.w b/texk/web2c/luatexdir/pdf/pdffont.c +similarity index 51% +rename from texk/web2c/luatexdir/pdf/pdffont.w +rename to texk/web2c/luatexdir/pdf/pdffont.c +index 278efa9d6..1e1f0f8d9 100644 +--- a/texk/web2c/luatexdir/pdf/pdffont.w ++++ b/texk/web2c/luatexdir/pdf/pdffont.c +@@ -1,48 +1,50 @@ +-% pdffont.w +-% +-% Copyright 2009-2014 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-\def\pdfTeX{pdf\TeX} +- +-@ @c ++/* ++ ++Copyright 2009-2014 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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. ++/*tex + +-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. ++ 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. + +-@ The following code typesets a character to PDF output ++ 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 */ ++ /*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); +- /* char_warning(f,c); */ ++ /*tex Not here |char_warning(f,c);| */ + return ci; + } + ci.wd = ext_xn_over_d(ci.wd, 1000000 + ex_glyph(p), 1000000); +@@ -66,14 +68,18 @@ scaled_whd output_one_char(PDF pdf, halfword p) + 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 ++/*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); +@@ -84,19 +90,24 @@ static void pdf_use_font(internal_font_number f, int fontnum) + } + } + +-@ 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 ++/*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. ++ ++*/ + +-@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 ++/*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) +@@ -111,8 +122,21 @@ static boolean font_shareable(internal_font_number f, internal_font_number k) + return 0; + } + +-@ create a font object +-@c ++/*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; +@@ -121,15 +145,6 @@ void pdf_init_font(PDF pdf, internal_font_number f) + 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; +@@ -150,38 +165,42 @@ void pdf_init_font(PDF pdf, internal_font_number f) + } + i = obj_link(pdf, i); + } +- /* create a new font object for |f| */ ++ /*tex 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. ++/*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. ++ ++*/ + +-@c + internal_font_number pdf_set_font(PDF pdf, internal_font_number f) + { +- int ff; /* for use with |set_ff| */ ++ /*tex For use with |set_ff|: */ ++ int 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)| */ ++ /*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; + } + +-@ @c + void pdf_include_chars(PDF pdf) + { + str_number s; +- unsigned char *k, *j; /* running index */ ++ 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(cur_val); ++ /* pdf_check_vf(c); */ + if (!font_used(f)) + pdf_init_font(pdf, f); + scan_toks(false, true); +diff --git a/texk/web2c/luatexdir/pdf/pdfgen.w b/texk/web2c/luatexdir/pdf/pdfgen.c +similarity index 61% +rename from texk/web2c/luatexdir/pdf/pdfgen.w +rename to texk/web2c/luatexdir/pdf/pdfgen.c +index 26b7112ed..0409dbc33 100644 +--- a/texk/web2c/luatexdir/pdf/pdfgen.w ++++ b/texk/web2c/luatexdir/pdf/pdfgen.c +@@ -1,26 +1,26 @@ +-% pdfgen.w +-% +-% Copyright 2009-2013 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2009-2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ + #include "ptexlib.h" + +-@ @c + #include + #include + #include "lua/luatex-api.h" +@@ -32,43 +32,60 @@ + + PDF static_pdf = NULL; + +-@ commandline interface ++/*tex The 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 */ ++/*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; +-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 */ + ++/*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. + +-@ 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) ++ if (size > 0) { + b->p = b->data = xtalloc(b->size, unsigned char); +- else +- b->p = b->data = NULL; /* for other alloc */ ++ } else { ++ /*tex For other alloc: */ ++ b->p = b->data = NULL; ++ } + return b; + } + +-@ Check that |n| bytes more fit into buffer; increase it if required. ++/*tex 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; +@@ -88,25 +105,22 @@ static void strbuf_room(strbuf_s * b, size_t n) + } + } + +-@ Seek to position |offset| within buffer. Position must be valid. ++/*tex 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. ++/*tex 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. ++/*tex 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) +@@ -114,81 +128,71 @@ void strbuf_putchar(strbuf_s * b, unsigned char c) + *b->p++ = c; + } + +-@ Dump filled buffer part to PDF. ++/*tex Dump filled buffer part to the \PDF\ file. */ + +-@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. ++/*tex We 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 ++/*tex This |init_pdf_struct| is called early and only once. */ + +-@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 */ ++ /*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; +- +- /* init PDF and object stream writing */ ++ /*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]; +- /* +- 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. ++ /*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; +- +- /* allocated size of |obj_tab| array */ ++ /*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; +@@ -198,25 +202,22 @@ PDF init_pdf_struct(PDF pdf) + 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; + } + +-@ We use |pdf_get_mem| to allocate memory in |mem|. ++/*tex We use |pdf_get_mem| to allocate memory in |mem|. */ + +-@c + int pdf_get_mem(PDF pdf, int s) + { + int a; +@@ -239,39 +240,19 @@ int pdf_get_mem(PDF pdf, int 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; +-} ++/*tex + +-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. + +-@ 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) { ++ /*tex It is unset. */ + if (pdf_major_version == 0) { + normal_warning("pdf backend","unset major version, using 1 instead"); + pdf->major_version = 1; +@@ -284,7 +265,8 @@ void fix_pdf_version(PDF pdf) + } 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) { ++ /*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; +@@ -301,19 +283,18 @@ 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 */ ++ /*tex Fix these as they might have been changed in between. */ ++ pdf->compress_level = 0; + 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; +@@ -363,7 +344,6 @@ static void write_zip(PDF pdf) + pdf->stream_length = (off_t) s->total_out; + } + +-@ @c + void zip_free(PDF pdf) + { + if (pdf->zipbuf != NULL) { +@@ -373,7 +353,6 @@ void zip_free(PDF pdf) + xfree(pdf->c_stream); + } + +-@ @c + static void write_nozip(PDF pdf) + { + strbuf_s *buf = pdf->buf; +@@ -385,12 +364,15 @@ static void write_nozip(PDF pdf) + 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. ++/*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. ++ ++*/ + +-@c + void pdf_flush(PDF pdf) + { + os_struct *os = pdf->os; +@@ -422,20 +404,25 @@ void pdf_flush(PDF pdf) + } + } + +-@ @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 */ ++ 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]; + } + +-@ create new \.{/ObjStm} object if required, and set up cross reference info ++/*tex ++ ++ We 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; +@@ -447,15 +434,18 @@ static void pdf_prepare_obj(PDF pdf, int k, int pdf_os_threshold) + 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 */ ++ /*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; +- obuf->p = obuf->data; /* start fresh object stream */ +- os->ostm_ctr++; /* only for statistics */ ++ /*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; +@@ -467,12 +457,13 @@ static void pdf_prepare_obj(PDF pdf, int k, int pdf_os_threshold) + } + } + +-@* Low-level buffer checkers. ++/*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. + +-@ 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; +@@ -490,7 +481,6 @@ inline void pdf_room(PDF pdf, int n) + } + } + +-@ @c + void pdf_out_block(PDF pdf, const char *s, size_t n) + { + size_t l; +@@ -507,7 +497,6 @@ void pdf_out_block(PDF pdf, const char *s, size_t n) + } while (n > 0); + } + +-@ @c + __attribute__ ((format(printf, 2, 3))) + void pdf_printf(PDF pdf, const char *fmt, ...) + { +@@ -521,9 +510,6 @@ void pdf_printf(PDF pdf, const char *fmt, ...) + va_end(args); + } + +-@ print out a string to PDF buffer +- +-@c + void pdf_print(PDF pdf, str_number s) + { + const char *ss; +@@ -537,9 +523,6 @@ void pdf_print(PDF pdf, str_number s) + } + } + +-@ print out a integer to PDF buffer +- +-@c + void pdf_print_int(PDF pdf, longinteger n) + { + char s[24]; +@@ -549,33 +532,6 @@ void pdf_print_int(PDF pdf, longinteger n) + 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; +@@ -584,7 +540,7 @@ void print_pdffloat(PDF pdf, pdffloat f) + } else { + int e = f.e; + if (e == 0) { +- /* division by ten_pow[0] == 1 */ ++ /*tex division by |ten_pow[0] == 1| */ + if (m == 1) { + pdf_out(pdf, '1'); + } else { +@@ -622,18 +578,19 @@ void print_pdffloat(PDF pdf, pdffloat f) + } + } + +-@ 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 */ ++ /*tex This initializes at the last string index. */ ++ int l = (int) strlen(s) - 1; + if (l < 0) { + pdf_puts(pdf, "()"); + return; + } +- /* the next is not really safe, the string could be "(a)xx(b)" */ ++ /*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; +@@ -653,12 +610,11 @@ void pdf_print_str(PDF pdf, const char *s) + pdf_out(pdf, ')'); + return; + } +- pdf_puts(pdf, orig); /* it was a hex string after all */ ++ pdf_puts(pdf, orig); + } + +-@ begin a stream (needs to have a stream dictionary also) ++/*tex A stream needs to have a stream dictionary also. */ + +-@c + void pdf_begin_stream(PDF pdf) + { + pdf_puts(pdf, "\nstream\n"); +@@ -672,9 +628,6 @@ void pdf_begin_stream(PDF pdf) + pdf->last_byte = 0; + } + +-@ end a stream +- +-@c + void pdf_end_stream(PDF pdf) + { + os_struct *os = pdf->os; +@@ -682,7 +635,8 @@ void pdf_end_stream(PDF pdf) + case PDFOUT_BUF: + if (pdf->zip_write_state == ZIP_WRITING) + pdf->zip_write_state = ZIP_FINISH; +- pdf_flush(pdf); /* sets pdf->last_byte */ ++ /*tex This sets| pdf->last_byte|. */ ++ pdf_flush(pdf); + break; + case OBJSTM_BUF: + normal_error("pdf backend", "bad buffer in end stream, case 1"); +@@ -692,43 +646,71 @@ void pdf_end_stream(PDF pdf) + } + pdf->stream_deflate = false; + pdf->stream_writing = false; +- pdf_out(pdf, '\n'); /* doesn't really belong to the stream */ ++ /*tex This doesn't really belong to the stream: */ ++ pdf_out(pdf, '\n'); + pdf_puts(pdf, "endstream"); +- /* write stream /Length */ ++ /*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); ++ 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. ++/*tex ++ ++ 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 ++/*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$ */ + }; + +-@ 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. ++/*tex ++ ++ The function |divide_scaled| divides |s| by |m| using |dd| decimal digits of ++ precision. ++ ++*/ + +-@c + scaled round_xn_over_d(scaled x, int n, unsigned int d) + { + boolean positive = true; +@@ -757,28 +739,22 @@ scaled round_xn_over_d(scaled x, int n, unsigned int d) + 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, ' '); ++ pdf_check_space(pdf); + print_pdffloat(pdf, a); +- pdf->cave = 1; ++ pdf_set_space(pdf); + } + +-@* 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; +@@ -791,7 +767,6 @@ static int comp_page_resources(const void *pa, const void *pb, void *p) + return 0; + } + +-@ @c + void addto_page_resources(PDF pdf, pdf_obj_type t, int k) + { + pdf_resource_struct *re; +@@ -819,8 +794,10 @@ void addto_page_resources(PDF pdf, pdf_obj_type t, int k) + 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 */ ++ 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) { +@@ -834,7 +811,6 @@ void addto_page_resources(PDF pdf, pdf_obj_type t, int k) + } + } + +-@ @c + pdf_object_list *get_page_resources_list(PDF pdf, pdf_obj_type t) + { + pdf_resource_struct *re = pdf->page_resources; +@@ -848,7 +824,6 @@ pdf_object_list *get_page_resources_list(PDF pdf, pdf_obj_type t) + return pr->list; + } + +-@ @c + static void reset_page_resources(PDF pdf) + { + pdf_resource_struct *re = pdf->page_resources; +@@ -864,19 +839,18 @@ static void reset_page_resources(PDF pdf) + l2 = l1->link; + free(l1); + } +- p->list = NULL; /* but the AVL tree remains */ ++ /*tex We reset but the AVL tree remains! */ ++ p->list = NULL; + } + } + } + +-@ @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; +@@ -886,11 +860,6 @@ static void destroy_page_resources_tree(PDF pdf) + 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]; +@@ -901,20 +870,26 @@ static void pdf_print_fw_int(PDF pdf, longinteger n) + 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."); ++ 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); + } + +-@ print out an integer |n| as a fixed number |w| of bytes; used for outputting \.{/XRef} cross-reference stream +-@c ++/*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) + { +- int k; +- unsigned char bytes[8]; /* digits in a number being output */ +- k = (int) 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); +@@ -923,32 +898,24 @@ static void pdf_out_bytes(PDF pdf, longinteger n, size_t w) + 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_check_space(pdf); + pdf_puts(pdf, s); +- pdf->cave = 1; ++ pdf_set_space(pdf); + } + xfree(s); + } + +-@ prints a rect spec +- +-@c + void pdf_add_rect_spec(PDF pdf, halfword r) + { + pdf_add_bp(pdf, pdf_ann_left(r)); +@@ -957,9 +924,6 @@ void pdf_add_rect_spec(PDF pdf, halfword 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"); +@@ -968,7 +932,6 @@ void pdf_rectangle(PDF pdf, halfword r) + pdf_end_array(pdf); + } + +-@ @c + static void init_pdf_outputparameters(PDF pdf) + { + int pk_mode; +@@ -980,6 +943,7 @@ static void init_pdf_outputparameters(PDF pdf) + 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); +@@ -992,19 +956,19 @@ static void init_pdf_outputparameters(PDF pdf) + } + 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} */ ++ 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; /* lookup once */ ++ pk_mode = pdf_pk_mode; + 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); ++ kpse_init_prog("LUATEX", (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); ++ 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); +@@ -1014,75 +978,54 @@ static void init_pdf_outputparameters(PDF pdf) + pdf->resname_prefix = get_resname_prefix(pdf); + } + +-@ Checks that we have a name for the generated PDF file and that it's open. ++/*tex + +-@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; +-} ++ This checks that we have a name for the generated PDF file and that it's ++ open. + +-@ @c +-static void ensure_pdf_header_written(PDF pdf) ++*/ ++ ++void pdf_write_header(PDF pdf) + { +- /* Initialize variables for \.{PDF} output */ ++ /*tex Initialize variables for \PDF\ output. */ + fix_pdf_version(pdf); + init_pdf_outputparameters(pdf); + fix_pdf_draftmode(pdf); +- /* Write \.{PDF} header */ ++ /*tex Write \PDF\ header */ + pdf_printf(pdf, "%%PDF-%d.%d\n", pdf->major_version, pdf->minor_version); +- /* The next blob will be removed 1.0. */ ++ /* Some binary crap. */ + pdf_out(pdf, '%'); +- pdf_out(pdf, 'P' + 128); ++ 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'); + } + +-@ @c ++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) ++ 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"); +- } ++ backend_out_control[backend_control_open_file](pdf); + 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"); +- } ++ backend_out_control[backend_control_write_header](pdf); + break; + case ST_HEADER_WRITTEN: + break; +@@ -1095,29 +1038,37 @@ void ensure_output_state(PDF pdf, output_state s) + } + } + +-@ Write out an accumulated object stream. ++/*tex + +-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|. ++ 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|. ++ ++*/ + +-@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! */ ++ /*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) /* no object stream started */ ++ if (os->cur_objstm == 0) { ++ /*tex 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 */ ++ } ++ /*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) { /* print out in groups of ten for better readability */ ++ if (j == 9 || i == os->idx - 1) { ++ /*tex Print out in groups of ten for better readability. */ + pdf_out(pdf, '\n'); + j = 0; + } else { +@@ -1125,192 +1076,168 @@ static void pdf_os_write_objstream(PDF 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 */ ++ /*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"); +- pdf_dict_add_int(pdf, "N", (int) os->idx); /* number of objects in 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); +- /* write object-number/byte-offset list */ ++ /*tex Write object-number/byte-offset list. */ + pdf_out_block(pdf, (const char *) (obuf->data + n1), (size_t) (n2 - n1)); +- /* write collected object stream contents */ ++ /*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); +- os->cur_objstm = 0; /* to force object stream generation next time */ ++ /*tex We force object stream generation next time. */ ++ os->cur_objstm = 0; + } + +-@ begin a PDF dictionary ++/*tex Here comes a bunch of flushers: */ + +-@c + void pdf_begin_dict(PDF pdf) + { ++ pdf_check_space(pdf); + pdf_puts(pdf, "<<"); +- pdf->cave = 0; ++ pdf_set_space(pdf); + } + +-@ end a PDF dictionary +- +-@c + void pdf_end_dict(PDF pdf) + { ++ pdf_check_space(pdf); + pdf_puts(pdf, ">>"); +- pdf->cave = 0; ++ pdf_set_space(pdf); + } + +-@ 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_check_space(pdf); + pdf_print_str(pdf, val); ++ pdf_set_space(pdf); + } + +-@ 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_check_space(pdf); + pdf_puts(pdf, "null"); +- pdf->cave = 1; ++ pdf_set_space(pdf); + } + + void pdf_add_bool(PDF pdf, int i) + { +- if (pdf->cave > 0) +- pdf_out(pdf, ' '); ++ pdf_check_space(pdf); + if (i == 0) + pdf_puts(pdf, "false"); + else + pdf_puts(pdf, "true"); +- pdf->cave = 1; ++ pdf_set_space(pdf); + } + + void pdf_add_int(PDF pdf, int i) + { +- if (pdf->cave > 0) +- pdf_out(pdf, ' '); ++ pdf_check_space(pdf); + pdf_print_int(pdf, i); +- pdf->cave = 1; ++ pdf_set_space(pdf); + } + + void pdf_add_longint(PDF pdf, longinteger n) + { +- if (pdf->cave > 0) +- pdf_out(pdf, ' '); ++ pdf_check_space(pdf); + pdf_print_int(pdf, n); +- pdf->cave = 1; ++ pdf_set_space(pdf); + } + + void pdf_add_string(PDF pdf, const char *s) + { +- if (pdf->cave > 0) +- pdf_out(pdf, ' '); ++ pdf_check_space(pdf); + pdf_print_str(pdf, s); +- pdf->cave = 1; ++ 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->cave = 1; ++ pdf_set_space(pdf); + } + + void pdf_add_ref(PDF pdf, int num) + { +- if (pdf->cave > 0) +- pdf_out(pdf, ' '); ++ pdf_check_space(pdf); + pdf_print_int(pdf, num); + pdf_puts(pdf, " 0 R"); +- pdf->cave = 1; ++ pdf_set_space(pdf); + } + +-@ add stream length and filter entries to a stream dictionary, +-remember file position for seek ++/*tex ++ ++ When we add the 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; + } ++ 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); + } + +-@ begin a PDF array +- +-@c + void pdf_begin_array(PDF pdf) + { ++ pdf_check_space(pdf); + pdf_out(pdf, '['); +- pdf->cave = 0; ++ pdf_set_space(pdf); + } + +-@ end a PDF array +- +-@c + void pdf_end_array(PDF pdf) + { ++ pdf_check_space(pdf); + pdf_out(pdf, ']'); +- pdf->cave = 0; ++ pdf_set_space(pdf); + } + +-@ begin a PDF object +- +-@c + void pdf_begin_obj(PDF pdf, int i, int pdf_os_threshold) + { + os_struct *os = pdf->os; +@@ -1321,45 +1248,90 @@ void pdf_begin_obj(PDF pdf, int i, int pdf_os_threshold) + 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 */ ++ 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->cave = 0; ++ pdf_reset_space(pdf); + } + +-@ 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 */ ++ /*tex End a \PDF\ object. */ ++ pdf_puts(pdf, "\nendobj\n"); + 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) ++ /*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 +- pdf_out(pdf, '\n'); /* Adobe Reader seems to need this */ ++ } else { ++ /*tex Adobe Reader seems to need this. */ ++ pdf_out(pdf, '\n'); ++ } + 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! ++/* ++ 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! ++ ++*/ + +-@c + char *convertStringToPDFString(const char *in, int len) + { + static char pstrbuf[MAX_PSTRING_LEN]; +@@ -1370,7 +1342,7 @@ char *convertStringToPDFString(const char *in, int len) + 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 */ ++ /*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]; +@@ -1378,15 +1350,15 @@ char *convertStringToPDFString(const char *in, int len) + out[j++] = buf[2]; + out[j++] = buf[3]; + } else if ((in[i] == '(') || (in[i] == ')')) { +- /* escape paranthesis */ ++ /*tex Escape parenthesis: */ + out[j++] = '\\'; + out[j++] = in[i]; + } else if (in[i] == '\\') { +- /* escape backslash */ ++ /*tex Escape backslash: */ + out[j++] = '\\'; + out[j++] = '\\'; + } else { +- /* copy char :-) */ ++ /* Copy char : */ + out[j++] = in[i]; + } + } +@@ -1394,10 +1366,13 @@ char *convertStringToPDFString(const char *in, int len) + 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$. ++/*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$. ++ ++*/ + +-@c + static void convertStringToHexString(const char *in, char *out, int lin) + { + int i, k; +@@ -1412,40 +1387,18 @@ static void convertStringToHexString(const char *in, char *out, int lin) + 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. ++/*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. + +-@c +- +-/* + A user supplied trailerid had better be an array! So maybe we need to check +- for [] and error otherwise. ++ for |[]| and error otherwise. ++ + */ + + static void print_ID(PDF pdf) +@@ -1457,10 +1410,10 @@ static void print_ID(PDF pdf) + if (p && strlen(p) > 0) { + pdf_puts(pdf,p); + } else if (pdf_trailer_id != 0) { +- /* user provided one */ ++ /*tex The user provided one: */ + pdf_print_toks(pdf, pdf_trailer_id); + } else { +- /* system provided one */ ++ /*tex The system provided one: */ + time_t t; + size_t size; + char time_str[32]; +@@ -1468,13 +1421,10 @@ static void print_ID(PDF pdf) + 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)); + } +@@ -1492,63 +1442,27 @@ static void print_ID(PDF pdf) + 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_check_space(pdf); + pdf_printf(pdf, "<%s> <%s>", id, id); ++ pdf_set_space(pdf); + 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. ++/*tex + +-For example, December 23, 1998, at 7:52 PM, U.S. Pacific Standard Time, is +-represented by the string ++ 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). + +-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'"| */ ++#define TIME_STR_SIZE 30 + + static void makepdftime(PDF pdf) + { +@@ -1557,32 +1471,31 @@ static void makepdftime(PDF pdf) + int i, off, off_hours, off_mins; + time_t t = pdf->start_time; + char *time_str = pdf->start_time_str; +- /* get the time */ ++ /*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", <); +- /* expected format: "YYYYmmddHHMMSS" */ ++ /*tex Expected format: |YYYYmmddHHMMSS|. */ + if (size == 0) { +- /* unexpected, contents of |time_str| is undefined */ ++ /*tex 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" ++ /*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'; +- /* for safety */ ++ /*tex For safety: */ + time_str[16] = '\0'; + } +- /* get the time zone offset */ ++ /*tex 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; +@@ -1601,7 +1514,6 @@ static void makepdftime(PDF pdf) + pdf->start_time = t; + } + +-@ @c + void initialize_start_time(PDF pdf) + { + if (pdf->start_time == 0) { +@@ -1611,14 +1523,12 @@ void initialize_start_time(PDF pdf) + } + } + +-@ @c + char *getcreationdate(PDF pdf) + { + initialize_start_time(pdf); + return pdf->start_time_str; + } + +-@ @c + void remove_pdffile(PDF pdf) + { + if (pdf != NULL) { +@@ -1629,10 +1539,9 @@ void remove_pdffile(PDF pdf) + } + } + +-@ Use |check_o_mode()| in the backend-specific "Implement..." chunks ++/*tex We use this checker in other modules. It is not pdf specific. */ + +-@c +-void check_o_mode(PDF pdf, const char *s, int o_mode_bitpattern, boolean strict) /* s ignored now */ ++void check_o_mode(PDF pdf, const char *s, int o_mode_bitpattern, boolean strict) + { + + output_mode o_mode; +@@ -1641,16 +1550,13 @@ void check_o_mode(PDF pdf, const char *s, int o_mode_bitpattern, boolean strict) + 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 */ ++ /*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"; +@@ -1669,7 +1575,23 @@ void check_o_mode(PDF pdf, const char *s, int o_mode_bitpattern, boolean strict) + ensure_output_state(pdf, ST_HEADER_WRITTEN); + } + +-@ @c ++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; +@@ -1681,7 +1603,7 @@ void set_job_id(PDF pdf, int year, int month, int day, int time) + 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. */ ++ /*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); +@@ -1690,11 +1612,11 @@ void set_job_id(PDF pdf, int year, int month, int day, int time) + 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 */ ++ /*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); +@@ -1707,12 +1629,11 @@ char *get_resname_prefix(PDF pdf) + 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 */ ++ scaled form_margin = pdf_xform_margin; + ensure_output_state(pdf, ST_HEADER_WRITTEN); + init_pdf_pagecalculations(pdf); + if (pdf->page_resources == NULL) { +@@ -1724,7 +1645,8 @@ void pdf_begin_page(PDF 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 */ ++ /*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; +@@ -1733,15 +1655,16 @@ void pdf_begin_page(PDF pdf) + 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 */ ++ /*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; /* lookup once */ +- form_margin = obj_xform_margin(pdf, pdf_cur_form); /* now stored in object */ ++ 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) { +@@ -1750,7 +1673,7 @@ void pdf_begin_page(PDF pdf) + 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)); ++ 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); + } +@@ -1776,11 +1699,12 @@ void pdf_begin_page(PDF pdf) + } + pdf_dict_add_ref(pdf, "Resources", pdf->page_resources->last_resources); + } +- /* Start stream of page/form contents */ ++ /*tex Start a stream of page or form contents: */ + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); +- pos_stack_used = 0; /* start with empty stack */ ++ /*tex Start with an empty stack: */ ++ pos_stack_used = 0; + if (global_shipping_mode == SHIPPING_PAGE) { + colorstackpagestart(); + } +@@ -1788,40 +1712,37 @@ void pdf_begin_page(PDF pdf) + 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 ... */ ++ lua_pushstring(Luas, s); ++ lua_rawget(Luas, -2); ++ if (lua_type(Luas, -1) == LUA_TSTRING) { + ls = lua_tolstring(Luas, -1, &len); + if (len > 0) { +- if (pdf->cave == 1) +- pdf_out(pdf, ' '); ++ pdf_check_space(pdf); + pdf_out_block(pdf, ls, len); +- pdf->cave = 1; ++ pdf_set_space(pdf); + } + } + 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 ... */ ++ lua_pushstring(Luas, s); ++ lua_rawget(Luas, -2); ++ if (lua_type(Luas, -1) == LUA_TSTRING) { + 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 ++ /*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; +@@ -1830,7 +1751,6 @@ const char *get_pdf_table_string(const char *s) + return NULL ; + } + +-@ @c + void pdf_end_page(PDF pdf) + { + char s[64], *p; +@@ -1838,12 +1758,14 @@ void pdf_end_page(PDF pdf) + 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 */ ++ /*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; +- /* Finish stream of page/form contents */ ++ /*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, +@@ -1856,7 +1778,7 @@ void pdf_end_page(PDF pdf) + 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 */ ++ /*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"); +@@ -1869,7 +1791,7 @@ void pdf_end_page(PDF pdf) + 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 */ ++ page_attributes = pdf_page_attr ; + if (page_attributes != null) + pdf_print_toks(pdf, page_attributes); + print_pdf_table_string(pdf, "pageattributes"); +@@ -1891,7 +1813,7 @@ void pdf_end_page(PDF pdf) + pdf_end_dict(pdf); + pdf_end_obj(pdf); + pdf->img_page_group_val = 0; +- /* Generate array of annotations or beads in page */ ++ /*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); +@@ -1917,37 +1839,38 @@ void pdf_end_page(PDF pdf) + pdf_end_obj(pdf); + } + } +- /* Write out resource lists and pending raw objects */ ++ /*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. ++ |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); +- /* Restore page size and page resources */ ++ /*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; + } +- /* Write out pending images */ ++ /*tex Write out pending images. */ + ol = get_page_resources_list(pdf, obj_type_ximage); + while (ol != NULL) { + if (!is_obj_written(pdf, ol->info)) +@@ -1955,11 +1878,12 @@ void pdf_end_page(PDF pdf) + ol = ol->link; + } + if (global_shipping_mode == SHIPPING_PAGE) { +- /* Write out pending PDF marks and annotations */ ++ /*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) { +- j = obj_annot_ptr(pdf, ol->info); /* |j| points to |pdf_annot_node| */ ++ /*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"); +@@ -1970,7 +1894,7 @@ void pdf_end_page(PDF pdf) + } + ol = ol->link; + } +- /* Write out PDF link annotations */ ++ /*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); +@@ -1989,49 +1913,52 @@ void pdf_end_page(PDF pdf) + pdf_end_obj(pdf); + ol = ol->link; + } +- /* Flush |pdf_start_link_node|'s created by |append_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); +- /* +- 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 ++ /*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; + } + } +- /* Write out PDF mark destinations */ ++ /*tex Write out \PDF\ mark destinations. */ + write_out_pdf_mark_destinations(pdf); +- /* Write out PDF bead rectangle specifications */ ++ /*tex Write out \PDF\ bead rectangle specifications. */ + print_bead_rectangles(pdf); + } +- /* Write out resources dictionary */ ++ /*tex Write out resources dictionary. */ + pdf_begin_obj(pdf, res_p->last_resources, OBJSTM_ALWAYS); + pdf_begin_dict(pdf); +- /* Print additional resources */ ++ /*tex Print additional resources. */ + if (global_shipping_mode == SHIPPING_PAGE) { +- page_resources = pdf_page_resources; /* lookup once */ +- if (page_resources != null) ++ 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; /* lookup once */ +- if (xform_resources != null) ++ 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)); ++ 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); + } + } +- /* Generate font resources */ ++ /*tex Generate font resources. */ + if ((ol = get_page_resources_list(pdf, obj_type_font)) != NULL) { + pdf_add_name(pdf, "Font"); + pdf_begin_dict(pdf); +@@ -2046,7 +1973,7 @@ void pdf_end_page(PDF pdf) + pdf_end_dict(pdf); + procset |= PROCSET_TEXT; + } +- /* Generate XObject resources */ ++ /*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) { +@@ -2071,7 +1998,7 @@ void pdf_end_page(PDF pdf) + } + pdf_end_dict(pdf); + } +- /* Generate ProcSet */ ++ /*tex Generate |ProcSet| in version 1.*/ + if (pdf->major_version == 1) { + pdf_add_name(pdf, "ProcSet"); + pdf_begin_array(pdf); +@@ -2091,13 +2018,15 @@ void pdf_end_page(PDF pdf) + pdf_end_obj(pdf); + } + +-@* Finishing the PDF output file. ++/*tex + +-@ 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. ++ 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. ++ ++*/ + +-@c + static void check_nonexisting_destinations(PDF pdf) + { + int k; +@@ -2120,25 +2049,27 @@ static void check_nonexisting_destinations(PDF 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 */ ++ /*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)); + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@c + static boolean substr_of_str(const char *s, const char *t) + { + if (strstr(t, s) == NULL) +@@ -2146,9 +2077,8 @@ static boolean substr_of_str(const char *s, const char *t) + return true; + } + +-static int pdf_print_info(PDF pdf, int luatexversion, +- str_number luatexrevision) +-{ /* print info object */ ++static int pdf_print_info(PDF pdf, int luatexversion, str_number luatexrevision) ++{ + boolean creator_given = false; + boolean producer_given = false; + boolean creationdate_given = false; +@@ -2158,7 +2088,7 @@ static int pdf_print_info(PDF pdf, int luatexversion, + 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_obj(pdf, k, 3); + pdf_begin_dict(pdf); + if (pdf_info_toks != 0) { + s = tokenlist_to_cstring(pdf_info_toks, true, &len); +@@ -2178,18 +2108,18 @@ static int pdf_print_info(PDF pdf, int luatexversion, + } + if (pdf_info_toks != null) { + if (len > 0) { +- pdf_out(pdf, '\n'); ++ pdf_check_space(pdf); + pdf_puts(pdf, s); +- pdf_out(pdf, '\n'); ++ pdf_set_space(pdf); + 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'); ++ 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"); +@@ -2223,7 +2153,8 @@ 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 */ ++ /*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); +@@ -2233,272 +2164,297 @@ static void build_free_object_list(PDF pdf) + 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. ++/*tex + +-@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."); ++ 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 { +- 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); +- } +- } ++ 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, "->"); + } +- 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); ++ if (pdf->gone > 0) { ++ /* number of bytes gone */ ++ normal_error("pdf backend","already written content discarded, no output file produced."); + } +- 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); ++ } 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, "->"); + } +- pdf_end_array(pdf); +- pdf_end_obj(pdf); +- k = pdf->head_tab[obj_type_thread]; ++ 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) { +- out_thread(pdf, k); ++ int f = obj_info(pdf, k); ++ do_pdf_font(pdf, f); + 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); ++ 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", "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; ++ 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; ++ } + } +- print_pdf_table_string(pdf, "trailer"); +- print_ID(pdf); +- pdf_dict_add_streaminfo(pdf); ++ 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_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_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"); ++ } + } + } +- 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"); ++ /*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'); + } +- } +- /* 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; ++ 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, "->"); + } +- 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); ++ 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"); + } + } +- 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" : "")); ++ 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); + } +- 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; +@@ -2518,3 +2474,43 @@ void scan_pdfcatalog(PDF pdf) + } + } + } ++ ++/*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; ++} +diff --git a/texk/web2c/luatexdir/pdf/pdfglyph.w b/texk/web2c/luatexdir/pdf/pdfglyph.c +similarity index 63% +rename from texk/web2c/luatexdir/pdf/pdfglyph.w +rename to texk/web2c/luatexdir/pdf/pdfglyph.c +index 0703beed2..c7cf04392 100644 +--- a/texk/web2c/luatexdir/pdf/pdfglyph.w ++++ b/texk/web2c/luatexdir/pdf/pdfglyph.c +@@ -1,40 +1,47 @@ +-% pdfglyph.w +-% +-% Copyright 2009-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2009-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + #include "pdf/pdfpage.h" + +-@ eternal constants ++/*tex + +-@c +-#define e_tj 3 /* must be 3; movements in []TJ are in fontsize/$10^3$ units */ ++ 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. ++ ++*/ + +-@ @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; +@@ -44,13 +51,11 @@ void pdf_print_charwidth(PDF pdf, internal_font_number f, int i) + 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 slant, extend, squeeze, 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; +@@ -58,35 +63,55 @@ static void setup_fontparameters(PDF pdf, internal_font_number f, int ex_glyph) + 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; +- 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 */ ++ /*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 * ten_pow[p->tm[3].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])); +- p->cur_ex = ex_glyph ; /* we keep track of the state of ex */ ++ /*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); + } + +- +-@ @c + 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 "); ++ pdf_puts(pdf, " Tf\n"); + 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 */ ++ /*tex Always follow |Tf| by |Tm|: */ ++ p->need_tm = true; + } + +-@ @c + static void set_textmatrix(PDF pdf, scaledpos pos) + { + boolean move; +@@ -97,24 +122,27 @@ static void set_textmatrix(PDF pdf, scaledpos 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 */ ++ /*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; + } + +-@ 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). ++/*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). ++ ++*/ + +-@c + static void pdf_print_char(PDF pdf, int c) + { +- if (c > 255) ++ if (c > 255) { + return; +- /* pdf_print_escaped(c) */ +- if (c <= 32 || c == '\\' || c == '(' || c == ')' || c > 127) { ++ } 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))); +@@ -131,7 +159,6 @@ static void pdf_print_wide_char(PDF pdf, int 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)) +@@ -146,7 +173,6 @@ static void begin_charmode(PDF pdf, internal_font_number f, pdfstructure * p) + p->mode = PMODE_CHAR; + } + +-@ @c + void end_charmode(PDF pdf) + { + pdfstructure *p = pdf->pstruct; +@@ -161,7 +187,6 @@ void end_charmode(PDF pdf) + p->mode = PMODE_CHARARRAY; + } + +-@ @c + static void begin_chararray(PDF pdf) + { + pdfstructure *p = pdf->pstruct; +@@ -173,7 +198,6 @@ static void begin_chararray(PDF pdf) + p->mode = PMODE_CHARARRAY; + } + +-@ @c + void end_chararray(PDF pdf) + { + pdfstructure *p = pdf->pstruct; +@@ -184,23 +208,32 @@ void end_chararray(PDF pdf) + 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. ++/*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. ++ ++*/ + +-@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: ++ /*tex ++ ++ This is already done: ++ ++ \startyping + if (!char_exists(f, c)) { + return; + } ++ \stoptyping ++ + */ + if (font_writingmode(f) == vertical_writingmode) { + if (p->wmode != WMODE_V) { +@@ -221,8 +254,8 @@ void pdf_place_glyph(PDF pdf, internal_font_number f, int c, int ex) + setup_fontparameters(pdf, f, ex); + p->need_tm = true; + } +- /* all movements */ +- move = calc_pdfpos(p, pos); /* within text or chararray or char mode */ ++ /*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) +@@ -231,7 +264,8 @@ void pdf_place_glyph(PDF pdf, internal_font_number f, int c, int ex) + pdf_goto_textmode(pdf); + set_textmatrix(pdf, pos); + begin_chararray(pdf); +- move = calc_pdfpos(p, pos); /* for fine adjustment */ ++ /*tex For fine adjustment: */ ++ move = calc_pdfpos(p, pos); + } + if (move) { + if (is_charmode(p)) +@@ -240,7 +274,7 @@ void pdf_place_glyph(PDF pdf, internal_font_number f, int c, int ex) + p->cw.m -= p->tj_delta.m * ten_pow[p->cw.e - p->tj_delta.e]; + } + } +- /* glyph output */ ++ /*tex Glyph output: */ + if (is_chararraymode(p)) + begin_charmode(pdf, f, p); + else if (!is_charmode(p)) +@@ -250,5 +284,6 @@ void pdf_place_glyph(PDF pdf, internal_font_number f, int c, int ex) + 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()| */ ++ /*tex Also known as |adv_char_width()|: */ ++ p->cw.m += pdf_char_width(p, p->f_pdf, c); + } +diff --git a/texk/web2c/luatexdir/pdf/pdfimage.w b/texk/web2c/luatexdir/pdf/pdfimage.c +similarity index 50% +rename from texk/web2c/luatexdir/pdf/pdfimage.w +rename to texk/web2c/luatexdir/pdf/pdfimage.c +index fd3642438..a8cffb0c8 100644 +--- a/texk/web2c/luatexdir/pdf/pdfimage.w ++++ b/texk/web2c/luatexdir/pdf/pdfimage.c +@@ -1,38 +1,39 @@ +-% pdfimage.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ @c + void place_img(PDF pdf, image_dict * idict, scaled_whd dim, int transform) + { +- float a[6]; /* transformation matrix */ ++ /*tex A transformation matrix: */ ++ float a[6]; + float xoff, yoff, tmp; + pdfstructure *p = pdf->pstruct; + scaledpos pos = pdf->posstruct->pos; +- int r; /* number of digits after the decimal point */ ++ /*tex The number of digits after the decimal point: */ ++ int r; + int k; + scaledpos tmppos; + pdffloat cm[6]; +- int groupref; /* added from web for 1.40.8 */ ++ 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 +@@ -43,48 +44,51 @@ void place_img(PDF pdf, image_dict * idict, scaled_whd dim, int transform) + 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 */ ++ if ((transform & 7) > 3) { ++ /*tex 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:; ++ 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); +@@ -98,21 +102,24 @@ void place_img(PDF pdf, image_dict * idict, scaled_whd dim, int transform) + 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:; ++ 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:; + } +- /* 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); +@@ -124,7 +131,7 @@ void place_img(PDF pdf, image_dict * idict, scaled_whd dim, int transform) + 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->img_page_group_val = img_group_ref(idict); + pdf_puts(pdf, "q\n"); + pdf_print_cm(pdf, cm); + pdf_puts(pdf, "/Im"); +@@ -136,9 +143,8 @@ void place_img(PDF pdf, image_dict * idict, scaled_whd dim, int transform) + img_state(idict) = DICT_OUTIMG; + } + +-@ for normal output, see \.{pdflistout.w} ++/*tex For normal output see |pdflistout.c|: */ + +-@c + void pdf_place_image(PDF pdf, halfword p) + { + scaled_whd dim; +diff --git a/texk/web2c/luatexdir/pdf/pdflink.w b/texk/web2c/luatexdir/pdf/pdflink.c +similarity index 69% +rename from texk/web2c/luatexdir/pdf/pdflink.w +rename to texk/web2c/luatexdir/pdf/pdflink.c +index b21d0774e..55521b176 100644 +--- a/texk/web2c/luatexdir/pdf/pdflink.w ++++ b/texk/web2c/luatexdir/pdf/pdflink.c +@@ -1,31 +1,34 @@ +-% pdflink.w +-% +-% Copyright 2009-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2009-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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. ++/*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. ++ ++*/ + +-@c + void push_link_level(PDF pdf, halfword p) + { + if (pdf->link_stack_ptr >= pdf_max_link_level) +@@ -36,14 +39,12 @@ void push_link_level(PDF pdf, halfword 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; +@@ -59,13 +60,13 @@ void do_link(PDF pdf, halfword p, halfword parent_box, scaledpos cur) + 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 */ ++ /*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); + } + +-@ @c + void end_link(PDF pdf, halfword p) + { + halfword q; +@@ -76,10 +77,10 @@ void end_link(PDF pdf, halfword p) + 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 ++ /*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 ++ |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; +@@ -110,15 +111,16 @@ void end_link(PDF pdf, halfword p) + 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. ++/*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. + +-@ 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; +@@ -126,7 +128,8 @@ void append_link(PDF pdf, halfword parent_box, scaledpos cur, small_number i) + 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 */ ++ /*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); +@@ -137,7 +140,6 @@ void append_link(PDF pdf, halfword parent_box, scaledpos cur, small_number i) + addto_page_resources(pdf, obj_type_link, k); + } + +-@ @c + void scan_startlink(PDF pdf) + { + int k; +@@ -155,10 +157,9 @@ void scan_startlink(PDF 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 ++ /*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. + */ + } +diff --git a/texk/web2c/luatexdir/pdf/pdflistout.w b/texk/web2c/luatexdir/pdf/pdflistout.c +similarity index 66% +rename from texk/web2c/luatexdir/pdf/pdflistout.w +rename to texk/web2c/luatexdir/pdf/pdflistout.c +index 53ed24af6..9dbdaeefa 100644 +--- a/texk/web2c/luatexdir/pdf/pdflistout.w ++++ b/texk/web2c/luatexdir/pdf/pdflistout.c +@@ -1,120 +1,48 @@ +-% pdflistout.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-\def\pdfTeX{pdf\TeX} ++Copyright 2009-2010 Taco Hoekwater + +-@ @c +-#include "ptexlib.h" ++This file is part of LuaTeX. + +-@ @c +-#define kern_width(q) width(q) + ex_kern(q) ++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. + +-/* +- ext_xn_over_d(width(q), 1000000+ex_kern(q), 1000000); +-*/ ++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. + +-@ @c +-pos_info_structure pos_info; /* to be accessed from Lua */ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . + +-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); +-} ++#include "ptexlib.h" + +-@ @c +-static void init_none_backend_functions(void) +-{ +- backend_struct *p = &backend[OMODE_NONE]; +- p->name = strdup("(None)"); +-} ++#define kern_width(q) width(q) + ex_kern(q) + +-@ @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; +-} ++#define billion 1000000000.0 + +-@ @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; +-} ++#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) + +-@ @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; +-} ++/*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). + +-@ 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; +@@ -124,8 +52,6 @@ static scaled simple_advance_width(halfword p) + switch (type(q)) { + case glyph_node: + w += glyph_width(q); +-/* experimental */ +-w += x_advance(q); + break; + case hlist_node: + case vlist_node: +@@ -134,11 +60,11 @@ w += x_advance(q); + w += width(q); + break; + case kern_node: +- /* officially we should check the subtype */ ++ /*tex Officially we should check the subtype. */ + w += kern_width(q); + break; + case disc_node: +- /* hh: the frontend should append already */ ++ /* (HH): The frontend should append already. */ + if (vlink(no_break(q)) != null) + w += simple_advance_width(no_break(q)); + default: +@@ -148,13 +74,13 @@ w += x_advance(q); + 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 */ ++ /*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)) { +@@ -172,18 +98,18 @@ static halfword calculate_width_to_enddir(halfword p, real cur_glue, scaled cur_ + w += width(q); + break; + case kern_node: +- /* officially we should check the subtype */ ++ /*tex Officially we should check the subtype. */ + w += kern_width(q); + break; + case math_node: +- /* begin mathskip code */ ++ /*tex Begin of |mathskip| code. */ + if (glue_is_zero(q)) { + w += surround(q); + break; + } else { +- /* fall through: mathskip */ ++ /*tex Fall through |mathskip|. */ + } +- /* end mathskip code */ ++ /*tex End of |mathskip| code. */ + case glue_node: + w += width(q) - cur_g; + if (g_sign != normal) { +@@ -202,12 +128,12 @@ static halfword calculate_width_to_enddir(halfword p, real cur_glue, scaled cur_ + w += cur_g; + break; + case disc_node: +- /* hh: the frontend should append already */ ++ /* (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) ++ if (subtype(q) == normal_dir) + dir_nest++; + else + dir_nest--; +@@ -223,123 +149,115 @@ static halfword calculate_width_to_enddir(halfword p, real cur_glue, scaled cur_ + } + } + if (enddir_ptr == p) +- /* no enddir found, just transport w by begindir */ ++ /*tex 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. ++/*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.) +-@^leaders@> ++ 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.) ++ ++*/ + +-@c +-void out_what(PDF pdf, halfword p) ++static void handle_backend_whatsit(PDF pdf, halfword p, halfword parent_box, scaledpos cur) + { +- 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); ++ 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; ++ } + } + } + +-@ @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 */ ++ /*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; +- scaledpos size = { 0, 0 }; /* rule dimensions */ ++ /*tex rule dimensions */ ++ scaledpos size = { 0, 0 }; + 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 */ ++ /*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; +- 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 */ ++ /*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; +- pdf->posstruct = &localpos; /* use local structure for recursion */ ++ /*tex use local structure for recursion */ ++ pdf->posstruct = &localpos; + 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; +- } +- ++ 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) ++ if (synctex) { + synctexhlist(this_box); ++ } + while (p != null) { + if (is_char_node(p)) { + do { +@@ -351,8 +269,6 @@ void hlist_out(PDF pdf, halfword this_box, int rule_callback_id) + 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; + } +@@ -362,7 +278,10 @@ cur.h += x_advance(p); + if (synctex) + synctexcurrent(); + } else { +- /* output the non-|char_node| |p| for |hlist_out| and move to the next node */ ++ /*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: +@@ -387,26 +306,36 @@ cur.h += x_advance(p); + 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 */ ++ 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))) ++ if (partextdir_eq(localpos.dir, box_dir(p))) { + basepoint.v = 0; +- else +- basepoint.v = width(p); /* down */ ++ } else { ++ /*tex down */ ++ basepoint.v = width(p); ++ } + } else { +- if (partextdir_eq(localpos.dir, box_dir(p))) +- basepoint.v = -width(p); /* up */ +- else ++ if (partextdir_eq(localpos.dir, box_dir(p))) { ++ /*tex up */ ++ basepoint.v = -width(p); ++ } 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 (!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) +@@ -426,10 +355,11 @@ cur.h += x_advance(p); + cur.h += effective_horizontal; + break; + case disc_node: +- /* hh: the frontend should append already */ ++ /*tex (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 */ ++ /*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; +@@ -441,17 +371,20 @@ cur.h += x_advance(p); + if (synctex) { + synctexmath(p, this_box); + } +- /* begin mathskip code */ ++ /*tex Begin |mathskip| code. */ + if (glue_is_zero(p)) { + cur.h += surround(p); + break; + } else { +- /* fall through: mathskip */ ++ /*tex Fall through |mathskip|. */ + } +- /* end mathskip code */ ++ /*tex End |mathskip| code. */ + case glue_node: + { +- /* move right or output leaders, we use real multiplication */ ++ /*tex ++ Move right or output leaders, we use real ++ multiplication. ++ */ + rule.wd = width(p) - cur_g; + if (g_sign != normal) { + if (g_sign == stretching) { +@@ -468,11 +401,16 @@ cur.h += x_advance(p); + } + 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 */ ++ /*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)) +@@ -480,12 +418,14 @@ cur.h += x_advance(p); + 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 */ ++ /*tex Compensate for floating-point rounding. */ ++ rule.wd = rule.wd + 10; + 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 ++ /*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; +@@ -522,8 +462,10 @@ cur.h += x_advance(p); + 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 */ ++ /*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 { +@@ -532,7 +474,10 @@ cur.h += x_advance(p); + } + } + while (cur.h + leader_wd <= edge) { +- /* output a leader box at |cur.h|, then advance |cur.h| by |leader_wd+lx| */ ++ /*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)) +@@ -582,7 +527,7 @@ cur.h += x_advance(p); + case kern_node: + if (synctex) + synctexkern(p, this_box); +- /* officially we should check the subtype */ ++ /*tex officially we should check the subtype */ + cur.h += kern_width(p); + break; + case rule_node: +@@ -597,14 +542,18 @@ cur.h += x_advance(p); + 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: +- /* output a reflection instruction if the direction has changed */ +- if (dir_dir(p) >= 0) { +- /* ++ /*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| 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)) { +@@ -614,14 +563,14 @@ cur.h += x_advance(p); + } else + dir_cur_h(enddir_ptr) = cur.h; + if (enddir_ptr != p) { +- /* only if it is an enddir */ ++ /*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; +- /* negative: mark it as |enddir| */ +- dir_dir(enddir_ptr) = localpos.dir - dir_swap; ++ /*tex Negative: mark it as |enddir|. */ ++ dir_dir(enddir_ptr) = localpos.dir; + } +- /* fake a nested |hlist_out| */ ++ /*tex fake a nested |hlist_out|. */ + synch_pos_with_cur(pdf->posstruct, refpos, cur); + refpos->pos = pdf->posstruct->pos; + localpos.dir = dir_dir(p); +@@ -630,32 +579,44 @@ cur.h += x_advance(p); + } else { + refpos->pos.h = dir_refpos_h(p); + refpos->pos.v = dir_refpos_v(p); +- localpos.dir = dir_dir(p) + dir_swap; ++ localpos.dir = dir_dir(p); + 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); ++ /*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: +@@ -666,12 +627,21 @@ cur.h += x_advance(p); + } + goto NEXTP; + FIN_RULE: +- /* output a rule in an hlist */ +- if (is_running(rule.ht)) ++ /*tex Output a rule in an hlist. */ ++ if (is_running(rule.ht)) { + rule.ht = height(this_box); +- if (is_running(rule.dp)) ++ } ++ if (rleft != 0) { ++ rule.ht -= rleft; ++ pos_down(-rleft); ++ } ++ if (is_running(rule.dp)) { + rule.dp = depth(this_box); +- /* we don't output empty rules */ ++ } ++ 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: +@@ -725,92 +695,85 @@ cur.h += x_advance(p); + synch_pos_with_cur(pdf->posstruct, refpos, cur); + } + } +- if (synctex) ++ 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; +- } + } ++ backend_out_control[backend_control_pop_list](pdf,&saved_pos,&saved_loc); + 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 */ +- ++ /*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; +- scaledpos size = { 0, 0 }; /* rule dimensions */ ++ /*tex rule dimensions */ ++ scaledpos size = { 0, 0 }; + 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 */ ++ /*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; +- int save_loc = 0; /* DVI byte location upon entry */ +- scaledpos save_dvi = { 0, 0 }; /* DVI! what |dvi| should pop to */ ++ /*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; +- pdf->posstruct = &localpos; /* use local structure for recursion */ ++ /*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++; +- 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) ++ backend_out_control[backend_control_push_list](pdf,&saved_pos,&saved_loc); ++ if (synctex) { + synctexvlist(this_box); +- +- /* create thread for the current vbox if needed */ ++ } ++ /*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: +- /* +- 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?) +- +- */ ++ /*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))) +@@ -839,7 +802,8 @@ void vlist_out(PDF pdf, halfword this_box, int rule_callback_id) + else + basepoint.v = width(p); + } +- basepoint.h = basepoint.h + shift_amount(p); /* shift the box right */ ++ /*tex Shift the box right. */ ++ basepoint.h = basepoint.h + shift_amount(p); + if (list_ptr(p) == null) { + cur.v += effective_vertical; + if (synctex) { +@@ -872,11 +836,15 @@ void vlist_out(PDF pdf, halfword this_box, int rule_callback_id) + 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: + { +- /* move down or output leaders, we use real multiplication */ ++ /*tex ++ Move down or output leaders, we use real multiplication. ++ */ + rule.ht = width(p) - cur_g; + if (g_sign != normal) { + if (g_sign == stretching) { +@@ -893,25 +861,38 @@ void vlist_out(PDF pdf, halfword this_box, int rule_callback_id) + } + 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 */ ++ /*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)) { +- rule.ht = rule.ht + 10; /* compensate for floating-point rounding */ ++ /*tex Compensate for floating-point rounding: */ ++ rule.ht = rule.ht + 10; + 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 ++ /*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); +@@ -922,12 +903,6 @@ void vlist_out(PDF pdf, halfword this_box, int rule_callback_id) + 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; +@@ -944,8 +919,10 @@ void vlist_out(PDF pdf, halfword this_box, int rule_callback_id) + 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 */ ++ /*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 { +@@ -953,9 +930,11 @@ void vlist_out(PDF pdf, halfword this_box, int rule_callback_id) + 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| */ ++ /*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); +@@ -979,41 +958,62 @@ void vlist_out(PDF pdf, halfword this_box, int rule_callback_id) + 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); ++ /*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: +- confusion("vlistout"); /* this can't happen */ ++ /*tex This can't happen unless one messes up in \LUA. */ ++ confusion("vlistout"); + break; + default: + break; + } + goto NEXTP; + FIN_RULE: +- /* output a rule in a vlist, |goto next_p| */ +- if (is_running(rule.wd)) ++ /*tex Output a rule in a vlist and |goto next_p|. */ ++ if (is_running(rule.wd)) { + rule.wd = width(this_box); +- /* we don't output empty rules */ ++ } ++ 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: +@@ -1063,16 +1063,10 @@ void vlist_out(PDF pdf, halfword this_box, int rule_callback_id) + p = vlink(p); + synch_pos_with_cur(pdf->posstruct, refpos, cur); + } +- if (synctex) ++ 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; +- } + } ++ backend_out_control[backend_control_pop_list](pdf,&saved_pos,&saved_loc); + cur_s--; + pdf->posstruct = refpos; + } +diff --git a/texk/web2c/luatexdir/pdf/pdfliteral.w b/texk/web2c/luatexdir/pdf/pdfliteral.c +similarity index 64% +rename from texk/web2c/luatexdir/pdf/pdfliteral.w +rename to texk/web2c/luatexdir/pdf/pdfliteral.c +index 94bb2c93c..e8b00a64e 100644 +--- a/texk/web2c/luatexdir/pdf/pdfliteral.w ++++ b/texk/web2c/luatexdir/pdf/pdfliteral.c +@@ -1,27 +1,26 @@ +-% pdfliteral.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ @c + void pdf_special(PDF pdf, halfword p) + { + int old_setting = selector; +@@ -34,18 +33,22 @@ void pdf_special(PDF pdf, halfword p) + 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|. ++/*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|. ++ ++*/ + +-@c + void pdf_out_literal(PDF pdf, halfword p) + { +- int old_setting; /* holds print |selector| */ ++ int old_setting; + str_number s; ++ int t = pdf_literal_type(p); + pdfstructure *ps = pdf->pstruct; +- if (pdf_literal_type(p) == normal) { ++ if (t == normal) { + old_setting = selector; + selector = new_string; + show_token_list(token_link(pdf_literal_data(p)), null, -1); +@@ -53,7 +56,7 @@ void pdf_out_literal(PDF pdf, halfword p) + s = make_string(); + pdf_literal(pdf, s, pdf_literal_mode(p), false); + flush_str(s); +- } else { ++ } else if (t == lua_refid_literal) { + switch (pdf_literal_mode(p)) { + case set_origin: + pdf_goto_pagemode(pdf); +@@ -79,12 +82,12 @@ void pdf_out_literal(PDF pdf, halfword p) + normal_error("pdf backend","bad literal mode"); + break; + } +- lua_pdf_literal(pdf, pdf_literal_data(p)); ++ lua_pdf_literal(pdf, pdf_literal_data(p), 0); + } + } + +-@ test equality of start of strings +-@c ++/*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; +@@ -99,38 +102,50 @@ static boolean str_in_cstr(str_number s, const char *r, unsigned i) + 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 */ ++ /*tex ++ The current character code position, initialized to make the compiler ++ happy: ++ */ ++ pool_pointer j = 0; + pdfstructure *p = pdf->pstruct; + if (s >= STRING_OFFSET) { +- /* needed for |out_save| */ ++ /*tex Needed for |out_save|: */ + j = 0; +- /* unfortunately we always go through this when we have vf specials (and also via temp strings) */ ++ /*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; + } +- j = j + (pool_pointer) 4; /* strlen("PDF:") */ +- if (str_in_cstr(s, "direct:", 4)) { /* strlen("PDF:") */ +- j = j + (pool_pointer) 7; /* strlen("direct:") */ ++ /*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)) { /* strlen("PDF:") */ +- j = j + (pool_pointer) 5; /* strlen("page:") */ ++ } 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)) { /* strlen("PDF:") */ +- j = j + (pool_pointer) 5; /* strlen("text:") */ ++ } 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)) { /* strlen("PDF:") */ +- j = j + (pool_pointer) 4; /* strlen("raw:") */ ++ } 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)) { /* strlen("PDF:") */ +- j = j + (pool_pointer) 7; /* strlen("origin:") */ ++ } 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; +@@ -147,7 +162,7 @@ void pdf_literal(PDF pdf, str_number s, int literal_mode, boolean warn) + break; + case direct_text: + pdf_goto_fontmode(pdf); +-// pdf_goto_textmode(pdf); ++ /*tex not: |pdf_goto_textmode(pdf);| */ + break; + case direct_always: + pdf_end_string_nl(pdf); +diff --git a/texk/web2c/luatexdir/pdf/pdfobj.w b/texk/web2c/luatexdir/pdf/pdfobj.c +similarity index 72% +rename from texk/web2c/luatexdir/pdf/pdfobj.w +rename to texk/web2c/luatexdir/pdf/pdfobj.c +index e5ca09de6..71d6a943f 100644 +--- a/texk/web2c/luatexdir/pdf/pdfobj.w ++++ b/texk/web2c/luatexdir/pdf/pdfobj.c +@@ -1,44 +1,48 @@ +-% pdfobj.w +-% +-% Copyright 2009-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2009-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ + #include "ptexlib.h" + #include "lua/luatex-api.h" + +-@ write a raw PDF object ++/*tex 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| */ ++ /*tex The index into |data.s|: */ ++ size_t li; + 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 */ ++ /*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; +- /* we can have an immediate object before we are initialized */ ++ /*tex 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" */ ++ /*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; +@@ -56,9 +60,10 @@ void pdf_write_obj(PDF pdf, int k) + 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); +- if (st.s[st.l - 1] != '\n') +- pdf_out(pdf, '\n'); ++ pdf_set_space(pdf); + luaL_unref(Luas, LUA_REGISTRYINDEX, l); + obj_obj_stream_attr(pdf, k) = LUA_NOREF; + } +@@ -75,10 +80,12 @@ void pdf_write_obj(PDF pdf, int k) + 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 */ ++ /*tex The callback status value: */ ++ boolean res = false; ++ /*tex The callback found filename: */ ++ const char *fnam = NULL; + int callback_id; +- /* st.s is also |\0|-terminated, even as lstring */ ++ /*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) { +@@ -88,7 +95,8 @@ void pdf_write_obj(PDF pdf, int k) + if (!file_opened) + normal_error("pdf backend", "cannot open file for embedding"); + } else { +- byte_file f; /* the data file's FILE* */ ++ /*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)) +@@ -112,32 +120,37 @@ void pdf_write_obj(PDF pdf, int k) + if (obj_obj_is_stream(pdf, k)) { + pdf_end_stream(pdf); + pdf_end_obj(pdf); +- } else /* here we do the \n */ ++ } 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; + } + +-@ @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); ++ unset_obj_obj_no_length(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|. ++/*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|. ++ ++*/ + +-@c + void scan_obj(PDF pdf) + { + int k; +@@ -154,7 +167,7 @@ void scan_obj(PDF pdf) + 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"); ++ normal_error("pdf backend", "scheduled object is already used"); + } else { + pdf->obj_count++; + k = pdf_create_obj(pdf, obj_type_obj, 0); +@@ -190,7 +203,6 @@ void scan_obj(PDF pdf) + pdf_last_obj = k; + } + +-@ @c + void scan_refobj(PDF pdf) + { + scan_int(); +@@ -206,14 +218,12 @@ void scan_refobj_lua(PDF pdf, int k) + 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)) +diff --git a/texk/web2c/luatexdir/pdf/pdfoutline.w b/texk/web2c/luatexdir/pdf/pdfoutline.c +similarity index 82% +rename from texk/web2c/luatexdir/pdf/pdfoutline.w +rename to texk/web2c/luatexdir/pdf/pdfoutline.c +index 6c0a30a88..ae17e29da 100644 +--- a/texk/web2c/luatexdir/pdf/pdfoutline.w ++++ b/texk/web2c/luatexdir/pdf/pdfoutline.c +@@ -1,36 +1,46 @@ +-% pdfoutline.w +-% +-% Copyright 2009-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2009-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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| ++/*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|. ++ ++*/ + +-@c +-#define pdfmem_outline_size 8 /* size of memory in |pdf->mem| which |obj_outline_ptr| points to */ ++/*tex The 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 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] +@@ -38,7 +48,7 @@ outline entries in |pdf->mem| instead of |mem| + #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_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 +@@ -52,7 +62,6 @@ outline entries in |pdf->mem| instead of |mem| + #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; +@@ -78,9 +87,8 @@ static int open_subentries(PDF pdf, halfword p) + return k; + } + +-@ return number of outline entries in the same level with |p| ++/*tex Return the number of outline entries in the same level with |p|: */ + +-@c + static int outline_list_count(PDF pdf, pointer p) + { + int k = 1; +@@ -91,7 +99,6 @@ static int outline_list_count(PDF pdf, pointer p) + return k; + } + +-@ @c + void scan_pdfoutline(PDF pdf) + { + halfword q, r; +@@ -178,10 +185,13 @@ void scan_pdfoutline(PDF pdf) + } + } + +-@ In the end we must flush PDF objects that cannot be written out immediately +-after shipping out pages. ++/*tex ++ ++ In the end we must flush \PDF\F objects that cannot be written out ++ immediately after shipping out pages. ++ ++*/ + +-@c + int print_outlines(PDF pdf) + { + int k, l, a; +@@ -206,8 +216,7 @@ int print_outlines(PDF pdf) + pdf_dict_add_int(pdf, "Count", k); + pdf_end_dict(pdf); + pdf_end_obj(pdf); +- /* Output PDF outline entries */ +- ++ /*tex Output \PDF\ outline entries. */ + k = pdf->head_tab[obj_type_outline]; + while (k != 0) { + if (obj_outline_parent(pdf, k) == pdf->parent_outline) { +@@ -243,7 +252,6 @@ int print_outlines(PDF pdf) + pdf_end_obj(pdf); + k = obj_link(pdf, k); + } +- + } else { + outlines = 0; + } +diff --git a/texk/web2c/luatexdir/pdf/pdfpage.w b/texk/web2c/luatexdir/pdf/pdfpage.c +similarity index 74% +rename from texk/web2c/luatexdir/pdf/pdfpage.w +rename to texk/web2c/luatexdir/pdf/pdfpage.c +index ecc51ddfd..211248a02 100644 +--- a/texk/web2c/luatexdir/pdf/pdfpage.w ++++ b/texk/web2c/luatexdir/pdf/pdfpage.c +@@ -1,23 +1,23 @@ +-% pdfpage.w +-% +-% Copyright 2006-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2006-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +@@ -26,7 +26,6 @@ + #include + #include + +-@ @c + void init_pdf_pagecalculations(PDF pdf) + { + pdfstructure *p; +@@ -37,23 +36,30 @@ void init_pdf_pagecalculations(PDF pdf) + 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 */ ++ /*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); +- 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 */ ++ /*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); +- setpdffloat(p->tm[2], 0, 3); /* mantissa holds SlantFont, 0 = default */ ++ /*tex mantissa holds SlantFont, 0 = default */ ++ setpdffloat(p->tm[2], 0, 3); + 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| */ +- /* */ ++ /*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; +@@ -61,10 +67,11 @@ void init_pdf_pagecalculations(PDF pdf) + 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; + } + +-@ @c + void synch_pos_with_cur(posstructure * pos, posstructure * refpos, scaledpos cur) + { + switch (pos->dir) { +@@ -93,7 +100,6 @@ void synch_pos_with_cur(posstructure * pos, posstructure * refpos, scaledpos cur + } + } + +-@ @c + boolean calc_pdfpos(pdfstructure * p, scaledpos pos) + { + scaledpos new; +@@ -102,7 +108,8 @@ boolean calc_pdfpos(pdfstructure * p, scaledpos pos) + 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 */ ++ /*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; +@@ -110,7 +117,8 @@ boolean calc_pdfpos(pdfstructure * p, scaledpos pos) + 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 */ ++ /*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; +@@ -122,14 +130,16 @@ boolean calc_pdfpos(pdfstructure * p, scaledpos pos) + 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 */ ++ /*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); +- p->tm[4].m = new.h - p->pdf_bt_pos.h.m; /* p->tm[5] is meaningless */ ++ /*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; +@@ -145,7 +155,6 @@ boolean calc_pdfpos(pdfstructure * p, scaledpos pos) + return move_pdfpos; + } + +-@ @c + void print_pdf_matrix(PDF pdf, pdffloat * tm) + { + int i; +@@ -156,14 +165,12 @@ void print_pdf_matrix(PDF pdf, pdffloat * tm) + 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; +@@ -178,7 +185,6 @@ void pdf_set_pos(PDF pdf, scaledpos pos) + } + } + +-@ @c + void pdf_set_pos_temp(PDF pdf, scaledpos pos) + { + boolean move; +@@ -191,7 +197,6 @@ void pdf_set_pos_temp(PDF pdf, scaledpos pos) + } + } + +-@ @c + static void begin_text(PDF pdf) + { + pdfstructure *p = pdf->pstruct; +@@ -201,6 +206,8 @@ static void begin_text(PDF 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) +@@ -208,6 +215,16 @@ 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; +@@ -222,7 +239,6 @@ void pdf_end_string_nl(PDF pdf) + end_chararray(pdf); + } + +-@ @c + void pdf_goto_pagemode(PDF pdf) + { + pdfstructure *p = pdf->pstruct; +@@ -245,7 +261,7 @@ void pdf_goto_textmode(PDF pdf) + 0, 0 + }; + if (is_pagemode(p)) { +- /* reset to page origin */ ++ /*tex Reset to the page origin: */ + pdf_set_pos(pdf, origin); + begin_text(pdf); + } else if (!is_textmode(p)) { +diff --git a/texk/web2c/luatexdir/pdf/pdfpagetree.w b/texk/web2c/luatexdir/pdf/pdfpagetree.c +similarity index 56% +rename from texk/web2c/luatexdir/pdf/pdfpagetree.w +rename to texk/web2c/luatexdir/pdf/pdfpagetree.c +index 76d008c43..4fcd48e34 100644 +--- a/texk/web2c/luatexdir/pdf/pdfpagetree.w ++++ b/texk/web2c/luatexdir/pdf/pdfpagetree.c +@@ -1,38 +1,39 @@ +-% pdfpagetree.w +-% +-% Copyright 2006-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + +-#include "ptexlib.h" ++Copyright 2006-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. + +-@* Page diversions. ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . + +-@ @c +-# define PAGES_TREE_KIDSMAX 10 ++*/ ++ ++#include "ptexlib.h" ++ ++#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 */ ++ /*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; + +@@ -52,7 +53,6 @@ static int comp_divert_list_entry(const void *pa, const void *pb, void *p) + return 0; + } + +-@ @c + static pages_entry *new_pages_entry(PDF pdf) + { + int i; +@@ -65,7 +65,6 @@ static pages_entry *new_pages_entry(PDF pdf) + return p; + } + +-@ @c + static divert_list_entry *new_divert_list_entry(void) + { + divert_list_entry *d; +@@ -74,7 +73,6 @@ static divert_list_entry *new_divert_list_entry(void) + return d; + } + +-@ @c + static void ensure_list_tree(void) + { + if (divert_list_tree == NULL) { +@@ -82,7 +80,6 @@ static void ensure_list_tree(void) + } + } + +-@ @c + static divert_list_entry *get_divert_list(int divnum) + { + divert_list_entry *d, tmp; +@@ -92,7 +89,7 @@ static divert_list_entry *get_divert_list(int divnum) + if (d == NULL) { + d = new_divert_list_entry(); + d->divnum = divnum; +- /* the next bit of code can actually be removed */ ++ /*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"); +@@ -101,18 +98,18 @@ static divert_list_entry *get_divert_list(int divnum) + return d; + } + +-@ |pdf_do_page_divert()| returns the current /Parent object number +-@c ++/*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; +- /* initialize the tree */ ++ /*tex Initialize the tree. */ + ensure_list_tree(); +- /* make sure we have a list for this diversion */ ++ /*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) { +- /* append a new |pages_entry| */ ++ /*tex Append a new |pages_entry|. */ + p = new_pages_entry(pdf); + if (d->first == NULL) + d->first = p; +@@ -126,33 +123,32 @@ int pdf_do_page_divert(PDF pdf, int objnum, int divnum) + 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 */ ++ /*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; +- /* one could as well remove this |divert_list_entry| */ ++ /*tex One could as well remove this |divert_list_entry|. */ + d->first = d->last = NULL; + } + } + +-@ undivert from diversion |divnum| into diversion |curdivnum| +-@c ++/*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; +- /* initialize the tree */ ++ /*tex Initialize the tree. */ + ensure_list_tree(); +- /* find the diversion |curdivnum| list where diversion |divnum| should go */ ++ /*tex 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 */ ++ /*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)) +@@ -164,10 +160,9 @@ void pdf_do_page_undivert(int divnum, int curdivnum) + } + } + +-@ write a /Pages object +-@c ++/*tex Write a |/Pages| object. */ + +-static void write_pages(PDF pdf, pages_entry * p, int parent) ++static void write_pages(PDF pdf, pages_entry * p, int parent, int callback_id) + { + int i; + int pages_attributes ; +@@ -175,39 +170,57 @@ static void write_pages(PDF pdf, pages_entry * p, int parent) + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "Pages"); + if (parent == 0) { +- /* it's root */ +- pages_attributes = pdf_pages_attr; /* lookup once */ ++ /*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 ++ } 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]); ++ 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); + } + +-@ loop over all /Pages objects, output them, create their parents, +-recursing bottom up, return the /Pages root object number ++/*tex ++ ++ 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) ++static int output_pages_list(PDF pdf, pages_entry * pe, int callback_id) + { + pages_entry *p, *q, *r; + if (pe->next == NULL) { +- /* everything fits into one |pages_entry| */ +- write_pages(pdf, pe, 0); /* /Pages root found */ ++ /*tex Everything fits into one |pages_entry|. */ ++ write_pages(pdf, pe, 0, callback_id); + return pe->objnum; + } +- q = r = new_pages_entry(pdf); /* one level higher needed */ ++ /*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); +@@ -215,16 +228,19 @@ static int output_pages_list(PDF pdf, pages_entry * pe) + } + q->kids[q->number_of_kids++] = p->objnum; + q->number_of_pages += p->number_of_pages; +- write_pages(pdf, p, q->objnum); ++ write_pages(pdf, p, q->objnum, callback_id); + } +- return output_pages_list(pdf, r); /* recurse through next higher level */ ++ /*tex Recurse through next higher level. */ ++ return output_pages_list(pdf, r, callback_id); + } + +-@ @c + int output_pages_tree(PDF pdf) + { ++ int callback_id = callback_defined(page_objnum_provider_callback); + 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); ++ /*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); + } +diff --git a/texk/web2c/luatexdir/pdf/pdfrule.w b/texk/web2c/luatexdir/pdf/pdfrule.c +similarity index 56% +rename from texk/web2c/luatexdir/pdf/pdfrule.w +rename to texk/web2c/luatexdir/pdf/pdfrule.c +index df4979088..de689a147 100644 +--- a/texk/web2c/luatexdir/pdf/pdfrule.w ++++ b/texk/web2c/luatexdir/pdf/pdfrule.c +@@ -1,29 +1,26 @@ +-% pdfrule.w +-% +-% Copyright 2010-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c +-#include "ptexlib.h" +-#include "pdf/pdfpage.h" ++Copyright 2010-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. + +-@ @c ++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. + +-/* maybe we should have an extra callback on normal rules or on any rule in 2.0+ */ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#include "ptexlib.h" ++#include "pdf/pdfpage.h" + + void pdf_place_rule(PDF pdf, halfword q, scaledpos size, int callback_id) + { +@@ -31,7 +28,6 @@ void pdf_place_rule(PDF pdf, halfword q, scaledpos size, int callback_id) + 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; +@@ -44,7 +40,7 @@ void pdf_place_rule(PDF pdf, halfword q, scaledpos size, int callback_id) + } else if (s == image_rule) { + pdf_place_image(pdf,q); + } else if (s == empty_rule) { +- /* place nothing, only take space */ ++ /*tex Place nothing, only take space. */ + } else if (s == user_rule) { + if (callback_id != 0) { + pdf_goto_pagemode(pdf); +@@ -54,7 +50,7 @@ void pdf_place_rule(PDF pdf, halfword q, scaledpos size, int callback_id) + pdf_puts(pdf, "\nQ\n"); + } + } else { +- /* normal_rule or >= 100 being a leader rule */ ++ /*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; +@@ -64,7 +60,7 @@ void pdf_place_rule(PDF pdf, halfword q, scaledpos size, int callback_id) + 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 "); ++ pdf_puts(pdf, "[] 0 d 0 J "); + print_pdffloat(pdf, dim.v); + pdf_puts(pdf, " w 0 0 m "); + print_pdffloat(pdf, dim.h); +@@ -72,18 +68,32 @@ void pdf_place_rule(PDF pdf, halfword q, scaledpos size, int callback_id) + } 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 "); ++ 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); +- pdf_puts(pdf, " re f\n"); ++ if (s == outline_rule) { ++ pdf_puts(pdf, " re S\n"); ++ } else { ++ pdf_puts(pdf, " re f\n"); ++ } + } + pdf_puts(pdf, "Q\n"); + } +diff --git a/texk/web2c/luatexdir/pdf/pdfsaverestore.w b/texk/web2c/luatexdir/pdf/pdfsaverestore.c +similarity index 63% +rename from texk/web2c/luatexdir/pdf/pdfsaverestore.w +rename to texk/web2c/luatexdir/pdf/pdfsaverestore.c +index 0d91de544..abfb48142 100644 +--- a/texk/web2c/luatexdir/pdf/pdfsaverestore.w ++++ b/texk/web2c/luatexdir/pdf/pdfsaverestore.c +@@ -1,32 +1,30 @@ +-% pdfsaverestore.w +-% +-% Copyright 2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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 */ ++pos_entry *pos_stack = 0; ++int pos_stack_size = 0; ++int pos_stack_used = 0; + +-@ @c + static void checkpdfsave(scaledpos pos) + { + pos_entry *new_stack; +@@ -45,7 +43,6 @@ static void checkpdfsave(scaledpos pos) + pos_stack_used++; + } + +-@ @c + static void checkpdfrestore(scaledpos pos) + { + scaledpos diff; +@@ -64,7 +61,6 @@ static void checkpdfrestore(scaledpos pos) + } + } + +-@ @c + void pdf_out_save(PDF pdf, halfword p) + { + (void) p; +@@ -72,7 +68,6 @@ void pdf_out_save(PDF pdf, halfword p) + pdf_literal(pdf, 'q', set_origin, false); + } + +-@ @c + void pdf_out_restore(PDF pdf, halfword p) + { + (void) p; +diff --git a/texk/web2c/luatexdir/pdf/pdfsetmatrix.w b/texk/web2c/luatexdir/pdf/pdfsetmatrix.c +similarity index 65% +rename from texk/web2c/luatexdir/pdf/pdfsetmatrix.w +rename to texk/web2c/luatexdir/pdf/pdfsetmatrix.c +index b107033b9..f19c1721d 100644 +--- a/texk/web2c/luatexdir/pdf/pdfsetmatrix.w ++++ b/texk/web2c/luatexdir/pdf/pdfsetmatrix.c +@@ -1,34 +1,36 @@ +-% pdfsetmatrix.w +-% +-% Copyright 2009 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2009 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ stack for \.{\\pdfextension setmatrix} ++/*tex ++ ++ We keep a 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; +@@ -46,40 +48,40 @@ static void matrix_stack_room(void) + } + } + +-@ \.{\\pdfextension setmatrix{a b c d}} +- +-|e| := pos.h ++/*tex + +-|f| := pos.v ++ 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|. + +-|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: + +-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 ) + +-{\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 ) + +- ( 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 + +- ( 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]|. ++ /*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) { +@@ -87,7 +89,7 @@ static void pdfsetmatrix(const char *in, scaledpos pos) + formatted_warning("pdf backend","unrecognized format of setmatrix: %s", in); + return; + } +- /* calculate this transformation matrix */ ++ /*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(); +@@ -112,19 +114,21 @@ static void pdfsetmatrix(const char *in, scaledpos pos) + } + } + +-@ Apply matrix to point (x,y) ++/*tex + +-{\obeylines\obeyspaces\tt +- ( a b 0 ) +- ( x y 1 ) x ( c d 0 ) = ( xa+yc+e xb+yd+f 1 ) +- ( e f 1 ) +-} ++ 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. ++ If \.{\\pdfextension setmatrix} wasn't used, then return the value unchanged. ++ The return values for matrix tranform functions are: + +-@ Return values for matrix tranform functions: ++*/ + +-@c + static scaled ret_llx; + static scaled ret_lly; + static scaled ret_urx; +@@ -150,7 +154,6 @@ scaled getury(void) + return ret_ury; + } + +-@ @c + static int last_llx; + static int last_lly; + static int last_urx; +@@ -210,11 +213,10 @@ 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| */ ++ int old_setting; + str_number s; + old_setting = selector; + selector = new_string; +diff --git a/texk/web2c/luatexdir/pdf/pdfshipout.w b/texk/web2c/luatexdir/pdf/pdfshipout.c +similarity index 75% +rename from texk/web2c/luatexdir/pdf/pdfshipout.w +rename to texk/web2c/luatexdir/pdf/pdfshipout.c +index 8665f685e..084efc086 100644 +--- a/texk/web2c/luatexdir/pdf/pdfshipout.w ++++ b/texk/web2c/luatexdir/pdf/pdfshipout.c +@@ -1,51 +1,55 @@ +-% pdfshipout.w +-% +-% Copyright 2010-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2010-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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. ++/*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. ++ ++*/ + +-@c + void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + { +- int j, k; /* indices to first ten count registers */ ++ /*tex Indices to first ten count registers: */ ++ int j, k; + int post_callback_id; + int pre_callback_id; +- posstructure refpoint; /* the origin pos. on the page */ ++ /*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); +- fix_o_mode(); /* this is only for complaining if \.{\\outputmode} has changed */ +- init_backend_functionpointers(output_mode_used); ++ /*tex This is only for complaining if \.{\\outputmode} has changed: */ ++ fix_o_mode(); + pdf->f_cur = null_font; +- /* +- Start sheet {\sl Sync\TeX} information record; we assume that |pdf_output| is +- properly set up. ++ /*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) { +@@ -82,16 +86,18 @@ void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + } + } + if ((tracing_output_par > 0) && shipping_mode == SHIPPING_PAGE) { +- print_char(']'); ++ if (pre_callback_id == 0) { ++ print_char(']'); ++ } + update_terminal(); + begin_diagnostic(); + show_box(p); + end_diagnostic(true); + } +- /* Ship box |p| out */ ++ /*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 +@@ -116,7 +122,7 @@ void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + 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 */ ++ /*tex Calculate page dimensions and margins. */ + if (global_shipping_mode == SHIPPING_PAGE) { + if (page_width_par > 0) + pdf->page_size.h = page_width_par; +@@ -156,28 +162,14 @@ void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + 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). + */ +- 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"); +- } +- +- /* ++ 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 ++ |page_direction| within the upright (TLT) page coordinate system. + */ + switch (page_direction_par) { + case dir_TLT: +@@ -195,7 +187,7 @@ void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + 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. + */ +@@ -204,7 +196,7 @@ void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + cur.v = height(p); + synch_pos_with_cur(pdf->posstruct, &refpoint, cur); + } else { +- /* shipping a /Form */ ++ /*tex We're shipping out a |/Form|. */ + pdf->posstruct->dir = box_dir(p); + switch (pdf->posstruct->dir) { + case dir_TLT: +@@ -245,18 +237,12 @@ void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + 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"); +- } ++ /*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: +@@ -271,24 +257,15 @@ void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + 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"); +- } ++ /*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; +- /* Flush the box from memory, showing statistics if requested */ ++ /*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); +@@ -306,7 +283,7 @@ void ship_out(PDF pdf, halfword p, shipping_mode_e shipping_mode) + } + if (shipping_mode == SHIPPING_PAGE && (post_callback_id > 0)) + (void) run_callback(post_callback_id, "->"); +- /* Finish sheet {\sl Sync\TeX} information record */ ++ /*tex Finish sheet {\sl Sync\TeX} information record. */ + if (synctex_par) + synctexteehs(); + global_shipping_mode = NOT_SHIPPING; +diff --git a/texk/web2c/luatexdir/pdf/pdftables.w b/texk/web2c/luatexdir/pdf/pdftables.c +similarity index 79% +rename from texk/web2c/luatexdir/pdf/pdftables.w +rename to texk/web2c/luatexdir/pdf/pdftables.c +index 31fc2c9ad..180c23234 100644 +--- a/texk/web2c/luatexdir/pdf/pdftables.w ++++ b/texk/web2c/luatexdir/pdf/pdftables.c +@@ -1,27 +1,26 @@ +-% pdftables.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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", +@@ -31,21 +30,26 @@ const char *pdf_obj_typenames[PDF_OBJ_TYPE_MAX + 1] = { + 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 */ ++int pdf_retval; ++int pdf_cur_form; ++ ++/*tex ++ ++ AVL sort entry into |avl_table[]|. ++*/ + +-@ 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) ++ 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 */ ++ } else { ++ /*tex String type: */ + return strcmp(a->u.str0, b->u.str0); ++ } + } else if (a->u_type == union_type_int) { + return -1; + } else { +@@ -78,7 +82,8 @@ static void avl_put_int_obj(PDF pdf, int int0, int objptr, int t) + 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 */ ++ /*tex No |xstrdup| here! */ ++ oe->u.str0 = str0; + oe->u_type = union_type_cstring; + oe->objptr = objptr; + avl_put_obj(pdf, t, oe); +@@ -112,9 +117,8 @@ static int avl_find_str_obj(PDF pdf, int t, char *s) + return p->objptr; + } + +-@ Create an object with type |t| and identifier |i| ++/*tex Create an object with type |t| and identifier |i|: */ + +-@c + int pdf_create_obj(PDF pdf, int t, int i) + { + int a; +@@ -148,7 +152,6 @@ int pdf_create_obj(PDF pdf, int t, int i) + return pdf->obj_ptr; + } + +-@ @c + int find_obj(PDF pdf, int t, int i, boolean byname) + { + char *ss = NULL; +@@ -163,14 +166,17 @@ int find_obj(PDF pdf, int t, int i, boolean byname) + 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|. ++/*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|. ++ ++*/ + +-@c + int pdf_get_obj(PDF pdf, int t, int i, boolean byname) + { + int r; +@@ -197,8 +203,8 @@ int pdf_get_obj(PDF pdf, int t, int i, boolean byname) + return r; + } + +-@ object checking +-@c ++/*tex Some object checking: */ ++ + void check_obj_exists(PDF pdf, int objnum) + { + if (objnum < 0 || objnum > pdf->obj_ptr) +@@ -216,14 +222,15 @@ void check_obj_type(PDF pdf, int t, int objnum) + } + } + +-@ @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 */ ++ /*tex The positions relative to cur: */ ++ scaledpos ll, ur; + scaledpos pos_ll, pos_ur, tmp; + posstructure localpos; + localpos.dir = pdf->posstruct->dir; +- ll.h = 0; /* pdf contains current point on page */ ++ /*tex |pdf| contains current point on page: */ ++ ll.h = 0; + if (is_running(alt_rule.dp)) + ll.v = depth(parent_box) - cur.v; + else +@@ -263,7 +270,6 @@ void set_rect_dimens(PDF pdf, halfword p, halfword parent_box, scaledpos cur, sc + pdf_ann_top(p) = pos_ur.v + margin; + } + +-@ @c + void libpdffinish(PDF pdf) + { + strbuf_free(pdf->fb); +diff --git a/texk/web2c/luatexdir/pdf/pdfthread.w b/texk/web2c/luatexdir/pdf/pdfthread.c +similarity index 88% +rename from texk/web2c/luatexdir/pdf/pdfthread.w +rename to texk/web2c/luatexdir/pdf/pdfthread.c +index 6b3dd4b94..20cc7bf0a 100644 +--- a/texk/web2c/luatexdir/pdf/pdfthread.w ++++ b/texk/web2c/luatexdir/pdf/pdfthread.c +@@ -1,28 +1,32 @@ +-% pdfthread.w +-% +-% Copyright 2009-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++Copyright 2009-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ Threads are handled in similar way as link annotations +-@c ++/*tex ++ ++ Threads are handled in similar way as link annotations. ++ ++*/ ++ + void append_bead(PDF pdf, halfword p) + { + int a, b, c, t; +@@ -52,7 +56,6 @@ void append_bead(PDF pdf, halfword p) + 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; +@@ -78,7 +81,6 @@ void do_thread(PDF pdf, halfword p, halfword parent_box, scaledpos cur) + pdf->last_thread = p; + } + +-@ @c + void append_thread(PDF pdf, halfword parent_box, scaledpos cur) + { + scaled_whd alt_rule; +@@ -102,7 +104,6 @@ void append_thread(PDF pdf, halfword parent_box, scaledpos cur) + pdf->last_thread = p; + } + +-@ @c + void end_thread(PDF pdf, halfword p) + { + scaledpos pos = pdf->posstruct->pos; +@@ -131,8 +132,8 @@ void end_thread(PDF pdf, halfword p) + pdf->last_thread = null; + } + +-@ The following function are needed for outputing article thread. +-@c ++/*tex The following function are needed for outputing article thread. */ ++ + void thread_title(PDF pdf, int t) + { + pdf_add_name(pdf, "Title"); +@@ -224,7 +225,6 @@ void out_thread(PDF pdf, int t) + } while (a != b); + } + +-@ @c + void scan_thread_id(void) + { + if (scan_keyword("num")) { +@@ -251,7 +251,6 @@ void check_running_thread(PDF pdf, halfword this_box, scaledpos cur) + append_thread(pdf, this_box, cur); + } + +-@ @c + void print_bead_rectangles(PDF pdf) + { + halfword i; +@@ -262,13 +261,15 @@ void print_bead_rectangles(PDF pdf) + 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 */ ++ /*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); +- set_obj_bead_rect(pdf, k->info, l); /* rewrite |obj_bead_data| */ ++ /*tex Rewrite |obj_bead_data|: */ ++ set_obj_bead_rect(pdf, k->info, l); + k = k->link; + } + } +diff --git a/texk/web2c/luatexdir/pdf/pdfxform.w b/texk/web2c/luatexdir/pdf/pdfxform.c +similarity index 74% +rename from texk/web2c/luatexdir/pdf/pdfxform.w +rename to texk/web2c/luatexdir/pdf/pdfxform.c +index 731dc2290..7fd4eef1d 100644 +--- a/texk/web2c/luatexdir/pdf/pdfxform.w ++++ b/texk/web2c/luatexdir/pdf/pdfxform.c +@@ -1,28 +1,30 @@ +-% pdfxform.w +-% +-% Copyright 2009-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* ++ ++Copyright 2009-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + +-@ @c + #include "ptexlib.h" + #include "pdf/pdfpage.h" + +-@ @c +-int pdf_cur_form; /* the form being output */ ++/*tex The form being output: */ ++ ++int pdf_cur_form; + + void pdf_place_form(PDF pdf, halfword p) + { +@@ -35,7 +37,7 @@ void pdf_place_form(PDF pdf, halfword 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 No transform yet: */ + tex.wd = width(p); + tex.ht = height(p); + tex.dp = depth(p); +@@ -60,9 +62,8 @@ void pdf_place_form(PDF pdf, halfword p) + addto_page_resources(pdf, obj_type_xform, objnum); + } + +-/* we will store token lists as strings too */ ++/*tex We will store token lists as strings too. */ + +-@ @c + void scan_pdfxform(PDF pdf) + { + int k; +@@ -100,7 +101,8 @@ void scan_pdfxform(PDF pdf) + 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 */ ++ /*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)); +@@ -108,11 +110,10 @@ void scan_pdfxform(PDF pdf) + last_saved_box_index = k; + } + +-@ @c + void scan_pdfrefxform(PDF pdf) + { + scaled_whd alt_rule, dim, nat; +- alt_rule = scan_alt_rule(); /* scans || to |alt_rule| */ ++ alt_rule = scan_alt_rule(); + scan_int(); + check_obj_type(pdf, obj_type_xform, cur_val); + tail_append(new_rule(box_rule)); +diff --git a/texk/web2c/luatexdir/tex/align.c b/texk/web2c/luatexdir/tex/align.c +new file mode 100644 +index 000000000..b2384cc62 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/align.c +@@ -0,0 +1,1297 @@ ++/* ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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 \ token list: */ ++ ++#define u_part(A) vlink((A)+depth_offset) ++ ++/*tex The pointer to \ 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 \, 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 \, 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 \ 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 \ 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 \ 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 \ 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 k1$. 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) { ++ 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; ++} +diff --git a/texk/web2c/luatexdir/tex/align.w b/texk/web2c/luatexdir/tex/align.w +deleted file mode 100644 +index dd0ed1033..000000000 +--- a/texk/web2c/luatexdir/tex/align.w ++++ /dev/null +@@ -1,1144 +0,0 @@ +-% align.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-\def\<#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 \ token list */ +-#define v_part(A) vinfo((A)+depth_offset) /* pointer to \ 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 \, 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 \, 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 \ +-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 \ 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 \ 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 \ 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 k1$. +-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) { +- 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; +-} +diff --git a/texk/web2c/luatexdir/tex/arithmetic.c b/texk/web2c/luatexdir/tex/arithmetic.c +new file mode 100644 +index 000000000..2782c2534 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/arithmetic.c +@@ -0,0 +1,815 @@ ++/* ++ ++arithmetic.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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 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= 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=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)); ++} +diff --git a/texk/web2c/luatexdir/tex/arithmetic.w b/texk/web2c/luatexdir/tex/arithmetic.w +deleted file mode 100644 +index 56fb9568a..000000000 +--- a/texk/web2c/luatexdir/tex/arithmetic.w ++++ /dev/null +@@ -1,735 +0,0 @@ +-% arithmetic.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-\def\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 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= 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=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)); +-} +diff --git a/texk/web2c/luatexdir/tex/backend.c b/texk/web2c/luatexdir/tex/backend.c +new file mode 100644 +index 000000000..d8c7daed2 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/backend.c +@@ -0,0 +1,123 @@ ++/* 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); ++} +diff --git a/texk/web2c/luatexdir/tex/backend.h b/texk/web2c/luatexdir/tex/backend.h +new file mode 100644 +index 000000000..5cfbd06fb +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/backend.h +@@ -0,0 +1,54 @@ ++/* 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 +diff --git a/texk/web2c/luatexdir/tex/buildpage.c b/texk/web2c/luatexdir/tex/buildpage.c +new file mode 100644 +index 000000000..81e69dc3b +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/buildpage.c +@@ -0,0 +1,1164 @@ ++/* ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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(); ++} +diff --git a/texk/web2c/luatexdir/tex/buildpage.w b/texk/web2c/luatexdir/tex/buildpage.w +deleted file mode 100644 +index cba07cbf6..000000000 +--- a/texk/web2c/luatexdir/tex/buildpage.w ++++ /dev/null +@@ -1,1013 +0,0 @@ +-% buildpage.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +-#include "ptexlib.h" +- +-@ @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(); +-} +diff --git a/texk/web2c/luatexdir/tex/commands.w b/texk/web2c/luatexdir/tex/commands.c +similarity index 92% +rename from texk/web2c/luatexdir/tex/commands.w +rename to texk/web2c/luatexdir/tex/commands.c +index 38975eb16..aceb0eb09 100644 +--- a/texk/web2c/luatexdir/tex/commands.w ++++ b/texk/web2c/luatexdir/tex/commands.c +@@ -1,34 +1,36 @@ +-% commands.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-\def\eTeX{e-\TeX} +- +-@ @c ++/* + ++commands.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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. ++/*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. ++ ++*/ + +-@c + void initialize_commands(void) + { + +@@ -53,6 +55,9 @@ void initialize_commands(void) + 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); +@@ -63,7 +68,7 @@ void initialize_commands(void) + 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 */ ++ /*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); +@@ -166,11 +171,18 @@ void initialize_commands(void) + 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: + +- /* 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); +@@ -240,29 +252,27 @@ void initialize_commands(void) + 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("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_tex("par", par_end_cmd, too_big_char, too_big_char); /* cf.\ |scan_file_name| */ ++ 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; +- @; +- @; +- @; +-} +- + +-@ These are in a separate module due to a CWEAVE limitation. +- +-@= +- +- /* ++ /*tex + The processing of \.{\\input} involves the |start_input| subroutine, + which will be declared later; the processing of \.{\\endinput} is trivial. + */ +@@ -318,7 +328,7 @@ void initialize_commands(void) + 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_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); +@@ -331,9 +341,12 @@ void initialize_commands(void) + 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); +@@ -352,15 +365,15 @@ void initialize_commands(void) + 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("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]; +@@ -435,12 +448,13 @@ void initialize_commands(void) + 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("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); +@@ -514,9 +528,10 @@ void initialize_commands(void) + 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("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); +@@ -539,9 +554,6 @@ void initialize_commands(void) + primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0); + primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0); + +-@ These are in a separate module due to a CWEAVE limitation. +- +-@= + primitive_luatex("Umathoperatorsize", set_math_param_cmd, math_param_operator_size, 0); + primitive_luatex("Umathoverbarkern", set_math_param_cmd, math_param_overbar_kern, 0); + primitive_luatex("Umathoverbarrule", set_math_param_cmd, math_param_overbar_rule, 0); +@@ -654,9 +666,6 @@ void initialize_commands(void) + primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, math_param_inner_punct_spacing, 0); + primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, math_param_inner_inner_spacing, 0); + +-@ These are in a separate module due to a CWEAVE limitation. +- +-@= + primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base, math_code_base); + primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base, del_code_base); + primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 1, math_code_base); +@@ -695,6 +704,7 @@ void initialize_commands(void) + 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]; +@@ -720,6 +730,7 @@ void initialize_commands(void) + 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); +@@ -729,6 +740,15 @@ void initialize_commands(void) + 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. +@@ -737,8 +757,8 @@ void initialize_commands(void) + 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); +@@ -746,8 +766,10 @@ void initialize_etex_commands(void) + 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. ++ /*tex ++ ++ First we implement the additional \eTeX\ parameters in the table of ++ equivalents. + */ + + primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base); +@@ -795,39 +817,48 @@ void initialize_etex_commands(void) + + primitive_etex("showgroups", xray_cmd, show_groups, 0); + +- /* ++ /*tex ++ + 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. ++ /*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); +@@ -843,7 +874,8 @@ void initialize_etex_commands(void) + 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} +@@ -852,8 +884,10 @@ void initialize_etex_commands(void) + + 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); +@@ -869,21 +903,25 @@ void initialize_etex_commands(void) + 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 ++ /*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 ++ |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); +diff --git a/texk/web2c/luatexdir/tex/conditional.w b/texk/web2c/luatexdir/tex/conditional.c +similarity index 61% +rename from texk/web2c/luatexdir/tex/conditional.w +rename to texk/web2c/luatexdir/tex/conditional.c +index 318775141..948db5803 100644 +--- a/texk/web2c/luatexdir/tex/conditional.w ++++ b/texk/web2c/luatexdir/tex/conditional.c +@@ -1,67 +1,73 @@ +-% conditional.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++conditional.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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|. +- +-@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 ++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; /* level of $\.{\\if}\ldots\.{\\fi}$ nesting */ +- int save_scanner_status = scanner_status; /* |scanner_status| upon entry */ ++ int l = 0; ++ int save_scanner_status = scanner_status; + scanner_status = skipping; + skip_line = line; + while (1) { +@@ -80,13 +86,16 @@ void pass_text(void) + 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'. ++/*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'. ++ ++*/ + +-@c + void push_condition_stack(void) + { + halfword p = new_node(if_node, 0); +@@ -103,9 +112,10 @@ void push_condition_stack(void) + void pop_condition_stack(void) + { + halfword p; +- if (if_stack[in_open] == cond_ptr) ++ if (if_stack[in_open] == cond_ptr) { ++ /*tex Conditionals are possibly not properly nested with files. */ + if_warning(); +- /* conditionals possibly not properly nested with files */ ++ } + p = cond_ptr; + if_line = if_line_field(p); + cur_if = if_limit_subtype(p); +@@ -114,14 +124,16 @@ void pop_condition_stack(void) + flush_node(p); + } + +-@ Here's a procedure that changes the |if_limit| code corresponding to +-a given value of |cond_ptr|. ++/*tex ++ ++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; +@@ -137,22 +149,29 @@ void change_if_limit(int l, halfword p) + } + } + +-@ 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). ++/*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). ++ ++*/ + +-@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 */ ++ /*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(); +- halfword p = n; /*head of the list of characters */ +-is_in_csname += 1; ++ /*tex Head of the list of characters: */ ++ halfword p = n; ++ is_in_csname += 1; + while (1) { + get_x_token(); + if (cur_cs != 0) +@@ -166,13 +185,13 @@ is_in_csname += 1; + get_x_token(); + } while (cur_cmd != end_cs_name_cmd); + flush_list(n); +-is_in_csname -= 1; ++ 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| */ ++ /*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) { +@@ -200,21 +219,26 @@ is_in_csname -= 1; + p = token_link(p); + } + if (m > first) { +- cur_cs = id_lookup(first, m - first); /* |no_new_control_sequence| is |true| */ ++ /*tex |no_new_control_sequence| is |true| */ ++ cur_cs = id_lookup(first, m - first); + } else if (m == first) { +- cur_cs = null_cs; /* the list is empty */ ++ /*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; ++ is_in_csname -= 1; + return b; + } + +-@ An active character will be treated as category 13 following +-\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. ++/*tex ++ ++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) { \ +@@ -225,36 +249,47 @@ is_in_csname -= 1; + } \ + } 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@> ++/*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@> ++ ++*/ + +-@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)) ++ /*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; +- /* Either process \.{\\ifcase} or set |b| to the value of a boolean condition */ ++ /*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: +- /* Test if two characters match */ ++ /*tex Test if two characters match. */ + get_x_token_or_active_char(); + if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) { +- /*not a character */ ++ /*tex It's not a character. */ + m = relax_cmd; + n = too_big_char; + } else { +@@ -275,8 +310,10 @@ void conditional(void) + 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. */ ++ /*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 +@@ -284,15 +321,10 @@ void conditional(void) + 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... */ ++ /*tex 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 "); +@@ -318,13 +350,13 @@ void conditional(void) + b = (n > cur_val); + break; + default: +- /* can't happen */ ++ /*tex This can't happen. */ + b = false; + break; + } + break; + case if_odd_code: +- /* Test if an integer is odd */ ++ /*tex Test if an integer is odd. */ + scan_int(); + b = odd(cur_val); + break; +@@ -340,22 +372,6 @@ void conditional(void) + 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); +@@ -371,16 +387,15 @@ void conditional(void) + 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. ++ 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. ++ 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; +@@ -394,11 +409,10 @@ void conditional(void) + } 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: ++ /*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\}& +@@ -407,7 +421,7 @@ void conditional(void) + {}\\def\\d\{\}\cr}}$$ + */ + p = token_link(cur_chr); +- /*omit reference counts */ ++ /*tex Omit reference counts. */ + q = token_link(equiv(n)); + if (p == q) { + b = true; +@@ -437,9 +451,9 @@ void conditional(void) + b = false; + break; + case if_case_code: +- /* Select the appropriate case and |return| or |goto common_ending| */ ++ /*tex Select the appropriate case and |return| or |goto common_ending|. */ + scan_int(); +- /* |n| is the number of cases to pass */ ++ /*tex |n| is the number of cases to pass. */ + n = cur_val; + if (tracing_commands_par > 1) { + begin_diagnostic(); +@@ -460,7 +474,7 @@ void conditional(void) + } + } + change_if_limit(or_code, save_cond_ptr); +- /*wait for \.{\\or}, \.{\\else}, or \.{\\fi} */ ++ /*tex Wait for \.{\\or}, \.{\\else}, or \.{\\fi}. */ + return; + break; + case if_primitive_code: +@@ -475,10 +489,11 @@ void conditional(void) + (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. ++ /*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; +@@ -493,9 +508,9 @@ void conditional(void) + b = is_in_csname; + break; + case if_font_char_code: +- /* +- The conditional \.{\\iffontchar} tests the existence of a character in +- a font. ++ /*tex ++ The conditional \.{\\iffontchar} tests the existence of a ++ character in a font. + */ + scan_font_ident(); + n = cur_val; +@@ -503,13 +518,13 @@ void conditional(void) + b = char_exists(n, cur_val); + break; + default: +- /* there are no other cases, but for -Wall: */ ++ /*tex there are no other cases, but we need to please |-Wall|. */ + b = false; + } + if (is_unless) + b = !b; + if (tracing_commands_par > 1) { +- /* Display the value of |b| */ ++ /*tex Display the value of |b|. */ + begin_diagnostic(); + if (b) + tprint("{true}"); +@@ -519,16 +534,15 @@ void conditional(void) + } + if (b) { + change_if_limit(else_code, save_cond_ptr); +- /*wait for \.{\\else} or \.{\\fi} */ ++ /*tex 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 ++ /*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. ++ not the \.{\\else} we're looking for. Hence the following curious logic ++ is needed. + */ + while (1) { + pass_text(); +@@ -536,7 +550,9 @@ void conditional(void) + if (cur_chr != or_code) + goto COMMON_ENDING; + print_err("Extra \\or"); +- help1("I'm ignoring this; it doesn't match any \\if."); ++ help1( ++ "I'm ignoring this; it doesn't match any \\if." ++ ); + error(); + } else if (cur_chr == fi_code) { + pop_condition_stack(); +@@ -546,7 +562,7 @@ void conditional(void) + if (cur_chr == fi_code) { + pop_condition_stack(); + } else { +- /*wait for \.{\\fi} */ ++ /*tex Wait for \.{\\fi}. */ + if_limit = fi_code; + } + } +diff --git a/texk/web2c/luatexdir/tex/directions.w b/texk/web2c/luatexdir/tex/directions.c +similarity index 61% +rename from texk/web2c/luatexdir/tex/directions.w +rename to texk/web2c/luatexdir/tex/directions.c +index c844e934c..59b2f7dcd 100644 +--- a/texk/web2c/luatexdir/tex/directions.w ++++ b/texk/web2c/luatexdir/tex/directions.c +@@ -1,27 +1,28 @@ +-% directions.w +-% +-% Copyright 2009-2014 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++directions.w ++ ++Copyright 2009-2014 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ @c + void scan_direction(void) + { + int save_cur_cmd = cur_cmd; +@@ -53,9 +54,12 @@ void scan_direction(void) + cur_chr = save_cur_chr; + } + +-@ the next two are used by postlinebreak.c ++/*tex ++ ++ The next two are used by |postlinebreak.c|: ++ ++*/ + +-@c + halfword do_push_dir_node(halfword p, halfword a) + { + halfword n = copy_node(a); +@@ -70,18 +74,14 @@ halfword do_pop_dir_node(halfword 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) + { ++ /*tex There is no need to do anything here at the moment. */ + } + +-@ @c + halfword new_dir(int s) + { + halfword p = new_node(dir_node, 0); +@@ -90,55 +90,32 @@ halfword new_dir(int s) + 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","???", "???", "???", "???", "???", "???", "???", +- "???", "???", "???", "???", "???", "???", "???", "???", +- "???", "???", "???", "???", "???", "???", "???", "???", +- "???", "???", "???", "???", "???", "???", "???", "???", +- "???", "???", "???", "???", "???", "???", "???", "???" ++const char *dir_strings_par[4] = { [0] = ++ "TLT","TRT","LTL","RTT", + }; + +-int dir_swap = 64; +-*/ +- +-const char *dir_strings[8] = { +- "-TLT","-TRT","-LTL","-RTT", ++const char *dir_strings_text_normal[4] = { [0] = + "+TLT","+TRT","+LTL","+RTT", + }; + +-int dir_swap = 4; ++const char *dir_strings_text_cancel[4] = { [0] = ++ "-TLT","-TRT","-LTL","-RTT", ++}; + +-const char *string_dir(int d) ++void print_dir_par(int d) + { +- return (dir_strings[d+dir_swap]+1); ++ tprint(dir_strings_par[d]); + } + +-@ @c +-void print_dir(int d) ++void print_dir_text(halfword d) + { +- tprint(string_dir(d)); ++ if (subtype(d) == cancel_dir) { ++ tprint(dir_strings_text_cancel[dir_dir(d)]); ++ } else { ++ tprint(dir_strings_text_normal[dir_dir(d)]); ++ } + } + +-@ @c + scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph) + { + scaled wd = 0; +@@ -153,9 +130,7 @@ scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph) + } else { + wd = glyph_depth(p) + glyph_height(p); + } +-/* experimental */ +-wd += x_advance(p); +- } else { /* hlist, vlist, image, form, rule */ ++ } else { + if (textdir_parallel(pdir, curdir)) + wd = width(p); + else +@@ -164,7 +139,6 @@ wd += x_advance(p); + return wd; + } + +-@ @c + scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph) + { + scaled_whd whd = { 0, 0, 0 }; +@@ -208,7 +182,6 @@ scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isg + return whd; + } + +-@ @c + void update_text_dir_ptr(int val) + { + if (dir_level(text_dir_ptr) == cur_level) { +diff --git a/texk/web2c/luatexdir/tex/dumpdata.w b/texk/web2c/luatexdir/tex/dumpdata.c +similarity index 56% +rename from texk/web2c/luatexdir/tex/dumpdata.w +rename to texk/web2c/luatexdir/tex/dumpdata.c +index 9d06f1c8e..0091685fb 100644 +--- a/texk/web2c/luatexdir/tex/dumpdata.w ++++ b/texk/web2c/luatexdir/tex/dumpdata.c +@@ -1,84 +1,97 @@ +-% dumpdata.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++dumpdata.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-/* we start with 907: the sum of the values of the bytes of "don knuth" */ ++/*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+37) ++*/ ++ ++#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. ++ ++*/ + +-@ 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 */ ++str_number format_name; + + +-@ Format files consist of |memory_word| items, and we use the following +-macros to dump words of different types: ++/*tex + +-@c +-FILE *fmt_file; /* for input or output of format information */ ++Format files consist of |memory_word| items, and we use the following macros to ++dump words of different types: ++ ++*/ ++ ++FILE *fmt_file; + +-@ @c + void store_fmt_file(void) + { +- int j, k, l; /* all-purpose indices */ +- halfword p; /* all-purpose pointer */ +- int x; /* something to dump */ ++ int j, k, l, x; ++ halfword p; + char *format_engine; +- int callback_id; /* |pre_dump| callback */ ++ int callback_id; + 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. */ ++ /*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(); + } +- +- /* Create the |format_ident|, open the format file, and inform the user +- that dumping has begun */ ++ /*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, "->"); +@@ -97,11 +110,11 @@ void store_fmt_file(void) + format_ident = make_string(); + print(job_name); + format_name = make_string(); +- if (interaction == batch_mode) ++ if (interaction == batch_mode) { + selector = log_only; +- else ++ } 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); +@@ -111,15 +124,16 @@ void store_fmt_file(void) + 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" */ ++ /*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); +- +- /* Align engine to 4 bytes with one or more trailing NUL */ ++ /*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); +@@ -129,28 +143,26 @@ void store_fmt_file(void) + dump_int(x); + dump_things(format_engine[0], x); + xfree(format_engine); +- dump_int(0x57325458); /* TODO HM, what checksum would make sense? */ ++ dump_int(0x57325458); + dump_int(max_halfword); + dump_int(hash_high); + dump_int(eqtb_size); + dump_int(hash_prime); +- +- /* Dump the string pool */ ++ /*tex 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. +- */ ++ /*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); +@@ -180,14 +192,13 @@ void store_fmt_file(void) + 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)$. +- */ ++ /*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; +@@ -198,7 +209,8 @@ void store_fmt_file(void) + incr(j); + } + l = int_base; +- goto DONE1; /* |j=int_base-1| */ ++ /*tex |j=int_base-1| */ ++ goto DONE1; + FOUND1: + incr(j); + l = j; +@@ -214,8 +226,7 @@ void store_fmt_file(void) + k = j + 1; + dump_int(k - l); + } while (k != int_base); +- +- /* Dump regions 5 and 6 of |eqtb| */ ++ /*tex Dump regions 5 and 6 of |eqtb|. */ + do { + j = k; + while (j < eqtb_size) { +@@ -224,7 +235,8 @@ void store_fmt_file(void) + incr(j); + } + l = eqtb_size + 1; +- goto DONE2; /* |j=eqtb_size| */ ++ /*tex |j=eqtb_size| */ ++ goto DONE2; + FOUND2: + incr(j); + l = j; +@@ -239,18 +251,20 @@ void store_fmt_file(void) + 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 */ +- ++ 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(); +- /* 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. ++ /*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); +@@ -262,19 +276,18 @@ void store_fmt_file(void) + incr(cs_count); + } + } +- dump_things(hash[hash_used + 1], +- undefined_control_sequence - 1 - hash_used); +- if (hash_high > 0) ++ 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 */ ++ /*tex 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| */ ++ /*tex Dump the array info for internal font number |k|. */ + dump_font(k); + tprint_nl("\\font"); + print_esc(font_id_text(k)); +@@ -293,73 +306,78 @@ void store_fmt_file(void) + if (max_font_id() != 1) + print_char('s'); + dump_math_data(); +- +- /* Dump the hyphenation tables */ ++ /*tex Dump the hyphenation tables. */ + dump_language_data(); +- +- /* Dump a couple more things and the closing check word */ ++ /*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); +- /* We have already printed a lot of statistics, so we set |tracing_stats:=0| +- to prevent them from appearing again. */ ++ /*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; +- +- /* Dump the lua bytecodes */ ++ /*tex Dump the \LUA\ bytecodes. */ + dump_luac_registers(); +- +- /* Close the format file */ ++ /*tex 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 ++/*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; /* all-purpose indices */ +- halfword p; /* all-purpose pointer */ +- int x; /* something undumped */ ++ int j, k, x; ++ halfword p; + char *format_engine; +- /* Undump constants for consistency check */ ++ /*tex Undump constants for consistency check .*/ + if (ini_version) { + libcfree(hash); + libcfree(eqtb); +@@ -368,22 +386,25 @@ boolean load_fmt_file(const char *fmtname) + } + undump_int(x); + format_debug("format magic number", x); +- if (x != 0x57325458) +- goto BAD_FMT; /* not a format file */ +- ++ 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) +- goto BAD_FMT; /* FORMAT_ID mismatch */ +- ++ 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)) +- goto BAD_FMT; /* corrupted format file */ +- ++ 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; /* force string termination, just in case */ ++ format_engine[x - 1] = 0; + if (strcmp(engine_name, format_engine)) { + wake_up_terminal(); + wterm_cr(); +@@ -394,7 +415,7 @@ boolean load_fmt_file(const char *fmtname) + xfree(format_engine); + undump_int(x); + format_debug("string pool checksum", x); +- if (x != 0x57325458) { /* todo: @@\$ *//* check that strings are the same */ ++ if (x != 0x57325458) { + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "---! %s was written by a different version", +@@ -403,7 +424,7 @@ boolean load_fmt_file(const char *fmtname) + } + undump_int(x); + if (x != max_halfword) +- goto BAD_FMT; /* check |max_halfword| */ ++ goto BAD_FMT; + undump_int(hash_high); + if ((hash_high < 0) || (hash_high > sup_hash_extra)) + goto BAD_FMT; +@@ -428,10 +449,9 @@ boolean load_fmt_file(const char *fmtname) + undump_int(x); + if (x != hash_prime) + goto BAD_FMT; +- +- /* Undump the string pool */ ++ /*tex Undump the string pool */ + str_ptr = undump_string_pool(); +- /* Undump the dynamic memory */ ++ /*tex Undump the dynamic memory */ + undump_node_mem(); + undump_int(temp_token_head); + undump_int(hold_token_head); +@@ -447,9 +467,7 @@ boolean load_fmt_file(const char *fmtname) + 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| */ ++ /*tex Undump regions 1 to 6 of the table of equivalents |eqtb|. */ + k = null_cs; + do { + undump_int(x); +@@ -464,15 +482,16 @@ boolean load_fmt_file(const char *fmtname) + eqtb[j] = eqtb[k - 1]; + k = k + x; + } while (k <= eqtb_size); +- if (hash_high > 0) /* undump |hash_extra| part */ ++ 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(); +- /* Undump the hash table */ ++ /*tex Undump the hash table */ + undump_primitives(); + undump(hash_base, frozen_control_sequence, hash_used); + p = hash_base - 1; +@@ -480,8 +499,7 @@ boolean load_fmt_file(const char *fmtname) + 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); ++ 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) { +@@ -490,20 +508,17 @@ boolean load_fmt_file(const char *fmtname) + print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1)); + } + undump_int(cs_count); +- +- /* Undump the font information */ ++ /*tex 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| */ ++ /*tex Undump the array info for internal font number |k| */ + undump_font(k); + } + undump_math_data(); +- +- /* Undump the hyphenation tables */ ++ /*tex Undump the hyphenation tables */ + undump_language_data(); +- +- /* Undump a couple more things and the closing check word */ ++ /*tex Undump a couple more things and the closing check word */ + undump(batch_mode, error_stop_mode, interaction); + if (interactionoption != unspecified_mode) + interaction = interactionoption; +@@ -512,12 +527,10 @@ boolean load_fmt_file(const char *fmtname) + undump_int(x); + if (x != 69069) + goto BAD_FMT; +- +- /* Undump the lua bytecodes */ ++ /*tex Undump the lua bytecodes. */ + undump_luac_registers(); +- + prev_depth_par = ignore_depth; +- return true; /* it worked! */ ++ return true; + BAD_FMT: + wake_up_terminal(); + wterm_cr(); +diff --git a/texk/web2c/luatexdir/tex/equivalents.w b/texk/web2c/luatexdir/tex/equivalents.c +similarity index 57% +rename from texk/web2c/luatexdir/tex/equivalents.w +rename to texk/web2c/luatexdir/tex/equivalents.c +index 271fc67ff..3d260d5c5 100644 +--- a/texk/web2c/luatexdir/tex/equivalents.w ++++ b/texk/web2c/luatexdir/tex/equivalents.c +@@ -1,29 +1,31 @@ +-% equivalents.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++equivalents.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + + halfword last_cs_name = null_cs; + +-/* |eqtb[p]| has just been restored or retained */ ++/*tex |eqtb[p]| has just been restored or retained. */ + + static void diagnostic_trace(halfword p, const char *s) + { +@@ -36,60 +38,54 @@ static void diagnostic_trace(halfword p, const char *s) + end_diagnostic(false); + } + +-@ @c +-void show_eqtb_meaning(halfword n); /* forward */ ++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. + +-@ 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|: + +-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. + +-\yskip\hang 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. + +-\yskip\hang 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. + +-\yskip\hang 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. + +-\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. ++5) |eqtb[int_base..(dimen_base-1)]| holds the current equivalents of fullword ++integer parameters like the current hyphenation penalty. + +-\yskip\hang 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. + +-\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. ++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. + +-\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|. + +-@ 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| */ ++halfword eqtb_top; + quarterword xeq_level[(eqtb_size + 1)]; + +-@ @c + void initialize_equivalents(void) + { + int k; +@@ -97,48 +93,45 @@ void initialize_equivalents(void) + 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 |save_level(p)| should replace the corresponding ++entry in |xeq_level| (if |save_value(p)max_save_stack) { \ + max_save_stack=save_ptr; \ +@@ -188,30 +183,32 @@ get by with testing for overflow in only a few places. + } \ + } 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|. ++/*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. ++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 */ ++{ /*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) ++ 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| */ ++ } ++ /*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) +@@ -220,7 +217,6 @@ void new_save_level(group_code c) + incr(save_ptr); + } + +-@ @c + static const char *save_stack_type(int v) + { + const char *s = ""; +@@ -243,13 +239,13 @@ static const char *save_stack_type(int v) + 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; + } + +-@ @c + void print_save_stack(void) + { + int i; +@@ -274,7 +270,7 @@ void print_save_stack(void) + 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_char(','); + print_int(equiv_field(save_word(i - 1))); + } + i--; +@@ -303,17 +299,21 @@ void print_save_stack(void) + break; + case saved_adjust: + tprint(", "); +- print_int(save_level(i)); /* vadjust vs vadjust pre */ ++ /*tex vadjust vs vadjust pre */ ++ print_int(save_level(i)); + break; + case saved_insert: + tprint(", "); +- print_int(save_value(i)); /* insert number */ ++ /*tex insert number */ ++ print_int(save_value(i)); + break; +- case saved_boxtype: /* \.{\\localleftbox} vs \.{\\localrightbox} */ ++ case saved_boxtype: ++ /*tex \.{\\localleftbox} vs \.{\\localrightbox} */ + tprint(", "); + print_int(save_value(i)); + break; +- case saved_eqno: /* \.{\\eqno} vs \.{\\leqno} */ ++ case saved_eqno: ++ /*tex \.{\\eqno} vs \.{\\leqno} */ + tprint(", "); + print_int(save_value(i)); + break; +@@ -339,10 +339,11 @@ void print_save_stack(void) + case saved_textdir: + case saved_boxdir: + tprint(", "); +- print_dir(dir_dir(save_value(i))); ++ print_dir_text(save_value(i)); + break; + case saved_boxattr: + case saved_boxpack: ++ case saved_attrlist: + tprint(", "); + print_int(save_value(i)); + break; +@@ -357,27 +358,28 @@ void print_save_stack(void) + end_diagnostic(true); + } + +-@ The \.{\\showgroups} command displays all currently active grouping +- levels. ++/*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@> + +-@ 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. + +- 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 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; +@@ -447,16 +449,21 @@ void show_save_groups(void) + break; + case disc_group: + tprint_esc("discretionary"); +- for (i = 1; i < 3; i++) +- if (i <= saved_value(-2)) ++ 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| */ ++ 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: +@@ -500,7 +507,7 @@ void show_save_groups(void) + confusion("showgroups"); + break; + } +- /* Show the box context */ ++ /*tex Show the box context */ + i = saved_value(-5); + if (i != 0) { + if (i < box_flag) { +@@ -528,9 +535,8 @@ void show_save_groups(void) + } + FOUND1: + tprint_esc(s); +- /* Show the box packaging info */ ++ /*tex Show the box packaging info. The offsets may vary. */ + { +- /* offsets may vary */ + int ii = -1; + while (saved_type(ii) != saved_boxspec) + ii--; +@@ -558,18 +564,20 @@ void show_save_groups(void) + 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@> ++/*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@> + +-@c +-/* we don't need to destroy when an assignment has the same node */ ++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| */ ++{ ++ halfword q; + switch (eq_type_field(w)) { + case call_cmd: + case long_call_cmd: +@@ -581,10 +589,16 @@ void eq_destroy(memory_word w) + flush_node(equiv_field(w)); + break; + case shape_ref_cmd: +- q = equiv_field(w); /* we need to free a \.{\\parshape} block */ +- if (q != null) ++ 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; /* such a block is |2n+1| words long, where |n=vinfo(q)| */ ++ } ++ break; + case box_ref_cmd: + flush_node_list(equiv_field(w)); + break; +@@ -593,12 +607,14 @@ void eq_destroy(memory_word w) + } + } + +-@ To save a value of |eqtb[p]| that was established at level |l|, we +-can use the following subroutine. ++/*tex + +-@c ++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) +-{ /* saves |eqtb[p]| */ ++{ + check_full_save_stack(); + if (l == level_zero) { + save_type(save_ptr) = restore_zero; +@@ -613,16 +629,17 @@ void eq_save(halfword p, quarterword l) + 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. ++/*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. ++ ++*/ + +-@ new data for |eqtb| +-@c + void eq_define(halfword p, quarterword t, halfword e) + { + boolean trace = tracing_assigns_par > 0; +@@ -645,11 +662,14 @@ void eq_define(halfword p, quarterword t, halfword e) + 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. ++/*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. ++ ++*/ + +-@c + void eq_word_define(halfword p, int w) + { + boolean trace = tracing_assigns_par > 0; +@@ -669,15 +689,17 @@ void eq_word_define(halfword p, int w) + 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|. ++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"); +@@ -690,7 +712,7 @@ void geq_define(halfword p, quarterword t, halfword e) + } + + 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"); +@@ -700,9 +722,12 @@ void geq_word_define(halfword p, int w) + diagnostic_trace(p, "into"); + } + +-@ Subroutine |save_for_after| puts a token on the stack for save-keeping. ++/*tex ++ ++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) { +@@ -714,16 +739,20 @@ void save_for_after(halfword t) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@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} ? */ ++{ ++ 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); +@@ -731,7 +760,7 @@ void unsave(void) + if (cur_level > level_one) { + boolean trace = tracing_restores_par > 0; + decr(cur_level); +- /* Clear off top level from |save_stack| */ ++ /*tex Clear off top level from |save_stack|. */ + while (true) { + decr(save_ptr); + if (save_type(save_ptr) == level_boundary) +@@ -739,7 +768,8 @@ void unsave(void) + p = save_value(save_ptr); + if (save_type(save_ptr) == insert_token) { + reinsert_token(a, p); +- a = true; /* always ... always etex now */ ++ /*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); +@@ -747,21 +777,25 @@ void unsave(void) + } 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. +- */ ++ /*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) { +- eq_destroy(save_word(save_ptr)); /* destroy the saved value */ ++ /*tex Destroy the saved value: */ ++ eq_destroy(save_word(save_ptr)); + 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 */ ++ /*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"); + } +@@ -776,32 +810,40 @@ void unsave(void) + } + } + } +- if (tracing_groups_par > 0) ++ ++ if (tracing_groups_par > 0) { + group_trace(true); +- if (grp_stack[in_open] == cur_boundary) +- group_warning(); /* groups possibly not properly nested with files */ ++ } ++ 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 { +- confusion("curlevel"); /* |unsave| is not used when |cur_group=bottom_level| */ ++ /*tex |unsave| is not used when |cur_group=bottom_level| */ ++ confusion("curlevel"); + } + 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. ++/*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. + +-@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. If nonzero, this magnification should be used henceforth. We might ++drop magnifaction at some point. + +-@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| +-for magnification. ++*/ ++ ++int mag_set = 0; + +-@c + void prepare_mag(void) + { + if ((mag_set > 0) && (mag_par != mag_set)) { +@@ -809,14 +851,18 @@ void prepare_mag(void) + 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."); ++ 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."); ++ help1( ++ "The magnification ratio must be between 1 and 32768." ++ ); + int_error(mag_par); + geq_word_define(int_base + mag_code, 1000); + } +@@ -829,65 +875,74 @@ void prepare_mag(void) + 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 ++/*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; /* level of \.{\\if...\\fi} nesting */ +- int l; /* line where \.{\\if} started */ ++ int n, l; + halfword p; + begin_diagnostic(); + tprint_nl("{"); +@@ -926,23 +981,26 @@ void show_cur_cmd_chr(void) + end_diagnostic(false); + } + +-@ Here is a procedure that displays the contents of |eqtb[n]| symbolically. ++/*tex ++ ++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('?'); ++ /*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))) { +- /* +- 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.) ++ /*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)); +@@ -951,10 +1009,9 @@ void show_eqtb(halfword n) + 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}'. ++ /*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) +@@ -979,18 +1036,17 @@ void show_eqtb(halfword n) + } + + } 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. ++ /*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) +@@ -1032,13 +1088,16 @@ void show_eqtb(halfword n) + show_node_list(equiv(n)); + } + } else if (n == cur_font_loc) { +- /* Show the font identifier in |eqtb[n]| */ ++ /*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); /* that's |font_id_text(equiv(n))| */ ++ print_esc(hash[font_id_base + equiv(n)].rh); + } + } else if (n < dimen_base) { +- /* Show equivalent |n|, in region 5 */ ++ /*tex Show equivalent |n| in region 5: */ + if (n < dir_base) { + print_cmd_chr(assign_int_cmd, n); + print_char('='); +@@ -1046,7 +1105,7 @@ void show_eqtb(halfword n) + } else if (n < count_base) { + print_cmd_chr(assign_dir_cmd, n); + print_char(' '); +- print_dir(eqtb[n].cint); ++ print_dir_par(eqtb[n].cint); + } else if (n < attribute_base) { + tprint_esc("count"); + print_int(n - count_base); +@@ -1059,7 +1118,7 @@ void show_eqtb(halfword n) + print_int(eqtb[n].cint); + } + } else if (n <= eqtb_size) { +- /* Show equivalent |n|, in region 6 */ ++ /*tex Show equivalent |n| in region 6: */ + if (n < scaled_base) { + print_cmd_chr(assign_dimen_cmd, n); + } else { +@@ -1070,31 +1129,28 @@ void show_eqtb(halfword n) + print_scaled(eqtb[n].cint); + tprint("pt"); + } else { +- /* this can't happen either */ +- print_char('?'); ++ /*tex This can't happen unless you messed up at the \LUA\ end. */ ++ tprint("? bad token, case 2: "); ++ print_int(n); + } + } + +-@ @c + void show_eqtb_meaning(halfword n) + { + if (n < null_cs) { +- /* this can't happen */ ++ /*tex 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.) ++ /*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}'. ++ 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) +@@ -1108,20 +1164,16 @@ void show_eqtb_meaning(halfword n) + 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. ++ /*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) +@@ -1137,11 +1189,11 @@ void show_eqtb_meaning(halfword n) + tprint_esc("box"); + print_int(n - box_base); + } else if (n == cur_font_loc) { +- /* Show the font identifier in |eqtb[n]| */ ++ /*tex Show the font identifier in |eqtb[n]|. */ + tprint("current font"); + } + } else if (n < dimen_base) { +- /* Show equivalent |n|, in region 5 */ ++ /*tex Show equivalent |n| in region 5. */ + if (n < dir_base) { + print_cmd_chr(assign_int_cmd, n); + } else if (n < count_base) { +@@ -1154,7 +1206,7 @@ void show_eqtb_meaning(halfword n) + print_int(n - attribute_base); + } + } else if (n <= eqtb_size) { +- /* Show equivalent |n|, in region 6 */ ++ /*tex Show equivalent |n| in region 6. */ + if (n < scaled_base) { + print_cmd_chr(assign_dimen_cmd, n); + } else { +@@ -1162,7 +1214,7 @@ void show_eqtb_meaning(halfword n) + print_int(n - scaled_base); + } + } else { +- /* this can't happen either */ ++ /*tex This can't happen either. */ + print_char('?'); + } + } +diff --git a/texk/web2c/luatexdir/tex/errors.c b/texk/web2c/luatexdir/tex/errors.c +new file mode 100644 +index 000000000..b630422ee +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/errors.c +@@ -0,0 +1,1032 @@ ++/* ++ ++errors.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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 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); ++} +diff --git a/texk/web2c/luatexdir/tex/errors.w b/texk/web2c/luatexdir/tex/errors.w +deleted file mode 100644 +index 93dbe141b..000000000 +--- a/texk/web2c/luatexdir/tex/errors.w ++++ /dev/null +@@ -1,972 +0,0 @@ +-% errors.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ 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 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); +-} +diff --git a/texk/web2c/luatexdir/tex/expand.c b/texk/web2c/luatexdir/tex/expand.c +new file mode 100644 +index 000000000..a0a317db2 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/expand.c +@@ -0,0 +1,967 @@ ++/* ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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 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 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; ++} +diff --git a/texk/web2c/luatexdir/tex/expand.w b/texk/web2c/luatexdir/tex/expand.w +deleted file mode 100644 +index c912863b2..000000000 +--- a/texk/web2c/luatexdir/tex/expand.w ++++ /dev/null +@@ -1,829 +0,0 @@ +-% expand.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +- +-#include "ptexlib.h" +- +-@ 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 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 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; +-} +diff --git a/texk/web2c/luatexdir/tex/extensions.c b/texk/web2c/luatexdir/tex/extensions.c +new file mode 100644 +index 000000000..d5914db8a +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/extensions.c +@@ -0,0 +1,1329 @@ ++/* ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ ++#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"); ++ } ++ } ++ } ++} +diff --git a/texk/web2c/luatexdir/tex/extensions.w b/texk/web2c/luatexdir/tex/extensions.w +deleted file mode 100644 +index 73dc3ba45..000000000 +--- a/texk/web2c/luatexdir/tex/extensions.w ++++ /dev/null +@@ -1,1211 +0,0 @@ +-% extensions.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-\def\eTeX{e-\TeX} +-\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.) +-@^ +- +-@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; +diff --git a/texk/web2c/luatexdir/tex/filename.w b/texk/web2c/luatexdir/tex/filename.c +similarity index 56% +rename from texk/web2c/luatexdir/tex/filename.w +rename to texk/web2c/luatexdir/tex/filename.c +index 9e5d9c5cc..fdc6641ef 100644 +--- a/texk/web2c/luatexdir/tex/filename.w ++++ b/texk/web2c/luatexdir/tex/filename.c +@@ -1,59 +1,62 @@ +-% filename.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + ++filename.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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 ++/*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; +@@ -61,13 +64,16 @@ static void begin_name(void) + 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@> ++/*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@> ++ ++*/ + +-@c + static boolean more_name(ASCII_code c) + { + if (c == ' ' && stop_at_space && (!quoted_filename)) { +@@ -77,7 +83,7 @@ static boolean more_name(ASCII_code c) + return true; + } else { + str_room(1); +- append_char(c); /* contribute |c| to the current string */ ++ append_char(c); + if (IS_DIR_SEP(c)) { + area_delimiter = (pool_pointer) cur_length; + ext_delimiter = 0; +@@ -87,17 +93,21 @@ static boolean more_name(ASCII_code c) + } + } + +-@ The third. +-@^system dependencies@> ++/*tex ++ ++ 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| */ ++ 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 { +@@ -105,7 +115,7 @@ static void end_name(void) + cur_string[area_delimiter] = '\0'; + cur_length = (unsigned) strlen((char *) cur_string); + cur_area = make_string(); +- xfree(cur_string); ++ xfree(cur_string); + cur_length = (unsigned) strlen((char *) s); + cur_string = s; + } +@@ -118,24 +128,27 @@ static void end_name(void) + cur_string[l] = '\0'; + cur_length = (unsigned) strlen((char *) cur_string); + cur_name = make_string(); +- xfree(cur_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 ++/*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. + +-@c ++*/ ++ + void scan_file_name(void) + { + str_number u = 0; + name_in_progress = true; + begin_name(); +- /* Get the next non-blank non-call token; */ ++ /*tex Get the next non-blank non-call token: */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); +@@ -145,9 +158,11 @@ void scan_file_name(void) + 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. */ ++ /*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; +@@ -174,8 +189,10 @@ void scan_file_name(void) + name_in_progress = false; + } + +-@ This function constructs a the three file name strings from a token list +-@c ++/* ++ 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; +@@ -192,12 +209,14 @@ void scan_file_name_toks(void) + e = s + i; + } + } +- if (n != s) { /* explicit area */ ++ if (n != s) { ++ /*tex explicit area */ + cur_area = maketexlstring(a, (size_t) (n - a)); + } else { + cur_area = get_nullstr(); + } +- if (e != NULL) { /* explicit extension */ ++ if (e != NULL) { ++ /*tex explicit extension */ + cur_name = maketexlstring(n, (size_t) (e - n)); + cur_ext = maketexstring(e); + } else { +@@ -205,33 +224,34 @@ void scan_file_name_toks(void) + 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|. ++ 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| ++*/ + +-@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. ++/*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. ++ ++*/ + +-@c + char *prompt_file_name(const char *s, const char *e) + { + int k; /* index into |buffer| */ +@@ -290,12 +310,10 @@ char *prompt_file_name(const char *s, const char *e) + 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 */ ++ boolean must_quote; /* whether to quote the filename */ ++ unsigned char *j; /* index into string */ + must_quote = false; + if (a != NULL) { + j = a; +@@ -318,13 +336,11 @@ void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e) + 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|]]="""")); */ +- ++ /*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) { +@@ -346,15 +362,17 @@ void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e) + 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); ++ tprint_file_name( ++ (unsigned char *) nam, ++ (unsigned char *) are, ++ (unsigned char *) ext ++ ); + free(nam); + free(are); + free(ext); +diff --git a/texk/web2c/luatexdir/tex/inputstack.w b/texk/web2c/luatexdir/tex/inputstack.c +similarity index 53% +rename from texk/web2c/luatexdir/tex/inputstack.w +rename to texk/web2c/luatexdir/tex/inputstack.c +index 68b22e1a3..c64111edc 100644 +--- a/texk/web2c/luatexdir/tex/inputstack.w ++++ b/texk/web2c/luatexdir/tex/inputstack.c +@@ -1,51 +1,85 @@ +-% inputstack.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++inputstack.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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 */ ++/*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; +-int line = 0; /* current line number in the current source file */ ++ ++/*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; + +-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 */ ++/*tex Can a subfile end now? */ + +-@ Here is a procedure that uses |scanner_status| to print a warning message +-when a subfile has ended, and at certain other crucial times: ++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: ++ ++*/ + +-@c + void runaway(void) + { +- pointer p = null; /* head of runaway list */ ++ /*tex The head of the runaway list: */ ++ pointer p = null; + if (scanner_status > skipping) { + switch (scanner_status) { + case defining: +@@ -65,7 +99,7 @@ void runaway(void) + p = def_ref; + break; + default: +- /* there are no other cases */ ++ /*tex There are no other cases. */ + break; + } + print_char('?'); +@@ -74,48 +108,69 @@ void runaway(void) + } + } + +-@ 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 ++/*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) { +@@ -172,47 +227,49 @@ static void print_token_list_type(int t) + case write_text: + tprint_nl(" "); + break; ++ case local_text: ++ tprint_nl(" "); ++ break; + default: + tprint_nl("?"); +- /* this should never happen */ ++ /*tex 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(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 ++/*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(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; +@@ -230,10 +287,12 @@ void set_trick_count(void) + + #define PSEUDO_PRINT_THE_LINE() do { \ + begin_pseudoprint(); \ +- if (buffer[ilimit]==end_line_char_par) \ ++ if (buffer[ilimit]==end_line_char_par) { \ + j=ilimit; \ +- else \ +- j=ilimit+1; /* determine the effective end of the line */ \ ++ } 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) \ +@@ -243,10 +302,12 @@ void set_trick_count(void) + } \ + } 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. ++/*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 { \ +@@ -268,46 +329,66 @@ void set_trick_count(void) + print_char(trick_buf[(q+2) % error_line]); \ + print_char(trick_buf[(q+3) % error_line]); \ + } else { \ +- /* invalid */ \ ++ /*tex Invalid character! */ \ + } \ + } while (0) + +-@ @c ++/*tex ++ ++This one prints where the scanner is. ++ ++*/ ++ + 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 */ ++{ ++ /*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; +- /* store current state */ ++ /*tex Store the current state. */ + while (true) { +- cur_input = input_stack[base_ptr]; /* enter into the context */ ++ /*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)) { +- /* Display the current context */ ++ /*tex 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 */ ++ /*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) { +- /* +- 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. ++ /*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) { +@@ -327,7 +408,8 @@ void show_context(void) + tprint_nl("l."); + if (iindex == in_open) { + print_int(line); +- } else { /* input from a pseudo file */ ++ } else { ++ /*tex Input from a pseudo file. */ + print_int(line_stack[iindex + 1]); + } + } +@@ -337,21 +419,26 @@ void show_context(void) + print_token_list_type(token_type); + + begin_pseudoprint(); +- if (token_type < macro) ++ if (token_type < macro) { + show_token_list(istart, iloc, 100000); +- else +- show_token_list(token_link(istart), iloc, 100000); /* avoid reference count */ ++ } else { ++ /*tex Avoid reference count. */ ++ show_token_list(token_link(istart), iloc, 100000); ++ } + } +- /* stop pseudoprinting */ ++ /*tex Stop pseudoprinting. */ + selector = old_setting; +- /* Print two lines using the tricky pseudoprinted information */ +- if (trick_count == 1000000) ++ /*tex 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) ++ } ++ /*tex The |set_trick_count| must be performed. */ ++ if (tally < trick_count) { + m = tally - first_count; +- else +- m = trick_count - first_count; /* context on line 2 */ ++ } else { ++ /* The context on line 2: */ ++ m = trick_count - first_count; ++ } + if (l + first_count <= half_error_line) { + p = 0; + n = l + first_count; +@@ -360,12 +447,14 @@ void show_context(void) + p = l + first_count - half_error_line + 3; + n = half_error_line; + } +- for (q = p; q <= first_count - 1; q++) ++ 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_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 +@@ -379,24 +468,26 @@ void show_context(void) + } else if (nn == error_context_lines_par) { + tprint_nl("..."); + incr(nn); +- /* omitted if |error_context_lines_par<0| */ ++ /*tex Omitted if |error_context_lines_par<0|. */ + } + if (bottom_line) + break; + decr(base_ptr); + } +- /* restore original state */ ++ /*tex Restore the original state. */ + cur_input = input_stack[input_ptr]; + } + +-@ The following subroutines change the input status in commonly needed ways. ++/*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). + +-@c ++Enter a new input level, save the old: + +-/* enter a new input level, save the old */ ++*/ + + # define pop_input() \ + cur_input=input_stack[--input_ptr] +@@ -411,12 +502,14 @@ new level (having, initially, the same properties as the old). + 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|. ++/*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|. ++ ++*/ + +-@c + void begin_token_list(halfword p, quarterword t) + { + push_input(); +@@ -424,7 +517,7 @@ void begin_token_list(halfword p, quarterword t) + istart = p; + token_type = (unsigned char) t; + if (t >= macro) { +- /* the token list starts with a reference count */ ++ /*tex The token list starts with a reference count. */ + add_token_ref(p); + if (t == macro) { + param_start = param_ptr; +@@ -438,8 +531,7 @@ void begin_token_list(halfword p, quarterword t) + else if (t == write_text) + tprint_esc("write"); + else +- print_cmd_chr(assign_toks_cmd, +- t - output_text + output_routine_loc); ++ print_cmd_chr(assign_toks_cmd, t - output_text + output_routine_loc); + tprint("->"); + token_show(p); + end_diagnostic(false); +@@ -450,24 +542,26 @@ void begin_token_list(halfword p, quarterword t) + } + } + +-@ 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@> ++/*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@> ++ ++*/ + +-@c + void end_token_list(void) + { +- /* leave a token-list input level */ ++ /*tex Leave a token-list input level: */ + if (token_type >= backed_up) { +- /* token list to be deleted */ ++ /*tex The token list to be deleted: */ + if (token_type <= inserted) { + flush_list(istart); + } else { +- /* update reference count */ ++ /*tex Update the reference count: */ + delete_token_ref(istart); + if (token_type == macro) { +- /* parameters must be flushed */ ++ /*tex Parameters must be flushed: */ + while (param_ptr > param_start) { + decr(param_ptr); + flush_list(param_stack[param_ptr]); +@@ -484,23 +578,24 @@ void end_token_list(void) + 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@> ++/*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@> + +-@c ++*/ + + /* undoes one token of input */ + + void back_input(void) + { +- halfword p; /* a token list of length one */ ++ /*tex A token list of length one: */ ++ halfword p; + while ((istate == token_list) && (iloc == null) && (token_type != v_template)) { +- /* conserve stack space */ ++ /*tex Conserve stack space. */ + end_token_list(); + } + p = get_avail(); +@@ -512,14 +607,18 @@ void back_input(void) + 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; /* that was |back_list(p)|, without procedure overhead */ ++ iloc = p; + } + +-@ Insert token |p| into \TeX's input +-@c ++/*tex ++ ++Insert token |p| into \TeX's input ++ ++*/ + + void reinsert_token(boolean a, halfword pp) + { +@@ -545,13 +644,15 @@ void reinsert_token(boolean a, halfword pp) + 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|. ++/*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@> + +-@c ++*/ ++ + void begin_file_reading(void) + { + if (in_open == max_in_open) +@@ -569,48 +670,59 @@ void begin_file_reading(void) + line_stack[iindex] = line; + istart = first; + istate = mid_line; +- iname = 0; /* |terminal_input| is now |true| */ ++ iname = 0; ++ /*tex Variable |terminal_input| is now |true|. */ + line_catcode_table = DEFAULT_CAT_TABLE; + line_partial = false; +- /* Prepare terminal input {\sl Sync\TeX} information */ ++ /*tex Prepare terminal input \SYNCTEX\ information. */ + synctex_tag = 0; + } + +-@ Conversely, the variables must be downdated when such a level of input +-is finished: ++/*tex ++ ++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)) ++ if ((iname >= 18) && (iname <= 20)) { + pseudo_close(); +- else if (iname == 21) ++ } else if (iname == 21) { + luacstring_close(iindex); +- else if (iname > 17) +- lua_a_close_in(cur_file, 0); /* forget it */ ++ } else if (iname > 17) { ++ /*tex Forget it. */ ++ lua_a_close_in(cur_file, 0); ++ } + pop_input(); + decr(in_open); + } + +-@ In order to keep the stack from overflowing during a long sequence of ++/*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. + +-@c ++*/ + void clear_for_error_prompt(void) + { +- while ((istate != token_list) && terminal_input +- && (input_ptr > 0) && (iloc > ilimit)) ++ 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. ++/*tex ++ ++To get \TeX's whole input mechanism going, we perform the following actions. ++ ++*/ + +-@c + void initialize_inputstack(void) + { + input_ptr = 0; +@@ -645,29 +757,29 @@ void initialize_inputstack(void) + line_catcode_table = DEFAULT_CAT_TABLE; + line_partial = false; + align_state = 1000000; +- if (!init_terminal()) +- exit(EXIT_FAILURE); /* |goto final_end|; */ ++ if (!init_terminal()) { ++ /*tex |goto final_end|; */ ++ exit(EXIT_FAILURE); ++ } ++ /* |init_terminal| has set |loc| and |last| */ + ilimit = last; +- first = last + 1; /* |init_terminal| has set |loc| and |last| */ ++ first = last + 1; + } + +-@ 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. ++/*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. + +- 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. ++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 */ ++halfword pseudo_files; + + static halfword string_to_pseudo(str_number str, int nl) + { +@@ -680,7 +792,8 @@ static halfword string_to_pseudo(str_number str, int nl) + len = (unsigned) str_length(str); + l = 0; + while (l < len) { +- unsigned m = l; /* start of current line */ ++ /*tex start of current line */ ++ unsigned m = l; + while ((l < len) && (s[l] != nl)) + l++; + sz = (int) (l - m + 7) / 4; +@@ -703,7 +816,8 @@ static halfword string_to_pseudo(str_number str, int nl) + if (q == null) { + pseudo_lines(h) = r; + } else { +- vlink(q) = r ; /* no prev node here so no couple_nodes !*/ ++ /*tex There is no |prev| node here so no need to couple_nodes! */ ++ vlink(q) = r ; + } + q = r ; + if (s[l] == nl) +@@ -712,24 +826,33 @@ static halfword string_to_pseudo(str_number str, int nl) + return h; + } + +-@ The |pseudo_start| procedure initiates reading from a pseudo file. ++/*tex ++ ++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 */ ++ /*tex The string to be converted into a pseudo file: */ ++ str_number s; ++ /*tex A helper for list construction: */ ++ halfword p; + s = make_string(); +- /* Convert string |s| into a new pseudo file */ ++ /*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); +- /* Initiate input from new pseudo file */ +- begin_file_reading(); /* set up |cur_file| and new level of input */ ++ /*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; +- iloc = ilimit + 1; /* force line read */ ++ /*tex force line read */ ++ iloc = ilimit + 1; + if (tracing_scan_tokens_par > 0) { + if (term_offset > max_print_line - 3) + print_ln(); +@@ -742,7 +865,7 @@ void pseudo_from_string(void) + } else { + iname = 18; + } +- /* Prepare pseudo file {\sl Sync\TeX} information */ ++ /*tex Prepare pseudo file \SYNCTEX\ information. */ + synctex_tag = 0; + } + +@@ -759,29 +882,37 @@ void pseudo_start(void) + pseudo_from_string(); + } + +-@ @c + void lua_string_start(void) + { +- begin_file_reading(); /* set up |cur_file| and new level of input */ ++ /*tex Set up |cur_file| and a new level of input: */ ++ begin_file_reading(); + line = 0; + ilimit = istart; +- iloc = ilimit + 1; /* force line read */ ++ /*tex Force line read: */ ++ iloc = ilimit + 1; + iname = 21; + luacstring_start(iindex); + } + +-@ Here we read a line from the current pseudo file into |buffer|. ++/*tex + +-@c +-/* inputs the next line or returns |false| */ ++Here we read a line from the current pseudo file into |buffer|. ++It 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 */ ++ /*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; +@@ -808,10 +939,11 @@ boolean pseudo_input(void) + return true; + } + +-@ When we are done with a pseudo file we `close' it. ++/*tex ++ ++When we are done with a pseudo file we `close' it. + +-@c +-/* close the top level pseudo file */ ++*/ + + void pseudo_close(void) + { +diff --git a/texk/web2c/luatexdir/tex/linebreak.c b/texk/web2c/luatexdir/tex/linebreak.c +new file mode 100644 +index 000000000..df13931e1 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/linebreak.c +@@ -0,0 +1,2520 @@ ++/* ++ ++Copyright 2006-2008 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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; ++} +diff --git a/texk/web2c/luatexdir/tex/linebreak.w b/texk/web2c/luatexdir/tex/linebreak.w +deleted file mode 100644 +index 345a1a424..000000000 +--- a/texk/web2c/luatexdir/tex/linebreak.w ++++ /dev/null +@@ -1,2164 +0,0 @@ +-% linebreak.w +-% +-% Copyright 2006-2008 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +-#include "ptexlib.h" +- +-@ We 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; +-} +diff --git a/texk/web2c/luatexdir/tex/mainbody.c b/texk/web2c/luatexdir/tex/mainbody.c +new file mode 100644 +index 000000000..ca9a79dbb +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/mainbody.c +@@ -0,0 +1,769 @@ ++/* ++ ++mainbody.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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. ++ ++*/ +diff --git a/texk/web2c/luatexdir/tex/mainbody.w b/texk/web2c/luatexdir/tex/mainbody.w +deleted file mode 100644 +index 4d38b9d4a..000000000 +--- a/texk/web2c/luatexdir/tex/mainbody.w ++++ /dev/null +@@ -1,708 +0,0 @@ +-% mainbody.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-\def\eTeX{e-\TeX} +-\def\Aleph{Aleph} +-\def\pdfTeX{pdf\TeX} +- +-@ @c +- +- +-#include "ptexlib.h" +- +-@ +-pdfTeX is copyright (C) 1996-2006 Han The Thanh, . +- +-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 +diff --git a/texk/web2c/luatexdir/tex/maincontrol.w b/texk/web2c/luatexdir/tex/maincontrol.c +similarity index 66% +rename from texk/web2c/luatexdir/tex/maincontrol.w +rename to texk/web2c/luatexdir/tex/maincontrol.c +index 7b4cf9065..295e2c6cc 100644 +--- a/texk/web2c/luatexdir/tex/maincontrol.w ++++ b/texk/web2c/luatexdir/tex/maincontrol.c +@@ -1,80 +1,79 @@ +-% maincontrol.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++maincontrol.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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. ++/*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. +- +-@c ++|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); +@@ -90,21 +89,21 @@ void adjust_space_factor(void) + } + } + +-@ 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: ++/*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. ++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; ++static int local_level = 0; ++ ++/*tex + + @* Main control helpers. + +@@ -112,8 +111,8 @@ 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; +@@ -126,23 +125,49 @@ static void run_char (void) { + 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. ++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); ++ } ++} + +-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_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. ++ ++*/ + +-@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 */ ++ /*tex Don't inject anything, not even zero skip. */ + } else if (method == 2) { + p = new_glue(zero_glue); + couple_nodes(tail,p); +@@ -150,9 +175,9 @@ static void run_app_space (void) { + } 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 */ ++ /*tex 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 */ ++ /*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); +@@ -161,20 +186,24 @@ static void run_app_space (void) { + } else { + p = new_param_glue(space_skip_code); + } +- /* so from now we have a subtype with spaces: */ ++ /*tex 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 ++/*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) ) { +- /* user boundary or protrusion boundary */ ++ /*tex We expect a user boundary or protrusion boundary. */ + scan_int(); + boundary_value(n) = cur_val; + } +@@ -182,7 +211,6 @@ static void run_boundary (void) { + tail = n; + } + +-@ @c + static void run_char_ghost (void) { + int t; + t = cur_chr; +@@ -199,19 +227,21 @@ static void run_char_ghost (void) { + } + } + +-@ @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. ++/*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. ++ ++*/ + +-@c + static void run_ignore_spaces (void) { + if (cur_chr == 0) { +- /* Get the next non-blank non-call... */ ++ /*tex Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); +@@ -231,24 +261,33 @@ static void run_ignore_spaces (void) { + } + } + +-@ |stop| is the second special case. We want |main_control| to return to its caller ++/*tex ++ ++|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 */ ++ if (its_all_over()) { ++ /*tex this is the only way out */ ++ main_control_state= goto_return; ++ } + } + +-@ @c + static void run_non_math_math (void) { + back_input(); + new_graf(true); + } + +-@ @c ++/*tex ++ ++ We build up an argument to |set_math_char|: ++ ++*/ ++ + static void run_math_char_num (void) { +- mathcodeval mval; /* to build up an argument to |set_math_char| */ ++ mathcodeval mval; + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) +@@ -258,35 +297,47 @@ static void run_math_char_num (void) { + math_char_in_text(mval); + } + +-@ @c ++/*tex ++ ++ We build up an argument to |set_math_char|: ++*/ ++ + static void run_math_given (void) { +- mathcodeval mval; /* to build up an argument to |set_math_char| */ ++ 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; /* to build up an argument to |set_math_char| */ ++ mathcodeval mval; + 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. ++/*tex + +-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. ++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. + +-Note that baselineskip calculations are disabled after a rule in vertical +-mode, by setting |prev_depth:=ignore_depth|. ++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) +@@ -295,29 +346,30 @@ static void run_rule (void) { + 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 ++/*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); +@@ -338,11 +390,14 @@ static void run_end_group (void) { + } + } + +-@ 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|. ++/*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|. ++ ++*/ + +-@c + static void run_move (void) { + int t = cur_chr; + scan_normal_dimen(); +@@ -352,17 +407,14 @@ static void run_move (void) { + 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); +@@ -372,37 +424,51 @@ static void run_box_dir (void) { + 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). ++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}'. ++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}. ++/*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}. ++ ++*/ + +-@c + static void run_par_end_vmode (void) { + normal_paragraph(); + if (mode > 0) { +@@ -411,28 +477,27 @@ static void run_par_end_vmode (void) { + } + } + +-@ @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 (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(); + } + } + +-@ @c + static void append_italic_correction_mmode (void) { +- tail_append(new_kern(0)); /* what subtype to use */ ++ tail_append(new_kern(0)); + } + +-@ @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) +@@ -442,7 +507,6 @@ static void run_halign_mmode (void) { + } + } + +-@ @c + static void run_eq_no (void) { + if (privileged()) { + if (cur_group == math_shift_group) +@@ -452,21 +516,18 @@ static void run_eq_no (void) { + } + } + +-@ @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| */ ++ mathcodeval mval; + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) +@@ -476,31 +537,27 @@ static void run_math_char_num_mmode (void) { + set_math_char(mval); + } + +-@ @c + static void run_math_given_mmode (void) { +- mathcodeval mval; /* to build up an argument to |set_math_char| */ ++ mathcodeval mval; + 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| */ ++ mathcodeval mval; + 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| */ ++ 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); +- + } + +-@ @c + static void run_vcenter (void) { + scan_spec(vcenter_group); + normal_paragraph(); +@@ -511,18 +568,15 @@ static void run_vcenter (void) { + 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(); +@@ -530,7 +584,6 @@ static void run_math_choice (void) { + setup_math_style(); + } + +-@ @c + static void run_math_shift (void) { + if (cur_group == math_shift_group) + after_math(); +@@ -538,19 +591,16 @@ static void run_math_shift (void) { + 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); + } +@@ -565,12 +615,16 @@ static void run_normal (void) { + 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"); ++ 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"); ++ help1( ++ "You cannot overwrite the current \\catcode table" ++ ); + error(); + } else { + copy_cat_codes(cat_code_table_par, cur_val); +@@ -581,12 +635,16 @@ static void run_normal (void) { + 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"); ++ 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"); ++ help1( ++ "You cannot overwrite the current \\catcode table" ++ ); + error(); + } else { + initex_cat_codes(cur_val); +@@ -607,6 +665,12 @@ static void run_normal (void) { + (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; +@@ -617,9 +681,10 @@ static void run_normal (void) { + } + } + +-/* +- this is experimental and not used for production, only for testing and writing +- macros (some options stay) ++/*tex ++ ++This is experimental and not used for production, only for testing and writing ++macros (some options stay). + + */ + +@@ -633,14 +698,10 @@ static void run_option(void) { + 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"); + } +@@ -651,20 +712,55 @@ static void run_option(void) { + } + } + +-@ For mode-independent commands, the following macro is useful. ++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. ++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; + ++/*tex ++ ++The |main_control| uses a jump table, and |init_main_control| sets that table up. ++ ++*/ + +-@ 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) { +@@ -749,6 +845,7 @@ static void init_main_control (void) { + 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; +@@ -835,6 +932,7 @@ static void init_main_control (void) { + 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); +@@ -879,46 +977,181 @@ static void init_main_control (void) { + 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); ++ + } + +-@ And here is |main_control| itself. It is quite short nowadays. ++/*tex ++ ++ 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) ++ 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 ++ if (main_control_state == goto_skip_token) { ++ main_control_state = goto_next; ++ } 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. */ ++ } ++ /*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) ++ 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; ++} + +- (jump_table[(abs(mode) + cur_cmd)])(); /* run the command */ ++/*tex + +- if (main_control_state == goto_return) { ++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; /* not reached */ ++ 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; + } + +-@ @c + void app_space(void) + { /* handle spaces when |space_factor<>1000| */ + halfword q; /* glue node */ +@@ -948,20 +1181,24 @@ void app_space(void) + 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."); ++ 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. ++/*tex ++ ++ We can silently ignore \.{\\par}s in a math formula. ++ ++*/ + +-@c + void insert_dollar_sign_par_end(void) + { + if (!suppress_mathpar_error_par) { +@@ -969,10 +1206,13 @@ void insert_dollar_sign_par_end(void) + } + } + +-@ The `|you_cant|' procedure prints a line saying that the current command +-is illegal in the current mode; it identifies these things symbolically. ++/*tex ++ ++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 `"); +@@ -980,34 +1220,40 @@ void you_cant(void) + 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.' ++/*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.' ++ ++*/ + +-@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'."); ++ 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. ++/*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. ++ ++*/ + +-@c + boolean privileged(void) + { + if (mode > 0) { +@@ -1018,15 +1264,18 @@ boolean privileged(void) + } + } + +-@ 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.'' ++/*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.'' ++ ++*/ ++ + +-@c + boolean its_all_over(void) + { /* do this when \.{\\end} or \.{\\dump} occurs */ + if (privileged()) { +@@ -1044,16 +1293,18 @@ boolean its_all_over(void) + 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|. + +-@ 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. + +-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; +@@ -1085,35 +1336,37 @@ void append_glue(void) + } + } + +-@ @c + void append_kern(void) + { +- int s; /* |subtype| of the kern node */ +- s = cur_chr; ++ 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; + } + +-@ 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. ++/*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. ++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 */ ++ halfword p, q; + if (cur_group == bottom_level) { +- /* Drop current token and complain that it was unmatched */ ++ /*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."); ++ help1( ++ "Things are pretty mixed up, but I think the worst is over." ++ ); + error(); + + } else { +@@ -1121,8 +1374,11 @@ void off_save(void) + 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. */ ++ /*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); +@@ -1147,23 +1403,27 @@ void off_save(void) + } + tprint(" inserted"); + ins_list(token_link(temp_token_head)); +- help5("I've inserted something that you may have forgotten.", +- "(See the above.)", +- "With luck, this will get me unwedged. But if you", +- "really didn't forget anything, try typing `2' now; then", +- "my insertion and my current dilemma will both disappear."); ++ help5( ++ "I've inserted something that you may have forgotten.", ++ "(See the above.)", ++ "With luck, this will get me unwedged. But if you", ++ "really didn't forget anything, try typing `2' now; then", ++ "my insertion and my current dilemma will both disappear." ++ ); + error(); + } + } ++/*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. + +-@ 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 */ +@@ -1176,8 +1436,10 @@ void handle_right_brace(void) + 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."); ++ help2( ++ "You've closed more groups than you opened.", ++ "Such booboos are generally harmless, so keep going." ++ ); + error(); + break; + case semi_simple_group: +@@ -1185,10 +1447,20 @@ void handle_right_brace(void) + 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: +- /* 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. */ ++ 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: +@@ -1211,7 +1483,9 @@ void handle_right_brace(void) + f = floating_penalty_par; + unsave(); + save_ptr--; +- /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */ ++ /*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) { +@@ -1236,9 +1510,11 @@ void handle_right_brace(void) + } + break; + case output_group: +- /* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */ ++ /*tex ++ 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| */ ++ /*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; +@@ -1255,7 +1531,9 @@ void handle_right_brace(void) + 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."); ++ help1( ++ "I'm guessing that you meant to end an alignment here." ++ ); + ins_error(); + break; + case no_align_group: +@@ -1279,7 +1557,6 @@ void handle_right_brace(void) + } + } + +-@ @c + void extra_right_brace(void) + { + print_err("Extra }, or forgotten "); +@@ -1294,19 +1571,24 @@ void extra_right_brace(void) + 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$}'."); ++ 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 ++/*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. + +-@c ++*/ ++ + void normal_paragraph(void) + { + if (looseness_par != 0) +@@ -1323,27 +1605,35 @@ void normal_paragraph(void) + 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. ++/*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. ++ ++*/ + +-@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. ++/*tex ++ ++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. +- */ ++ /*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) { +@@ -1372,15 +1662,28 @@ void box_end(int box_context) + } + } + } else if (box_context < ship_out_flag) { +- /* Store |cur_box| in a box register */ ++ /*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) { +- if (box_context > ship_out_flag) { +- /* Append a new leader node that uses |cur_box| */ +- /* Get the next non-blank non-relax... */ ++ /*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)); +@@ -1391,25 +1694,32 @@ void box_end(int box_context) + leader_ptr(tail) = cur_box; + } else { + print_err("Leaders not followed by proper glue"); +- help3 +- ("You should say `\\leaders '.", +- "I found the , but there's no suitable", +- ", so I'm ignoring these leaders."); ++ help3( ++ "You should say `\\leaders '.", ++ "I found the , but there's no suitable", ++ ", so I'm ignoring these leaders." ++ ); + back_error(); + flush_node_list(cur_box); + } + } else { ++ if (box_context != ship_out_flag) { ++ normal_error("scanner","shipout expected"); ++ } + ship_out(static_pdf, cur_box, SHIPPING_PAGE); + } + } + } + +-@ the next input should specify a box or perhaps a rule ++/*tex ++ ++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... */ ++ /*tex Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); +@@ -1422,26 +1732,36 @@ void scan_box(int box_context) + box_end(box_context); + } else { + print_err("A was supposed to be here"); +- help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or", +- "something like that. So you might find something missing in", +- "your output. But keep trying; you can fix this later."); ++ 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); ++ } + } + } + +-@ @c + void new_graf(boolean indented) + { + halfword p, q, dir_graf_tmp; + halfword dir_rover; +- prev_graf_par = 0; ++ 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; +- /* LOCAL: Add local paragraph node */ ++ /*tex Add local paragraph node */ + tail_append(make_local_par_node(new_graf_par_code)); + if (indented) { + p = new_null_box(); +@@ -1470,15 +1790,16 @@ void new_graf(boolean indented) + 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 */ ++ /*tex put |par_skip| glue on current page */ ++ build_page(); + } + } + +-@ @c + void indent_in_hmode(void) + { + halfword p; +- if (cur_chr > 0) { /* \.{\\indent} */ ++ if (cur_chr > 0) { ++ /*tex \.{\\indent} */ + p = new_null_box(); + width(p) = par_indent_par; + if (abs(mode) == hmode) +@@ -1489,7 +1810,6 @@ void indent_in_hmode(void) + } + } + +-@ @c + void head_for_vmode(void) + { + if (mode < 0) { +@@ -1497,8 +1817,10 @@ void head_for_vmode(void) + 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)."); ++ help2( ++ "To put a horizontal rule in an hbox or an alignment,", ++ "you should use \\leaders or \\hrulefill (see The TeXbook)." ++ ); + error(); + } + } else { +@@ -1509,19 +1831,23 @@ void head_for_vmode(void) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@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 */ ++ /*tex |null| paragraphs are ignored, all contain a |local_paragraph| node */ ++ pop_nest(); + } else { + line_break(false, line_break_context); + } +@@ -1534,7 +1860,6 @@ void end_graf(int line_break_context) + } + } + +-@ @c + void begin_insert_or_adjust(void) + { + if (cur_cmd != vadjust_cmd) { +@@ -1542,7 +1867,9 @@ void begin_insert_or_adjust(void) + 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."); ++ help1( ++ "I'm changing to \\insert0; box \\outputbox is special." ++ ); + error(); + cur_val = 0; + } +@@ -1561,12 +1888,14 @@ void begin_insert_or_adjust(void) + 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. ++/*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. ++ ++*/ + +-@c + void handle_mark(void) + { + halfword p; /* new node */ +@@ -1597,7 +1926,6 @@ void handle_mark(void) + } + } + +-@ @c + void append_penalty(void) + { + scan_int(); +@@ -1608,29 +1936,34 @@ void append_penalty(void) + } + } + +-@ When |delete_last| is called, |cur_chr| is the |type| of node that +-will be deleted, if present. ++/*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. ++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 */ ++ /*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."); ++ 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.", +@@ -1643,7 +1976,7 @@ void delete_last(void) + error(); + } + } else { +- /* todo: clean this up */ ++ /*tex Todo: clean this up! */ + if (!is_char_node(tail)) { + if (type(tail) == cur_chr) { + q = head; +@@ -1665,7 +1998,6 @@ void delete_last(void) + } + } + +-@ @c + void unpackage(void) + { + halfword p; /* the box */ +@@ -1673,7 +2005,7 @@ void unpackage(void) + int c; /* should we copy? */ + halfword s; /* for varmem assignment */ + if (cur_chr > copy_code) { +- /* Handle saved items and |goto done| */ ++ /*tex Handle saved items and |goto done| */ + try_couple_nodes(tail, disc_ptr[cur_chr]); + disc_ptr[cur_chr] = null; + goto DONE; +@@ -1687,9 +2019,11 @@ void unpackage(void) + || ((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."); ++ 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; + } +@@ -1713,12 +2047,14 @@ void unpackage(void) + } + } + +-@ ++/*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. ++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 */ +@@ -1734,7 +2070,6 @@ void append_italic_correction(void) + } + } + +-@ @c + void append_local_box(int kind) + { + incr(save_ptr); +@@ -1746,20 +2081,23 @@ void append_local_box(int kind) + 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. ++/*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. + +-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); +@@ -1774,7 +2112,7 @@ void append_discretionary(void) + } + set_explicit_disc_penalty(tail); + } else if (cur_chr == automatic_disc) { +- /* - as done in hyphenator */ ++ /*tex As done in hyphenator: */ + c = get_pre_exhyphen_char(cur_lang_par); + if (c <= 0) { + c = ex_hyphen_char_par; +@@ -1798,7 +2136,7 @@ void append_discretionary(void) + } + set_automatic_disc_penalty(tail); + } else { +- /* \discretionary */ ++ /*tex |\discretionary| */ + if (scan_keyword("penalty")) { + scan_int(); + disc_penalty(tail) = cur_val; +@@ -1810,14 +2148,17 @@ void append_discretionary(void) + push_nest(); + mode = -hmode; + space_factor_par = 1000; +- /* already preset: disc_penalty(tail) = hyphen_penalty_par; */ ++ /*tex Already preset: |disc_penalty(tail) = hyphen_penalty_par;| */ + } + } + +-@ The test for |p != null| ensures that empty \.{\\localleftbox} and +- \.{\\localrightbox} commands are not applied. ++/*tex ++ ++The test for |p != null| ensures that empty \.{\\localleftbox} and ++\.{\\localrightbox} commands are not applied. ++ ++*/ + +-@c + void build_local_box(void) + { + halfword p; +@@ -1828,41 +2169,51 @@ void build_local_box(void) + decr(save_ptr); + p = vlink(head); + pop_nest(); +- if (p != null) +- p = hpack(p, 0, additional, -1); ++ 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) { +- /* LOCAL: Add local paragraph node */ ++ /*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); + } + +-@ 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.) ++/*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.) ++ ++*/ + +-@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|. */ ++ /*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."); ++ help1( ++ "Discretionary lists must contain only boxes and kerns." ++ ); + error(); + begin_diagnostic(); + tprint_nl("The following discretionary sublist has been deleted:"); +@@ -1897,12 +2248,16 @@ void build_discretionary(void) + } + break; + case 2: +- /* Attach list |p| to the current list, and record its length; +- then finish up and |return| */ ++ /*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."); ++ 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 { +@@ -1913,9 +2268,10 @@ void build_discretionary(void) + } + } + decr(save_ptr); ++ /*tex There are no other cases. */ + 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(); +@@ -1924,15 +2280,18 @@ void build_discretionary(void) + 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. ++/*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. ++ ++*/ + +-@c + void make_accent(void) + { + double s, t; /* amount of slant */ +@@ -1944,11 +2303,14 @@ void make_accent(void) + p = new_glyph(f, cur_val); + if (p != null) { + x = x_height(f); +- s = float_cast(slant(f)) / float_constant(65536); /* real division */ ++ /*tex real division */ ++ s = float_cast(slant(f)) / float_constant(65536); + a = glyph_width(p); + do_assignments(); +- /* Create a character node |q| for the next character, +- but set |q:=null| if problems arise */ ++ /*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) || +@@ -1962,22 +2324,26 @@ void make_accent(void) + } + + 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}. ++ /*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) { /* the accent must be shifted up or down */ ++ if (h != x) { ++ /*tex 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 */ ++ /*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); +@@ -1994,34 +2360,43 @@ void make_accent(void) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@c + void align_error(void) + { + if (abs(align_state) > 2) { +- /* Express consternation over the fact that no alignment is in progress */ ++ /*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."); ++ 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."); ++ 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(); + +@@ -2036,43 +2411,55 @@ void align_error(void) + 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."); ++ 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\{...\}}'. ++/*tex ++ ++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."); ++ 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."); ++ 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. ++/*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. ++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. ++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; +@@ -2085,7 +2472,7 @@ void do_endv(void) + (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... */ ++ /*tex interwoven alignment preambles... */ + if (cur_group == align_group) { + end_graf(align_group); + if (fin_col()) +@@ -2095,55 +2482,62 @@ void do_endv(void) + } + } + +-@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|. ++/*tex ++ ++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."); ++ 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.) ++/*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. ++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. ++*/ + ++/*tex + +-@ The different types of code values have different legal ranges; the ++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; \ +- } \ ++*/ ++ ++#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) { +@@ -2176,40 +2570,51 @@ void prefixed_command(void) + while (cur_cmd == prefix_cmd) { + if (!odd(a / cur_chr)) + a = a + cur_chr; +- /* Get the next non-blank non-relax... */ ++ /*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) { +- /* Discard erroneous prefixes and |return| */ ++ /*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."); ++ 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 */ ++ /*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) && ((a % 4 != 0) || (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."); ++ help1( ++ "I'll pretend you didn't say \\long or \\outer or \\protected here." ++ ); + error(); + } +- /* Adjust for the setting of \.{\\globaldefs} */ ++ /*tex ++ Adjust for the setting of \.{\\globaldefs} ++ */ + if (global_defs_par != 0) { + if (global_defs_par < 0) { + if (is_global(a)) +@@ -2221,15 +2626,18 @@ void prefixed_command(void) + } + 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.) */ ++ /*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: +- /* 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. */ +- ++ /*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); +@@ -2246,35 +2654,15 @@ void prefixed_command(void) + 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); ++ 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(); +@@ -2284,21 +2672,60 @@ void prefixed_command(void) + if (cur_cmd == spacer_cmd) + get_token(); + } +- } else { ++ 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; +- tex_error("invalid number for \\letcharcode",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: +- /* 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}'. +- */ ++ /*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; +@@ -2311,13 +2738,13 @@ void prefixed_command(void) + 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 { ++ /* 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); +@@ -2363,8 +2790,10 @@ void prefixed_command(void) + n = cur_val; + if (!scan_keyword("to")) { + print_err("Missing `to' inserted"); +- help2("You should have said `\\read to \\cs'.", +- "I'm going to look for the \\cs now."); ++ help2( ++ "You should have said `\\read to \\cs'.", ++ "I'm going to look for the \\cs now." ++ ); + error(); + } + get_r_token(); +@@ -2374,25 +2803,30 @@ void prefixed_command(void) + 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.) */ ++ /*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 { +- p = cur_chr; /* |p=every_par_loc| or |output_routine_loc| or \dots */ ++ /*tex |p=every_par_loc| or |output_routine_loc| or \dots */ ++ p = cur_chr; + } + scan_optional_equals(); +- /* Get the next non-blank non-relax non-call token */ ++ /*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) { +- /* If the right-hand side is a token parameter +- or token register, finish the assignment and |goto done| */ ++ /*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; +@@ -2431,7 +2865,7 @@ void prefixed_command(void) + } + break; + case assign_int_cmd: +- /* Similar routines are used to assign values to the numeric parameters. */ ++ /*tex Similar routines are used to assign values to the numeric parameters. */ + p = cur_chr; + scan_optional_equals(); + scan_int(); +@@ -2446,9 +2880,18 @@ void prefixed_command(void) + attr_list_cache = cache_disabled; + word_define(p, cur_val); + break; ++ case assign_direction_cmd: + case assign_dir_cmd: +- /* DIR: Assign direction codes */ +- scan_direction(); ++ /*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); +@@ -2487,17 +2930,19 @@ void prefixed_command(void) + } + if (abs(mode) == hmode) { + if (no_local_dirs_par > 0) { +- /* tail is non zero but we test anyway */ ++ /*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 - dir_swap); ++ 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 - dir_swap)); ++ tail_append(new_dir(text_direction_par)); ++ subtype(tail) = cancel_dir; + } + } else { +- /* what is the use of nolocaldirs .. maybe we should get rid of it */ ++ /*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)); +@@ -2505,19 +2950,6 @@ void prefixed_command(void) + } 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; +@@ -2542,14 +2974,13 @@ void prefixed_command(void) + break; + case def_char_code_cmd: + case def_del_code_cmd: +- /* Let |n| be the largest legal code value, based on |cur_chr| */ ++ /*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)) +@@ -2621,8 +3052,9 @@ void prefixed_command(void) + 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'."); ++ help1( ++ "A style should have been here; I inserted `\\displaystyle'." ++ ); + cur_val1 = display_style; + back_error(); + } else { +@@ -2652,9 +3084,11 @@ void prefixed_command(void) + 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. */ ++ /*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; +@@ -2665,16 +3099,22 @@ void prefixed_command(void) + 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."); ++ 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: +- /* 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. */ ++ /*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: +@@ -2687,8 +3127,11 @@ void prefixed_command(void) + 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. */ ++ /*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: +@@ -2722,7 +3165,7 @@ void prefixed_command(void) + p = new_node(shape_node, 2 * n + 1 + 1); + vinfo(p + 1) = n; + n = cur_val; +- varmem[p + 2].cint = n; /* number of penalties */ ++ 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 */ +@@ -2733,8 +3176,10 @@ void prefixed_command(void) + 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. ++ /*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: +@@ -2801,9 +3246,20 @@ void prefixed_command(void) + } + break; + case def_font_cmd: +- /* Here is where the information for a new font gets loaded. */ ++ /*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; +@@ -2821,9 +3277,10 @@ void prefixed_command(void) + default: + confusion("prefix"); + break; +- } /* end of Assignments cases */ ++ } ++ /*tex End of assignments cases. */ + DONE: +- /* Insert a token saved by \.{\\afterassignment}, if any */ ++ /*tex Insert a token saved by \.{\\afterassignment}, if any. */ + if (after_token != 0) { + cur_tok = after_token; + back_input(); +@@ -2831,14 +3288,13 @@ void prefixed_command(void) + } + } + +-@ @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| */ ++ /* Remove from |text_dir_ptr|. */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; +@@ -2846,22 +3302,50 @@ void fixup_directions(void) + unsave(); + if (abs(mode) == hmode) { + if (temp_no_dirs != 0) { +- /* DIR: Add local dir node */ ++ /* Add local dir node. */ + tail_append(new_dir(text_direction_par)); +- dir_dir(tail) = temporary_dir - dir_swap; ++ dir_dir(tail) = temporary_dir; ++ subtype(tail) = cancel_dir; + } + if (temp_no_whatsits != 0) { +- /* LOCAL: Add local paragraph node */ ++ /*tex 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. ++/*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. ++ ++*/ + +-@c + void get_r_token(void) + { + RESTART: +@@ -2871,11 +3355,13 @@ void get_r_token(void) + 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."); ++ 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; +@@ -2884,7 +3370,6 @@ void get_r_token(void) + } + } + +-@ @c + void assign_internal_value(int a, halfword p, int val) + { + halfword n; +@@ -2896,17 +3381,19 @@ void assign_internal_value(int a, halfword p, int val) + 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"); ++ 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."); ++ help1( ++ "The value for \\outputbox has to be between 0 and 65535." ++ ); + error(); + } else { + word_define(p, val); +@@ -2915,9 +3402,10 @@ void assign_internal_value(int a, halfword p, int val) + 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."); ++ help2( ++ "The value for \\newlinechar has to be no higher than 127.", ++ "Your invalid assignment will be ignored." ++ ); + error(); + } else { + word_define(p, val); +@@ -2926,9 +3414,10 @@ void assign_internal_value(int a, halfword p, int val) + 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."); ++ help2( ++ "The value for \\endlinechar has to be no higher than 127.", ++ "Your invalid assignment will be ignored." ++ ); + error(); + } else { + word_define(p, val); +@@ -2940,9 +3429,10 @@ void assign_internal_value(int a, halfword p, int val) + 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."); ++ 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); +@@ -2953,15 +3443,17 @@ void assign_internal_value(int a, halfword p, int val) + 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. */ ++ /*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)))) { +- /* LOCAL: Add local paragraph node */ ++ /*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); + } +@@ -2980,20 +3472,23 @@ void assign_internal_value(int a, halfword p, int val) + word_define(dimen_base + page_top_offset_code, n); + } + word_define(p, val); +- } else if ((p >= local_base) && (p < toks_base)) { /* internal locals */ ++ } else if ((p >= local_base) && (p < toks_base)) { ++ /*tex internal locals */ + define(p, call_cmd, val); + } else { + confusion("assign internal value"); + } + } + +-@ We use the fact that |register= glue_val_level) + flush_node(cur_val); + error(); +@@ -3126,10 +3624,9 @@ void do_register_command(int a) + } + } + +-@ @c + void alter_aux(void) + { +- halfword c; /* |hmode| or |vmode| */ ++ halfword c; /* |hmode| or |vmode| */ + if (cur_chr != abs(mode)) { + report_illegal_case(); + } else { +@@ -3142,7 +3639,9 @@ void alter_aux(void) + 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."); ++ help1( ++ "I allow only values in the range 1..32767 here." ++ ); + int_error(cur_val); + } else { + space_factor_par = cur_val; +@@ -3151,39 +3650,39 @@ void alter_aux(void) + } + } + +-@ @c + void alter_prev_graf(void) + { +- int p; /* index into |nest| */ +- p = nest_ptr; ++ 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."); ++ 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; ++ int c = cur_chr; /* index into |page_so_far| */ + scan_optional_equals(); + scan_normal_dimen(); + page_so_far[c] = cur_val; + } + +-@ @c ++/*tex ++ The value of |c| is 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. ++*/ ++ + void alter_integer(void) + { +- int c; /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */ +- c = cur_chr; ++ int c = cur_chr; + scan_optional_equals(); + scan_int(); + if (c == 0) { +@@ -3191,8 +3690,10 @@ void alter_integer(void) + } 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."); ++ 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; +@@ -3203,11 +3704,10 @@ void alter_integer(void) + } + } + +-@ @c + void alter_box_dimen(void) + { +- int c; /* |width_offset| or |height_offset| or |depth_offset| */ +- int b; /* box number */ ++ int c; /* |width_offset| or |height_offset| or |depth_offset| */ ++ int b; /* box number */ + c = cur_chr; + scan_register_num(); + b = cur_val; +@@ -3217,7 +3717,6 @@ void alter_box_dimen(void) + varmem[box(b) + c].cint = cur_val; + } + +-@ @c + void new_interaction(void) + { + print_ln(); +@@ -3229,21 +3728,27 @@ void new_interaction(void) + 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. ++/*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 + +-@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'. + +-@ 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... */ ++ /*tex Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); +@@ -3255,13 +3760,13 @@ void do_assignments(void) + } + } + +-@ @c ++/*tex |cur_chr| is 1 for \.{\\openin}, 0 for \.{\\closein}: */ ++ + void open_or_close_in(void) + { +- int c; /* 1 for \.{\\openin}, 0 for \.{\\closein} */ +- int n; /* stream number */ ++ int c = cur_chr; ++ int n; + char *fn; +- c = cur_chr; + scan_four_bit_int(); + n = cur_val; + if (read_open[n] != closed) { +@@ -3275,7 +3780,8 @@ void open_or_close_in(void) + } 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 */ ++ /*tex Set |cur_name| to desired file name. */ ++ scan_file_name(); + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(".tex"); + } else { +@@ -3288,14 +3794,17 @@ void open_or_close_in(void) + } + } + +-@ @c +-boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */ ++/*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 */ ++ 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; +@@ -3306,7 +3815,7 @@ void issue_message(void) + str_room(1); + s = make_string(); + if (c == 0) { +- /* Print string |s| on the terminal */ ++ /*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)) +@@ -3315,23 +3824,29 @@ void issue_message(void) + 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. */ ++ /*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.)"); ++ 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."); ++ 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; +@@ -3340,34 +3855,40 @@ void issue_message(void) + flush_str(s); + } + +-@ The |error| routine calls on |give_err_help| if help is requested from +-the |err_help| parameter. ++/*tex ++ ++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. ++/*tex ++ ++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 */ ++ 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. ++ /*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) { +@@ -3390,27 +3911,30 @@ void shift_case(void) + p = token_link(p); + } + back_list(token_link(def_ref)); +- free_avail(def_ref); /* omit reference count */ ++ free_avail(def_ref); + } + +-@ We come finally to the last pieces missing from |main_control|, namely the ++/*tex ++ ++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 */ ++ 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 */ ++ /*tex Show the current contents of a box. */ + scan_register_num(); + begin_diagnostic(); + tprint_nl("> \\box"); +@@ -3422,7 +3946,7 @@ void show_whatever(void) + show_box(box(cur_val)); + break; + case show_code: +- /* Show the current meaning of a token, then |goto common_ending| */ ++ /*tex Show the current meaning of a token, then |goto common_ending|. */ + get_token(); + if (interaction == error_stop_mode) + wake_up_terminal(); +@@ -3434,7 +3958,7 @@ void show_whatever(void) + print_meaning(); + goto COMMON_ENDING; + break; +- /* Cases for |show_whatever| */ ++ /*tex Cases for |show_whatever| */ + case show_groups: + begin_diagnostic(); + show_save_groups(); +@@ -3474,8 +3998,10 @@ void show_whatever(void) + } + break; + default: +- /* Show the current value of some parameter or register, +- then |goto common_ending| */ ++ /*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(); +@@ -3485,7 +4011,7 @@ void show_whatever(void) + goto COMMON_ENDING; + break; + } +- /* Complete a potentially long \.{\\show} command */ ++ /*tex Complete a potentially long \.{\\show} command: */ + end_diagnostic(true); + print_err("OK"); + if (selector == term_and_log) { +@@ -3500,32 +4026,40 @@ void show_whatever(void) + 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)."); ++ 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."); ++ 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 ++/*tex ++ This procedure gets things started properly: ++*/ ++ + 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 */ ++{ ++ 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(); +- +- /* Start a new current page */ ++ /*tex Start a new current page: */ + page_contents = empty; + page_tail = page_head; + #if 0 +@@ -3539,7 +4073,7 @@ void initialize(void) + page_max_depth = 0; + + initialize_equivalents(); +- no_new_control_sequence = true; /* new identifiers are usually forbidden */ ++ no_new_control_sequence = true; /* new identifiers are usually forbidden */ + init_primitives(); + + mag_set = 0; +@@ -3559,11 +4093,10 @@ void initialize(void) + stop_at_space = true; + + if (ini_version) { +- /* Initialize table entries (done by \.{INITEX} only) */ +- ++ /*tex Initialize table entries (done by \.{INITEX} only). */ + init_node_mem(500); + initialize_tokens(); +- /* Initialize the special list heads and constant nodes */ ++ /*tex Initialize the special list heads and constant nodes. */ + initialize_alignments(); + initialize_buildpage(); + +@@ -3629,7 +4162,9 @@ void initialize(void) + 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 */ +@@ -3642,7 +4177,7 @@ void initialize(void) + page_right_offset_par = one_inch; + page_bottom_offset_par = one_inch; + ini_init_primitives(); +- hash_used = frozen_control_sequence; /* nothing is used */ ++ hash_used = frozen_control_sequence; + hash_high = 0; + cs_count = 0; + set_eq_type(frozen_dont_expand, dont_expand_cmd); +@@ -3655,16 +4190,16 @@ void initialize(void) + 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); +- /* bah */ ++ /*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; +- + } +diff --git a/texk/web2c/luatexdir/tex/mathcodes.w b/texk/web2c/luatexdir/tex/mathcodes.c +similarity index 80% +rename from texk/web2c/luatexdir/tex/mathcodes.w +rename to texk/web2c/luatexdir/tex/mathcodes.c +index a0a357e53..42e8b0b15 100644 +--- a/texk/web2c/luatexdir/tex/mathcodes.w ++++ b/texk/web2c/luatexdir/tex/mathcodes.c +@@ -1,81 +1,88 @@ +-% mathnodes.w +-% +-% Copyright 2006-2012 Taco Hoekwater +-% Copyright 2012 Khaled Hosny +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++mathnodes.w ++ ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ math codes +-@c ++/*tex ++ ++We support tre traditional math codes as well as larger ones suitable for ++\UNICODE\ input and fonts. ++ ++*/ ++ + static sa_tree mathcode_head = NULL; + +-/* the 0xFFFFFFFF is a flag value */ ++/*tex the |0xFFFFFFFF| is a flag value. */ + + #define MATHCODESTACK 8 + #define MATHCODEDEFAULT 0xFFFFFFFF + #define MATHCODEACTIVE 0xFFFFFFFE + +-@ delcodes +-@c ++/*tex ++ ++Delcodes are also went larger. ++ ++*/ ++ + static sa_tree delcode_head = NULL; + + #define DELCODESTACK 4 + #define DELCODEDEFAULT 0xFFFFFFFF + +-@ some helpers for mathcode printing ++/*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. ++ ++*/ ++ + +-@c +-#define print_hex_digit(A) do { \ +- if ((A)>=10) print_char('A'+(A)-10); \ +- else print_char('0'+(A)); \ ++#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); \ ++#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); \ ++#define four_hex(A) do { \ ++ two_hex((A)/256); \ ++ two_hex((A)%256); \ + } while (0) + +-#define six_hex(A) do { \ +- two_hex((A)/65536); \ ++#define six_hex(A) do { \ ++ two_hex((A)/65536); \ + two_hex(((A)%65536)/256); \ +- two_hex((A)%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); +@@ -88,12 +95,12 @@ mathcodeval mathchar_from_integer(int value, int extcode) + return mval; + } + +-@ @c + void show_mathcode_value_old(int value) + { + print_char('"'); + four_hex(value); + } ++ + void show_mathcode_value(mathcodeval c) + { + print_char('"'); +@@ -104,7 +111,6 @@ void show_mathcode_value(mathcodeval c) + six_hex(c.character_value); + } + +-@ @c + static void show_mathcode(int n) + { + mathcodeval c = get_math_code(n); +@@ -114,7 +120,6 @@ static void show_mathcode(int n) + show_mathcode_value(c); + } + +-@ @c + static void unsavemathcode(quarterword gl) + { + sa_stack_item st; +@@ -138,7 +143,6 @@ static void unsavemathcode(quarterword gl) + } + } + +-@ @c + void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword level) + { + sa_tree_item v; +@@ -161,9 +165,6 @@ void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quar + } + } + +-@ @c +-/* we could use two structs ... tex and umath */ +- + mathcodeval get_math_code(int n) + { + mathcodeval d; +@@ -189,14 +190,12 @@ mathcodeval get_math_code(int n) + 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 }; +@@ -214,7 +213,6 @@ static void undumpmathcode(void) + mathcode_head = undump_sa_tree("mathcodes"); + } + +-@ @c + static void show_delcode(int n) + { + delcodeval c; +@@ -232,7 +230,6 @@ static void show_delcode(int n) + } + } + +-@ @c + static void unsavedelcode(quarterword gl) + { + sa_stack_item st; +@@ -256,7 +253,6 @@ static void unsavedelcode(quarterword gl) + } + } + +-@ @c + void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl) + { + sa_tree_item v; +@@ -266,7 +262,8 @@ void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, i + 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 */ ++ /*tex Always global! */ ++ set_sa_item(delcode_head, n, v, gl); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + print_char('{'); +@@ -278,7 +275,6 @@ void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, i + } + } + +-@ @c + delcodeval get_del_code(int n) + { + delcodeval d; +@@ -299,9 +295,12 @@ delcodeval get_del_code(int n) + return d; + } + +-@ this really only works for old-style delcodes! ++/*tex ++ ++This really only works for old-style delcodes! ++ ++*/ + +-@c + int get_del_code_num(int n) + { + delcodeval d = get_del_code(n); +@@ -313,7 +312,6 @@ int get_del_code_num(int n) + } + } + +-@ @c + static void initializedelcode(void) + { + sa_tree_item sa_value = { 0 }; +@@ -321,7 +319,6 @@ static void initializedelcode(void) + delcode_head = new_sa_tree(DELCODESTACK, 2, sa_value); + } + +-@ @c + static void dumpdelcode(void) + { + dump_sa_tree(delcode_head,"delcodes"); +@@ -332,28 +329,24 @@ 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(); +diff --git a/texk/web2c/luatexdir/tex/memoryword.c b/texk/web2c/luatexdir/tex/memoryword.c +new file mode 100644 +index 000000000..7bf758dd0 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/memoryword.c +@@ -0,0 +1,29 @@ ++/* ++memoryword.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#include "ptexlib.h" ++ ++/*tex ++ ++The debug code is no longer present! ++ ++*/ +diff --git a/texk/web2c/luatexdir/tex/memoryword.w b/texk/web2c/luatexdir/tex/memoryword.w +deleted file mode 100644 +index b596abb37..000000000 +--- a/texk/web2c/luatexdir/tex/memoryword.w ++++ /dev/null +@@ -1,55 +0,0 @@ +-% memoryword.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +- +-#include "ptexlib.h" +- +-@ 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 +diff --git a/texk/web2c/luatexdir/tex/mlist.w b/texk/web2c/luatexdir/tex/mlist.c +similarity index 66% +rename from texk/web2c/luatexdir/tex/mlist.w +rename to texk/web2c/luatexdir/tex/mlist.c +index 291ce8c48..8f6d71eb2 100644 +--- a/texk/web2c/luatexdir/tex/mlist.w ++++ b/texk/web2c/luatexdir/tex/mlist.c +@@ -1,71 +1,72 @@ +-% mlist.w +-% +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-% (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 ++/* ++ ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ ++/*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)) + +-@ +-\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; \ +@@ -97,83 +98,160 @@ already apply that shift. + #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 ++/*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. +-@^parameters for symbols@> +-@^font parameters@> ++ 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. ++ ++*/ + +-@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 ++/*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) +-#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. ++/*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 */ + +-ScriptPercentScaleDown, +-ScriptScriptPercentScaleDown: +- These should be handled by the macro package, on the engine +- side there are three separate fonts ++#define big_op_spacing4(A) mathex(A,12) + +-DelimitedSubFormulaMinHeight: +- This is perhaps related to word's natural math input? I have +- no idea what to do about it ++/*tex padding above and below displayed limits */ + +-MathLeading: +- LuaTeX does not currently handle multi-line displays, and +- the parameter does not seem to make much sense elsewhere ++#define big_op_spacing5(A) mathex(A,13) + +-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 ++/*tex + +-Also still TODO for OpenType Math: +- * prescripts ++ \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. + +-@ this is not really a math parameter at all ++ \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 ++ ++*/ + +-@c + static void math_param_error(const char *param, int style) + { + char s[256]; +@@ -184,16 +262,11 @@ static void math_param_error(const char *param, int style) + "the parameter mentioned earlier.", + NULL + }; +- snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", +- param, math_style_names[style]); ++ 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; +@@ -207,11 +280,14 @@ static scaled accent_base_height(int 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. ++/*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. ++ ++*/ + +-@c + scaled get_math_quad_style(int var) + { + scaled a = get_math_param(math_param_quad, var); +@@ -223,10 +299,13 @@ scaled get_math_quad_style(int var) + } + } + +-@ For this reason the next one is different because it is called with a size +-specifier instead of a style specifier. ++/*tex ++ ++ 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; +@@ -246,7 +325,6 @@ static scaled math_axis_size(int b) + } + } + +-@ @c + scaled get_math_quad_size(int b) + { + int var; +@@ -259,26 +337,32 @@ scaled get_math_quad_size(int b) + 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. ++/*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. ++ ++*/ + +-@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 ++/*tex ++ ++ 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) + +@@ -301,9 +385,12 @@ static scaled do_get_math_param_or_zero(int var, int param, const char *name) + return a; + } + +-@ A variant on a suggestion on the list based on analysis by UV. ++/*tex ++ ++ 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) { +@@ -313,7 +400,7 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { + } + delta1 = max_h + max_d - delta2; + if (delta2 > delta1) { +- /* |delta1| is max distance from axis */ ++ /*tex |delta1| is max distance from axis */ + delta1 = delta2; + } + delta = (delta1 / 500) * delimiter_factor_par; +@@ -325,7 +412,6 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { + } + } + +-@ @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) +@@ -359,9 +445,7 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { + #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) +-*/ ++/* 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) +@@ -390,11 +474,11 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { + + #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)) { + +- if (is_new_mathfont(f)) { /* fix all known parameters */ ++ /*tex Fix all known parameters. */ + + DEFINE_MATH_PARAMETERS(math_param_quad, size_id, + font_size(f), lvl); +@@ -632,7 +716,7 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) + + } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) { + +- /* fix old-style |sy| parameters */ ++ /*tex Fix old-style |sy| parameters. */ + + DEFINE_MATH_PARAMETERS(math_param_quad, size_id, + math_quad(size_id), lvl); +@@ -746,9 +830,11 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int 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. ++ /*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, +@@ -768,7 +854,7 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) + + } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) { + +- /* fix old-style |ex| parameters */ ++ /*tex Fix old-style |ex| parameters. */ + + DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id, + default_rule_thickness(size_id), lvl); +@@ -853,9 +939,12 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int 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 ++ /*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, +@@ -880,9 +969,11 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int 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. ++ /*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, +@@ -891,10 +982,13 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) + } + } + +-@ This needs to be called just at the start of |mlist_to_hlist|, for +-backward compatibility with \.{\\scriptspace}. ++/*tex ++ ++ 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; +@@ -920,16 +1014,19 @@ static void finalize_math_parameters(void) + 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. ++/*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.) + +-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) +@@ -940,10 +1037,13 @@ static const char *math_size_string(int s) + return "scriptscriptfont"; + } + +-@ When the style changes, the following piece of program computes associated +-information: ++/*tex ++ ++ 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; \ +@@ -954,8 +1054,12 @@ information: + } while (0) + + +-@ a simple routine that creates a flat copy of a nucleus +-@c ++/*tex ++ ++ A simple routine that creates a flat copy of a nucleus. ++ ++*/ ++ + static pointer math_clone(pointer q) + { + pointer x; +@@ -972,14 +1076,17 @@ static pointer math_clone(pointer 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. ++/*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. ++ ++*/ + +-@c + static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfword cur_size, halfword cur_fam) + { +- pointer p; /* the new node */ ++ pointer p; + if (math_rules_mode_par) { + p = new_rule(some_rule); + rule_math_size(p) = cur_size; +@@ -994,14 +1101,17 @@ static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfw + 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|. ++/*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|. ++ ++*/ + +-@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 */ ++ pointer p, q; + p = new_kern(k); + reset_attributes(p, att); + couple_nodes(p,b); +@@ -1015,16 +1125,20 @@ static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, ha + 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. ++/*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. ++ ++*/ + +-@c + static pointer char_box(internal_font_number f, int c, pointer bb) + { +- pointer b, p; /* the new box and its character node */ ++ /*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); +@@ -1039,24 +1153,31 @@ static pointer char_box(internal_font_number f, int c, pointer bb) + return b; + } + +-@ Another handy subroutine computes the height plus depth of +- a given character: ++/*tex ++ ++ 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|: ++/*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|: ++ ++*/ + +-@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 */ ++ /*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; +@@ -1085,7 +1206,9 @@ 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 (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; +@@ -1094,59 +1217,96 @@ static void stack_glue_into_box(pointer b, scaled min, scaled max) { + if (q == null) { + list_ptr(b) = p; + } else { +- while (vlink(q) != null) ++ 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) ++/*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) + { +- pointer b; /* new box */ +- scaled b_max; /* natural (maximum) size of the stack */ +- scaled s_max; /* amount of possible shrink in the stack */ ++ 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; +- 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| */ ++ 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; +- +- 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; +- ++ 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(f, cur->glyph)) { ++ 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!", +@@ -1160,7 +1320,7 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s + num_extenders++; + else + num_normal++; +- /* no negative overlaps or advances are allowed */ ++ /*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.", +@@ -1190,16 +1350,16 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s + 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. ++ 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; +@@ -1217,12 +1377,15 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s + 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); ++ /*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; +@@ -1236,12 +1399,15 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s + 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); ++ /*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; +@@ -1250,10 +1416,11 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s + } + } + } ++ /*tex ++ ++ Assemble box using |with_extenders| copies of each extender, with ++ appropriate glue wherever an overlap occurs. + +- /* +- assemble box using |with_extenders| copies of each extender, with +- appropriate glue wherever an overlap occurs + */ + prev_overlap = 0; + b_max = 0; +@@ -1271,7 +1438,7 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s + s_max += (-c) - (-d); + b_max -= d; + } +- b_max += stack_into_box(b, f, cur->glyph); ++ b_max += stack_into_box(b, fnt, cur->glyph); + prev_overlap = cur->end_overlap; + i--; + } else { +@@ -1288,19 +1455,17 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s + s_max += (-c) - (-d); + b_max -= d; + } +- b_max += stack_into_box(b, f, cur->glyph); ++ b_max += stack_into_box(b, fnt, cur->glyph); + prev_overlap = cur->end_overlap; + i--; + } + } + } +- +- /* set glue so as to stretch the connections if needed */ +- ++ /*tex 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| */ ++ /*tex Don't stretch more than |s_max|. */ + if (d > s_max) + d = s_max; + glue_order(b) = normal; +@@ -1308,32 +1473,33 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s + glue_set(b) = unfloat(d/(float) s_max); + b_max += d; + } +- +- if (boxtype == vlist_node) { +- height(b) = b_max; +- } else { ++ if (horizontal) { + width(b) = b_max; ++ } else { ++ height(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 ++/*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]; +@@ -1343,23 +1509,31 @@ static void endless_loop_error(internal_font_number g, int y) + "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)); ++ 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 */ ++ /*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; +@@ -1373,10 +1547,12 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, + 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. ++ /*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); +@@ -1403,7 +1579,6 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, + goto FOUND; + } + if (i > 10000) { +- /* endless loop */ + endless_loop_error(g, y); + goto FOUND; + } +@@ -1414,8 +1589,10 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, + } + } + } +- if (large_attempt) +- goto FOUND; /* there were none large enough */ ++ if (large_attempt) { ++ /*tex There were none large enough. */ ++ goto FOUND; ++ } + large_attempt = true; + z = large_fam(d); + x = large_char(d); +@@ -1427,17 +1604,20 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, + 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. ++ /*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(d, ext, f, v, att, cur_style, hlist_node); ++ b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 1, att); + } else { +- b = get_delim_box(d, ext, f, v, att, cur_style, vlist_node); ++ b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 0, att); + } + if (delta != NULL) { + if (do_new_math(f)) { +@@ -1449,16 +1629,18 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, + 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)) { +- /* italic gets added to width */ ++ /*tex Italic gets added to width. */ + width(b) += char_italic(f, c); + } + if (delta != NULL) { +- *delta = char_italic(f, c); /* was historically (f, x) */ ++ /*tex This used to be (f, x). */ ++ *delta = char_italic(f, c); + } + if (stack != NULL) + *stack = false ; +@@ -1469,7 +1651,7 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, + if (flat) { + width(b) = 0; + } else { +- /* use this width if no delimiter was found */ ++ /*tex Use this width if no delimiter was found. */ + width(b) = null_delimiter_space_par; + } + if (delta != NULL) { +@@ -1479,38 +1661,54 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, + *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); +- } ++ /*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; + } + +-@ 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. ++/*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. + +-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 */ +- ++ /*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); +@@ -1549,23 +1747,30 @@ static pointer rebox(pointer b, scaled w) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@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 */ ++ /*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); +- width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */ ++ /* 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)); +@@ -1581,13 +1786,16 @@ static pointer math_glue(pointer g, scaled m) + + 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| */ ++ /*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; + } +- width(p) = mu_mult(width(p)); /* convert \.{mu} to \.{pt} */ ++ /* 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) +@@ -1595,14 +1803,18 @@ static void math_glue_to_glue(pointer p, scaled m) + subtype(p) = normal; + } + +-@ The |math_kern| subroutine removes |mu_glue| from a kern node, given +-the value of the math unit. ++/*tex ++ ++ 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| */ ++ /*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; +@@ -1611,15 +1823,15 @@ static void math_kern(pointer p, scaled m) + f = f + unity; + } + width(p) = mu_mult(width(p)); +- subtype(p) = italic_kern; /* this is weird, it's not a italic but explicit_kern */ ++ /* this is weird, it's not a italic but explicit_kern */ ++ subtype(p) = italic_kern; + } + } + +-@ @c + void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle) + { + int callback_id; +- int a, sfix; ++ int a, sfix, i; + if (p == null) { + vlink(temp_head) = null; + return; +@@ -1636,18 +1848,16 @@ void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle) + 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'; ++ if ((i=lua_pcall(Luas, 3, 1, 0)) != 0) { ++ formatted_warning("mlist to hlist","error: %s",lua_tostring(Luas, -1)); + lua_settop(Luas, sfix); +- normal_error("mlist to hlist",errmsg); /* to be done */ ++ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return; + } +- a = nodelist_from_lua(Luas); ++ a = nodelist_from_lua(Luas,-1); + /* alink(vlink(a)) = null; */ +- lua_settop(Luas, sfix); + vlink(temp_head) = a; ++ lua_settop(Luas, sfix); + } else if (callback_id == 0) { + mlist_to_hlist(p, penalties, mstyle); + } else { +@@ -1655,22 +1865,27 @@ void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle) + } + } + +-@ 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@> ++/*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. ++ 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 */ ++ /*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(); +@@ -1689,18 +1904,20 @@ static pointer clean_box(pointer p, int s, int cur_style) + goto FOUND; + } + mlist_to_hlist(mlist, false, s); +- q = vlink(temp_head); /* recursive call */ ++ /*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)) +- x = q; /* it's already clean */ ++ /*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)); +- /* Here we save memory space in a common case. */ ++ /*tex Here we save memory space in a common case. */ + q = list_ptr(x); + if (is_char_node(q)) { + r = vlink(q); +@@ -1708,7 +1925,7 @@ static pointer clean_box(pointer p, int s, int cur_style) + if (vlink(r) == null) { + if (!is_char_node(r)) { + if (type(r) == kern_node) { +- /* unneeded italic correction */ ++ /*tex Unneeded italic correction. */ + flush_node(r); + vlink(q) = null; + } +@@ -1719,22 +1936,30 @@ static pointer clean_box(pointer p, int s, int cur_style) + 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|. ++/*tex + +-The outputs of |fetch| are placed in global variables. ++ 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|. + +-@c +-internal_font_number cur_f; /* the |font| field of a |math_char| */ +-int cur_c; /* the |character| field of a |math_char| */ ++ The outputs of |fetch| are placed in global variables. + +-@ Here we unpack the |math_char| field |a|. ++*/ ++ ++/*tex the |font| field of a |math_char| */ ++ ++internal_font_number cur_f; ++ ++/*tex the |character| field of a |math_char| */ + +-@c static void fetch(pointer a) ++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); +@@ -1757,75 +1982,125 @@ int cur_c; /* the |character| field of a |math_char| */ + } + } + +-@ We need to do a lot of different things, so |mlist_to_hlist| makes two +-passes over the given mlist. ++/*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|. +-@^recursion@> ++ 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. ++ 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; ++ 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. ++/*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. ++ ++*/ + +-@c + 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), overbar_rule(cur_style), +- overbar_kern(cur_style), node_attr(nucleus(q)), math_over_rule, cur_size, cur_fam); ++ 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) + { +- pointer p, x, y, r; /* temporary registers for box construction */ +- scaled delta; /* overall height plus depth */ ++ /*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); +- r = do_fraction_rule(underbar_rule(cur_style), node_attr(q), math_under_rule, cur_size, 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, 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)); +@@ -1838,8 +2113,10 @@ static void make_under(pointer q, int cur_style, int cur_size, int cur_fam) + + static void make_vcenter(pointer q) + { +- pointer v; /* the box that should be centered vertically */ +- scaled delta; /* its height plus depth */ ++ /*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"); +@@ -1848,16 +2125,19 @@ static void make_vcenter(pointer q) + 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. ++/*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. ++ ++*/ + +-@c + static void make_hextension(pointer q, int cur_style) + { + pointer e, p; +@@ -1885,25 +2165,50 @@ static void make_hextension(pointer q, int cur_style) + + 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 */ ++ /*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) { +- /* a real radical */ ++ /*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)) { +- /* possible composite */ ++ /*tex possible composite */ + l2 = list_ptr(l1); + if ((l2 != null) && (type(l2) == glyph_node)) { +- /* top character */ ++ /*tex top character */ + theta = char_height(font(l2), character(l2)); + } else { + theta = height(y); +@@ -1912,18 +2217,29 @@ static void make_radical(pointer q, int cur_style) + theta = height(y); + } + } else { +- /* not really a radical but we use its node, historical sharing (like in mathml) */ ++ /*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) { +- /* increase the actual clearance */ ++ /*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, small_fam(left_delimiter(q))); ++ 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; +@@ -1949,7 +2265,7 @@ static void make_radical(pointer q, int cur_style) + couple_nodes(x,r); + y = x; + } +- /* for \.{\\Uroot ..{}{}} : */ ++ /*tex for \.{\\Uroot ..{}{}} : */ + math_list(degree(q)) = null; + flush_node(degree(q)); + } +@@ -1959,12 +2275,11 @@ static void make_radical(pointer q, int cur_style) + type(nucleus(q)) = sub_box_node; + } + +-@ Construct a vlist box ++/*tex 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 p; + pointer v = new_null_box(); + type(v) = vlist_node; + height(v) = shift_up + height(x); +@@ -1978,9 +2293,7 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal + return v; + } + +-/* when exact use radicalwidth (y is delimiter) */ +- +-@ @c ++/*tex When |exact| use radicalwidth (|y| is delimiter). */ + + #define fixup_widths(q,x,y) do { \ + if (width(y) >= width(x)) { \ +@@ -2014,7 +2327,7 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal + width(r) = radicalwidth(q); \ + reset_attributes(r, node_attr(q)); \ + } else if (radicalright(q)) { \ +- /* also kind of exact compared to vertical */ \ ++ /*tex also kind of exact compared to vertical */ \ + r = hpack(r, 0, additional, -1); \ + width(r) = radicalwidth(q); \ + reset_attributes(r, node_attr(q)); \ +@@ -2030,12 +2343,15 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal + } \ + } while (0) + +-@ this has the |nucleus| box |x| as a limit above an extensible delimiter |y| ++/*tex ++ ++ 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 */ ++ 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); +@@ -2052,17 +2368,21 @@ static void make_over_delimiter(pointer q, int cur_style) + 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)| */ ++ /*tex This also equals |width(y)|: */ ++ width(v) = width(x); + math_list(nucleus(q)) = v; + type(nucleus(q)) = sub_box_node; + } + +-@ this has the extensible delimiter |x| as a limit below |nucleus| box |y| ++/*tex ++ ++ 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 */ ++ 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); +@@ -2079,17 +2399,21 @@ static void make_under_delimiter(pointer q, int cur_style) + 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)| */ ++ /*tex This also equals |width(y)|: */ ++ width(v) = 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| ++/*tex ++ ++ 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 */ ++ pointer x, y, v; + scaled shift_up, shift_down, clr, actual, wd; + boolean stack; + y = clean_box(nucleus(q), cur_style, cur_style); +@@ -2106,17 +2430,21 @@ static void make_delimiter_over(pointer q, int cur_style) + 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)| */ ++ /*tex This also equals |width(y)|: */ ++ width(v) = width(x); + 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| ++/*tex ++ ++ 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 */ ++ pointer x, y, v; + scaled shift_up, shift_down, clr, actual, wd; + boolean stack; + x = clean_box(nucleus(q), cur_style, cur_style); +@@ -2133,16 +2461,20 @@ static void make_delimiter_under(pointer q, int cur_style) + 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)| */ ++ /*tex This also equals |width(y)|: */ ++ width(v) = 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. ++/*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. ++ ++*/ + +-@c + #define TOP_CODE 1 + #define BOT_CODE 2 + #define OVERLAY_CODE 4 +@@ -2150,14 +2482,17 @@ respect to the size of the final box. + + 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| */ ++ /*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)) { +- /* +- there is no bot_accent so let's assume similarity ++ /*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) { +@@ -2169,6 +2504,7 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s) + s_is_absolute = true; + } + } ++ \stoptyping + */ + *s = char_top_accent(cur_f, cur_c); + if (*s != INT_MIN) { +@@ -2182,15 +2518,24 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s) + } + } + } 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: ++ /*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 +@@ -2213,12 +2558,18 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s) + + 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| */ ++ /*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 ; +@@ -2232,7 +2583,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl + if (fraction == 0) { + fraction = 1000; + } +- /* Compute the amount of skew, or set |s| to an alignment point */ ++ /*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); +@@ -2241,7 +2592,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl + s = half(w); + s_is_absolute = true; + } +- /* Switch to a larger accent if available and appropriate */ ++ /*tex Switch to a larger accent if available and appropriate */ + y = null; + ext = NULL; + if (flags & OVERLAY_CODE) { +@@ -2258,31 +2609,32 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl + } + } + 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)) { ++ 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 if (flags & OVERLAY_CODE) { +- if (char_height(f, yy) > target) { +- break; +- } + } else { +- if (char_width(f, yy) > target) +- break; ++ 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; + } +- c = yy; + } +- } + } + if (y == null) { +- y = char_box(f, c, node_attr(attr_p)); /* italic gets added to width */ ++ /*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)) { +@@ -2291,13 +2643,14 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl + 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 */ ++ /*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) { +- /* swap the subscript and superscript into box |x| */ ++ /*tex swap the subscript and superscript into box |x| */ + flush_node_list(x); + x = new_noad(); + r = math_clone(nucleus(q)); +@@ -2313,32 +2666,34 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl + h = height(x); + } + } else if ((vlink(q) != null) && (type(nucleus(q)) == math_char_node)) { +- /* only pure math char nodes */ ++ /*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))); + } + } +- /* the top accents of both characters are aligned */ ++ /*tex 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 */ ++ /*tex if the accent is extensible just take the center */ + sa = half(width(y)); + } else { +- /* +- there is no bot_accent so let's assume similarity ++ /*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) { +- /* just take the center */ ++ /*tex just take the center */ + sa = half(width(y)); + } + if (math_direction_par == dir_TRT) { +@@ -2371,7 +2726,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl + y = r; + if (flags & (TOP_CODE | OVERLAY_CODE)) { + if (height(y) < h) { +- /* make the height of box |y| equal to |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)); +@@ -2382,7 +2737,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl + shift_amount(y) = -(h - height(y)); + } + if (ic != 0) { +- /* old font codepath has ic built in, new font code doesn't */ ++ /*tex old font codepath has ic built in, new font code doesn't */ + width(r) += ic ; + } + math_list(nucleus(q)) = y; +@@ -2420,26 +2775,48 @@ static void make_math_accent(pointer q, int cur_style) + } + } + +-@ The |make_fraction| procedure is a bit different because it sets +-|new_hlist(q)| directly rather than making a sub-box. ++/*tex ++ ++ 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 */ ++ 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 +- */ ++ 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); +@@ -2452,17 +2829,18 @@ static void make_fraction(pointer q, int cur_style) + 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|. ++ /*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))); +@@ -2473,9 +2851,11 @@ static void make_fraction(pointer q, int cur_style) + } 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. ++ /*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); +@@ -2497,15 +2877,17 @@ static void make_fraction(pointer q, int cur_style) + } + } + if (m != null) { +- /* +- construct a hlist box for the fraction, according to |hgap| and |vgap| ++ /*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)); +@@ -2516,7 +2898,6 @@ static void make_fraction(pointer q, int cur_style) + 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; +@@ -2526,7 +2907,6 @@ static void make_fraction(pointer q, int cur_style) + 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; +@@ -2546,7 +2926,6 @@ static void make_fraction(pointer q, int cur_style) + if (depth(m) > depth(v)) { + depth(v) = depth(m); + } +- + if (fractionexact(q)) { + delta1 = -half(skewed_fraction_hgap(cur_style)); + delta2 = delta1; +@@ -2557,33 +2936,34 @@ static void make_fraction(pointer q, int cur_style) + 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| ++ /*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; +- width(v) = width(x); /* this also equals |width(z)| */ ++ /*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, math_rules_fam_par); ++ 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); +@@ -2595,16 +2975,14 @@ static void make_fraction(pointer q, int cur_style) + 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 ++ /*tex ++ ++ 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); +- } ++ delta = fraction_del_size_new(cur_style); + if (delta == undefined_math_parameter) { + delta = get_delimiter_height(depth(v), height(v), true); + } +@@ -2622,31 +3000,38 @@ static void make_fraction(pointer q, int cur_style) + 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. ++/*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. + +-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. + +-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 */ ++ /*tex offset between subscript and superscript */ ++ scaled delta = 0; + 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 */ ++ /*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)) { +@@ -2655,10 +3040,10 @@ static scaled make_op(pointer q, int cur_style) + if (type(nucleus(q)) == math_char_node) { + fetch(nucleus(q)); + if (cur_style < text_style) { +- /* try to make it larger */ ++ /*tex 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 */ ++ /*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)); +@@ -2666,9 +3051,9 @@ static scaled make_op(pointer q, int cur_style) + 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 */ ++ /*tex we never added italic correction */ + } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { +- /* remove italic correction */ ++ /*tex remove italic correction */ + width(x) -= delta; + } + } +@@ -2685,82 +3070,91 @@ static scaled make_op(pointer q, int cur_style) + x = clean_box(nucleus(q), cur_style, cur_style); + if (delta != 0) { + if (do_new_math(cur_f)) { +- /* we never added italic correction */ ++ /*tex we never added italic correction */ + } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { +- /* remove italic correction */ ++ /*tex remove italic correction */ + width(x) -= delta; + } + } + axis_shift = true; + } + } else { +- /* normal size */ ++ /*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)) { +- /* we never added italic correction */ ++ /*tex we never added italic correction */ + } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { +- /* remove italic correction */ ++ /*tex remove italic correction */ + width(x) -= delta; + } + } + axis_shift = true; + } + if (axis_shift) { +- /* center vertically */ ++ /*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; + } +- +- /* we now handle op_nod_type_no_limits here too */ +- ++ /*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 */ +- /* +- for easy configuration ... fonts are somewhat inconsistent and the +- values for italic correction run from 30 to 60% of the width ++ /*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 : +- /* full bottom correction */ ++ /*tex full bottom correction */ + make_scripts(q, p, 0, cur_style, 0, -delta); + break; + case 1 : +- /* MathConstants driven */ ++ /*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 : +- /* no correction */ ++ /*tex no correction */ + make_scripts(q, p, 0, cur_style, 0, 0); + break ; + case 3 : +- /* half bottom correction */ ++ /*tex half bottom correction */ + make_scripts(q, p, 0, cur_style, 0, -half(delta)); + break; + case 4 : +- /* half bottom and top correction */ ++ /*tex 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 */ ++ /*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); +@@ -2770,7 +3164,7 @@ static scaled make_op(pointer q, int cur_style) + } + delta = 0; + } else { +- /* similar code then the caller (before CHECK_DIMENSIONS) */ ++ /*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); +@@ -2779,8 +3173,13 @@ static scaled make_op(pointer q, int cur_style) + } + } + } 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. */ ++ /*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); +@@ -2788,38 +3187,36 @@ static scaled make_op(pointer q, int cur_style) + 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); ++ 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)); + } +- } 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 { ++ 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); + } ++ 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)); +- } ++ } ++ } else { ++ n = nucleus(q); ++ if (type(n) == math_char_node) { ++ delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n)); + } + } + } +@@ -2834,23 +3231,23 @@ static scaled make_op(pointer q, int cur_style) + z = rebox(z, width(v)); + shift_amount(x) = half(delta); + shift_amount(z) = -shift_amount(x); +- /* v is the still empty target */ ++ /*tex 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 ++ /*tex + +- 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| ++ Attach the limits to |y| and adjust |height(v)|, |depth(v)| to ++ account for their presence. + +- 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 ++ 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|. + +- b: baseline v: minumum gap +- */ ++ 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); +@@ -2905,17 +3302,20 @@ static scaled make_op(pointer q, int cur_style) + 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|. ++/*tex + +-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|). ++ 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|. + +-No boundary characters enter into these ligatures. ++ 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) && \ +@@ -2928,10 +3328,14 @@ No boundary characters enter into these ligatures. + + 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 */ ++ /*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); +@@ -2939,7 +3343,7 @@ static void make_ord(pointer q) + type(nucleus(q)) = math_text_char_node; + fetch(nucleus(q)); + a = cur_c; +- /* add italic correction */ ++ /*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; +@@ -2948,45 +3352,49 @@ static void make_ord(pointer q) + couple_nodes(q,p); + return; + } +- /* construct ligatures, quite unlikely in new math fonts */ ++ /*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)); +- /* +- 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| ++ /*tex + +- 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 ++ 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|. + +- 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 +- */ ++ 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 */ ++ /*tex allow a way out of infinite ligature loop */ ++ check_interrupt(); + switch (lig_type(lig)) { + case 1: +- /* \.{=:\char`\|} */ ++ /*tex \.{=:\char`\|} */ + case 5: +- /* \.{=:\char`\|>} */ ++ /*tex \.{=:\char`\|>} */ + math_character(nucleus(q)) = lig_replacement(lig); + break; + case 2: +- /* \.{\char`\|=:} */ ++ /*tex \.{\char`\|=:} */ + case 6: +- /* \.{\char`\|=:>} */ ++ /*tex \.{\char`\|=:>} */ + math_character(nucleus(p)) = lig_replacement(lig); + break; + case 3: +- /* \.{\char`\|=:\char`\|} */ ++ /*tex \.{\char`\|=:\char`\|} */ + case 7: +- /* \.{\char`\|=:\char`\|>} */ ++ /*tex \.{\char`\|=:\char`\|>} */ + case 11: +- /* \.{\char`\|=:\char`\|>>} */ ++ /*tex \.{\char`\|=:\char`\|>>} */ + r = new_noad(); + reset_attributes(r, node_attr(q)); + s = new_node(math_char_node, 0); +@@ -2996,21 +3404,20 @@ static void make_ord(pointer q) + math_fam(nucleus(r)) = math_fam(nucleus(q)); + couple_nodes(q,r); + couple_nodes(r,p); +- if (lig_type(lig) < 11) ++ if (lig_type(lig) < 11) { + type(nucleus(r)) = math_char_node; +- else +- /* prevent combination */ ++ } 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); /* \.{=:} */ +- 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)); ++ subscr(q) = subscr(p); ++ supscr(q) = supscr(p); ++ subscr(p) = null ; ++ supscr(p) = null ; + flush_node(p); + break; + } +@@ -3021,7 +3428,7 @@ static void make_ord(pointer q) + } + } + if (disable_kern_par == 0 && has_kern(cur_f, a)) { +- /* todo: should this use mathkerns? */ ++ /*tex todo: should this use mathkerns? */ + k = get_kern(cur_f, a, cur_c); + if (k != 0) { + p = new_kern(k); +@@ -3036,32 +3443,35 @@ static void make_ord(pointer q) + } + } + +-@ 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 ++/*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 ++ ++*/ + +-@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. ++/*tex + +-The microsoft spec says that there are four quadrants, but the +-actual images say ++ 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 */ ++ /*tex Known to exist: */ ++ charinfo *co = char_info(f, c); + 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) { +@@ -3073,21 +3483,15 @@ static scaled math_kern_at(internal_font_number f, int c, int side, int v) + } 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; /* not reached */ ++ kerns_heights = NULL; + } +-#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; + } +@@ -3095,68 +3499,55 @@ static scaled math_kern_at(internal_font_number f, int c, int side, int v) + 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) ++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))) ++ 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 */ ++ /*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); +-#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 */ ++ /*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); +-#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 */ ++ /*tex Not reached: */ ++ return 0; + } + +-@ just a small helper +-@c +-static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2) ++/*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) { +- /* this is somewhat weird */ ++ /*tex this is somewhat weird */ + new_hlist(q) = z; + } else { + y = new_hlist(q); +@@ -3167,67 +3558,36 @@ static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2) + 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; +- } +-} ++/*tex + +-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|. + +-@ 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. + +-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. + +-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) { \ ++ 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)) { \ +@@ -3312,10 +3672,10 @@ the new fonts so eventualy there will be an option to ignore such corrections. + + 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 */ ++ pointer x, y, z; ++ scaled shift_up, shift_down, clr; + scaled delta1, delta2; +- halfword sub_n, sup_n; ++ halfword sub_n, sup_n, subtyp; + internal_font_number sub_f, sup_f; + int sub_c, sup_c; + sub_n = null; +@@ -3326,18 +3686,13 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled + 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 ++ subtyp = 0; + 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 */ ++ /*tex todo: selective italic correction */ ++ x = new_kern(delta1); + subtype(x) = italic_kern; + reset_attributes(x, node_attr(nucleus(q))); + couple_nodes(p,x); +@@ -3355,22 +3710,20 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled + 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}) */ ++ /*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); +- /* we look at the superscript character (^i) or first character in a list (^{ij}) */ ++ /*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) { +- /* +- construct a subscript box |x| when there is no superscript ++ /*tex + +- when there is a subscript without a superscript, the top of the subscript ++ 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) { +@@ -3398,30 +3751,29 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled + break; + } + shift_amount(x) = shift_down; +- +- /* now find and correct for horizontal shift */ ++ /*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); ++ p = attach_hkern_to_new_hlist(q, delta2, subtyp); + } +- + } else { +- /* +- construct a superscript box |x| ++ /*tex ++ ++ Construct a superscript box |x|. The bottom of a superscript should ++ never descend below the baseline plus one-fourth of the x-height. + +- 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) { +@@ -3451,33 +3803,35 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled + } + if (subscr(q) == null) { + shift_amount(x) = -shift_up; +- /* now find and correct for horizontal shift */ ++ /*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); ++ p = attach_hkern_to_new_hlist(q, clr, subtyp); + } + } else { +- /* +- construct a sub/superscript combination box |x|, with the superscript offset +- by |delta| ++ /*tex + +- when both subscript and superscript are present, the subscript must be +- separated from the superscript by at least four times |default_rule_thickness| ++ 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. + +- 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) { +@@ -3510,30 +3864,37 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled + } + break; + } +- /* now find and correct for horizontal shift */ ++ /*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); ++ p = attach_hkern_to_new_hlist(q, delta2, subtyp); + } +- /* +- now the horizontal shift for the superscript; the superscript is also to be shifted +- by |delta1| (the italic correction) ++ /*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 + +- /* delta can already have been applied and now be 0 */ ++ The delta can already have been applied and now be 0. ++ ++ */ + if (delta2 == MATH_KERN_NOT_FOUND) + delta2 = - supshift ; + else +@@ -3543,18 +3904,17 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled + } else { + shift_amount(x) = delta1 - delta2; + } +- /* todo: only if kern != 0 */ ++ /*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); +- /* we end up with funny dimensions */ ++ /*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 { +@@ -3575,12 +3935,15 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@c + static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h) + { + scaled delta; +@@ -3589,21 +3952,21 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m + 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) */ ++ 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); +@@ -3629,7 +3992,7 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m + } + delimiter(q) = null; + assign_new_hlist(q, tmp); +- delimitersamesize(q) = same; /* new */ ++ delimitersamesize(q) = same; + if (delimiterclass(q) >= ord_noad_type) { + if (delimiterclass(q) <= inner_noad_type) { + return delimiterclass(q); +@@ -3643,35 +4006,32 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m + } + } + +-@ @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); \ ++#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); \ ++#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)); \ ++#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)); \ ++#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); +@@ -3743,10 +4103,8 @@ void initialize_math_spacing(void) + 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) +@@ -3758,102 +4116,99 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu) + 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* */ ++ 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) { +- /* trap thin/med/thick settings cf. old TeX */ +- z = math_glue(glue_par(x), mmu); /* allocates a glue */ +- /* store a symbolic subtype */ ++ /*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); /* allocates a glue */ ++ z = math_glue(x, mmu); + } + } + return z; + } + +-@ @c + static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same) + { + pointer p = null; +@@ -3866,37 +4221,36 @@ static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_styl + case math_text_char_node: + fetch(nucleus(q)); + if (char_exists(cur_f, cur_c)) { +- /* we could look at neighbours */ ++ /*tex we could look at neighbours */ + if (do_new_math(cur_f)) { +- *delta = 0 ; /* cf spec only the last one */ ++ /*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 (! 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 */ ++ if (get_char_cat_code(cur_c) == 11) { ++ /*tex no italic correction in mid-word of text font */ + *delta = 0; + } + } else { +- /* no italic correction in mid-word of text font */ ++ /*tex 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 */ ++ /*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 /* needs checking but looks ok */ +- if (do_new_math(cur_f)) { +- *delta = char_italic(cur_f, cur_c); /* must be more selective */ ++ } 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; +@@ -3905,60 +4259,80 @@ static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_styl + 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) ; +-} ++ /*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"); /* this can't happen mlist2 */ ++ confusion("mlist2"); + } + return p; + } + +-@ Here is the overall plan of |mlist_to_hlist|, and the list of its +- local variables. ++/*tex ++ ++ 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 */ ++ /*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; +- 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| */ ++ /*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) { +- /* we could do this via the callback but it's nice to have it as primitive too */ ++ /*tex ++ 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. ++ /*tex + +- 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 ++ 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. + +- the values of |r| and |r_type| make this fairly easy + */ + RESWITCH: + delta = 0; +@@ -3966,66 +4340,69 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) + 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; ++ 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 fence_noad: +- if (r_subtype == left_noad_side) { +- subtype(q) = ord_noad_type; /* so these can best be the same size */ +- goto RESWITCH; ++ 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; +- } +- 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; ++ 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 */ ++ /*tex Assumes the same size. */ ++ type(r) = simple_noad; + subtype(r) = ord_noad_type; + } + } +@@ -4060,16 +4437,20 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) + break; + case choice_node: + switch (cur_style / 2) { +- case 0: /* |display_style=0| */ ++ case 0: ++ /*tex |display_style=0| */ + choose_mlist(display_mlist); + break; +- case 1: /* |text_style=2| */ ++ case 1: ++ /*tex |text_style=2| */ + choose_mlist(text_mlist); + break; +- case 2: /* |script_style=4| */ ++ case 2: ++ /*tex |script_style=4| */ + choose_mlist(script_mlist); + break; +- case 3: /* |script_script_style=6| */ ++ case 3: ++ /*tex |script_script_style=6| */ + choose_mlist(script_script_mlist); + break; + } +@@ -4105,14 +4486,16 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) + 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| ++ /*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| ++ Unconditional math glue (`\.{\\muskip}') is converted to ++ normal glue by multiplying the dimensions by |cur_mu|. + + */ + if (subtype(q) == mu_glue) { +@@ -4139,29 +4522,31 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) + 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. ++ /*tex + +- 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. ++ 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 ; ++ same = 0 ; + p = check_nucleus_complexity(q, &delta, cur_style, &same); +-if (same) { +- noadextra4(q) = 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. ++ 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) { +@@ -4199,7 +4584,7 @@ if (same) { + } + assign_new_hlist(q, p); + } else { +- /* top, bottom */ ++ /*tex top, bottom */ + make_scripts(q, p, delta, cur_style, 0, 0); + } + CHECK_DIMENSIONS: +@@ -4209,7 +4594,7 @@ if (same) { + if (depth(z) > max_d) + max_d = depth(z); + list_ptr(z) = null; +- /* only drop the \.{\\hbox} */ ++ /*tex only drop the \.{\\hbox} */ + flush_node(z); + DONE_WITH_NOAD: + r = q; +@@ -4219,7 +4604,8 @@ if (same) { + 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 */ ++ /*tex style */ ++ cur_mu = x_over_n(get_math_quad_size(cur_size), 18); + } + DONE_WITH_NODE: + q = vlink(q); +@@ -4228,15 +4614,18 @@ if (same) { + 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. ++ 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; +@@ -4248,15 +4637,18 @@ if (same) { + 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 ++ 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. + +- 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; +@@ -4292,7 +4684,7 @@ if (same) { + t_subtype = make_left_right(q, style, max_d, max_hl); + break; + case style_node: +- /* Change the current style and |goto delete_q| */ ++ /*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); +@@ -4316,34 +4708,36 @@ if (same) { + default: + confusion("mlist3"); + } +- /* Append inter-element spacing based on |r_type| and |t| */ ++ /*tex Append inter-element spacing based on |r_type| and |t| */ + if (r_type > 0) { +- /* not the first noad */ ++ /*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 (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 */ ++ /*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; + } + } +- /* +- Append any |new_hlist| entries for |q|, and any appropriate penalties ++ /*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. + +- 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)); +@@ -4370,12 +4764,13 @@ if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1) + 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). ++ /*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. + +- 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)); +diff --git a/texk/web2c/luatexdir/tex/nesting.c b/texk/web2c/luatexdir/tex/nesting.c +new file mode 100644 +index 000000000..0e50c1031 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/nesting.c +@@ -0,0 +1,429 @@ ++/* ++ ++nesting.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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; ++ } ++ } ++} +diff --git a/texk/web2c/luatexdir/tex/nesting.w b/texk/web2c/luatexdir/tex/nesting.w +deleted file mode 100644 +index d9e7cf744..000000000 +--- a/texk/web2c/luatexdir/tex/nesting.w ++++ /dev/null +@@ -1,436 +0,0 @@ +-% nesting.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +-#include "ptexlib.h" +- +-@ 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 */ +- +- } +-} +diff --git a/texk/web2c/luatexdir/tex/packaging.w b/texk/web2c/luatexdir/tex/packaging.c +similarity index 62% +rename from texk/web2c/luatexdir/tex/packaging.w +rename to texk/web2c/luatexdir/tex/packaging.c +index f094a0d96..e2b007f11 100644 +--- a/texk/web2c/luatexdir/tex/packaging.w ++++ b/texk/web2c/luatexdir/tex/packaging.c +@@ -1,84 +1,68 @@ +-% packaging.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ + + #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(); +- } ++/*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) +-{ /* scans a box specification and left brace */ ++{ + int spec_code; + boolean done = false ; + do { +@@ -89,7 +73,7 @@ void scan_spec(group_code c) + cur_val = 0; + done = true; + } else { +- /* todo: attr */ ++ /*tex todo: attr */ + back_input(); + if (scan_keyword("to")) { + spec_code = exactly; +@@ -110,84 +94,21 @@ void scan_spec(group_code c) + } + } + +-@ 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. ++/*tex + +-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}). ++ 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 */ ++/*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) + { +@@ -195,10 +116,12 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) + 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; +- s = saved_value(0); /* the box context */ ++ /*tex The box context: */ ++ s = saved_value(0); + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); +@@ -219,6 +142,7 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) + } + } + KEYWORDS: ++ /*tex Multiple |attr| keys possible (before or after dir). */ + if (scan_keyword("attr")) { + scan_register_num(); + i = cur_val; +@@ -232,11 +156,23 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) + attr_list = do_set_attribute(attr_list, i, v); + goto CONTINUE; + } +- if (scan_keyword("dir")) { +- scan_direction(); +- spec_direction = cur_val; +- 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")) { +@@ -256,7 +192,7 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) + 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| */ ++ /*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); +@@ -270,73 +206,91 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) + if (! done) { + scan_left_brace(); + } +- /* no gain: if (body_direction_par != spec_direction) etc */ ++ /*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. ++ 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.) ++ 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}. ++ 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|. */ + +-@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 */ ++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|. ++ 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 */ ++*/ ++ ++/*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|. ++ 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 */ ++/*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. */ + +-@ 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) +-*/ ++/*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). + ++*/ + +-@ @c + scaled char_stretch(halfword p) + { + internal_font_number f = font(p); +@@ -345,7 +299,7 @@ scaled char_stretch(halfword p) + 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); ++ scaled dw = calc_char_width(f, c, m) - char_width(f, c); + if (dw > 0) { + return round_xn_over_d(dw, ef, 1000); + } +@@ -354,7 +308,6 @@ scaled char_stretch(halfword p) + return 0; + } + +-@ @c + scaled char_shrink(halfword p) + { + internal_font_number f = font(p); +@@ -363,7 +316,7 @@ scaled char_shrink(halfword p) + 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); ++ scaled dw = char_width(f, c) - calc_char_width(f, c, -m); + if (dw > 0) { + return round_xn_over_d(dw, ef, 1000); + } +@@ -372,33 +325,6 @@ scaled char_shrink(halfword p) + 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; +@@ -407,69 +333,39 @@ scaled kern_stretch(halfword p) + halfword l; + halfword r; + if (w == 0) { +- /* why bother about zero kerns */ ++ /*tex 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*/ ++ /*tex 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 */ ++ /*tex 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) */ ++ /*tex 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 */ ++ /*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) { +- /* nothing to kern */ ++ /*tex 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 */ ++ /*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); + } +- /* +- 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; +@@ -478,26 +374,26 @@ scaled kern_shrink(halfword p) + halfword l; + halfword r; + if (w == 0) { +- /* why bother about zero kerns */ ++ /*tex 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*/ ++ /*tex 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 */ ++ /*tex 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) */ ++ /*tex 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 */ ++ /*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) { +- /* nothing to kern */ ++ /*tex Nothing to kern. */ + return 0; + } + d = round_xn_over_d(w, 1000 - m, 1000); +@@ -507,13 +403,9 @@ scaled kern_shrink(halfword p) + } 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) { +@@ -555,7 +447,6 @@ void do_subst_font(halfword p, int ex_ratio) + } + } + +-@ @c + scaled char_pw(halfword p, int side) + { + internal_font_number f; +@@ -582,7 +473,6 @@ scaled char_pw(halfword p, int side) + return round_xn_over_d(w, c, 1000); + } + +-@ @c + halfword new_margin_kern(scaled w, halfword p, int side) + { + halfword k, q; +@@ -595,11 +485,16 @@ halfword new_margin_kern(scaled w, halfword p, int side) + return k; + } + +-@ Here is |hpack|, which is place where we do font substituting when font +-expansion is being used. ++/*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. */ + +-@c +-int font_expand_ratio = 0; /* current expansion ratio, needed for recursive call */ ++int font_expand_ratio = 0; + + int ignore_math_skip(halfword p) + { +@@ -632,47 +527,60 @@ int ignore_math_skip(halfword p) + + 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 */ ++ /*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; +- scaled s; /* shift amount */ +- int o; /* order of infinity */ +- halfword dir_ptr1 = null; /* for managing the direction stack */ +- int hpack_dir; /* the current direction */ ++ /*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; +- +-/* +- int font_expand_ratio = 0; +-*/ + last_badness = 0; +- r = new_node(hlist_node, min_quarterword); /* the box node that will be returned */ ++ /*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; +- /* +- potential optimimization, save a little but neglectable in practice (not so +- many empty boxes are used) ++ /*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 ++ + */ +- push_dir(dir_ptr1,hpack_dir); /* push null */ +- q = r + list_offset; /* hm, adding something to a node address? */ ++ /*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) { +- prev_char_p = null; /* why not always */ ++ /*tex Why not always: */ ++ prev_char_p = null; + } + if (adjust_spacing > 2) { + adjust_spacing = 0; +@@ -687,7 +595,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + total_shrink[fill] = 0; + total_stretch[filll] = 0; + total_shrink[filll] = 0; +- + RESWITCH: + while ((p != null) || (disc_level > 0)) { + if (p == null) { +@@ -695,20 +602,24 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + 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 ++ /*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) { +@@ -730,13 +641,16 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + switch (type(p)) { + case hlist_node: + case vlist_node: +- /* +- Incorporate box dimensions into the dimensions of the hbox +- that will contain~it ++ /*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. + +- 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); +@@ -746,20 +660,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + 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); +@@ -768,18 +668,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + if (depth(p) > d) + d = depth(p); + break; +- /* */ + case math_node: +- /* begin mathskip code */ ++ /*tex Begin mathskip code. */ + if (glue_is_zero(p) || ignore_math_skip(p)) { + x += surround(p); + break; + } else { +- /* fall through */ ++ /*tex Fall through. */ + } +- /* end mathskip code */ ++ /*tex End mathskip code. */ + case glue_node: +- /* Incorporate glue into the horizontal totals */ ++ /*tex Incorporate glue into the horizontal totals. */ + x += width(p); + o = stretch_order(p); + total_stretch[o] = total_stretch[o] + stretch(p); +@@ -796,12 +695,12 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + case kern_node: + x += width(p); + if (subtype(p) == font_kern && adjust_spacing) { +- /* so only when 1 or 2 */ ++ /*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) { +- /* this is the finalizer */ ++ /*tex This is the finalizer. */ + int k = 0; + if (font_expand_ratio > 0) { + k = kern_stretch(p); +@@ -810,9 +709,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + } + ex_kern(p) = k; + x += k; +- /* +- if (x!=0) printf("SET %i %i %i\n",font_expand_ratio,k,x); +- */ + } + } + break; +@@ -826,8 +722,8 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + } + break; + case dir_node: +- /* Adjust the dir stack for the |hpack| routine */ +- if (dir_dir(p) >= 0) { ++ /*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 { +@@ -856,15 +752,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + 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. +- */ ++ /*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); +@@ -885,13 +783,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + p = q; + } + break; +- /* */ + default: + break; + } + p = vlink(p); + } +- + } + + if (adjust_tail != null) +@@ -900,31 +796,35 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + 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; +- /* now |x| is the excess to be made up */ ++ /*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) { +- /* +- Determine horizontal glue stretch setting, then |return| +- or \hbox{|goto common_ending|}. ++ /*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; +@@ -936,7 +836,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + 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; +@@ -946,15 +845,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + if (total_stretch[o] != 0) { + glue_set(r) = unfloat((double) x / total_stretch[o]); + } else { +- /* there's nothing to stretch */ ++ /*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) { +- /* +- Report an underfull hbox and |goto common_ending|, if this box +- is sufficiently bad. ++ /*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) { +@@ -987,9 +888,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + } + goto EXIT; + } else { +- /* +- Determine horizontal glue shrink setting, then |return| +- or \hbox{|goto common_ending|}, ++ /*tex ++ ++ Determine horizontal glue shrink setting, then |return| or |goto ++ common_ending|, ++ + */ + if (total_shrink[filll] != 0) + o = filll; +@@ -1001,7 +904,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + 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; +@@ -1011,18 +913,20 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + if (total_shrink[o] != 0) { + glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]); + } else { +- /* there's nothing to shrink */ ++ /*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; +- /* use the maximum shrinkage */ ++ /*tex 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. ++ /*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); +@@ -1050,9 +954,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + } + } 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) { +@@ -1079,10 +985,7 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + } + + COMMON_ENDING: +- /* +- Finish issuing a diagnostic message for an overfull or underfull +- hbox. +- */ ++ /*tex Finish issuing a diagnostic message for an overfull or underfull hbox. */ + if (output_active) { + tprint(") has occurred while \\output is active"); + } else { +@@ -1113,17 +1016,16 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) + q = list_ptr(r); + list_ptr(r) = null; + flush_node(r); +- /* this nested call uses the more or less global font_expand_ratio */ ++ /*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); +- /* here we reset the font_expan_ratio */ ++ /*tex Here we reset the |font_expand_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; +@@ -1131,32 +1033,33 @@ halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int p + 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 */ ++ /*tex We don't care about the tail in this case. */ ++ (void) new_ligkern(p, qt); + 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 */ ++ /*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); + } + +-@ here is a function to calculate the natural whd of a (horizontal) node list ++/*tex + +-@c +-scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, +- int g_sign, int g_order, int pack_direction) ++ 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) + { +- scaled s; /* shift amount */ +- halfword g; /* points to a glue specification */ ++ /*tex shift amount */ ++ scaled s; ++ /*tex points to a glue specification */ ++ halfword g; + int hpack_dir; +- scaled_whd xx; /* for recursion */ ++ /*tex For recursion */ ++ scaled_whd xx; + scaled_whd whd, siz = { 0, 0, 0 }; + scaled gp = 0; + scaled gm = 0; +@@ -1187,20 +1090,6 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, + 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); +@@ -1209,29 +1098,28 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, + if (depth(p) > siz.dp) + siz.dp = depth(p); + break; +- /* */ + case math_node: +- /* begin mathskip code */ ++ /*tex Begin mathskip code. */ + if (glue_is_zero(p) || ignore_math_skip(p)) { + siz.wd += surround(p); + break; + } else { +- /* fall through */ ++ /*tex Fall through. */ + } +- /* end mathskip code */ ++ /*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) { +- /* +- siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p))); ++ /*tex ++ |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))); ++ /*tex ++ |siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));| + */ + gm += shrink(p); + } +@@ -1275,34 +1163,46 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, + 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. ++/*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: ++ 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. ++/*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. ++ ++*/ + +-@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 */ ++ /*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; +- scaled s; /* shift amount */ +- int o; /* order of infinity */ ++ /*tex shift amount */ ++ scaled s; ++ /*tex order of infinity */ ++ int o; + last_badness = 0; + r = new_node(vlist_node, 0); + if (pack_direction == -1) { +@@ -1323,12 +1223,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + 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. ++ /*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"); +@@ -1336,9 +1236,11 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + switch (type(p)) { + case hlist_node: + case vlist_node: +- /* +- Incorporate box dimensions into the dimensions of +- the vbox that will contain it. ++ /*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); +@@ -1347,19 +1249,6 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + 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); +@@ -1367,9 +1256,8 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + if (width(p) > w) + w = width(p); + break; +- /* */ + case glue_node: +- /* Incorporate glue into the vertical totals */ ++ /*tex Incorporate glue into the vertical totals. */ + x += d; + d = 0; + x += width(p); +@@ -1400,27 +1288,30 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + } else { + depth(r) = d; + } +- /* +- Determine the value of |height(r)| and the appropriate glue setting; +- then |return| or |goto common_ending|. ++ /*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. ++ 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 */ ++ /*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) { +- /* +- Determine vertical glue stretch setting, then |return| +- or \hbox{|goto common_ending|}. ++ /*tex ++ ++ Determine vertical glue stretch setting, then |return| or |goto ++ common_ending|. ++ + */ + if (total_stretch[filll] != 0) + o = filll; +@@ -1432,20 +1323,22 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + 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)); /* there's nothing to stretch */ ++ set_glue_ratio_zero(glue_set(r)); + } + if (o == normal) { + if (list_ptr(r) != null) { +- /* +- Report an underfull vbox and |goto common_ending|, if this box +- is sufficiently bad. ++ /*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) { +@@ -1471,11 +1364,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + } + } + return r; +- + } else { +- /* +- Determine vertical glue shrink setting, then |return| +- or \hbox{|goto common_ending|}. ++ /*tex ++ ++ Determine vertical glue shrink setting, then |return| or |goto ++ common_ending|. ++ + */ + if (total_shrink[filll] != 0) + o = filll; +@@ -1487,24 +1381,25 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + 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 */ ++ /*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; +- /* use the maximum shrinkage */ ++ /*tex 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. ++ /*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); +@@ -1521,9 +1416,11 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + } + } 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) { +@@ -1544,12 +1441,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + } + + COMMON_ENDING: +- /* Finish issuing a diagnostic message or an overfull or underfull vbox */ ++ /*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) { +- /* it's actually negative */ ++ /*tex It's actually negative. */ + tprint(") in alignment at lines "); + print_int(abs(pack_begin_line)); + tprint("--"); +@@ -1566,17 +1463,14 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) + 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; +@@ -1588,12 +1482,11 @@ void finish_vcenter(void) + 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 */ ++ scaled d = box_max_depth_par; + unsave(); + save_ptr -= 5; + saved0 = saved_value(0); +@@ -1608,11 +1501,13 @@ void package(int c) + 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); +@@ -1625,7 +1520,7 @@ void package(int c) + } + } + if (saved2 != null) { +- /* DIR: Adjust back |text_dir_ptr| for |scan_spec| */ ++ /*tex Adjust back |text_dir_ptr| for |scan_spec| */ + flush_node_list(text_dir_ptr); + text_dir_ptr = saved2; + +@@ -1635,14 +1530,19 @@ void package(int c) + 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. ++/*tex ++ ++ 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 */ ++ /*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; +@@ -1682,188 +1582,235 @@ void append_to_vlist(halfword b, int location) + } + } + +-@ 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 ++ ++ 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} */ + +-@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} */ ++#define split_disc disc_ptr[vsplit_code] + +-halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */ ++/*tex 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. ++halfword disc_ptr[(vsplit_code + 1)]; + +-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. ++/*tex + +-When the second argument |s| is |false| the deleted nodes are destroyed, +-otherwise they are collected in a list starting at |split_disc|. ++ 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| */ ++ /*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: +- /* 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; ++ 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 +- vlink(r) = q; +- r = q; +- } else { +- flush_node_list(q); +- } +- break; +- default: +- confusion("pruning"); +- break; ++ 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. ++/*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. + +-@c +-scaled active_height[10]; /* distance from first active node to~|cur_p| */ ++*/ ++ ++/*tex The distance from first active node to |cur_p|: */ ++ ++scaled active_height[10]; + +-@ 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. ++/*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. ++ ++*/ + +-@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.) ++/*tex + +-@ height of the best box, without stretching or shrinking ++ 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.) + +-@c +-scaled best_height_plus_depth; ++*/ + +-/* finds optimum page break */ ++/*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) + { +- 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 */ ++ /*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) { +- /* 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. */ ++ /*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 { +- /* 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 */ ++ /*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; ++ 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 */ ++ /*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) { +- /* Compute the badness, |b|, using |awful_bad| if the box is too full */ ++ /*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)) +@@ -1875,7 +1822,6 @@ halfword vert_break(halfword p, scaled h, scaled d) + } else { + b = badness(cur_height - h, active_height[7]); + } +- + if (b < awful_bad) { + if (pi <= eject_penalty) + b = pi; +@@ -1892,25 +1838,29 @@ halfword vert_break(halfword p, scaled h, scaled d) + 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. */ ++ /*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."); ++ 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; + } +@@ -1929,31 +1879,39 @@ halfword vert_break(halfword p, scaled h, scaled d) + 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. ++/*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|. ++ 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). ++ 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| */ ++*/ ++ ++/*tex Extract 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 */ ++ /*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); +@@ -1962,23 +1920,27 @@ halfword vsplit(halfword n, scaled h, int m) + delete_split_first_mark(i); + delete_split_bot_mark(i); + } +- /* Dispense with trivial cases of void or bad boxes */ ++ /*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."); ++ 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. ++ /*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) { +@@ -2009,7 +1971,7 @@ halfword vsplit(halfword n, scaled h, int m) + list_ptr(v) = null; + flush_node(v); + if (q == null) { +- /* the |eq_level| of the box stays the same */ ++ /*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); +@@ -2021,17 +1983,23 @@ halfword vsplit(halfword n, scaled h, int m) + } + } + +-@ 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|. ++/*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|. ++ ++*/ + +-@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 */ ++ /*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 ; +@@ -2039,7 +2007,7 @@ void begin_box(int box_context) + case box_code: + scan_register_num(); + cur_box = box(cur_val); +- /* the box becomes void, at the same level */ ++ /*tex The box becomes void, at the same level. */ + box(cur_val) = null; + break; + case copy_code: +@@ -2047,10 +2015,11 @@ void begin_box(int box_context) + 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|. ++ /*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) { +@@ -2059,17 +2028,15 @@ void begin_box(int box_context) + 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."); ++ 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 ... */ ++ /*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; +@@ -2086,9 +2053,11 @@ void begin_box(int box_context) + } + 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}'. ++ /*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; +@@ -2096,18 +2065,22 @@ void begin_box(int box_context) + split_mode = additional ; + } else if (!scan_keyword("to")) { + print_err("Missing `to' inserted"); +- help2("I'm working on `\\vsplit to ';", +- "will look for the next."); ++ help2( ++ "I'm working on `\\vsplit to ';", ++ "will look for the 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. ++ /*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: +@@ -2123,7 +2096,6 @@ void begin_box(int box_context) + just_pack = 1; + break; + } +- /* */ + k = cur_chr - vtop_code; + set_saved_record(0, saved_boxcontext, 0, box_context); + switch (abs(cur_list.mode_field)) { +@@ -2165,6 +2137,6 @@ void begin_box(int box_context) + return; + break; + } +- /* in simple cases, we use the box immediately */ ++ /*tex In simple cases, we use the box immediately. */ + box_end(box_context); + } +diff --git a/texk/web2c/luatexdir/tex/postlinebreak.w b/texk/web2c/luatexdir/tex/postlinebreak.c +similarity index 52% +rename from texk/web2c/luatexdir/tex/postlinebreak.w +rename to texk/web2c/luatexdir/tex/postlinebreak.c +index dd85cf01b..cbc77e556 100644 +--- a/texk/web2c/luatexdir/tex/postlinebreak.w ++++ b/texk/web2c/luatexdir/tex/postlinebreak.c +@@ -1,60 +1,65 @@ +-% postlinebreak.w +-% +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@ @c ++postlinebreak.w + ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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. ++/*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 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. ++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 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. + +-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 */ ++/*tex The new name for |prev_break| after links are reversed: */ ++ ++#define next_break prev_break ++ ++/*tex The |int|s are actually halfwords. */ + +-/* the ints are actually halfwords */ + void ext_post_line_break(int paragraph_dir, + int right_skip, + int left_skip, +@@ -78,32 +83,39 @@ void ext_post_line_break(int paragraph_dir, + { + + boolean have_directional = true; +- halfword q, r; /* temporary registers for list manipulation */ ++ /*tex temporary registers for list manipulation */ ++ halfword q, r; + 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 */ +- ++ /*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; +- /* 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. +- */ ++ /*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); +-#if 0 +- used_discs = used_disc(best_bet); +-#endif +- /* |has_direction| */ ++ /*tex |cur_p| will become the first breakpoint; */ + cur_p = null; + do { + r = q; +@@ -111,73 +123,68 @@ void ext_post_line_break(int paragraph_dir, + 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 */ +- ++ /*tex prevgraf + 1 */ ++ cur_line = cur_list.pg_field + 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} */ +- } ++ /*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|. ++ */ + +- /* 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); ++ /*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); + } +- /* end mathskip code */ ++ /*tex end mathskip code */ ++ } + + r = cur_break(cur_p); + q = null; +@@ -189,27 +196,26 @@ void ext_post_line_break(int paragraph_dir, + 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 ? */ ++ /*tex This should almost always be true... */ + q = r; +- /* |q| refers to the last node of the line (and paragraph) */ ++ /*tex |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 */ ++ /*tex |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 */ ++ /*tex begin mathskip code */ + reset_glue_to_zero(r); +- /* end mathskip code */ ++ /*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; +- /* |q| refers to the last node of the line */ ++ /*tex |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 */ ++ /*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); +@@ -228,7 +234,6 @@ void ext_post_line_break(int paragraph_dir, + 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; +@@ -239,11 +244,10 @@ void ext_post_line_break(int paragraph_dir, + 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 */ ++ 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); +@@ -273,27 +277,24 @@ void ext_post_line_break(int paragraph_dir, + } 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 ??? */ ++ /*tex Adjust the dir stack based on dir nodes in this line. */ + if (have_directional) { +- halfword e; +- halfword p; ++ halfword e, 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) { ++ 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_swap)) { ++ } 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)); +- +- /* DIR: Insert dir nodes at the end of the current line; */ ++ /*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) - dir_swap); ++ 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)); +@@ -303,7 +304,6 @@ void ext_post_line_break(int paragraph_dir, + } + } + 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); +@@ -313,28 +313,30 @@ void ext_post_line_break(int paragraph_dir, + 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| */ ++ /*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))) { +- p = q; /* |q| has been reset to the last node of |pre_break| */ ++ /*tex |q| is reset to the last node of |pre_break| */ ++ p = q; + ptmp = p; + } else { +- p = alink(q); /* get |vlink(p) = q| */ ++ /*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) { /* we have found a marginal kern, append it after |ptmp| */ ++ 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); +@@ -345,10 +347,12 @@ void ext_post_line_break(int paragraph_dir, + q = vlink(q); + } + } +- /* if |q| was not a breakpoint at glue and has been reset to |rightskip| +- then we append |rightskip| after |q| now */ ++ /*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) { +- /* Put the \.{\\rightskip} glue after node |q|; */ ++ /*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)); +@@ -358,21 +362,22 @@ void ext_post_line_break(int paragraph_dir, + 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. */ ++ /*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) { +- /* omega bits: */ + halfword s; + r = copy_node_list(passive_left_box(cur_p)); + s = vlink(q); +@@ -388,11 +393,14 @@ void ext_post_line_break(int paragraph_dir, + } + } + } +- /*at this point |q| is the leftmost node; all discardable nodes have been discarded */ ++ /*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); /* no more discardables */ ++ 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); +@@ -412,13 +420,14 @@ void ext_post_line_break(int paragraph_dir, + 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. */ ++ /*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; +@@ -438,8 +447,10 @@ void ext_post_line_break(int paragraph_dir, + } + shift_amount(just_box) = cur_indent; + subtype(just_box) = line_list; +- /* /Call the packaging subroutine, setting |just_box| to the justified box; */ +- ++ /*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) { +@@ -454,17 +465,17 @@ void ext_post_line_break(int paragraph_dir, + 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. */ ++ /*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) { +@@ -481,11 +492,13 @@ void ext_post_line_break(int paragraph_dir, + } + q = club_penalties_ptr; + if (q != null) { +- r = cur_line - cur_list.pg_field; /* prevgraf */ ++ /*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) { /* prevgraf */ ++ } else if (cur_line == cur_list.pg_field + 1) { ++ /*tex prevgraf */ + pen += club_penalty; + } + q = widow_penalties_ptr; +@@ -510,34 +523,37 @@ void ext_post_line_break(int paragraph_dir, + 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; */ ++ /*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) { +- /* 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. */ ++ /*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; +- /* +- 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. ++ /*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); +@@ -550,17 +566,17 @@ void ext_post_line_break(int paragraph_dir, + } + */ + if (type(q) == math_node) { +- /* begin mathskip code */ ++ /*tex begin mathskip code */ + surround(q) = 0 ; + reset_glue_to_zero(q); +- /* end mathskip code */ ++ /*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) { +- /* weird, in the middle somewhere */ ++ /*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) { +@@ -577,7 +593,9 @@ void ext_post_line_break(int paragraph_dir, + } 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| */ ++ /*tex prevgraf */ ++ cur_list.pg_field = best_line - 1; ++ /*tex |dir_save| */ ++ cur_list.dirs_field = dir_ptr; + dir_ptr = null; + } +diff --git a/texk/web2c/luatexdir/tex/primitive.c b/texk/web2c/luatexdir/tex/primitive.c +new file mode 100644 +index 000000000..f16d69a33 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/primitive.c +@@ -0,0 +1,785 @@ ++/* ++ ++primitive.w ++ ++Copyright 2008-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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 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 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 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; ++ } ++} +diff --git a/texk/web2c/luatexdir/tex/primitive.w b/texk/web2c/luatexdir/tex/primitive.w +deleted file mode 100644 +index 8c6592c9e..000000000 +--- a/texk/web2c/luatexdir/tex/primitive.w ++++ /dev/null +@@ -1,664 +0,0 @@ +-% primitive.w +-% +-% Copyright 2008-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +- +-#include "ptexlib.h" +- +-@ 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 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 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 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; +- } +-} +diff --git a/texk/web2c/luatexdir/tex/printing.w b/texk/web2c/luatexdir/tex/printing.c +similarity index 63% +rename from texk/web2c/luatexdir/tex/printing.w +rename to texk/web2c/luatexdir/tex/printing.c +index 724441564..0a961071b 100644 +--- a/texk/web2c/luatexdir/tex/printing.w ++++ b/texk/web2c/luatexdir/tex/printing.c +@@ -1,87 +1,137 @@ +-% printing.w +-% +-% Copyright 2009-2013 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++printing.w ++ ++Copyright 2009-2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ + #include "ptexlib.h" +-#include "lua/luatex-api.h" /* for luatex_banner */ ++#include "lua/luatex-api.h" + +-@ @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: ++/*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 + +-\yskip +-\hang |term_and_log|, the normal setting, prints on the terminal and on the +- transcript file. ++\stopitemize + +-\hang |log_only|, prints only on the transcript file. ++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|. + +-\hang |term_only|, prints only on the terminal. ++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. + +-\hang |no_print|, doesn't print at all. This is used only in rare cases +- before the transcript file is open. ++*/ ++ ++/*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 */ + +-\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. ++int term_offset = 0; + +-\hang |new_string|, appends the output to the current string in the +- string pool. ++/*tex the number of characters on the current file line */ + +-\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output. ++int file_offset = 0; + +-\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|. ++/*tex circular buffer for pseudoprinting */ + +-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. ++packed_ASCII_code trick_buf[(ssup_error_line + 1)]; + +-@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| */ ++/*tex threshold for pseudoprinting, explained later */ + +-@ To end a line of text output, we call |print_ln| ++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| ++ ++*/ + +-@c + void print_ln(void) + { + switch (selector) { +@@ -111,19 +161,21 @@ void print_ln(void) + fprintf(write_file[selector], "\n"); + break; + } +- /* |tally| is not affected */ ++ /*tex |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). ++/*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. ++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)) + +@@ -144,17 +196,6 @@ fuzzy anyway. + 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)) || \ +@@ -236,23 +277,25 @@ void print_char(int 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.) ++/*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.) + +-@^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. + +-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) { +@@ -262,9 +305,9 @@ void print(int s) + if (s < 0) { + normal_warning("print","bad string offset"); + } else { +- /* TH not sure about this, disabled for now! */ ++ /*tex We're not sure about this so it's disabled for now! */ + if ((false) && (selector > pseudo)) { +- /* internal strings are not expanded */ ++ /*tex internal strings are not expanded */ + print_char(s); + return; + } +@@ -307,12 +350,13 @@ void print(int s) + } + + void lprint (lstring *ss) { +- unsigned char *j, *l; /* current character code position */ ++ /*tex current character code position */ ++ unsigned char *j, *l; + 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 */ ++ /*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); +@@ -325,12 +369,17 @@ void lprint (lstring *ss) { + } + } + +-@ The procedure |print_nl| is like |print|, but it makes sure that the +-string appears at the beginning of a new line. ++/*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. */ + +-@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))) || +@@ -339,17 +388,22 @@ void print_nlp(void) + } + } + ++/*tex Prints string |s| at beginning of the next line. */ ++ + 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|. ++/*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|. ++ ++*/ + +-@c + #define t_flush_buffer(target,offset) \ + buffer[i++] = '\n'; \ + buffer[i++] = '\0';\ +@@ -361,7 +415,7 @@ most of the output actually comes through |tprint|. + void tprint(const char *sss) + { + char *buffer = NULL; +- int i = 0; /* buffer index */ ++ int i = 0; + int newlinechar = new_line_char_par; + int dolog = 0; + int doterm = 0; +@@ -409,7 +463,7 @@ void tprint(const char *sss) + } + break; + } +- /* what is left is the 3 term/log settings */ ++ /*tex What is left is the 3 term/log settings. */ + if (dolog || doterm) { + buffer = xmalloc(strlen(sss)*3); + if (dolog) { +@@ -469,13 +523,16 @@ void tprint_nl(const char *s) + 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. ++/*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. ++ ++*/ + +-@c + void print_banner(const char *v) + { + int callback_id = callback_defined(start_run_callback); +@@ -484,15 +541,16 @@ void print_banner(const char *v) + if (format_ident > 0) + print(format_ident); + print_ln(); +- if (show_luahashchars){ ++ if (show_luahashchars) { + wterm(' '); + fprintf(term_out,"Number of bits used by the hash function (" my_name "): %d",LUAI_HASHLIMIT); +- print_ln(); ++ print_ln(); + } + if (shellenabledp) { + wterm(' '); +- if (restrictedshell) ++ if (restrictedshell) { + fprintf(term_out, "restricted "); ++ } + fprintf(term_out, "system commands enabled.\n"); + } + } else if (callback_id > 0) { +@@ -500,7 +558,6 @@ void print_banner(const char *v) + } + } + +-@ @c + void log_banner(const char *v) + { + const char *months[] = { " ", +@@ -536,41 +593,51 @@ void log_banner(const char *v) + } + } + +-@ @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). ++/*tex ++ ++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) ++ /*tex Set variable |c| to the current escape character: */ ++ int c = escape_char_par; ++ if (c >= 0 && c < 0x110000) + print(c); + print(s); + } + +-@ This prints escape character, then |s|. ++/*tex ++ ++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) ++ /*tex Set variable |c| to the current escape character: */ ++ int c = escape_char_par; ++ if (c >= 0 && c < 0x110000) + print(c); + tprint(s); + } + +-@ An array of digits in the range |0..15| is printed by |print_the_digs|. ++/*tex ++ ++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]| */ ++ /*tex prints |dig[k-1]|$\,\ldots\,$|dig[0]| */ + while (k-- > 0) { + if (dig[k] < 10) + print_char('0' + dig[k]); +@@ -579,17 +646,22 @@ void print_the_digs(eight_bits 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. ++/*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. ++ ++*/ + +-@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 */ ++ /*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) { +@@ -615,11 +687,13 @@ void print_int(longinteger n) + 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|. ++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; +@@ -627,12 +701,16 @@ void print_two(int n) + print_char('0' + (n % 10)); + } + +-@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. ++/*tex + +-@c +-void print_hex(int n) ++Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. ++ ++*/ ++ ++void print_qhex(int n) + { +- int k = 0 ; /* index to current digit; we assume that $0\L n<16^{22}$ */ ++ /*tex index to current digit; we assume that $0\L n<16^{22}$ */ ++ int k = 0 ; + print_char('"'); + do { + dig[k] = n % 16; +@@ -642,16 +720,18 @@ void print_hex(int n) + 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}. ++/*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}. ++ ++*/ + +-@c + void print_roman_int(int n) + { +- char *j, *k; /* mysterious indices */ +- int u, v; /* mysterious numbers */ ++ char *j, *k; ++ int u, v; + char mystery[] = "m2d5c2l5x2v5i"; + j = (char *) mystery; + v = 1000; +@@ -661,7 +741,7 @@ void print_roman_int(int n) + n = n - v; + } + if (n <= 0) { +- /* nonpositive input produces no output */ ++ /*tex nonpositive input produces no output */ + return; + } + k = j + 2; +@@ -680,25 +760,31 @@ void print_roman_int(int n) + } + } + +-@ The |print| subroutine will not print a string that is still being +-created. The following procedure will. ++/*tex ++ ++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 */ ++ /*tex points to current character code */ ++ unsigned j = 0; + 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. ++/*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. ++ ++*/ + +-@c + void print_cs(int p) + { + str_number t = cs_text(p); +@@ -731,10 +817,12 @@ void print_cs(int p) + } + } + +-@ Here is a similar procedure; it avoids the error checks, and it never +-prints a space after the control sequence. ++/*tex + +-@c ++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; +@@ -762,9 +850,12 @@ void sprint_cs_name(pointer p) + } + } + +-@ This procedure is never called when |interaction ++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. + +-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. ++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@> + +-@c +-int font_in_short_display; /* an internal font number */ ++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. + +-@ Boxes, rules, inserts, whatsits, marks, and things in general that are +-sort of ``complicated'' are indicated only by printing `\.{[]}'. ++*/ + +-@c ++/*tex An internal font number: */ + +-/* +-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. ++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 xyz +-3 xyz ++3 xyz + 4 + 5 +-6 xyz ++6 xyz ++\stoptyping + + */ + +@@ -858,12 +960,12 @@ 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 */ ++ /*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(" @@ "); ++ tprint(" @ "); + print_scaled(font_size(f)); + tprint("pt"); + } +@@ -873,7 +975,7 @@ void print_font_identifier(internal_font_number f) + tprint(": "); + print_font_name(f); + if (tracing_fonts_par >= 6 || font_size(f) != font_dsize(f)) { +- tprint(" @@ "); ++ tprint(" @ "); + print_scaled(font_size(f)); + tprint("pt"); + } +@@ -881,7 +983,7 @@ void print_font_identifier(internal_font_number f) + } + print_char('>'); + } else { +- /* old method, inherited from pdftex */ ++ /*tex old method, inherited from pdftex */ + if (fonttext > 0) { + print_esc(fonttext); + } else { +@@ -892,7 +994,7 @@ void print_font_identifier(internal_font_number f) + tprint(" ("); + print_font_name(f); + if (font_size(f) != font_dsize(f)) { +- tprint("@@"); ++ tprint("@"); + print_scaled(font_size(f)); + tprint("pt"); + } +@@ -901,9 +1003,12 @@ void print_font_identifier(internal_font_number f) + } + } + +-@ This prints highlights of list |p|. ++/*tex ++ ++This prints highlights of list |p|. ++ ++*/ + +-@c + void short_display(int p) + { + while (p != null) { +@@ -922,20 +1027,27 @@ void short_display(int p) + print(character(p)); + } + } else { +- /* Print a short indication of the contents of node |p| */ ++ /*tex 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. ++/*tex + +-@ This prints |char_node| data. ++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. ++ ++*/ + +-@c + void print_font_and_char(int p) + { + if (!is_valid_font(font(p))) +@@ -946,9 +1058,12 @@ void print_font_and_char(int p) + print(character(p)); + } + +-@ This prints token list data in braces ++/*tex ++ ++This prints token list data in braces ++ ++*/ + +-@c + void print_mark(int p) + { + print_char('{'); +@@ -959,9 +1074,12 @@ void print_mark(int p) + print_char('}'); + } + +-@ This prints dimensions of a rule node. ++/*tex ++ ++This prints dimensions of a rule node. ++ ++*/ + +-@c + void print_rule_dimen(scaled d) + { + if (is_running(d)) +@@ -970,41 +1088,54 @@ void print_rule_dimen(scaled d) + 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. ++/*tex + +-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. ++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. + +-@c +-int depth_threshold; /* maximum nesting depth in box displays */ +-int breadth_max; /* maximum number of items shown at the same list level */ ++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. + +-@ The recursive machinery is started by calling |show_box|. Assign the values ++*/ ++ ++/*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| + +-@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| */ ++ /*tex the show starts at |p| */ + show_node_list(p); + print_ln(); + } + +-@ Helper for debugging purposes. It prints highlights of list |p| ++/*tex ++ ++Helper for debugging purposes. It prints highlights of list |p| ++ ++*/ + +-@c + void short_display_n(int p, int m) + { + int i = 0; +@@ -1041,7 +1172,7 @@ void short_display_n(int p, int m) + short_display(vlink(post_break(p))); + print_char('|'); + } else { +- /* Print a short indication of the contents of node |p| */ ++ /*tex Print a short indication of the contents of node |p| */ + print_short_node_contents(p); + } + } +@@ -1052,13 +1183,16 @@ void short_display_n(int p, int m) + 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.) ++/*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.) ++ ++*/ + +-@c + void print_csnames(int hstart, int hfinish) + { + int h; +@@ -1066,11 +1200,11 @@ void print_csnames(int hstart, int hfinish) + 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 */ ++ /*tex 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 */ ++ /*tex Print the characters. */ + fputc(*c++, stderr); + } + fprintf(stderr, "|"); +@@ -1078,11 +1212,14 @@ void print_csnames(int hstart, int hfinish) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@c + void print_file_line(void) + { + int level = in_open; +@@ -1102,11 +1239,14 @@ void print_file_line(void) + } + } + +-@ \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: ++/*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: ++ ++*/ + +-@c + void begin_diagnostic(void) + { + global_old_setting = selector; +@@ -1117,9 +1257,12 @@ void begin_diagnostic(void) + } + } + +-@ Restore proper conditions after tracing. ++/*tex ++ ++Restore proper conditions after tracing. ++ ++*/ + +-@c + void end_diagnostic(boolean blank_line) + { + tprint_nl(""); +@@ -1128,8 +1271,11 @@ void end_diagnostic(boolean blank_line) + selector = global_old_setting; + } + +-@ Of course we had better declare another global variable, if the previous +-routines are going to work. ++/*tex ++ ++Of course we had better declare another global variable, if the previous routines ++are going to work. ++ ++*/ + +-@c + int global_old_setting; +diff --git a/texk/web2c/luatexdir/tex/scanning.c b/texk/web2c/luatexdir/tex/scanning.c +new file mode 100644 +index 000000000..8f7e1d591 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/scanning.c +@@ -0,0 +1,2743 @@ ++/* ++ ++Copyright 2009-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ ++#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_cmdmax_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= 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; ++} +diff --git a/texk/web2c/luatexdir/tex/scanning.w b/texk/web2c/luatexdir/tex/scanning.w +deleted file mode 100644 +index 7bed4d290..000000000 +--- a/texk/web2c/luatexdir/tex/scanning.w ++++ /dev/null +@@ -1,2621 +0,0 @@ +-% scanning.w +-% +-% Copyright 2009-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +-#include "ptexlib.h" +- +-@ @c +-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_cmdmax_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= 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; +-} +diff --git a/texk/web2c/luatexdir/tex/stringpool.w b/texk/web2c/luatexdir/tex/stringpool.c +similarity index 53% +rename from texk/web2c/luatexdir/tex/stringpool.w +rename to texk/web2c/luatexdir/tex/stringpool.c +index 5972657f5..bc09f814b 100644 +--- a/texk/web2c/luatexdir/tex/stringpool.w ++++ b/texk/web2c/luatexdir/tex/stringpool.c +@@ -1,69 +1,89 @@ +-% stringpool.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + ++stringpool.w ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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 ++/*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= STRING_OFFSET) { + k = str_string(t); +@@ -167,8 +186,8 @@ boolean str_eq_str(str_number s, str_number t) + return true; + } + +-@ string compare +-@c ++/*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)) +@@ -177,43 +196,47 @@ boolean str_eq_cstr(str_number r, const char *s, size_t l) + } + + +-@ 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. ++/*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. ++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. ++/*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. ++ ++*/ + +-@c + str_number search_string(str_number search) + { +- str_number s; /* running index */ +- size_t len; /* length of searched string */ ++ str_number s; ++ size_t len; + len = str_length(search); + if (len == 0) { + return get_nullstr(); + } else { +- s = search - 1; /* start search with newest string below |s|; |search>1|! */ ++ /*tex We start the search with newest string below |s|; |search>1|! */ ++ s = search - 1; + while (s >= STRING_OFFSET) { +- /* first |string_offset| strings depend on implementation!! */ ++ /* The first |string_offset| of strings depend on the implementation! */ + if (str_length(s) == len) + if (str_eq_str(s, search)) + return s; +@@ -223,7 +246,6 @@ str_number search_string(str_number search) + return 0; + } + +-@ @c + str_number maketexstring(const char *s) + { + if (s == NULL || *s == 0) +@@ -231,7 +253,6 @@ str_number maketexstring(const char *s) + return maketexlstring(s, strlen(s)); + } + +-@ @c + str_number maketexlstring(const char *s, size_t l) + { + if (s == NULL || l == 0) +@@ -243,8 +264,12 @@ str_number maketexlstring(const char *s, size_t l) + return (str_ptr - 1); + } + +-@ append a C string to a TeX string +-@c ++/*tex ++ ++ This appends a C string to a \TEX\ string: ++ ++*/ ++ + void append_string(const unsigned char *s, unsigned l) + { + if (s == NULL || *s == 0) +@@ -256,14 +281,12 @@ void append_string(const unsigned char *s, unsigned 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) { +@@ -279,7 +302,6 @@ char *makeclstring(int s, size_t * len) + } + } + +-@ @c + int dump_string_pool(void) + { + int j; +@@ -297,7 +319,6 @@ int dump_string_pool(void) + return (k - STRING_OFFSET); + } + +-@ @c + int undump_string_pool(void) + { + int j; +@@ -325,7 +346,6 @@ int undump_string_pool(void) + return str_ptr; + } + +-@ @c + void init_string_pool_array(unsigned s) + { + string_pool = xmallocarray(lstring, s); +@@ -336,14 +356,16 @@ void init_string_pool_array(unsigned s) + string_pool[0].s[0] = '\0'; + } + +-@ To destroy an already made string, we say |flush_str|. +-@c ++/*tex ++ ++ To destroy an already made string, we say |flush_str|. ++ ++*/ ++ + 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 */ ++ 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)); +diff --git a/texk/web2c/luatexdir/tex/texdeffont.w b/texk/web2c/luatexdir/tex/texdeffont.c +similarity index 50% +rename from texk/web2c/luatexdir/tex/texdeffont.w +rename to texk/web2c/luatexdir/tex/texdeffont.c +index 7fe5ae1e1..f0b057d40 100644 +--- a/texk/web2c/luatexdir/tex/texdeffont.w ++++ b/texk/web2c/luatexdir/tex/texdeffont.c +@@ -1,57 +1,73 @@ +-% texdeffont.w +-% +-% Copyright 2008-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + ++texdeffont.w ++ ++Copyright 2008-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #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. ++/*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. ++ ++*/ + +-@c + int font_bytes; + + void set_cur_font(internal_font_number f) + { +- int a = 0; /* never global */ ++ int a = 0; + define(cur_font_loc, data_cmd, f); + } + +-@ @c ++/*tex ++ ++ This prints a scaled real, rounded to five digits. ++ ++*/ ++ + 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 */ ++ /*tex The amount of allowable inaccuracy: */ ++ scaled delta; + k = 0; + if (s < 0) { ++ /*tex Only print the sign, if negative */ + result[k++] = '-'; +- s = -s; /* print the sign, if negative */ ++ s = -s; + } + { + int l = 0; + char digs[8] = { 0 }; + n = s / unity; +- /* process the integer part */ ++ /*tex Process the integer part: */ + do { + digs[l++] = (char) (n % 10); + n = n / 10;; +@@ -64,29 +80,37 @@ static char *scaled_to_string(scaled s) + s = 10 * (s % unity) + 5; + delta = 10; + do { +- if (delta > unity) +- s = s + 0100000 - 050000; /* round the last digit */ ++ 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; + } + +-@ @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 */ ++ /*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) +- open_log_file(); /* avoid confusing \.{texput} with the font name */ ++ 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) +@@ -99,7 +123,7 @@ void tex_def_font(small_number a) + eq_define(u, set_font_cmd, null_font); + } + scan_optional_equals(); +- /* Get the next non-blank non-call token; */ ++ /*tex Get the next non-blank non-call token. */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); +@@ -108,8 +132,10 @@ void tex_def_font(small_number a) + 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 */ ++ /*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()) { +@@ -134,26 +160,27 @@ void tex_def_font(small_number a) + 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 */ ++ /*tex ++ Scan the font size specification. The next variable keeps |cur_name| from ++ being changed ++ */ ++ name_in_progress = true; + if (scan_keyword("at")) { +- /* Put the positive `at' size into |s| */ ++ /*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", ++ 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)); ++ snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt", scaled_to_string(s)); + tex_error(err, errhelp); + s = 10 * unity; + } +@@ -162,27 +189,31 @@ void tex_def_font(small_number a) + 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.", ++ 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); ++ 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; + } +diff --git a/texk/web2c/luatexdir/tex/texfileio.c b/texk/web2c/luatexdir/tex/texfileio.c +new file mode 100644 +index 000000000..143e694e1 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/texfileio.c +@@ -0,0 +1,1509 @@ ++/* ++ ++Copyright 2009-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#include "ptexlib.h" ++ ++#include ++#include ++ ++/*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 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 ++ 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 \.{}: */ ++ 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} \.= \.{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" "|. ++ ++*/ ++ ++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); ++} +diff --git a/texk/web2c/luatexdir/tex/texfileio.w b/texk/web2c/luatexdir/tex/texfileio.w +deleted file mode 100644 +index a10e9ba63..000000000 +--- a/texk/web2c/luatexdir/tex/texfileio.w ++++ /dev/null +@@ -1,1389 +0,0 @@ +-% texfileio.w +-% +-% Copyright 2009-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +- +-#include "ptexlib.h" +- +-#include +-#include +- +-@ 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 +- +-@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 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 \.{} */ +- 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} \.= \.{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 +- +-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" "|. +- +-@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); +-} +diff --git a/texk/web2c/luatexdir/tex/texmath.w b/texk/web2c/luatexdir/tex/texmath.c +similarity index 61% +rename from texk/web2c/luatexdir/tex/texmath.w +rename to texk/web2c/luatexdir/tex/texmath.c +index 6d4b87a38..69e58b0ea 100644 +--- a/texk/web2c/luatexdir/tex/texmath.w ++++ b/texk/web2c/luatexdir/tex/texmath.c +@@ -1,46 +1,43 @@ +-% texmath.w +-% +-% Copyright 2008-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++Copyright 2008-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ + #include "ptexlib.h" + +-@ @c + #define mode mode_par + #define tail tail_par + #define head head_par + #define dir_save dirs_par + +-/* +- +- \mathdisplayskipmode ++/*tex + +- tex normally always inserts before and only after when larger than zero ++ Concerning display skips, \TEX\ normally always inserts before and only after ++ when larger than zero. THis can ow be controlled with |\mathdisplayskipmode|: + +- 0 = normal tex +- 1 = always +- 2 = non-zero +- 3 = ignore ++ \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 + + */ + +-@ TODO: not sure if this is the right order +-@c + #define back_error(A,B) do { \ + OK_to_interrupt=false; \ + back_input(); \ +@@ -48,90 +45,107 @@ + 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 ++/*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(); +@@ -141,10 +155,13 @@ static void unsave_math(void) + 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|. ++/*tex ++ ++ 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)); +@@ -154,15 +171,13 @@ void flush_math(void) + incompleat_noad_par = null; + } + +-@ Before we can do anything in math mode, we need fonts. ++/*tex Before we can do anything in math mode, we need fonts. */ + +-@c + #define MATHFONTSTACK 8 +-#define MATHFONTDEFAULT 0 /* == nullfont */ ++#define MATHFONTDEFAULT 0 + + static sa_tree math_fam_head = NULL; + +-@ @c + int fam_fnt(int fam_id, int size_id) + { + int n = fam_id + (256 * size_id); +@@ -189,7 +204,6 @@ void def_fam_fnt(int fam_id, int size_id, int f, int lvl) + } + } + +-@ @c + static void unsave_math_fam_data(int gl) + { + sa_stack_item st; +@@ -201,7 +215,7 @@ static void unsave_math_fam_data(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 */ ++ /*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; +@@ -220,15 +234,13 @@ static void unsave_math_fam_data(int gl) + } + } + +-@ and parameters ++/*tex 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); +@@ -254,7 +266,6 @@ scaled get_math_param(int param_id, int 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; +@@ -266,7 +277,7 @@ static void unsave_math_param_data(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 */ ++ /*tex Do a trace message, if requested. */ + if (tracing_restores_par > 1) { + int param_id = st.code % 256; + int style_id = st.code / 256; +@@ -285,17 +296,16 @@ static void unsave_math_param_data(int gl) + } + } + +-@ saving and unsaving of both ++/*tex 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 ++/*tex Dumping and undumping: */ ++ + void dump_math_data(void) + { + sa_tree_item sa_value = { 0 }; +@@ -317,7 +327,6 @@ void undump_math_data(void) + math_param_head = undump_sa_tree("mathparameters"); + } + +-@ @c + void initialize_math(void) + { + sa_tree_item sa_value = { 0 }; +@@ -333,62 +342,63 @@ void initialize_math(void) + 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 ++/*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); +- /* all noad fields are zero after this */ ++ /*tex All noad fields are zero after this. */ + return p; + } + +-@ @c + pointer new_sub_box(pointer curbox) + { + pointer p, q; +@@ -399,40 +409,41 @@ pointer new_sub_box(pointer 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 ++/*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", +@@ -482,93 +493,96 @@ const char *math_param_names[] = { + 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. ++/*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. ++ ++*/ + +-@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 */ ++{ ++ 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; ++ 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. ++/*tex 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; +@@ -598,36 +612,38 @@ static void print_delimiter(pointer 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 */ ++ /*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_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)); ++ 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)); + } + } + +-@ 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@> ++/*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). ++ ++*/ + +-@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 */ ++ /*tex Include |c| in the recursion history. */ ++ append_char(c); + if (p != null) { + switch (type(p)) { + case math_char_node: +@@ -649,58 +665,58 @@ static void print_subsidiary_data(pointer p, ASCII_code c) + break; + } + } +- flush_char(); /* remove |c| from the recursion history */ ++ /*tex Remove |c| from the recursion history. */ ++ flush_char(); + } + } + +-@ @c + void display_normal_noad(pointer p) + { + switch (type(p)) { + case simple_noad: + switch (subtype(p)) { +- case ord_noad_type: +- tprint_esc("mathord"); +- break; +- case op_noad_type_normal: +- case op_noad_type_limits: +- case op_noad_type_no_limits: +- tprint_esc("mathop"); +- if (subtype(p) == op_noad_type_limits) +- tprint_esc("limits"); +- else if (subtype(p) == op_noad_type_no_limits) +- tprint_esc("nolimits"); +- break; +- case bin_noad_type: +- tprint_esc("mathbin"); +- break; +- case rel_noad_type: +- tprint_esc("mathrel"); +- break; +- case open_noad_type: +- tprint_esc("mathopen"); +- break; +- case close_noad_type: +- tprint_esc("mathclose"); +- break; +- case punct_noad_type: +- tprint_esc("mathpunct"); +- break; +- case inner_noad_type: +- tprint_esc("mathinner"); +- break; +- case over_noad_type: +- tprint_esc("overline"); +- break; +- case under_noad_type: +- tprint_esc("underline"); +- break; +- case vcenter_noad_type: +- tprint_esc("vcenter"); +- break; +- default: +- tprint(""); +- break; ++ case ord_noad_type: ++ tprint_esc("mathord"); ++ break; ++ case op_noad_type_normal: ++ case op_noad_type_limits: ++ case op_noad_type_no_limits: ++ tprint_esc("mathop"); ++ if (subtype(p) == op_noad_type_limits) ++ tprint_esc("limits"); ++ else if (subtype(p) == op_noad_type_no_limits) ++ tprint_esc("nolimits"); ++ break; ++ case bin_noad_type: ++ tprint_esc("mathbin"); ++ break; ++ case rel_noad_type: ++ tprint_esc("mathrel"); ++ break; ++ case open_noad_type: ++ tprint_esc("mathopen"); ++ break; ++ case close_noad_type: ++ tprint_esc("mathclose"); ++ break; ++ case punct_noad_type: ++ tprint_esc("mathpunct"); ++ break; ++ case inner_noad_type: ++ tprint_esc("mathinner"); ++ break; ++ case over_noad_type: ++ tprint_esc("overline"); ++ break; ++ case under_noad_type: ++ tprint_esc("underline"); ++ break; ++ case vcenter_noad_type: ++ tprint_esc("vcenter"); ++ break; ++ default: ++ tprint(""); ++ break; + } + break; + case radical_noad: +@@ -810,7 +826,6 @@ void display_normal_noad(pointer p) + print_subsidiary_data(subscr(p), '_'); + } + +-@ @c + void display_fence_noad(pointer p) + { + if (subtype(p) == right_noad_side) +@@ -822,7 +837,6 @@ void display_fence_noad(pointer p) + print_delimiter(delimiter(p)); + } + +-@ @c + void display_fraction_noad(pointer p) + { + tprint_esc("fraction, thickness "); +@@ -831,18 +845,14 @@ void display_fraction_noad(pointer p) + 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))) { ++ ((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))) { ++ ((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)); + } +@@ -850,16 +860,19 @@ void display_fraction_noad(pointer 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. ++/*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|. ++ 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); +@@ -871,7 +884,6 @@ static void new_save_level_math(group_code c) + 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) +@@ -883,7 +895,6 @@ static void push_math(group_code c, int mstyle) + new_save_level_math(c); + } + +-@ @c + static void enter_ordinary_math(void) + { + push_math(math_shift_group, text_style); +@@ -892,19 +903,21 @@ static void enter_ordinary_math(void) + 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. ++/*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. ++ ++*/ + +-@c + void init_math(void) + { + if (cur_cmd == math_shift_cmd) { +- get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ ++ /*tex |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ ++ get_token(); + if ((cur_cmd == math_shift_cmd) && (mode > 0)) { + enter_display_math(); + } else { +@@ -920,15 +933,17 @@ void init_math(void) + } + } + +-@ 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. ++/*tex + +-@ 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. ++ 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); +@@ -936,21 +951,24 @@ void start_eq_no(void) + 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. ++/*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. ++ 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; +@@ -961,18 +979,20 @@ void math_left_brace(void) + (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. ++/*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. ++ 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; +@@ -989,26 +1009,43 @@ static boolean math_and_text_reversed_p(void) + 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|. ++/*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|. ++ ++*/ + +-@c + void enter_display_math(void) + { +- scaled w; /* new or partial |pre_display_size| */ +- scaled l; /* new |display_width| */ +- scaled s; /* new |display_indent| */ ++ /*tex new or partial |pre_display_size| */ ++ scaled w; ++ /*tex new |display_width| */ ++ scaled l; ++ /*tex new |display_indent| */ ++ scaled s; + pointer p; +- int n; /* scope of paragraph shape specification */ +- if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ +- (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */ ++ /*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) { +- /* 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 */ ++ /*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(); +@@ -1017,9 +1054,13 @@ void enter_display_math(void) + 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|. */ ++ /*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); +@@ -1042,7 +1083,6 @@ void enter_display_math(void) + 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); +@@ -1058,12 +1098,15 @@ void enter_display_math(void) + } + } + +-@ 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}). ++/*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}). ++ ++*/ + +-@c + static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) + { + const char *hlp[] = { +@@ -1072,9 +1115,14 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) + }; + delcodeval d; + int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; +- if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */ ++ if (extcode == tex_mathcode) { ++ /*tex ++ ++ \.{\\delcode}, this is the easiest ++ ++ */ + scan_int(); +- /* "MFCCFCC or "FCCFCC */ ++ /*tex "MFCCFCC or "FCCFCC */ + if (doclass) { + mcls = (cur_val / 0x1000000); + cur_val = (cur_val & 0xFFFFFF); +@@ -1087,8 +1135,12 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) + 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> */ ++ } 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; +@@ -1104,11 +1156,13 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) + } + 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 +- */ ++ } 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"); + } +@@ -1123,7 +1177,7 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) + mlfam = 0; + mlchr = 0; + } else { +- /* something's gone wrong */ ++ /*tex Something's gone wrong! */ + confusion("unknown_extcode"); + } + d.class_value = mcls; +@@ -1134,7 +1188,6 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) + return d; + } + +-@ @c + void scan_extdef_del_code(int level, int extcode) + { + delcodeval d; +@@ -1144,11 +1197,9 @@ void scan_extdef_del_code(int level, int extcode) + 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)); ++ d.large_family_value, d.large_character_value, (quarterword) (level)); + } + +-@ @c + mathcodeval scan_mathchar(int extcode) + { + char errstr[255] = { 0 }; +@@ -1158,15 +1209,11 @@ mathcodeval scan_mathchar(int extcode) + }; + mathcodeval d; + int mcls = 0, mfam = 0, mchr = 0; +- if (extcode == tex_mathcode) { /* \.{\\mathcode} */ +- /* "TFCC */ ++ if (extcode == tex_mathcode) { ++ /*tex \.{\\mathcode}: "TFCC */ + scan_int(); + if (cur_val > 0x8000) { +- /* +- tex_error("Invalid math code", hlp); +- cur_val = 0; +- */ +- /* needed for latex: fallback to umathnum_mathcode */ ++ /*tex Needed for latex: fallback to umathnum_mathcode. */ + mfam = (cur_val / 0x200000) & 0x7FF; + mcls = mfam % 0x08; + mfam = mfam / 0x08; +@@ -1188,7 +1235,7 @@ mathcodeval scan_mathchar(int extcode) + mchr = (cur_val % 0x100); + } + } else if (extcode == umath_mathcode) { +- /* <0-0x7> <0-0xFF> <0-0x10FFFF> */ ++ /*tex <0-0x7> <0-0xFF> <0-0x10FFFF> */ + scan_int(); + mcls = cur_val; + scan_int(); +@@ -1202,11 +1249,14 @@ mathcodeval scan_mathchar(int extcode) + 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 */ ++ /*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; +@@ -1219,7 +1269,7 @@ mathcodeval scan_mathchar(int extcode) + mchr = 0; + } + } else { +- /* something's gone wrong */ ++ /*tex Something's gone wrong. */ + confusion("unknown_extcode"); + } + d.class_value = mcls; +@@ -1228,7 +1278,6 @@ mathcodeval scan_mathchar(int extcode) + return d; + } + +-@ @c + void scan_extdef_math_code(int level, int extcode) + { + mathcodeval d; +@@ -1237,12 +1286,11 @@ void scan_extdef_math_code(int level, int extcode) + 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)); ++ 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 ++/*tex This reads in a delcode when actually a mathcode is needed. */ ++ + mathcodeval scan_delimiter_as_mathchar(int extcode) + { + delcodeval dval; +@@ -1254,14 +1302,17 @@ mathcodeval scan_delimiter_as_mathchar(int extcode) + 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|. ++/*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|. ++ ++*/ + +-@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) +@@ -1277,7 +1328,6 @@ int scan_math_style(pointer p, int mstyle) + + int scan_math(pointer p, int mstyle) + { +- /* label restart,reswitch,exit; */ + mathcodeval mval = { 0, 0, 0 }; + assert(p != null); + RESTART: +@@ -1289,7 +1339,7 @@ int scan_math(pointer p, int mstyle) + 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 */ ++ /*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); +@@ -1329,8 +1379,12 @@ int scan_math(pointer p, int mstyle) + confusion("scan_math"); + break; + default: +- /* The pointer |p| is placed on |save_stack| while a complex subformula +- is being scanned. */ ++ /*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); +@@ -1347,12 +1401,15 @@ int scan_math(pointer p, int mstyle) + 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. ++/*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. ++ ++*/ + +-@c + #define math_class_to_type(target,source) \ + switch (source) { \ + case 0: target = ord_noad_type; break; \ +@@ -1366,9 +1423,9 @@ nothing is appended. + + void set_math_char(mathcodeval mval) + { +- pointer p; /* the new noad */ ++ pointer p; + if (mval.class_value == 8) { +- /* An active character that is an |outer_call| is allowed here */ ++ /*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); +@@ -1393,17 +1450,20 @@ void set_math_char(mathcodeval mval) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@c + void math_char_in_text(mathcodeval mval) + { +- pointer p; /* the new node */ ++ pointer p; + if (mval.class_value == 8) { +- /* An active character that is an |outer_call| is allowed here */ ++ /*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); +@@ -1416,7 +1476,6 @@ void math_char_in_text(mathcodeval mval) + } + } + +-@ @c + void math_math_comp(void) + { + pointer q; +@@ -1430,7 +1489,6 @@ void math_math_comp(void) + (void) scan_math(nucleus(tail), m_style); + } + +-@ @c + void math_limit_switch(void) + { + const char *hlp[] = { +@@ -1449,18 +1507,23 @@ void math_limit_switch(void) + 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. ++/*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. ++ ++*/ + +-@c + static void scan_delimiter(pointer p, int r) + { + delcodeval dval = { 0, 0, 0, 0, 0 }; +- if (r == tex_mathcode) { /* \.{\\radical} */ ++ if (r == tex_mathcode) { ++ /*tex \.{\\radical} */ + dval = do_scan_extdef_del_code(tex_mathcode, true); +- } else if (r == umath_mathcode) { /* \.{\\Uradical} */ ++ } 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(); +@@ -1470,12 +1533,15 @@ static void scan_delimiter(pointer p, int r) + dval = get_del_code(cur_chr); + break; + case delim_num_cmd: +- if (cur_chr == 0) /* \.{\\delimiter} */ ++ if (cur_chr == 0) { ++ /*tex \.{\\delimiter} */ + dval = do_scan_extdef_del_code(tex_mathcode, true); +- else if (cur_chr == 1) /* \.{\\Udelimiter} */ ++ } else if (cur_chr == 1) { ++ /*tex \.{\\Udelimiter} */ + dval = do_scan_extdef_del_code(umath_mathcode, true); +- else ++ } else { + confusion("scan_delimiter1"); ++ } + break; + default: + dval.small_family_value = -1; +@@ -1510,7 +1576,6 @@ static void scan_delimiter(pointer p, int r) + return; + } + +-@ @c + void math_radical(void) + { + halfword q; +@@ -1534,31 +1599,44 @@ void math_radical(void) + } + } + radicaloptions(tail) = options; +- if (chr_code == 0) /* \.{\\radical} */ ++ if (chr_code == 0) ++ /*tex \.{\\radical} */ + scan_delimiter(left_delimiter(tail), tex_mathcode); +- else if (chr_code == 1) /* \.{\\Uradical} */ ++ else if (chr_code == 1) ++ /*tex \.{\\Uradical} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); +- else if (chr_code == 2) /* \.{\\Uroot} */ ++ else if (chr_code == 2) ++ /*tex \.{\\Uroot} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); +- else if (chr_code == 3) /* \.{\\Uunderdelimiter} */ ++ else if (chr_code == 3) ++ /*tex \.{\\Uunderdelimiter} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); +- else if (chr_code == 4) /* \.{\\Uoverdelimiter} */ ++ else if (chr_code == 4) ++ /*tex \.{\\Uoverdelimiter} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); +- else if (chr_code == 5) /* \.{\\Udelimiterunder} */ ++ else if (chr_code == 5) ++ /*tex \.{\\Udelimiterunder} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); +- else if (chr_code == 6) /* \.{\\Udelimiterover} */ ++ else if (chr_code == 6) ++ /*tex \.{\\Udelimiterover} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); +- else if (chr_code == 7) /* \.{\\Uhextensible} */ ++ else if (chr_code == 7) ++ /*tex \.{\\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 */ ++ /*tex type will change */ ++ q = new_node(sub_box_node, 0); + 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 */ ++ /*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; +@@ -1575,7 +1653,6 @@ void math_radical(void) + } + } + +-@ @c + void math_ac(void) + { + halfword q; +@@ -1591,15 +1668,17 @@ void math_ac(void) + tex_error("Please use \\mathaccent for accents in math mode", hlp); + } + tail_append(new_node(accent_noad, 0)); +- if (cur_chr == 0) { /* \.{\\mathaccent} */ ++ if (cur_chr == 0) { ++ /*tex \.{\\mathaccent} */ + t = scan_mathchar(tex_mathcode); +- } else if (cur_chr == 1) { /* \.{\\Umathaccent} */ ++ } else if (cur_chr == 1) { ++ /*tex \.{\\Umathaccent} */ + if (scan_keyword("fixed")) { +- /* top */ ++ /*tex top */ + subtype(tail) = 1; + t = scan_mathchar(umath_mathcode); + } else if (scan_keyword("both")) { +- /* top bottom */ ++ /*tex top bottom */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } +@@ -1609,13 +1688,13 @@ void math_ac(void) + } + b = scan_mathchar(umath_mathcode); + } else if (scan_keyword("bottom")) { +- /* bottom */ ++ /*tex bottom */ + if (scan_keyword("fixed")) { + subtype(tail) = 2; + } + b = scan_mathchar(umath_mathcode); + } else if (scan_keyword("top")) { +- /* top */ ++ /*tex top */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } +@@ -1627,7 +1706,7 @@ void math_ac(void) + } + o = scan_mathchar(umath_mathcode); + } else { +- /* top */ ++ /*tex top */ + t = scan_mathchar(umath_mathcode); + } + if (scan_keyword("fraction")) { +@@ -1669,7 +1748,6 @@ void math_ac(void) + (void) scan_math(nucleus(tail), cramped_style(m_style)); + } + +-@ @c + pointer math_vcenter_group(pointer p) + { + pointer q, r; +@@ -1681,10 +1759,13 @@ pointer math_vcenter_group(pointer p) + return q; + } + +-@ The routine that scans the four mlists of a \.{\\mathchoice} is very +-much like the routine that builds discretionary nodes. ++/*tex ++ ++ 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()); +@@ -1694,10 +1775,9 @@ void append_choices(void) + scan_left_brace(); + } + +-@ @c + void build_choices(void) + { +- pointer p; /* the current mlist */ ++ pointer p; + int prev_style; + prev_style = m_style; + unsave_math(); +@@ -1718,16 +1798,19 @@ void build_choices(void) + 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|. ++/*tex ++ ++ 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; +@@ -1736,7 +1819,8 @@ static void do_sub_sup(int no) + 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 (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 +@@ -1781,19 +1865,21 @@ 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. ++ 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 */ ++ /*tex The type of generalized fraction we are scanning: */ ++ halfword c; + pointer q; + halfword options = 0; + halfword temp_value; +@@ -1870,16 +1956,19 @@ void math_fraction(void) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@c + pointer fin_mlist(pointer p) + { +- pointer q; /* the mlist to return */ ++ pointer q; + if (incompleat_noad_par != null) { + if (denominator(incompleat_noad_par) != null) { + type(denominator(incompleat_noad_par)) = sub_mlist_node; +@@ -1892,9 +1981,8 @@ pointer fin_mlist(pointer p) + 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 */ ++ 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; +@@ -1907,26 +1995,50 @@ pointer fin_mlist(pointer p) + 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. ++/*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. ++ ++*/ + +-@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) { ++ 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)); +@@ -1941,24 +2053,21 @@ void close_math_group(pointer 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; +- } +- } ++ } ++ } 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; + } + } + } +@@ -1969,30 +2078,36 @@ void close_math_group(pointer p) + vlink(saved_value(0)) = null; + saved_value(0) = q; + (void) scan_math(saved_value(0), old_style); +- /* restart */ ++ /*tex 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}. ++/*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}. ++ ++*/ + +-@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 */ ++ /*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) { +- /* we have \Uleft \Uright \Umiddle */ ++ /*tex we have \Uleft \Uright \Umiddle */ + t = t - 10; + while (1) { + if (scan_keyword("height")) { +@@ -2015,7 +2130,6 @@ void math_left_right(void) + } + } + } +- + 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); +@@ -2041,14 +2155,12 @@ void math_left_right(void) + 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()); +@@ -2058,7 +2170,6 @@ void math_left_right(void) + math_list(nucleus(tail)) = p; + return ; + } +- + if (t == left_noad_side) { + q = p; + } else { +@@ -2080,10 +2191,13 @@ void math_left_right(void) + } + } + +-@ \TeX\ gets to the following part of the program when +-the first `\.\$' ending a display has been scanned. ++/*tex ++ ++ \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(); +@@ -2119,7 +2233,6 @@ static void check_inline_math_end(void) + } + } + +-@ @c + static void resume_after_display(void) + { + if (cur_group != math_shift_group) +@@ -2129,7 +2242,7 @@ static void resume_after_display(void) + push_nest(); + mode = hmode; + space_factor_par = 1000; +- /* this needs to be intercepted in the display math start ! */ ++ /*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) +@@ -2140,30 +2253,40 @@ static void resume_after_display(void) + } + } + +-@ The fussiest part of math mode processing occurs when a displayed formula is +-being centered and placed with an optional equation number. ++/*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). ++ 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). ++ \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 ++ ++*/ + +-@c + #define inject_display_skip_before(g) \ + if (g > 0) { \ + switch (display_skip_mode_par) { \ +- case 0 : /* normal tex | always */ \ +- case 1 : /* always */ \ ++ case 0 : \ ++ /*tex normal tex | always */ \ ++ case 1 : \ ++ /*tex always */ \ + tail_append(new_param_glue(g)); \ + break; \ +- case 2 : /* non-zero */ \ ++ case 2 : \ ++ /*tex non-zero */ \ + if (! glue_is_zero(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ +- case 3: /* ignore */ \ ++ case 3: \ ++ /*tex ignore */ \ + break; \ +- default: /* > 3 reserved for future use */ \ ++ default: \ ++ /*tex > 3 reserved for future use */ \ + tail_append(new_param_glue(g)); \ + break; \ + } \ +@@ -2172,17 +2295,22 @@ At this time we are in vertical mode (or internal vertical mode). + #define inject_display_skip_after(g) \ + if (g > 0) { \ + switch (display_skip_mode_par) { \ +- case 0 : /* normal tex | always */ \ +- case 1 : /* always */ \ ++ case 0 : \ ++ /*tex normal tex | always */ \ ++ case 1 : \ ++ /*tex always */ \ + tail_append(new_param_glue(g)); \ + break; \ +- case 2 : /* non-zero */ \ ++ case 2 : \ ++ /*tex non-zero */ \ + if (! glue_is_zero(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ +- case 3: /* ignore */ \ ++ case 3: \ ++ /*tex ignore */ \ + break; \ +- default: /* > 3 reserved for future use */ \ ++ default: \ ++ /*tex > 3 reserved for future use */ \ + tail_append(new_param_glue(g)); \ + break; \ + } \ +@@ -2190,18 +2318,30 @@ At this time we are in vertical mode (or internal vertical mode). + + 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 */ ++ /*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) +@@ -2209,7 +2349,7 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + eq_box = hpack(p, 0, additional, -1); +- subtype(eq_box) = equation_list; /* new */ ++ subtype(eq_box) = equation_list; + build_attribute_list(eq_box); + p = list_ptr(eq_box); + t = adjust_tail; +@@ -2227,21 +2367,23 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) + 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 */ +- } ++ subtype(eqno_box) = equation_number_list; ++ /*tex build_attribute_list(eqno_box); */ ++ } + 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. */ ++ /*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))) { ++ || (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 */ ++ subtype(eq_box) = equation_list; + build_attribute_list(eq_box); + } else { + eqno_w = 0; +@@ -2249,47 +2391,52 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w, exactly, -1); +- subtype(eq_box) = equation_list; /* new */ ++ subtype(eq_box) = equation_list; + 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. +- */ ++ /*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)) { /* too close */ ++ 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) { /* not enough clearance */ ++ 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. + +- /* 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| */ ++ \.{\\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; +@@ -2300,40 +2447,27 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) + } 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 ++ /*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 { +- /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */ +-#ifdef DEBUG +- fprintf(stderr, "\nDEBUG: CASE 2\n"); +-#endif ++ /*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) { +- /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */ +-#ifdef DEBUG +- fprintf(stderr, "\nDEBUG: CASE 3\n"); +-#endif ++ /*tex TLT + TLT + \leqno: (swap_dir=false, math_direction_par=TLT, l=true) */ + 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 ++ /*tex TRT + TRT + \leqno: (swap_dir=false, math_direction_par=TRT, l=true) */ + s = new_kern(width(r)); + } + try_couple_nodes(eqno_box,r); +@@ -2344,30 +2478,18 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) + } 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 ++ /*tex TRT + TLT + \leqno: (swap_dir=true, math_direction_par=TLT, l=false) */ + } else { +- /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */ +-#ifdef DEBUG +- fprintf(stderr, "\nDEBUG: CASE 6\n"); +-#endif ++ /*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) { +- /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */ +-#ifdef DEBUG +- fprintf(stderr, "\nDEBUG: CASE 7\n"); +-#endif ++ /*tex TLT + TLT + \eqno: (swap_dir=false, math_direction_par=TLT, l=false) */ + 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 ++ /*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); +@@ -2383,9 +2505,8 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) + } else { + shift_amount(eq_box) = line_s + d; + } +-/* check for prev: */ ++ /*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) { */ +@@ -2395,15 +2516,14 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) + append_to_vlist(eqno_box,lua_key_index(equation_number)); + g2 = 0; + } +- if (t != adjust_head) { /* migrating material comes after equation number */ ++ if (t != adjust_head) { ++ /*tex 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; + } +@@ -2412,20 +2532,25 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) + 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}' */ ++ /*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; +- p = fin_mlist(null); /* this pops the nest */ ++ /*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) { /* end of equation number */ ++ if (mode == -m) { ++ /*tex end of equation number */ + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { +@@ -2435,7 +2560,8 @@ void after_math(void) + a = hpack(vlink(temp_head), 0, additional, -1); + build_attribute_list(a); + unsave_math(); +- decr(save_ptr); /* now |cur_group=math_shift_group| */ ++ /*tex now |cur_group=math_shift_group| */ ++ decr(save_ptr); + assert(saved_type(0) == saved_eqno); + if (saved_value(0) == 1) + l = true; +@@ -2444,47 +2570,50 @@ void after_math(void) + + } + 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. +- */ ++ /*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)); +- /* begin mathskip code */ ++ /*tex begin mathskip code */ + switch (math_skip_mode) { + case 0 : +- /* obey mathsurround when zero glue */ ++ /*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 : +- /* always left */ ++ /*tex always left */ + case 3 : +- /* always both */ ++ /*tex always both */ + case 6 : +- /* only when skip */ ++ /*tex only when skip */ + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + break ; + case 2 : +- /* only right */ ++ /*tex only right */ + surround(tail) = 0; + break ; + case 4 : +- /* ignore, obey marthsurround */ ++ /*tex ignore, obey marthsurround */ + break ; + case 5: +- /* all spacing disabled */ ++ /*tex all spacing disabled */ + surround(tail) = 0; + break ; + } +- /* end mathskip code */ ++ /*tex end mathskip code */ + if (dir_math_save) { + tail_append(new_dir(math_direction_par)); + } +@@ -2494,41 +2623,42 @@ void after_math(void) + tail = vlink(tail); + } + if (dir_math_save) { +- tail_append(new_dir(math_direction_par - dir_swap)); ++ tail_append(new_dir(math_direction_par)); ++ subtype(tail) = cancel_dir; + } + dir_math_save = false; + tail_append(new_math(math_surround_par, after)); +- /* begin mathskip code */ ++ /*tex begin mathskip code */ + switch (math_skip_mode) { + case 0 : +- /* obey mathsurround when zero glue */ ++ /*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 : +- /* always right */ ++ /*tex always right */ + case 3 : +- /* always both */ ++ /*tex always both */ + case 6 : +- /* only when skip */ ++ /*tex only when skip */ + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + break ; + case 1 : +- /* only left */ ++ /*tex only left */ + surround(tail) = 0; + break ; + case 4 : +- /* ignore, obey marthsurround */ ++ /*tex ignore, obey marthsurround */ + break ; + case 5: +- /* all spacing disabled */ ++ /*tex all spacing disabled */ + surround(tail) = 0; + break ; + } +- /* end mathskip code */ ++ /*tex end mathskip code */ + space_factor_par = 1000; + unsave_math(); + } else { +@@ -2544,12 +2674,15 @@ void after_math(void) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@c + void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) + { + do_assignments(); +@@ -2570,9 +2703,8 @@ void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) + resume_after_display(); + } + +-@ Interface to \.{\\Umath} and \.{\\mathstyle} ++/*tex Interface to \.{\\Umath} and \.{\\mathstyle}: */ + +-@c + void setup_math_style(void) + { + pointer q; +@@ -2582,7 +2714,6 @@ void setup_math_style(void) + (void) scan_math_style(nucleus(tail), num_style(m_style)); + } + +-@ @c + void print_math_style(void) + { + if (abs(mode) == mmode) +diff --git a/texk/web2c/luatexdir/tex/texnodes.c b/texk/web2c/luatexdir/tex/texnodes.c +new file mode 100644 +index 000000000..1dd28cdf5 +--- /dev/null ++++ b/texk/web2c/luatexdir/tex/texnodes.c +@@ -0,0 +1,4770 @@ ++/* ++ ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ ++#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 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(""); ++ 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; ++} +diff --git a/texk/web2c/luatexdir/tex/texnodes.w b/texk/web2c/luatexdir/tex/texnodes.w +deleted file mode 100644 +index 0c8bdb837..000000000 +--- a/texk/web2c/luatexdir/tex/texnodes.w ++++ /dev/null +@@ -1,3910 +0,0 @@ +-% texnodes.w +-% +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +-#include "ptexlib.h" +-#include "lua/luatex-api.h" +- +-/* we can consider less mode sizes: 2 4 6 8 */ +- +-@ +-This module started out using NDEBUG to trigger checking invalid node usage, +-something that is needed because users can mess up nodes in Lua. At some point +-that code was always enabled so it is now always on but still can be recognized +-as additional code. And as the performance hit is close to zero so disabling +-makes no sense, not even to make it configureable. There is a little more memory +-used but that is neglectable compared to other memory usage. +- +-@c +-#define MAX_CHAIN_SIZE 13 /* why not a bit larger */ +-#define CHECK_NODE_USAGE 1 /* this triggers checking */ +- +-memory_word *volatile varmem = NULL; +- +-#ifdef CHECK_NODE_USAGE +- char *varmem_sizes = NULL; +-#endif +- +-halfword var_mem_max = 0; +-halfword rover = 0; +- +-halfword free_chain[MAX_CHAIN_SIZE] = { null }; +- +-static int my_prealloc = 0; +- +-int fix_node_lists = 1; /* used in font and lang */ +- +-halfword slow_get_node(int s); /* defined below */ +- +-#define fake_node 100 +-#define fake_node_size 2 +-#define fake_node_name "fake" +- +-#define variable_node_size 2 +- +-/* core nodes */ +- +-const char *node_fields_list[] = { +- "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign", +- "glue_set", "head", NULL +-}; +-const char *node_fields_rule[] = { +- "attr", "width", "depth", "height", "dir", "index", NULL +-}; +-const char *node_fields_insert[] = { +- "attr", "cost", "depth", "height", "spec", "head", NULL +-}; +-const char *node_fields_mark[] = { +- "attr", "class", "mark", NULL +-}; +-const char *node_fields_adjust[] = { +- "attr", "head", NULL +-}; +-const char *node_fields_disc[] = { +- "attr", "pre", "post", "replace", "penalty", NULL +-}; +-const char *node_fields_math[] = { +- "attr", "surround", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL +-}; +-const char *node_fields_glue[] = { +- "attr", "leader", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL +-}; +-const char *node_fields_kern[] = { +- "attr", "kern", "expansion_factor", NULL +-}; +-const char *node_fields_penalty[] = { +- "attr", "penalty", NULL +-}; +-const char *node_fields_unset[] = { +- "attr", "width", "depth", "height", "dir", "shrink", "glue_order", +- "glue_sign", "stretch", "span", "head", NULL +-}; +-const char *node_fields_margin_kern[] = { +- "attr", "width", "glyph", NULL +-}; +-const char *node_fields_glyph[] = { +- "attr", "char", "font", "lang", "left", "right", "uchyph", "components", +- "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL +-}; +-const char *node_fields_inserting[] = { +- "height", "last_ins_ptr", "best_ins_ptr", +- "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL +-}; +-const char *node_fields_splitup[] = { +- "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL +-}; +-const char *node_fields_attribute[] = { +- "number", "value", NULL +-}; +-const char *node_fields_glue_spec[] = { +- "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL +-}; +-const char *node_fields_attribute_list[] = { +- NULL +-}; +-const char *node_fields_local_par[] = { +- "attr", "pen_inter", "pen_broken", "dir", "box_left", "box_left_width", +- "box_right", "box_right_width", NULL +-}; +-const char *node_fields_dir[] = { +- "attr", "dir", "level", NULL +-}; +-const char *node_fields_boundary[] = { +- "attr", "value", NULL +-}; +- +-/* math nodes */ +- +-const char *node_fields_noad[] = { +- "attr", "nucleus", "sub", "sup", NULL +-}; +- +-const char *node_fields_style[] = { +- "attr", "style", NULL +-}; +-const char *node_fields_choice[] = { +- "attr", "display", "text", "script", "scriptscript", NULL +-}; +-const char *node_fields_radical[] = { +- "attr", "nucleus", "sub", "sup", "left", "degree", "width", "options", NULL +-}; +-const char *node_fields_fraction[] = { +- "attr", "width", "num", "denom", "left", "right", "middle", "options", NULL +-}; +-const char *node_fields_accent[] = { +- "attr", "nucleus", "sub", "sup", "accent", "bot_accent", "top_accent", +- "overlay_accent", "fraction", NULL +-}; +-const char *node_fields_fence[] = { +- "attr", "delim", "italic", "height", "depth", "options", "class", NULL +-}; +-const char *node_fields_math_char[] = { +- "attr", "fam", "char", NULL +-}; +-const char *node_fields_sub_box[] = { +- "attr", "head", NULL +-}; +-const char *node_fields_sub_mlist[] = { +- "attr", "head", NULL +-}; +-const char *node_fields_math_text_char[] = { +- "attr", "fam", "char", NULL +-}; +-const char *node_fields_delim[] = { +- "attr", "small_fam", "small_char", "large_fam", "large_char", NULL +-}; +- +-/* whatsit nodes */ +- +-const char *node_fields_whatsit_open[] = { +- "attr", "stream", "name", "area", "ext", NULL +-}; +-const char *node_fields_whatsit_write[] = { +- "attr", "stream", "data", NULL +-}; +-const char *node_fields_whatsit_close[] = { +- "attr", "stream", NULL +-}; +-const char *node_fields_whatsit_special[] = { +- "attr", "data", NULL +-}; +-const char *node_fields_whatsit_save_pos[] = { +- "attr", NULL +-}; +-const char *node_fields_whatsit_late_lua[] = { +- "attr", "reg", "data", "name", "string", NULL +-}; +-const char *node_fields_whatsit_user_defined[] = { +- "attr", "user_id", "type", "value", NULL +-}; +- +-/* pdf backend whatsit nodes */ +- +-const char *node_fields_whatsit_pdf_literal[] = { +- "attr", "mode", "data", NULL +-}; +-const char *node_fields_whatsit_pdf_refobj[] = { +- "attr", "objnum", NULL +-}; +-const char *node_fields_whatsit_pdf_annot[] = { +- "attr", "width", "depth", "height", "objnum", "data", NULL +-}; +-const char *node_fields_whatsit_pdf_start_link[] = { +- "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL +-}; +-const char *node_fields_whatsit_pdf_end_link[] = { +- "attr", NULL +-}; +-const char *node_fields_whatsit_pdf_dest[] = { +- "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type", +- "xyz_zoom", "objnum", NULL +-}; +-const char *node_fields_whatsit_pdf_action[] = { +- "action_type", "named_id", "action_id", "file", "new_window", "data", NULL +-}; +-const char *node_fields_whatsit_pdf_thread[] = { +- "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL +-}; +-const char *node_fields_whatsit_pdf_start_thread[] = { +- "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL +-}; +-const char *node_fields_whatsit_pdf_end_thread[] = { +- "attr", NULL +-}; +-const char *node_fields_whatsit_pdf_colorstack[] = { +- "attr", "stack", "cmd", "data", NULL +-}; +-const char *node_fields_whatsit_pdf_setmatrix[] = { +- "attr", "data", NULL +-}; +-const char *node_fields_whatsit_pdf_save[] = { +- "attr", NULL +-}; +-const char *node_fields_whatsit_pdf_restore[] = { +- "attr", NULL +-}; +- +-/* subtypes */ +- +-const char *node_subtypes_glue[] = { +- "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip", +- "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip", +- "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip", +- "mathskip", "thinmuskip", "medmuskip", "thickmuskip", NULL +-}; +-const char *node_subtypes_mathglue[] = { /* 98+ */ +- "conditionalmathskip", "muglue", NULL +-}; +-const char *node_subtypes_leader[] = { /* 100+ */ +- "leaders", "cleaders", "xleaders", "gleaders", NULL +-}; +-const char *node_subtypes_fill[] = { +- "stretch", "fi", "fil", "fill", "filll", NULL +-}; +-const char *node_subtypes_boundary[] = { +- "cancel", "user", "protrusion", "word", NULL +-}; +-const char *node_subtypes_penalty[] = { +- "userpenalty", "linebreakpenalty", "linepenalty", "wordpenalty", "finalpenalty", +- "noadpenalty", "beforedisplaypenalty", "afterdisplaypenalty", "equationnumberpenalty", NULL +-}; +-const char *node_subtypes_kern[] = { +- "fontkern", "userkern", "accentkern", "italiccorrection", NULL +-}; +-const char *node_subtypes_rule[] = { +- "normal", "box", "image", "empty", "user", "over", "under", "fraction", "radical", NULL +-}; +-const char *node_subtypes_glyph[] = { +- "character", "glyph", "ligature", "ghost", "left", "right", NULL +-}; +-const char *node_subtypes_disc[] = { +- "discretionary", "explicit", "automatic", "regular", "first", "second", NULL +-}; +-const char *node_subtypes_marginkern[] = { +- "left", "right", NULL +-}; +-const char *node_subtypes_list[] = { +- "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL +-}; +-const char *node_subtypes_adjust[] = { +- "normal", "pre", NULL +-}; +-const char *node_subtypes_math[] = { +- "beginmath", "endmath", NULL +-}; +-const char *node_subtypes_noad[] = { +- "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close", +- "punct", "inner", "under", "over", "vcenter", NULL +-}; +-const char *node_subtypes_radical[] = { +- "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder", +- "udelimiterover", NULL +-}; +-const char *node_subtypes_accent[] = { +- "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL, +-}; +-const char *node_subtypes_fence[] = { +- "unset", "left", "middle", "right", NULL +-}; +- +-node_info node_data[] = { /* the last entry in a row is the etex number */ +- { hlist_node, box_node_size, node_fields_list, "hlist", 1 }, +- { vlist_node, box_node_size, node_fields_list, "vlist", 2 }, +- { rule_node, rule_node_size, node_fields_rule, "rule", 3 }, +- { ins_node, ins_node_size, node_fields_insert, "ins", 4 }, +- { mark_node, mark_node_size, node_fields_mark, "mark", 5 }, +- { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 }, +- { boundary_node, boundary_node_size, node_fields_boundary, "boundary", -1 }, +- { disc_node, disc_node_size, node_fields_disc, "disc", 8 }, +- { whatsit_node, -1, NULL, "whatsit", 9 }, +- { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 }, +- { dir_node, dir_node_size, node_fields_dir, "dir", -1 }, +- { math_node, math_node_size, node_fields_math, "math", 10 }, +- { glue_node, glue_node_size, node_fields_glue, "glue", 11 }, +- { kern_node, kern_node_size, node_fields_kern, "kern", 12 }, +- { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 }, +- { unset_node, box_node_size, node_fields_unset, "unset", 14 }, +- { style_node, style_node_size, node_fields_style, "style", 15 }, +- { choice_node, style_node_size, node_fields_choice, "choice", 15 }, +- { simple_noad, noad_size, node_fields_noad, "noad", 15 }, +- { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 }, +- { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 }, +- { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 }, +- { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 }, +- { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 }, +- { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 }, +- { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 }, +- { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 }, +- { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 }, +- { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 }, +- { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 }, +- { align_record_node, box_node_size, NULL, "align_record", -1 }, +- { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 }, +- { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 }, +- { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 }, +- { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 }, +- { expr_node, expr_node_size, NULL, "expr_stack", -1 }, +- { nesting_node, nesting_node_size, NULL, "nested_list", -1 }, +- { span_node, span_node_size, NULL, "span", -1 }, +- { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 }, +- { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 }, +- { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 }, +- { temp_node, temp_node_size, NULL, "temp", -1 }, +- { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 }, +- { movement_node, movement_node_size, NULL, "movement_stack", -1 }, +- { if_node, if_node_size, NULL, "if_stack", -1 }, +- { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 }, +- { hyphenated_node, active_node_size, NULL, "hyphenated", -1 }, +- { delta_node, delta_node_size, NULL, "delta", -1 }, +- { passive_node, passive_node_size, NULL, "passive", -1 }, +- { shape_node, variable_node_size, NULL, "shape", -1 }, +- { -1, -1, NULL, NULL, -1 }, +-}; +- +-const char *node_subtypes_pdf_destination[] = { +- "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL +-}; +-const char *node_subtypes_pdf_literal[] = { +- "origin", "page", "direct", NULL +-}; +- +-node_info whatsit_node_data[] = { +- { open_node, open_node_size, node_fields_whatsit_open, "open", -1 }, +- { write_node, write_node_size, node_fields_whatsit_write, "write", -1 }, +- { close_node, close_node_size, node_fields_whatsit_close, "close", -1 }, +- { special_node, special_node_size, node_fields_whatsit_special, "special", -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 }, +- { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 }, +- { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- { fake_node, fake_node_size, NULL, fake_node_name, -1 }, +- /* here starts the dvi backend section, todo: a separate list */ +- /* nothing for dvi */ +- /* here starts the pdf backend section, todo: a separate list */ +- { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 }, +- { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 }, +- { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 }, +- { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 }, +- { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 }, +- { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 }, +- { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 }, +- { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 }, +- { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 }, +- { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 }, +- { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 }, +- { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 }, +- { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 }, +- { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 }, +- { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 }, +- { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 }, +- /* done */ +- { -1, -1, NULL, NULL, -1 }, +-}; +- +-#define last_whatsit_node pdf_restore_node +- +-@ +-When we copy a node list, there are several possibilities: we do the same as a new node, +-we copy the entry to the table in properties (a reference), we do a deep copy of a table +-in the properties, we create a new table and give it the original one as a metatable. +-After some experiments (that also included timing) with these scenarios I decided that a +-deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable +-variant were both ok, although the second ons is slower. The most important aspect to keep +-in mind is that references to other nodes in properties no longer can be valid for that +-copy. We could use two tables (one unique and one shared) or metatables but that only +-complicates matters. +- +-When defining a new node, we could already allocate a table but it is rather easy to do +-that at the lua end e.g. using a metatable __index method. That way it is under macro +-package control. +- +-When deleting a node, we could keep the slot (e.g. setting it to false) but it could make +-memory consumption raise unneeded when we have temporary large node lists and after that +-only small lists. +- +-So, in the end this is what we ended up with. For the record, I also experimented with the +-following: +- +-- copy attributes to the properties so that we have fast access at the lua end: in the end +- the overhead is not compensated by speed and convenience, in fact, attributes are not +- that slow when it comes to accessing them +- +-- a bitset in the node but again the gain compared to attributes is neglectable and it also +- demands a pretty string agreement over what bit represents what, and this is unlikely to +- succeed in the tex community (I could use it for font handling, which is cross package, +- but decided that it doesn't pay off +- +-In case one wonders why properties make sense then, well, it is not so much speed that we +-gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and +-this mechanism makes sure that properties are cleaned up when a node is freed. Also, the +-advantage of a more or less global properties table is that we stay at the lua end. An +-alternative is to store a reference in the node itself but that is complicated by the fact +-that the register has some limitations (no numeric keys) and we also don't want to mess with +-it too much. +- +-@c +-int lua_properties_level = 0 ; /* can be private */ +-int lua_properties_enabled = 0 ; +-int lua_properties_use_metatable = 0 ; +- +-@ +-We keep track of nesting so that we don't oveflow the stack, and, what is more +-important, don't keep resolving the registry index. +- +-@c +-#define lua_properties_push do { \ +- if (lua_properties_enabled) { \ +- lua_properties_level = lua_properties_level + 1 ; \ +- if (lua_properties_level == 1) { \ +- lua_get_metatablelua_l(Luas,node_properties); \ +- } \ +- } \ +-} while(0) +- +-#define lua_properties_pop do { \ +- if (lua_properties_enabled) { \ +- if (lua_properties_level == 1) \ +- lua_pop(Luas,1); \ +- lua_properties_level = lua_properties_level - 1 ; \ +- } \ +-} while(0) +- +-/* No setting is needed: */ +- +-#define lua_properties_set(target) do { \ +-} while(0) +- +-/* Resetting boils down to nilling. */ +- +-#define lua_properties_reset(target) do { \ +- if (lua_properties_enabled) { \ +- if (lua_properties_level == 0) { \ +- lua_get_metatablelua_l(Luas,node_properties); \ +- lua_pushnil(Luas); \ +- lua_rawseti(Luas,-2,target); \ +- lua_pop(Luas,1); \ +- } else { \ +- lua_pushnil(Luas); \ +- lua_rawseti(Luas,-2,target); \ +- } \ +- } \ +-} while(0) +- +-/* +- For a moment I considered supporting all kind of data types but in practice +- that makes no sense. So we stick to a cheap shallow copy with as option a +- metatable. Btw, a deep copy would look like this: +- +- static void copy_lua_table(lua_State* L, int index) { +- lua_newtable(L); +- lua_pushnil(L); +- while(lua_next(L, index-1) != 0) { +- lua_pushvalue(L, -2); +- lua_insert(L, -2); +- if (lua_type(L,-1)==LUA_TTABLE) +- copy_lua_table(L,-1); +- lua_settable(L, -4); +- } +- lua_pop(L,1); +- } +- +- #define lua_properties_copy(target, source) do { \ +- if (lua_properties_enabled) { \ +- lua_pushinteger(Luas,source); \ +- lua_rawget(Luas,-2); \ +- if (lua_type(Luas,-1)==LUA_TTABLE) { \ +- copy_lua_table(Luas,-1); \ +- lua_pushinteger(Luas,target); \ +- lua_insert(Luas,-2); \ +- lua_rawset(Luas,-3); \ +- } else { \ +- lua_pop(Luas,1); \ +- } \ +- } \ +- } while(0) +- +-*/ +- +-/* isn't there a faster way to metatable? */ +- +-/* +- +-#define lua_properties_copy(target,source) do { \ +- if (lua_properties_enabled) { \ +- if (lua_properties_level == 0) { \ +- lua_get_metatablelua_l(Luas,node_properties); \ +- lua_rawgeti(Luas,-1,source); \ +- if (lua_type(Luas,-1)==LUA_TTABLE) { \ +- if (lua_properties_use_metatable) { \ +- lua_newtable(Luas); \ +- lua_insert(Luas,-2); \ +- lua_setfield(Luas,-2,"__index"); \ +- lua_newtable(Luas); \ +- lua_insert(Luas,-2); \ +- lua_setmetatable(Luas,-2); \ +- } \ +- lua_rawseti(Luas,-2,target); \ +- } else { \ +- lua_pop(Luas,1); \ +- } \ +- lua_pop(Luas,1); \ +- } else { \ +- lua_rawgeti(Luas,-1,source); \ +- if (lua_type(Luas,-1)==LUA_TTABLE) { \ +- if (lua_properties_use_metatable) { \ +- lua_newtable(Luas); \ +- lua_insert(Luas,-2); \ +- lua_setfield(Luas,-2,"__index"); \ +- lua_newtable(Luas); \ +- lua_insert(Luas,-2); \ +- lua_setmetatable(Luas,-2); \ +- } \ +- lua_rawseti(Luas,-2,target); \ +- } else { \ +- lua_pop(Luas,1); \ +- } \ +- } \ +- } \ +-} while(0) +- +-*/ +- +-/* +- A simple testrun on many pages of dumb text shows 1% gain (of course it depends +- on how properties are used but some other tests confirm it). +-*/ +- +-#define lua_properties_copy(target,source) do { \ +- if (lua_properties_enabled) { \ +- if (lua_properties_level == 0) { \ +- lua_get_metatablelua_l(Luas,node_properties); \ +- lua_rawgeti(Luas,-1,source); \ +- if (lua_type(Luas,-1)==LUA_TTABLE) { \ +- if (lua_properties_use_metatable) { \ +- lua_newtable(Luas); \ +- lua_insert(Luas,-2); \ +- lua_push_string_by_name(Luas,__index); \ +- lua_insert(Luas,-2); \ +- lua_rawset(Luas, -3); \ +- lua_newtable(Luas); \ +- lua_insert(Luas,-2); \ +- lua_setmetatable(Luas,-2); \ +- } \ +- lua_rawseti(Luas,-2,target); \ +- } else { \ +- lua_pop(Luas,1); \ +- } \ +- lua_pop(Luas,1); \ +- } else { \ +- lua_rawgeti(Luas,-1,source); \ +- if (lua_type(Luas,-1)==LUA_TTABLE) { \ +- if (lua_properties_use_metatable) { \ +- lua_newtable(Luas); \ +- lua_insert(Luas,-2); \ +- lua_push_string_by_name(Luas,__index); \ +- lua_insert(Luas,-2); \ +- lua_rawset(Luas, -3); \ +- lua_newtable(Luas); \ +- lua_insert(Luas,-2); \ +- lua_setmetatable(Luas,-2); \ +- } \ +- lua_rawseti(Luas,-2,target); \ +- } else { \ +- lua_pop(Luas,1); \ +- } \ +- } \ +- } \ +-} while(0) +- +-/* Here end the property handlers. */ +- +-@ @c +-int valid_node(halfword p) +-{ +- if (p > my_prealloc && p < var_mem_max) { +-#ifdef CHECK_NODE_USAGE +- if (varmem_sizes[p] > 0) { +- return 1; +- } +-#else +- return 1; +-#endif +- } +- return 0; +-} +- +-@ @c +-static int test_count = 1; +- +-#define dorangetest(a,b,c) do { \ +- if (!(b>=0 && b my_prealloc && varmem_sizes[s] == 0) { +- s--; +- } +- if (s != null +- && s != my_prealloc +- && s != var_mem_max +- && (r - s) < get_node_size(type(s), subtype(s)) +- && alink(s) != p) { +- if (type(s) == disc_node) { +- fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", +- get_node_name(type(s), subtype(s)), (int) s, +- (int) vlink(s), (int) alink(s)); +- fprintf(stdout, "pre_break(%d,%d,%d), ", +- (int) vlink_pre_break(s), (int) tlink(pre_break(s)), +- (int) alink(pre_break(s))); +- fprintf(stdout, "post_break(%d,%d,%d), ", +- (int) vlink_post_break(s), +- (int) tlink(post_break(s)), +- (int) alink(post_break(s))); +- fprintf(stdout, "no_break(%d,%d,%d)", +- (int) vlink_no_break(s), (int) tlink(no_break(s)), +- (int) alink(no_break(s))); +- fprintf(stdout, "\n"); +- } else { +- if (vlink(s) == p +- || (type(s) == glyph_node && lig_ptr (s) == p) +- || (type(s) == vlist_node && list_ptr(s) == p) +- || (type(s) == hlist_node && list_ptr(s) == p) +- || (type(s) == unset_node && list_ptr(s) == p) +- || (type(s) == ins_node && ins_ptr (s) == p) +- ) { +- fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", +- get_node_name(type(s), subtype(s)), (int) s, +- (int) vlink(s), (int) alink(s)); +- if (type(s) == glyph_node) { +- fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); +- } else if (type(s) == vlist_node || type(s) == hlist_node) { +- fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); +- } +- fprintf(stdout, "\n"); +- } else { +- if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) { +- fprintf(stdout, " pointed to from %s node %d\n", +- get_node_name(type(s), subtype(s)), (int) s); +- } +- } +- } +- } +- } +- } +-} +- +-#endif +- +-static int free_error(halfword p) +-{ +- if (p > my_prealloc && p < var_mem_max) { +-#ifdef CHECK_NODE_USAGE +- int i; +- if (varmem_sizes[p] == 0) { +- check_static_node_mem(); +- for (i = (my_prealloc + 1); i < var_mem_max; i++) { +- if (varmem_sizes[i] > 0) { +- check_node(i); +- } +- } +- test_count++; +- if (type(p) == glyph_node) { +- formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p); +- } else { +- formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); +- } +- node_mem_dump(p); +- return 1; +- } +-#endif +- } else { +- formatted_error("nodes", "attempt to free an impossible node %d", (int) p); +- return 1; +- } +- return 0; +-} +- +-@ @c +-static int copy_error(halfword p) +-{ +- if (p >= 0 && p < var_mem_max) { +-#ifdef CHECK_NODE_USAGE +- if (p > my_prealloc && varmem_sizes[p] == 0) { +- if (type(p) == glyph_node) { +- formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p); +- } else { +- formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); +- } +- return 1; +- } +-#endif +- } else { +- formatted_error("nodes", "attempt to copy an impossible node %d", (int) p); +- return 1; +- } +- return 0; +-} +- +-@ @c +-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(""); +- break; +- default: +- show_math_node(p); +- break; +- } +- } +- p = vlink(p); +- } +-} +- +-@ This routine finds the 'base' width of a horizontal box, using the same logic +- that \TeX82 used for \.{\\predisplaywidth} */ +- +-@c +-static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width) +-{ +- scaled d; /* increment to |v| */ +- scaled w = -max_dimen; /* calculated |size| */ +- scaled v = initial_width; /* |w| plus possible glue amount */ +- while (p != null) { +- if (is_char_node(p)) { +- d = glyph_width(p); +- goto FOUND; +- } +- switch (type(p)) { +- case hlist_node: +- case vlist_node: +- case rule_node: +- d = width(p); +- goto FOUND; +- break; +- case margin_kern_node: +- d = width(p); +- break; +- case kern_node: +- d = width(p); +- break; +- case disc_node: +- /* at the end of the line we should actually take the pre */ +- if (no_break(p) != null) { +- d = get_actual_box_width(r,vlink_no_break(p),0); +- if (d <= -max_dimen || d >= max_dimen) { +- d = 0; +- } +- } else { +- d = 0; +- } +- goto FOUND; +- break; +- case math_node: +- /* begin mathskip code */ +- if (glue_is_zero(p)) { +- d = surround(p); +- break; +- } else { +- /* fall through */ +- } +- /* end mathskip code */ +- case glue_node: +- /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set| +- values, since such values are subject to system-dependent rounding. +- System-dependent numbers are not allowed to infiltrate parameters like +- |pre_display_size|, since \TeX82 is supposed to make the same decisions on all +- machines. +- */ +- d = width(p); +- if (glue_sign(r) == stretching) { +- if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0)) +- v = max_dimen; +- } else if (glue_sign(r) == shrinking) { +- if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0)) +- v = max_dimen; +- } +- if (subtype(p) >= a_leaders) +- goto FOUND; +- break; +- default: +- d = 0; +- break; +- } +- if (v < max_dimen) +- v = v + d; +- goto NOT_FOUND; +- FOUND: +- if (v < max_dimen) { +- v = v + d; +- w = v; +- } else { +- w = max_dimen; +- break; +- } +- NOT_FOUND: +- p = vlink(p); +- } +- return w; +-} +- +-pointer actual_box_width(pointer r, scaled base_width) +-{ +- /* often this is the same as: +- return + shift_amount(r) + base_width + +- natural_sizes(list_ptr(r),null,(glue_ratio) glue_set(r),glue_sign(r),glue_order(r),box_dir(r)); +- */ +- return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width); +-} +- +-@ @c +-halfword tail_of_list(halfword p) +-{ +- halfword q = p; +- while (vlink(q) != null) +- q = vlink(q); +- return q; +-} +- +- +- +-@ @c +-int var_used; +- +-@ Attribute lists need two extra globals to increase processing efficiency. +-|max_used_attr| limits the test loop that checks for set attributes, and +-|attr_list_cache| contains a pointer to an already created attribute list. It is +-set to the special value |cache_disabled| when the current value can no longer be +-trusted: after an assignment to an attribute register, and after a group has +-ended. +- +-@c +-int max_used_attr; /* maximum assigned attribute id */ +-halfword attr_list_cache; +- +-@ From the computer's standpoint, \TeX's chief mission is to create +-horizontal and vertical lists. We shall now investigate how the elements +-of these lists are represented internally as nodes in the dynamic memory. +- +-A horizontal or vertical list is linked together by |link| fields in +-the first word of each node. Individual nodes represent boxes, glue, +-penalties, or special things like discretionary hyphens; because of this +-variety, some nodes are longer than others, and we must distinguish different +-kinds of nodes. We do this by putting a `|type|' field in the first word, +-together with the link and an optional `|subtype|'. +- +-@ Character nodes appear only in horizontal lists, never in vertical lists. +- +-An |hlist_node| stands for a box that was made from a horizontal list. +-Each |hlist_node| is seven words long, and contains the following fields +-(in addition to the mandatory |type| and |link|, which we shall not +-mention explicitly when discussing the other node types): The |height| and +-|width| and |depth| are scaled integers denoting the dimensions of the +-box. There is also a |shift_amount| field, a scaled integer indicating +-how much this box should be lowered (if it appears in a horizontal list), +-or how much it should be moved to the right (if it appears in a vertical +-list). There is a |list_ptr| field, which points to the beginning of the +-list from which this box was fabricated; if |list_ptr| is |null|, the box +-is empty. Finally, there are three fields that represent the setting of +-the glue: |glue_set(p)| is a word of type |glue_ratio| that represents +-the proportionality constant for glue setting; |glue_sign(p)| is +-|stretching| or |shrinking| or |normal| depending on whether or not the +-glue should stretch or shrink or remain rigid; and |glue_order(p)| +-specifies the order of infinity to which glue setting applies (|normal|, +-|sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used. +- +-@ The |new_null_box| function returns a pointer to an |hlist_node| in +-which all subfields have the values corresponding to `\.{\\hbox\{\}}'. +-The |subtype| field is set to |min_quarterword|, since that's the desired +-|span_count| value if this |hlist_node| is changed to an |unset_node|. +- +-@c +-halfword new_null_box(void) +-{ /* creates a new box node */ +- halfword p = new_node(hlist_node, min_quarterword); +- box_dir(p) = text_direction_par; +- return p; +-} +- +-@ A |vlist_node| is like an |hlist_node| in all respects except that it +-contains a vertical list. +- +-@ A |rule_node| stands for a solid black rectangle; it has |width|, +-|depth|, and |height| fields just as in an |hlist_node|. However, if +-any of these dimensions is $-2^{30}$, the actual value will be determined +-by running the rule up to the boundary of the innermost enclosing box. +-This is called a ``running dimension.'' The |width| is never running in +-an hlist; the |height| and |depth| are never running in a~vlist. +- +-@ A new rule node is delivered by the |new_rule| function. It +-makes all the dimensions ``running,'' so you have to change the +-ones that are not allowed to run. +- +-@c +-halfword new_rule(int s) +-{ +- halfword p = new_node(rule_node,s); +- return p; +-} +- +-@ Insertions are represented by |ins_node| records, where the |subtype| +-indicates the corresponding box number. For example, `\.{\\insert 250}' +-leads to an |ins_node| whose |subtype| is |250+min_quarterword|. +-The |height| field of an |ins_node| is slightly misnamed; it actually holds +-the natural height plus depth of the vertical list being inserted. +-The |depth| field holds the |split_max_depth| to be used in case this +-insertion is split, and the |split_top_ptr| points to the corresponding +-|split_top_skip|. The |float_cost| field holds the |floating_penalty| that +-will be used if this insertion floats to a subsequent page after a +-split insertion of the same class. There is one more field, the +-|ins_ptr|, which points to the beginning of the vlist for the insertion. +- +-@ A |mark_node| has a |mark_ptr| field that points to the reference count +-of a token list that contains the user's \.{\\mark} text. +-In addition there is a |mark_class| field that contains the mark class. +- +-@ An |adjust_node|, which occurs only in horizontal lists, +-specifies material that will be moved out into the surrounding +-vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}' +-operation. The |adjust_ptr| field points to the vlist containing this +-material. +- +-@ A |glyph_node|, which occurs only in horizontal lists, specifies a +-glyph in a particular font, along with its attribute list. Older +-versions of \TeX\ could use token memory for characters, because the +-font,char combination would fit in a single word (both values were +-required to be strictly less than $2^{16}$). In LuaTeX, room is +-needed for characters that are larger than that, as well as a pointer +-to a potential attribute list, and the two displacement values. +- +-In turn, that made the node so large that it made sense to merge +-ligature glyphs as well, as that requires only one extra pointer. A +-few extra classes of glyph nodes will be introduced later. The +-unification of all those types makes it easier to manipulate lists of +-glyphs. The subtype differentiates various glyph kinds. +- +-First, here is a function that returns a pointer to a glyph node for a given +-glyph in a given font. If that glyph doesn't exist, |null| is returned +-instead. Nodes of this subtype are directly created only for accents +-and their base (through |make_accent|), and math nucleus items (in the +-conversion from |mlist| to |hlist|). +- +-@c +-halfword new_glyph(int f, int c) +-{ +- halfword p = null; /* the new node */ +- if ((f == 0) || (char_exists(f, c))) { +- p = new_glyph_node(); +- set_to_glyph(p); +- font(p) = f; +- character(p) = c; +- } +- return p; +-} +- +-@ A subset of the glyphs nodes represent ligatures: characters +-fabricated from the interaction of two or more actual characters. The +-characters that generated the ligature have not been forgotten, since +-they are needed for diagnostic messages; the |lig_ptr| field points to +-a linked list of character nodes for all original characters that have +-been deleted. (This list might be empty if the characters that +-generated the ligature were retained in other nodes.) +- +-The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if +-the original source of the ligature included implicit left and/or +-right boundaries. These nodes are created by the C function |new_ligkern|. +- +-A third general type of glyphs could be called a character, as it +-only appears in lists that are not yet processed by the ligaturing and +-kerning steps of the program. +- +-|main_control| inserts these, and they are later converted to +-|subtype_normal| by |new_ligkern|. +- +-@c +-quarterword norm_min(int h) +-{ +- if (h <= 0) +- return 1; +- else if (h >= 255) +- return 255; +- else +- return (quarterword) h; +-} +- +-halfword new_char(int f, int c) +-{ +- halfword p; /* the new node */ +- p = new_glyph_node(); +- set_to_character(p); +- font(p) = f; +- character(p) = c; +- lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par); +- return p; +-} +- +-@ Left and right ghost glyph nodes are the result of \.{\\leftghost} +-and \.{\\rightghost}, respectively. They are going to be removed by +-|new_ligkern|, at the end of which they are no longer needed. +- +-@ Here are a few handy helpers used by the list output routines. +- +-@c +-scaled glyph_width(halfword p) +-{ +- scaled w = char_width(font(p), character(p)); +- return w; +-} +- +-scaled glyph_height(halfword p) +-{ +- scaled w = char_height(font(p), character(p)) + y_displace(p); +- if (w < 0) +- w = 0; +- return w; +-} +- +-scaled glyph_depth(halfword p) +-{ +- scaled w = char_depth(font(p), character(p)); +- if (y_displace(p) > 0) +- w = w - y_displace(p); +- if (w < 0) +- w = 0; +- return w; +-} +- +-@ A |disc_node|, which occurs only in horizontal lists, specifies a +-``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text +-that starts at |pre_break(p)| will precede the break, the text that starts at +-|post_break(p)| will follow the break, and text that appears in +-|no_break(p)| nodes will be ignored. For example, an ordinary +-discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with +-|pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|, +-and |no_break=null|. +- +-{TODO: Knuth said: All three of the discretionary texts must be lists +-that consist entirely of character, kern, box and rule nodes.} +- +-If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this +-break. Otherwise the |hyphen_penalty| will be charged. The texts will +-actually be substituted into the list by the line-breaking algorithm if it +-decides to make the break, and the discretionary node will disappear at +-that time; thus, the output routine sees only discretionaries that were +-not chosen. +- +-@c +-halfword new_disc(void) +-{ /* creates an empty |disc_node| */ +- halfword p = new_node(disc_node, 0); +- disc_penalty(p) = hyphen_penalty_par; +- return p; +-} +- +-@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The +-|subtype| field in its first word says what `\\{whatsit}' it is, and +-implicitly determines the node size (which must be 2 or more) and the +-format of the remaining words. When a |whatsit_node| is encountered +-in a list, special actions are invoked; knowledgeable people who are +-careful not to mess up the rest of \TeX\ are able to make \TeX\ do new +-things by adding code at the end of the program. For example, there +-might be a `\TeX nicolor' extension to specify different colors of ink, +-@^extensions to \TeX@> +-and the whatsit node might contain the desired parameters. +- +-The present implementation of \TeX\ treats the features associated with +-`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to +-illustrate how such routines might be coded. We shall defer further +-discussion of extensions until the end of this program. +- +-@ A |math_node|, which occurs only in horizontal lists, appears before and +-after mathematical formulas. The |subtype| field is |before| before the +-formula and |after| after it. There is a |surround| field, which represents +-the amount of surrounding space inserted by \.{\\mathsurround}. +- +-@c +-halfword new_math(scaled w, int s) +-{ +- halfword p = new_node(math_node, s); +- surround(p) = w; +- return p; +-} +- +-@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, +-|rule_node|, |ins_node|, |mark_node|, |adjust_node|, +-|disc_node|, |whatsit_node|, and |math_node| are at the low end of the +-type codes, by permitting a break at glue in a list if and only if the +-|type| of the previous node is less than |math_node|. Furthermore, a +-node is discarded after a break if its type is |math_node| or~more. +- +-@ A |glue_node| represents glue in a list. However, it is really only +-a pointer to a separate glue specification, since \TeX\ makes use of the +-fact that many essentially identical nodes of glue are usually present. +-If |p| points to a |glue_node|, |glue_ptr(p)| points to +-another packet of words that specify the stretch and shrink components, etc. +- +-Glue nodes also serve to represent leaders; the |subtype| is used to +-distinguish between ordinary glue (which is called |normal|) and the three +-kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|). +-The |leader_ptr| field points to a rule node or to a box node containing the +-leaders; it is set to |null| in ordinary glue nodes. +- +-Many kinds of glue are computed from \TeX's ``skip'' parameters, and +-it is helpful to know which parameter has led to a particular glue node. +-Therefore the |subtype| is set to indicate the source of glue, whenever +-it originated as a parameter. We will be defining symbolic names for the +-parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|, +-etc.); it suffices for now to say that the |subtype| of parametric glue +-will be the same as the parameter number, plus~one. +- +-@ In math formulas there are two more possibilities for the |subtype| in a +-glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu} +-instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}' +-feature that cancels the glue node immediately following if it appears +-in a subscript. +- +-@ A glue specification has a halfword reference count in its first word, +-@^reference counts@> +-representing |null| plus the number of glue nodes that point to it (less one). +-Note that the reference count appears in the same position as +-the |link| field in list nodes; this is the field that is initialized +-to |null| when a node is allocated, and it is also the field that is flagged +-by |empty_flag| in empty nodes. +- +-Glue specifications also contain three |scaled| fields, for the |width|, +-|stretch|, and |shrink| dimensions. Finally, there are two one-byte +-fields called |stretch_order| and |shrink_order|; these contain the +-orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|) +-corresponding to the stretch and shrink values. +- +-@ Here is a function that returns a pointer to a copy of a glue spec. +-The reference count in the copy is |null|, because there is assumed +-to be exactly one reference to the new specification. +- +-@c +-halfword new_spec(halfword q) /* safeguard for copying a glue node */ +-{ +- if (q == null) { +- return copy_node(zero_glue); +- } else if (type(q) == glue_spec_node) { +- return copy_node(q); +- } else if (type(q) == glue_node) { +- halfword p = copy_node(zero_glue); +- width(p) = width(q); +- stretch(p) = stretch(q); +- shrink(p) = shrink(q); +- stretch_order(p) = stretch_order(q); +- shrink_order(p) = shrink_order(q); +- return p; +- } else { +- /* alternatively we can issue a warning */ +- return copy_node(zero_glue); +- } +-} +- +-@ And here's a function that creates a glue node for a given parameter +-identified by its code number; for example, +-|new_param_glue(line_skip_code)| returns a pointer to a glue node for the +-current \.{\\lineskip}. +- +-@c +-halfword new_param_glue(int n) +-{ +- halfword p = new_node(glue_node, n + 1); +- halfword q = glue_par(n); +- width(p) = width(q); +- stretch(p) = stretch(q); +- shrink(p) = shrink(q); +- stretch_order(p) = stretch_order(q); +- shrink_order(p) = shrink_order(q); +- return p; +-} +- +-@ Glue nodes that are more or less anonymous are created by |new_glue|, +-whose argument points to a glue specification. +- +-@c +-halfword new_glue(halfword q) +-{ +- halfword p = new_node(glue_node, normal); +- width(p) = width(q); +- stretch(p) = stretch(q); +- shrink(p) = shrink(q); +- stretch_order(p) = stretch_order(q); +- shrink_order(p) = shrink_order(q); +- return p; +-} +- +-@ Still another subroutine is needed: This one is sort of a combination +-of |new_param_glue| and |new_glue|. It creates a glue node for one of +-the current glue parameters, but it makes a fresh copy of the glue +-specification, since that specification will probably be subject to change, +-while the parameter will stay put. +- +-/* +- The global variable |temp_ptr| is set to the address of the new spec. +-*/ +- +-@c +-halfword new_skip_param(int n) +-{ +- halfword p = new_node(glue_node, n + 1); +- halfword q = glue_par(n); +- width(p) = width(q); +- stretch(p) = stretch(q); +- shrink(p) = shrink(q); +- stretch_order(p) = stretch_order(q); +- shrink_order(p) = shrink_order(q); +- return p; +-} +- +-@ A |kern_node| has a |width| field to specify a (normally negative) +-amount of spacing. This spacing correction appears in horizontal lists +-between letters like A and V when the font designer said that it looks +-better to move them closer together or further apart. A kern node can +-also appear in a vertical list, when its `|width|' denotes additional +-spacing in the vertical direction. The |subtype| is either |normal| (for +-kerns inserted from font information or math mode calculations) or |explicit| +-(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern| +-(for kerns inserted from non-math accents) or |mu_glue| (for kerns +-inserted from \.{\\mkern} specifications in math formulas). +- +-@ The |new_kern| function creates a kern node having a given width. +- +-@c +-halfword new_kern(scaled w) +-{ +- halfword p = new_node(kern_node, normal); +- width(p) = w; +- return p; +-} +- +-@ A |penalty_node| specifies the penalty associated with line or page +-breaking, in its |penalty| field. This field is a fullword integer, but +-the full range of integer values is not used: Any penalty |>=10000| is +-treated as infinity, and no break will be allowed for such high values. +-Similarly, any penalty |<=-10000| is treated as negative infinity, and a +-break will be forced. +- +-@ Anyone who has been reading the last few sections of the program will +-be able to guess what comes next. +- +-@c +-halfword new_penalty(int m, int s) +-{ +- halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */ +- penalty(p) = m; +- subtype(p) = s; +- return p; +-} +- +-@ You might think that we have introduced enough node types by now. Well, +-almost, but there is one more: An |unset_node| has nearly the same format +-as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} +-or \.{\\valign} that are not yet in their final form, since the box +-dimensions are their ``natural'' sizes before any glue adjustment has been +-made. The |glue_set| word is not present; instead, we have a |glue_stretch| +-field, which contains the total stretch of order |glue_order| that is +-present in the hlist or vlist being boxed. +-Similarly, the |shift_amount| field is replaced by a |glue_shrink| field, +-containing the total shrink of order |glue_sign| that is present. +-The |subtype| field is called |span_count|; an unset box typically +-contains the data for |qo(span_count)+1| columns. +-Unset nodes will be changed to box nodes when alignment is completed. +- +-In fact, there are still more types coming. When we get to math formula +-processing we will see that a |style_node| has |type=14|; and a number +-of larger type codes will also be defined, for use in math mode only. +- +-Warning: If any changes are made to these data structure layouts, such as +-changing any of the node sizes or even reordering the words of nodes, +-the |copy_node_list| procedure and the memory initialization code +-below may have to be changed. Such potentially dangerous parts of the +-program are listed in the index under `data structure assumptions'. +-@!@^data structure assumptions@> +-However, other references to the nodes are made symbolically in terms of +-the \.{WEB} macro definitions above, so that format changes will leave +-\TeX's other algorithms intact. +-@^system dependencies@> +- +-@ This function creates a |local_paragraph| node +- +-@c +- +-halfword make_local_par_node(int mode) +-{ +- int callback_id; +- halfword q; +- halfword p = new_node(local_par_node,0); +- local_pen_inter(p) = local_inter_line_penalty_par; +- local_pen_broken(p) = local_broken_penalty_par; +- if (local_left_box_par != null) { +- q = copy_node_list(local_left_box_par); +- local_box_left(p) = q; +- local_box_left_width(p) = width(local_left_box_par); +- } +- if (local_right_box_par != null) { +- q = copy_node_list(local_right_box_par); +- local_box_right(p) = q; +- local_box_right_width(p) = width(local_right_box_par); +- } +- local_par_dir(p) = par_direction_par; +- /* callback with node passed */ +- callback_id = callback_defined(insert_local_par_callback); +- if (callback_id > 0) { +- int sfix = lua_gettop(Luas); +- if (!get_callback(Luas, callback_id)) { +- lua_settop(Luas, sfix); +- return p; +- } +- nodelist_to_lua(Luas, p); +- lua_push_local_par_mode(Luas,mode) +- if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */ +- char errmsg[256]; /* temp hack ... we will have a formatted error */ +- snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1)); +- errmsg[255]='\0'; +- lua_settop(Luas, sfix); +- normal_error("insert_local_par",errmsg); /* to be done */ +- return p; +- } +- lua_settop(Luas, sfix); +- } +- /* done */ +- return p; +-} +diff --git a/texk/web2c/luatexdir/tex/textcodes.w b/texk/web2c/luatexdir/tex/textcodes.c +similarity index 84% +rename from texk/web2c/luatexdir/tex/textcodes.w +rename to texk/web2c/luatexdir/tex/textcodes.c +index 89e936841..a6a0b3a55 100644 +--- a/texk/web2c/luatexdir/tex/textcodes.w ++++ b/texk/web2c/luatexdir/tex/textcodes.c +@@ -1,27 +1,35 @@ +-% textcodes.w +-% +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* ++ ++textcodes.w ++ ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ + + #include "ptexlib.h" + +-@ catcodes @c ++/*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 +@@ -174,7 +182,11 @@ static void freecatcodes(void) + xfree(catcode_valid); + } + +-@ lccodes @c ++/*tex ++ ++The lowercase mapping codes are also stored in a tree. ++ ++*/ + + #define LCCODESTACK 8 + #define LCCODEDEFAULT 0 +@@ -220,7 +232,11 @@ static void freelccodes(void) + destroy_sa_tree(lccode_head); + } + +-@ uccodes @c ++/*tex ++ ++And the uppercase mapping codes are again stored in a tree. ++ ++*/ + + #define UCCODESTACK 8 + #define UCCODEDEFAULT 0 +@@ -266,7 +282,11 @@ static void freeuccodes(void) + destroy_sa_tree(uccode_head); + } + +-@ sfcodes @c ++/*tex ++ ++By now it will be no surprise that the space factors get stored in a tree. ++ ++*/ + + #define SFCODESTACK 8 + #define SFCODEDEFAULT 1000 +@@ -312,19 +332,29 @@ static void freesfcodes(void) + destroy_sa_tree(sfcode_head); + } + +-@ hjcodes @c ++/*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 /* number of languages */ ++#define HJCODE_MAX 16383 + + 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. ++/*tex ++ ++Here we set codes but we don't initialize from lccodes. ++ ++*/ + +-@c void set_hj_code(int h, int n, halfword v, quarterword gl) ++void set_hj_code(int h, int n, halfword v, quarterword gl) + { + sa_tree_item sa_value = { 0 }; + sa_tree s = hjcode_heads[h]; +@@ -339,9 +369,13 @@ static unsigned char *hjcode_valid = NULL; + set_sa_item(s, n, sa_value, gl); + } + +-@ We just return the lccodes when nothing is set. ++/*tex + +-@c halfword get_hj_code(int h, int n) ++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) { +@@ -350,12 +384,14 @@ static unsigned char *hjcode_valid = NULL; + 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: ++/*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: ++ ++*/ + +-@c + /* + static void unsavehjcodes(quarterword gl) { } + */ +@@ -444,7 +480,11 @@ static void freehjcodes(void) + xfree(hjcode_valid); + } + +-/* management */ ++/*tex ++ ++The public management functions. ++ ++*/ + + void unsave_text_codes(quarterword grouplevel) + { +diff --git a/texk/web2c/luatexdir/tex/textoken.w b/texk/web2c/luatexdir/tex/textoken.c +similarity index 63% +rename from texk/web2c/luatexdir/tex/textoken.w +rename to texk/web2c/luatexdir/tex/textoken.c +index d58aa3dc4..6056bba25 100644 +--- a/texk/web2c/luatexdir/tex/textoken.w ++++ b/texk/web2c/luatexdir/tex/textoken.c +@@ -1,40 +1,26 @@ +-% textoken.w +-% +-% Copyright 2006-2011 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c ++/* + +-#include "ptexlib.h" ++Copyright 2006-2011 Taco Hoekwater + +-@ @c +-#define detokenized_line() (line_catcode_table==NO_CAT_TABLE) ++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 . + +-/* +-#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) + */ + ++#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); \ +@@ -45,47 +31,85 @@ + } 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. ++/*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 */ + +-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. ++unsigned fix_mem_min; + +-@ Locations in |fixmem| are used for storing one-word records; a conventional +-\.{AVAIL} stack is used for allocation in this array. ++/*tex the largest location of one-word memory in use */ + +-@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 */ ++unsigned fix_mem_max; + +-@ 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 ++ ++ 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 */ ++/*tex 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| */ ++int var_used, dyn_used; + +-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| */ ++/*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; + +-@ @c + void initialize_tokens(void) + { + halfword p; +@@ -109,37 +133,44 @@ void initialize_tokens(void) + p = get_avail(); + garbage = p; + set_token_info(garbage, 0); +- dyn_used = 0; /* initialize statistics */ ++ dyn_used = 0; + } + +-@ 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@> ++/*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. ++ 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: ++*/ + +-@c + halfword get_avail(void) +-{ /* single-word node allocation */ +- unsigned p; /* the new node being got */ ++{ ++ /*tex The new node being got: */ ++ unsigned p; + unsigned t; +- p = (unsigned) avail; /* get top location in the |avail| stack */ ++ /*tex Get top location in the |avail| stack. */ ++ p = (unsigned) avail; + if (p != null) { +- avail = token_link(avail); /* and pop it off */ +- } else if (fix_mem_end < fix_mem_max) { /* or go into virgin territory */ ++ /*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 { +- smemory_word *new_fixmem; /* the big dynamic storage area */ ++ /*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))); ++ 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 */ ++ /*tex If memory is exhausted, display possible runaway text. */ ++ runaway(); + overflow("token memory size", fix_mem_max); + } else { + fixmem = new_fixmem; +@@ -148,111 +179,127 @@ halfword get_avail(void) + 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 */ ++ /*tex Provide an oft-desired initialization of the new node. */ ++ token_link(p) = null; ++ /*tex Maintain statistics. */ ++ incr(dyn_used); + return (halfword) p; + } + +-@ The procedure |flush_list(p)| frees an entire linked list of one-word nodes +-that starts at position |p|. +-@^inner loop@> ++/*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: ++ ++*/ + +-@c + void flush_list(halfword p) +-{ /* makes list of single-word nodes available */ +- halfword q, r; /* list traversers */ ++{ ++ halfword q, r; + 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 */ ++ } while (r != null); ++ /*tex 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 +- +-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 ++/*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= call_cmd) { + print_char(':'); + print_ln(); + token_show(cur_chr); + } else { +- /* Show the meaning of a mark node */ ++ /*tex Show the meaning of a mark node. */ + if ((cur_cmd == top_bot_mark_cmd) && (cur_chr < marks_code)) { + print_char(':'); + print_ln(); +@@ -277,31 +324,35 @@ void print_meaning(void) + } + } + +-@ 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 ++/*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: \ +@@ -316,6 +367,18 @@ sequence named \.{BAD} would come out `\.{\\BAD\ }'. + 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; \ +@@ -323,18 +386,21 @@ sequence named \.{BAD} would come out `\.{\\BAD\ }'. + + 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 */ ++ /*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) { +- /* Do magic computation */ ++ /*tex Do magic computation. */ + set_trick_count(); + } +- /* Display token |p|, and |return| if there are problems */ ++ /*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; +@@ -349,11 +415,11 @@ void show_token_list(int p, int q, int l) + 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 ++ 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: +@@ -405,31 +471,36 @@ void show_token_list(int p, int q, int l) + 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. ++/*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. ++ ++*/ + +-@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@> ++/*tex + +-@ |p| points to the reference count of a token list that is losing one +-reference. ++ |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. ++ ++*/ + +-@c + void delete_token_ref(halfword p) + { + if (token_ref_count(p) == 0) +@@ -438,7 +509,6 @@ void delete_token_ref(halfword p) + decr(token_ref_count(p)); + } + +-@ @c + int get_char_cat_code(int curchr) + { + int a; +@@ -446,7 +516,6 @@ int get_char_cat_code(int curchr) + return a; + } + +-@ @c + static void invalid_character_error(void) + { + const char *hlp[] = { +@@ -459,10 +528,9 @@ static void invalid_character_error(void) + deletions_allowed = true; + } + +-@ @c +-static boolean process_sup_mark(void); /* below */ ++static boolean process_sup_mark(void); + +-static int scan_control_sequence(void); /* below */ ++static int scan_control_sequence(void); + + typedef enum { + next_line_ok, +@@ -470,57 +538,83 @@ typedef enum { + next_line_restart + } next_line_retval; + +-static next_line_retval next_line(void); /* below */ ++static next_line_retval next_line(void); + +-@ 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. ++/*tex + +-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@> ++ 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.). ++ ++*/ + +-@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| */ ++{ ++ /*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) /* was assert (strlen(s) > 1); */ +- return false ; /* but not with newtokenlib zero keyword simply doesn't match */ ++ 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) { +- get_x_token(); /* recursion is possible here */ ++ /*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)) { +- /* +- crashes on some alignments: ++ 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; ++} + +- 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(); +- } +- */ ++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; + } +@@ -531,36 +625,11 @@ boolean scan_keyword(const char *s) + return true; + } + +-@ We can not return |undefined_control_sequence| under some conditions +- (inside |shift_case|, for example). This needs thinking. ++/*tex + +-@c ++ We can not return |undefined_control_sequence| under some conditions (inside ++ |shift_case|, for example). This needs thinking. + +-/* +- 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 */ +@@ -581,7 +650,8 @@ halfword active_to_cs(int curchr, int force) + curcs = string_lookup(utfbytes, utf8_size(curchr)+3); + free(utfbytes); + } else { +- curcs = string_lookup("\xEF\xBF\xBF", 4); /* 0xFFFF ... why not 3 ? */ ++ /*tex 0xFFFF ... why not 3 ? */ ++ curcs = string_lookup("\xEF\xBF\xBF", 4); + } + no_new_control_sequence = nncs; + return curcs; +@@ -641,11 +711,12 @@ halfword active_to_cs(int curchr, int force) + + */ + +-@ TODO this function should listen to \.{\\escapechar} ++/*tex + +-@ prints a control sequence ++ Maybe this function should listen to \.{\\escapechar} but we can do without. ++ ++*/ + +-@c + static char *cs_to_string(halfword p) + { + const char *s; +@@ -687,9 +758,8 @@ static char *cs_to_string(halfword p) + return (char *) ret; + } + +-@ TODO this is a quick hack, will be solved differently soon ++/*tex This is sort of a hack. */ + +-@c + static char *cmd_chr_to_string(int cmd, int chr) + { + char *s; +@@ -704,56 +774,76 @@ static char *cmd_chr_to_string(int cmd, int chr) + 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 ++/*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; /* an index into |buffer| */ ++ int k; + ilimit = last; + if (pausing_par > 0) { + if (interaction > nonstop_mode) { +@@ -764,9 +854,10 @@ void firm_up_the_line(void) + print_char(buffer[k]); + } + first = ilimit; +- prompt_input("=>"); /* wait for user response */ ++ prompt_input("=>"); + if (last > first) { +- for (k = first; k < +last - 1; k++) /* move line down in buffer */ ++ /*tex Move line down in buffer. */ ++ for (k = first; k < +last - 1; k++) + buffer[k + istart - first] = buffer[k]; + ilimit = istart + last - first; + } +@@ -774,31 +865,42 @@ void firm_up_the_line(void) + } + } + +-@ 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. ++/*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. ++ ++*/ + +-@c + void check_outer_validity(void) + { +- halfword p; /* points to inserted token list */ +- halfword q; /* auxiliary pointer */ ++ /*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; +- /* 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. */ ++ /*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; +- begin_token_list(p, backed_up); /* prepare to read the control sequence again */ ++ /*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 = ' '; /* replace it by a space */ ++ cur_chr = ' '; + } + if (scanner_status > skipping) { + const char *errhlp[] = { +@@ -808,56 +910,64 @@ void check_outer_validity(void) + "you'd better type `E' or `X' now and fix your file.", + NULL + }; +- char errmsg[256]; ++ char errmsg[318]; + const char *startmsg; + const char *scannermsg; +- /* Tell the user what has run away and try to recover */ +- runaway(); /* print a definition, argument, or preamble */ ++ /*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"; + } +- /* 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}. */ ++ /*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: /* can't happen */ +- scannermsg = "unknown"; +- break; +- } /*there are no other cases */ ++ 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, 255, "%s while scanning %s of %s", +- startmsg, scannermsg, cs_to_string(warning_index)); ++ snprintf(errmsg, 318, "%s while scanning %s of %s", startmsg, scannermsg, cs_to_string(warning_index)); + tex_error(errmsg, errhlp); + } else { + char errmsg[256]; +@@ -883,9 +993,9 @@ void check_outer_validity(void) + snprintf(errmsg, 255, "Incomplete %s; all text was ignored after line %d", + ss, (int) skip_line); + free(ss); +- /* Incomplete \\if... */ ++ /*tex Incomplete |\if...| */ + cur_tok = cs_token_flag + frozen_fi; +- /* back up one inserted token and call |error| */ ++ /*tex back up one inserted token and call |error|. */ + { + OK_to_interrupt = false; + back_input(); +@@ -898,61 +1008,65 @@ void check_outer_validity(void) + } + } + +-@ @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. ++/*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) { +- /* current line not yet finished */ ++ /*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. ++ 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: ++ 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; ++ \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. + +- 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) { +@@ -961,13 +1075,13 @@ static boolean get_next_file(void) + case new_line + ignore_cmd: + case skip_blanks + spacer_cmd: + case new_line + spacer_cmd: +- /* Cases where character is ignored */ ++ /*tex 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 ...; */ ++ /*tex Scan a control sequence. */ + istate = (unsigned char) scan_control_sequence(); + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); +@@ -975,7 +1089,7 @@ static boolean get_next_file(void) + case mid_line + active_char_cmd: + case new_line + active_char_cmd: + case skip_blanks + active_char_cmd: +- /* Process an active-character */ ++ /*tex Process an active-character. */ + cur_cs = active_to_cs(cur_chr, false); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); +@@ -986,7 +1100,7 @@ static boolean get_next_file(void) + case mid_line + sup_mark_cmd: + case new_line + sup_mark_cmd: + case skip_blanks + sup_mark_cmd: +- /* If this |sup_mark| starts */ ++ /*tex If this |sup_mark| starts. */ + if (process_sup_mark()) + goto RESWITCH; + else +@@ -995,22 +1109,27 @@ static boolean get_next_file(void) + 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|; */ ++ /*tex Decry the invalid character and |goto restart|. */ + invalid_character_error(); +- return false; /* because state may be |token_list| now */ ++ /*tex Because state may be |token_list| now: */ ++ return false; + break; + case mid_line + spacer_cmd: +- /* Enter |skip_blanks| state, emit a space; */ ++ /*tex 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. ++ /*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; +@@ -1020,12 +1139,12 @@ static boolean get_next_file(void) + case mid_line + comment_cmd: + case new_line + comment_cmd: + case skip_blanks + comment_cmd: +- /* Finish line, |goto switch|; */ ++ /*tex Finish line, |goto switch|; */ + iloc = ilimit + 1; + goto SWITCH; + break; + case new_line + car_ret_cmd: +- /* Finish line, emit a \.{\\par}; */ ++ /*tex Finish line, emit a \.{\\par}; */ + iloc = ilimit + 1; + cur_cs = par_loc; + cur_cmd = eq_type(cur_cs); +@@ -1036,14 +1155,14 @@ static boolean get_next_file(void) + case skip_blanks + left_brace_cmd: + case new_line + left_brace_cmd: + istate = mid_line; +- /* fall through */ ++ /*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; +- /* fall through */ ++ /*tex Fall through. */ + case mid_line + right_brace_cmd: + align_state--; + break; +@@ -1075,11 +1194,12 @@ static boolean get_next_file(void) + } else { + if (iname != 21) + istate = new_line; +- /* +- Move to next line of file, +- or |goto restart| if there is no next 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) { +@@ -1096,13 +1216,10 @@ static boolean get_next_file(void) + + #else + +-/* 10 times less Bim in callgrind */ ++/*tex ++ ++ This variant gives 10 times less Bim in vallgrind! + +-/* +- 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) +@@ -1110,7 +1227,7 @@ static boolean get_next_file(void) + int c = 0; + SWITCH: + if (iloc <= ilimit) { +- /* current line not yet finished */ ++ /*tex current line not yet finished */ + do_buffer_to_unichar(cur_chr, iloc); + RESWITCH: + if (detokenized_line()) { +@@ -1118,10 +1235,12 @@ static boolean get_next_file(void) + } 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; ++ /*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)) { +@@ -1148,7 +1267,7 @@ static boolean get_next_file(void) + istate = mid_line; + return true; + case car_ret_cmd: +- /* Finish line, emit a \.{\\par}; */ ++ /*tex Finish line, emit a \.{\\par}. */ + iloc = ilimit + 1; + cur_cs = par_loc; + cur_cmd = eq_type(cur_cs); +@@ -1172,7 +1291,7 @@ static boolean get_next_file(void) + goto SWITCH; + return true; + case spacer_cmd: +- /* Cases where character is ignored */ ++ /*tex Cases where character is ignored. */ + goto SWITCH; + case letter_cmd: + istate = mid_line; +@@ -1192,8 +1311,9 @@ static boolean get_next_file(void) + iloc = ilimit + 1; + goto SWITCH; + case invalid_char_cmd: ++ /*tex Because state may be |token_list| now. */ + invalid_character_error(); +- return false; /* because state may be |token_list| now */ ++ return false; + default: + istate = mid_line; + return true; +@@ -1201,7 +1321,7 @@ static boolean get_next_file(void) + } else if (c >= skip_blanks) { + switch (c-skip_blanks) { + case escape_cmd: +- /* Scan a control sequence ...; */ ++ /*tex Scan a control sequence. */ + istate = (unsigned char) scan_control_sequence(); + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); +@@ -1227,7 +1347,6 @@ static boolean get_next_file(void) + istate = mid_line; + return true; + case sup_mark_cmd: +- /* If this |sup_mark| starts */ + if (process_sup_mark()) + goto RESWITCH; + else +@@ -1255,13 +1374,14 @@ static boolean get_next_file(void) + check_outer_validity(); + return true; + case comment_cmd: +- /* Finish line, |goto switch|; */ ++ /*tex Finish line, |goto switch|. */ + iloc = ilimit + 1; + goto SWITCH; + case invalid_char_cmd: +- /* Decry the invalid character and |goto restart|; */ ++ /*tex Decry the invalid character and |goto restart|. */ + invalid_character_error(); +- return false; /* because state may be |token_list| now */ ++ /*tex Because state may be |token_list| now. */ ++ return false; + default: + istate = mid_line; + return true; +@@ -1284,13 +1404,17 @@ static boolean get_next_file(void) + 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. +- */ ++ /*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 = ' '; +@@ -1308,7 +1432,7 @@ static boolean get_next_file(void) + case ignore_cmd: + goto SWITCH; + case spacer_cmd: +- /* Enter |skip_blanks| state, emit a space; */ ++ /*tex Enter |skip_blanks| state, emit a space. */ + istate = skip_blanks; + cur_chr = ' '; + return true; +@@ -1330,8 +1454,9 @@ static boolean get_next_file(void) + iloc = ilimit + 1; + goto SWITCH; + case invalid_char_cmd: ++ /*tex Because state may be |token_list| now. */ + invalid_character_error(); +- return false; /* because state may be |token_list| now */ ++ return false; + default: + istate = mid_line; + return true; +@@ -1344,9 +1469,11 @@ static boolean get_next_file(void) + 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; ++ /*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(); +@@ -1364,14 +1491,18 @@ static boolean get_next_file(void) + + #endif + +-@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit. +-We only support a limited set: ++/*tex + +-^^^^^^XXXXXX +-^^^^XXXXXX +-^^XX ^^ ++ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit. ++ We only support a limited set: + +-@c ++ \starttyping ++ ^^^^^^XXXXXX ++ ^^^^XXXXXX ++ ^^XX ^^ ++ \stoptyping ++ ++*/ + + #define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f')) + +@@ -1412,7 +1543,7 @@ static boolean process_sup_mark(void) + 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 */ ++ /*tex |^^^^^^XXXXXX| */ + if ((iloc + 10) <= ilimit) { + int c1 = buffer[iloc + 5]; + int c2 = buffer[iloc + 6]; +@@ -1432,7 +1563,7 @@ static boolean process_sup_mark(void) + tex_error("^^^^^^ needs six hex digits, end of input", NULL); + } + } else { +- /* ^^^^XXXX */ ++ /*tex |^^^^XXXX| */ + if ((iloc + 6) <= ilimit) { + int c1 = buffer[iloc + 3]; + int c2 = buffer[iloc + 4]; +@@ -1450,7 +1581,7 @@ static boolean process_sup_mark(void) + } + } + } else { +- /* ^^XX */ ++ /*tex |^^XX| */ + if ((iloc + 2) <= ilimit) { + int c1 = buffer[iloc + 1]; + int c2 = buffer[iloc + 2]; +@@ -1460,10 +1591,10 @@ static boolean process_sup_mark(void) + return true; + } + } +- /* go on, no error, good old tex */ ++ /*tex Go on, no error, good old \TEX . */ + } + } +- /* the rest */ ++ /*tex The rest. */ + { + int c1 = buffer[iloc + 1]; + if (c1 < 0200) { +@@ -1484,37 +1615,43 @@ static boolean process_sup_mark(void) + 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. ++/*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]|. ++ 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. ++ 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 */ ++ /*tex |state| is irrelevant in this case. */ ++ cur_cs = null_cs; + } else { +- register int cat; /* |cat_code(cur_chr)|, usually */ ++ /*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); +- if (cat == sup_mark_cmd && check_expanded_code(&k)) /* If an expanded...; */ ++ /*tex If an expanded \unknown */ ++ if (cat == sup_mark_cmd && check_expanded_code(&k)) + continue; + } else { + retval = skip_blanks; +@@ -1522,20 +1659,11 @@ static int scan_control_sequence(void) + 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...; */ ++ /*tex If an expanded \unknown */ ++ if (cat == sup_mark_cmd && check_expanded_code(&k)) + 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); +- */ ++ /*tex Backtrack one character which can be \UTF. */ + if (cur_chr <= 0x7F) { + k -= 1; /* in most cases */ + } else if (cur_chr > 0xFFFF) { +@@ -1545,7 +1673,7 @@ static int scan_control_sequence(void) + } else /* if (cur_chr > 0x7F) */ { + k -= 2; + } +- /* now |k| points to first nonletter */ ++ /*tex Now |k| points to first nonletter. */ + } + } + cur_cs = id_lookup(iloc, k - iloc); +@@ -1558,14 +1686,17 @@ static int scan_control_sequence(void) + 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. ++/*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. ++ ++*/ + +-@c + static boolean check_expanded_code(int *kk) + { + int l; +@@ -1666,26 +1797,38 @@ static boolean check_expanded_code(int *kk) + return false; + } + +-@ All of the easy branches of |get_next| have now been taken care of. There is +-one more branch. ++/*tex + +-@c static next_line_retval next_line(void) ++ 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) + { +- boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */ ++ /*tex A way to end a pseudo file without trailing space: */ ++ boolean inhibit_eol = false; + if (iname > 17) { +- /* Read next line of file into |buffer|, or |goto restart| if the file has ended */ ++ /*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()) { /* not end of file */ +- firm_up_the_line(); /* this sets |ilimit| */ ++ 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; +- eof_seen[iindex] = true; /* fake one empty line */ ++ /*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; +@@ -1694,30 +1837,58 @@ one more branch. + } + } 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; ++ 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 { +- 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; +- } ++ force_eof = true; + } + } + } +@@ -1725,7 +1896,7 @@ one more branch. + 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 */ ++ /*tex Give warning for some unfinished groups and/or conditionals. */ + file_warning(); + } + if ((iname > 21) || (iname == 20)) { +@@ -1733,7 +1904,7 @@ one more branch. + decr(open_parens); + } + force_eof = false; +- /* lua input or \.{\\scantextokens} */ ++ /*tex \LUA\ input or \.{\\scantextokens} */ + if (iname == 21 || iname == 19) { + end_file_reading(); + } else { +@@ -1748,18 +1919,20 @@ one more branch. + else + buffer[ilimit] = (packed_ASCII_code) end_line_char_par; + first = ilimit + 1; +- iloc = istart; /* ready to read */ ++ iloc = istart; ++ /*tex We're ready to read. */ + } else { + if (!terminal_input) { +- /* \.{\\read} line has ended */ ++ /*tex \.{\\read} line has ended */ + cur_cmd = 0; + cur_chr = 0; +- return next_line_return; /* OUTER */ ++ return next_line_return; + } + if (input_ptr > 0) { +- /* text was inserted during error recovery */ ++ /*tex Text was inserted during error recovery. */ + end_file_reading(); +- return next_line_restart; /* resume previous level */ ++ /*tex Resume previous level. */ ++ return next_line_restart; + } + if (selector < log_only) + open_log_file(); +@@ -1767,12 +1940,13 @@ one more branch. + if (end_line_char_inactive) + ilimit++; + if (ilimit == istart) { +- /* previous line was empty */ ++ /*tex Previous line was empty. */ + tprint_nl("(Please type a command or say `\\end')"); + } + print_ln(); + first = istart; +- prompt_input("*"); /* input on-line into |buffer| */ ++ /*tex Input on-line into |buffer| */ ++ prompt_input("*"); + ilimit = last; + if (end_line_char_inactive) + ilimit--; +@@ -1781,9 +1955,11 @@ one more branch. + 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)"); + } +@@ -1791,24 +1967,31 @@ one more branch. + return next_line_ok; + } + +-@ Let's consider now what happens when |get_next| is looking at a token list. ++/*tex ++ ++ 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 */ ++ /*tex Move to next. */ ++ iloc = token_link(iloc); + if (t >= cs_token_flag) { +- /* a control sequence token */ ++ /*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) { +- /* +- 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|. ++ /*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; +@@ -1834,7 +2017,7 @@ static boolean get_next_tokenlist(void) + align_state--; + break; + case out_param_cmd: +- /* Insert macro parameter and |goto restart|; */ ++ /*tex Insert macro parameter and |goto restart|. */ + begin_token_list(param_stack[param_start + cur_chr - 1], parameter); + return false; + break; +@@ -1843,61 +2026,73 @@ static boolean get_next_tokenlist(void) + 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@> ++/*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. + +-@ sets |cur_cmd|, |cur_chr|, |cur_cs| to next token ++ This 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 */ ++ /*tex 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 */ ++ /*tex List exhausted, resume previous level. */ ++ goto RESTART; + } else if (!get_next_tokenlist()) { +- goto RESTART; /* parameter needs to be expanded */ ++ /*tex Parameter needs to be expanded. */ ++ goto RESTART; + } + } +- /* If an alignment entry has just ended, take appropriate action */ ++ /*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; + } + } + +-@ 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|. ++/*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|: + +-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; +@@ -1909,14 +2104,16 @@ void get_token(void) + cur_tok = cs_token_flag + cur_cs; + } + +-@ changes the string |s| to a token list ++/*tex This 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 */ ++ /*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; +@@ -1933,29 +2130,37 @@ halfword string_to_toks(const char *ss) + 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|. ++/*tex + +-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 lists for macros and for other things like \.{\\mark} and ++ \.{\\output} and \.{\\write} are produced by a procedure called |scan_toks|. + +-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.) ++ 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|. + +-|lua_str_toks| is almost identical, but it also escapes the three symbols that +-|lua| considers special while scanning a literal string ++ 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.) + +-@ changes the string |str_pool[b..pool_ptr]| to a token list ++ |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: ++ ++*/ + +-@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 */ ++ /*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; +@@ -1978,18 +2183,25 @@ halfword lua_str_toks(lstring b) + return p; + } + +-@ Incidentally, the main reason for wanting |str_toks| is the function +-|the_toks|, which has similar input/output characteristics. ++/*tex ++ ++ 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 ++ This 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 */ ++ /*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; +@@ -2006,18 +2218,25 @@ halfword str_toks(lstring s) + 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 ++/*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) +-{ /* 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 */ ++{ ++ /*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); +@@ -2028,7 +2247,7 @@ halfword str_scan_toks(int ct, lstring s) + k += utf8_size(t); + cc = get_cat_code(ct,t); + if (cc == 0) { +- /* we have a potential control sequence so we check for it */ ++ /*tex We have a potential control sequence so we check for it. */ + int _lname = 0 ; + int _s = 0 ; + int _c = 0 ; +@@ -2042,7 +2261,7 @@ halfword str_scan_toks(int ct, lstring s) + k += _s ; + _lname = _lname + _s ; + } else if (_c == 10) { +- /* we ignore a trailing space like normal scanning does */ ++ /*tex We ignore a trailing space like normal scanning does. */ + k += _s ; + break ; + } else { +@@ -2050,7 +2269,7 @@ halfword str_scan_toks(int ct, lstring s) + } + } + if (_s > 0) { +- /* we have a potential \cs */ ++ /*tex We have a potential |\cs|. */ + _cs = string_lookup((const char *) _name, _lname); + if (_cs == undefined_control_sequence) { + /* let's play safe and backtrack */ +@@ -2060,15 +2279,24 @@ halfword str_scan_toks(int ct, lstring s) + 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 */ ++ /*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 { +- /* whatever token, so for instance $x^2$ just works given a tex */ +- /* catcode regime */ ++ /*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); +@@ -2077,171 +2305,227 @@ halfword str_scan_toks(int ct, lstring s) + return p; + } + +-@ Here's part of the |expand| subroutine that we are now ready to complete: ++/*tex ++ ++ 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) { \ ++#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 nt; ++ 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(); +- /* target */ ++ /*tex The target. */ + if (cur_cmd == assign_toks_cmd) { ++ /*tex Check range. */ + nt = equiv(cur_cs) - toks_base; +- /* check range */ + } else { + back_input(); +- scan_int(); ++ scan_register_num(); + nt = cur_val; + } +- /* source */ ++ /*tex The 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 */ ++ scan_toks(false,expand); + source = def_ref; +- /* action */ ++ /*tex The action. */ + if (source != null) { +- halfword target = toks(nt); ++ target = toks(nt); + if (target == null) { +- set_toks_register(nt,source,0); ++ set_toks_register(nt,source,global); + } else { +- halfword s = token_link(source); ++ s = token_link(source); + if (s != null) { +- halfword t = token_link(target); ++ t = token_link(target); + if (t == null) { +- /* can this happen ? */ ++ /*tex 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); ++ } 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); + } +- set_toks_register(nt,temp_token_head,0); + } else { +- set_token_link(x,t); +- set_token_link(target,s); ++ token_ref_count(target)--; ++ append_copied_toks_list(t,s); ++ set_toks_register(nt,temp_token_head,global); + } + } 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); ++ /* 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,s); +- set_toks_register(nt,temp_token_head,0); ++ set_token_link(p,t); ++ set_token_link(target,h); + } else { +- while (token_link(t) != null) { +- t = token_link(t); +- } +- set_token_link(t,s); ++ token_ref_count(target)--; ++ append_copied_toks_list(s,t); ++ set_toks_register(nt,temp_token_head,global); + } + } + } + } + } + } else { +- halfword source, ns; + if (cur_cmd == assign_toks_cmd) { + ns = equiv(cur_cs) - toks_base; +- /* check range */ ++ /*tex Check range. */ + } else { +- back_input(); +- scan_int(); ++ scan_register_num(); + ns = cur_val; + } +- /* action */ ++ /*tex The action. */ + source = toks(ns); + if (source != null) { +- halfword target = toks(nt); ++ target = toks(nt); + if (target == null) { ++ /*tex The assign. */ ++ token_ref_count(source)++; + equiv(toks_base+nt) = source; +- equiv(toks_base+ns) = null; ++ 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 { +- 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); ++ /*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; + } +- set_token_link(t,s); ++ s = token_link(s); + } +- equiv(toks_base+ns) = null; ++ 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); + } + } + } + } + } + +-@ This routine, used in the next one, prints the job name, possibly modified by +-the |process_jobname| callback. ++/*tex ++ ++ 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); +- } ++ 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); ++ } + } + +-@ 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. ++/*tex + +-@ 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. ++ 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. + +-@c ++ 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) + { +@@ -2263,15 +2547,18 @@ int scan_lua_state(void) + 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}'. ++/*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. ++ 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; \ +@@ -2327,6 +2614,8 @@ static int do_variable_pdf(halfword c) + 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); } +@@ -2353,23 +2642,30 @@ static int do_feedback_dvi(halfword c) + return 0; + } + +-/* codes not really needed but cleaner when testing */ ++/*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) + { +- 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}' */ ++ /*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; +- 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 */ +- ++ /*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); +@@ -2394,7 +2690,7 @@ static int do_feedback_pdf(halfword c) + pop_selector; + } else if (scan_keyword("creationdate")) { + ins_list(string_to_toks(getcreationdate(static_pdf))); +- /* no further action */ ++ /*tex No further action. */ + return 2; + } else if (scan_keyword("fontname")) { + scan_font_ident(); +@@ -2488,19 +2784,27 @@ static int do_feedback_pdf(halfword c) + + void conv_toks(void) + { +- int old_setting; /* holds |selector| setting */ ++ /*tex holds |selector| setting */ ++ int old_setting; + halfword p, q; +- int save_scanner_status; /* |scanner_status| upon entry */ +- halfword save_def_ref; /* |def_ref| upon entry, important if inside `\.{\\message}' */ ++ /*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; +- 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 */ ++ /*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; +- /* Scan the argument for command |c| */ ++ /*tex Scan the argument for command |c|. */ + switch (c) { + case number_code: + scan_int(); +@@ -2520,7 +2824,20 @@ void conv_toks(void) + if (luacstrings > 0) + lua_string_start(); + } +- /* no further action */ ++ 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: +@@ -2537,10 +2854,10 @@ void conv_toks(void) + luacstrings = 0; + luatokencall(s, sn); + delete_token_ref(s); +- restore_cur_string(u); /* TODO: check this, was different */ ++ restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); +- /* no further action */ ++ /*tex No further action. */ + return; + break; + case expanded_code: +@@ -2554,7 +2871,56 @@ void conv_toks(void) + ins_list(token_link(def_ref)); + def_ref = save_def_ref; + restore_cur_string(u); +- /* no further action */ ++ /*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: +@@ -2629,7 +2995,7 @@ void conv_toks(void) + free(escstr.s); + return; + } +- /* no further action */ ++ /*tex no further action */ + break; + case font_id_code: + scan_font_ident(); +@@ -2672,9 +3038,12 @@ void conv_toks(void) + 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 ++ /*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); +@@ -2685,10 +3054,12 @@ void conv_toks(void) + 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 ++ /*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. + */ + } + } +@@ -2832,7 +3203,7 @@ void do_feedback(void) + return ; + } + if (done==0) { +- /* we recover */ ++ /*tex We recover. */ + normal_warning("pdf backend","unexpected use of \\pdffeedback"); + return; + } else if (done==2) { +@@ -2857,7 +3228,7 @@ void do_variable(void) + case dvi_variable_code: + done = do_variable_dvi(c); + if (done==0) { +- /* we recover */ ++ /*tex We recover. */ + normal_warning("dvi backend","unexpected use of \\dvivariable"); + } + return; +@@ -2865,7 +3236,7 @@ void do_variable(void) + case pdf_variable_code: + done = do_variable_pdf(c); + if (done==0) { +- /* we recover */ ++ /*tex We recover. */ + normal_warning("pdf backend","unexpected use of \\pdfvariable"); + } + return; +@@ -2876,33 +3247,8 @@ void do_variable(void) + } + } + +-/* +- 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. +-*/ +- +-/* ++/*tex This boolean is keeping track of the lua string escape state */ + +-#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) +@@ -2948,7 +3294,7 @@ static int the_convert_string_pdf(halfword c, int i) + + str_number the_convert_string(halfword c, int i) + { +- int old_setting; /* saved |selector| setting */ ++ int old_setting; + str_number ret = 0; + boolean done = true ; + old_setting = selector; +@@ -3017,14 +3363,14 @@ str_number the_convert_string(halfword c, int i) + case font_identifier_code: + print_font_identifier(i); + break; +- /* backend: this might become obsolete */ ++ /*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; +- /* done */ ++ /*tex done */ + default: + done = false; + break; +@@ -3036,16 +3382,24 @@ str_number the_convert_string(halfword c, int i) + 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. ++/*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]; + +-@c +-FILE *read_file[16]; /* used for \.{\\read} */ +-int read_open[17]; /* state of |read_file[n]| */ ++/*tex state of |read_file[n]| */ ++ ++int read_open[17]; + + void initialize_read(void) + { +@@ -3054,41 +3408,51 @@ void initialize_read(void) + 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. ++/*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. ++ ++*/ + +-@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 */ ++ /*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); +- p = def_ref; /* the reference count */ ++ /*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; +- align_state = 1000000; /* disable tab marks, etc. */ ++ /*tex disable tab marks, etc. */ ++ align_state = 1000000; + do { +- /* Input and store tokens from the next line of the file */ ++ /*tex 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 ++ /*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. + +- 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) { +@@ -3106,11 +3470,12 @@ void read_toks(int n, halfword r, halfword j) + } + + } else if (read_open[m] == just_open) { +- /* +- Input the first line of |read_file[m]| ++ /*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|. + +- 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; +@@ -3120,10 +3485,11 @@ void read_toks(int n, halfword r, halfword j) + } + + } else { +- /* +- Input the next line of |read_file[m]| ++ /*tex ++ ++ Input the next line of |read_file[m]|. An empty line is appended ++ at the end of a |read_file|. + +- 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)); +@@ -3146,10 +3512,10 @@ void read_toks(int n, halfword r, halfword j) + first = ilimit + 1; + iloc = istart; + istate = new_line; +- /* Handle \.{\\readline} and |goto done|; */ ++ /*tex Handle \.{\\readline} and |goto done|. */ + if (j == 1) { + while (iloc <= ilimit) { +- /* current line not yet finished */ ++ /*tex Current line not yet finished. */ + do_buffer_to_unichar(cur_chr, iloc); + if (cur_chr == ' ') + cur_tok = space_token; +@@ -3161,11 +3527,11 @@ void read_toks(int n, halfword r, halfword j) + while (1) { + get_token(); + if (cur_tok == 0) { +- /* |cur_cmd=cur_chr=0| will occur at the end of the line */ ++ /*tex |cur_cmd=cur_chr=0| will occur at the end of the line. */ + break; + } + if (align_state < 1000000) { +- /* unmatched `\.\}' aborts the line */ ++ /*tex Unmatched right brace aborts the line. */ + do { + get_token(); + } while (cur_tok != 0); +@@ -3183,9 +3549,8 @@ void read_toks(int n, halfword r, halfword j) + align_state = s; + } + +-@ return a string from tokens list ++/*tex Return a string from tokens list: */ + +-@c + str_number tokens_to_string(halfword p) + { + int old_setting; +@@ -3198,11 +3563,20 @@ str_number tokens_to_string(halfword p) + return make_string(); + } + +-@ @c ++/*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+64)); \ +- alloci = alloci + 64; \ ++ ret = xrealloc(ret,(alloci+alloci_step)); \ ++ alloci = alloci + alloci_step; \ + } + + #define append_i_byte(a) ret[i++] = (char)(a) +@@ -3247,11 +3621,14 @@ str_number tokens_to_string(halfword p) + #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. ++/*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. ++ ++*/ + +-@c + char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) + { + register int p, c, m; +@@ -3262,7 +3639,7 @@ char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) + char *ret; + int match_chr = '#'; + int n = '0'; +- unsigned alloci = 1024; ++ unsigned alloci = alloci_default; + int i = 0; + p = pp; + if (p == null) { +@@ -3271,7 +3648,8 @@ char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) + return NULL; + } + ret = xmalloc(alloci); +- p = token_link(p); /* skip refcount */ ++ /*tex Skip refcount. */ ++ p = token_link(p); + if (p != null) { + e = escape_char_par; + } +@@ -3389,7 +3767,7 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz) + char *ret; + int match_chr = '#'; + int n = '0'; +- unsigned alloci = 1024; ++ unsigned alloci = alloci_default; + int i = 0; + int skipping = 1; + p = pp; +@@ -3399,13 +3777,14 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz) + return NULL; + } + ret = xmalloc(alloci); +- p = token_link(p); /* skip refcount */ ++ /*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) { +- /* nothing */ ++ /*tex Nothing done. */ + break; + } + infop = token_info(p); +@@ -3413,13 +3792,12 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz) + if (!(inhibit_par && infop == par_token)) { + q = infop - cs_token_flag; + if (q < hash_base) { +- /* nothing */ ++ /*tex Nothing done. */ + } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) { +- /* nothing */ ++ /*tex Nothing done. */ + } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) { +- /* nothing */ +- } else { +-if (!skipping) { ++ /*tex Nothing done. */ ++ } else if (!skipping) { + str_number txt = cs_text(q); + sh = makecstring(txt); + s = sh; +@@ -3440,12 +3818,11 @@ if (!skipping) { + } + } + free(sh); +-} + } + } + } else { + if (infop < 0) { +- /* nothing */ ++ /*tex Nothing done. */ + } else { + m = token_cmd(infop); + c = token_chr(infop); +@@ -3459,54 +3836,54 @@ if (!skipping) { + case spacer_cmd: + case letter_cmd: + case other_char_cmd: +-if (!skipping) { +- Print_uchar(c); +-} ++ if (!skipping) { ++ Print_uchar(c); ++ } + break; + case mac_param_cmd: +-if (!skipping) { +- if (!in_lua_escape && (is_in_csname==0)) ++ if (!skipping) { ++ if (!in_lua_escape && (is_in_csname==0)) ++ Print_uchar(c); + Print_uchar(c); +- Print_uchar(c); +-} ++ } + break; + case out_param_cmd: +-if (!skipping) { +- Print_uchar(match_chr); +-} ++ if (!skipping) { ++ Print_uchar(match_chr); ++ } + if (c <= 9) { +-if (!skipping) { +- Print_char(c + '0'); +-} ++ if (!skipping) { ++ Print_char(c + '0'); ++ } + } else { +- /* nothing */ ++ /*tex Nothing done. */ + goto EXIT; + } + break; + case match_cmd: + match_chr = c; +-if (!skipping) { +- Print_uchar(c); +-} ++ if (!skipping) { ++ Print_uchar(c); ++ } + n++; +-if (!skipping) { +- Print_char(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('>'); +-} ++ if (!skipping) { ++ Print_char('-'); ++ Print_char('>'); ++ } + i = 0; +-skipping = 0 ; ++ skipping = 0 ; + } + break; + default: +- /* nothing */ ++ /*tex Nothing done. */ + break; + } + } +@@ -3520,7 +3897,6 @@ skipping = 0 ; + return ret; + } + +-@ @c + lstring *tokenlist_to_lstring(int pp, int inhibit_par) + { + int siz; +@@ -3530,7 +3906,6 @@ lstring *tokenlist_to_lstring(int pp, int inhibit_par) + return ret; + } + +-@ @c + void free_lstring(lstring * ls) + { + if (ls == NULL) +diff --git a/texk/web2c/luatexdir/utils/avlstuff.c b/texk/web2c/luatexdir/utils/avlstuff.c +new file mode 100644 +index 000000000..887ba4f46 +--- /dev/null ++++ b/texk/web2c/luatexdir/utils/avlstuff.c +@@ -0,0 +1,63 @@ ++/* ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2009 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ ++#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); ++} +diff --git a/texk/web2c/luatexdir/utils/avlstuff.w b/texk/web2c/luatexdir/utils/avlstuff.w +deleted file mode 100644 +index 1ea148b09..000000000 +--- a/texk/web2c/luatexdir/utils/avlstuff.w ++++ /dev/null +@@ -1,61 +0,0 @@ +-% avlstuff.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2009 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@* 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); +-} +diff --git a/texk/web2c/luatexdir/utils/managed-sa.w b/texk/web2c/luatexdir/utils/managed-sa.c +similarity index 79% +rename from texk/web2c/luatexdir/utils/managed-sa.w +rename to texk/web2c/luatexdir/utils/managed-sa.c +index 6ac4a68ac..6cf10e301 100644 +--- a/texk/web2c/luatexdir/utils/managed-sa.w ++++ b/texk/web2c/luatexdir/utils/managed-sa.c +@@ -1,36 +1,36 @@ +-% managed-sa.w +-% +-% Copyright 2006-2010 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . ++/* + +-@* Sparse arrays with an embedded save stack. ++Copyright 2006-2010 Taco Hoekwater + +-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. ++This file is part of LuaTeX. + +-@ @c ++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 . ++ ++*/ ++ ++/*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" + +-@ @c + static void store_sa_stack(sa_tree a, int n, sa_tree_item v, int gl) + { + sa_stack_item st; +@@ -47,7 +47,6 @@ static void store_sa_stack(sa_tree a, int n, sa_tree_item v, int gl) + a->stack[a->stack_ptr] = st; + } + +-@ @c + static void skip_in_stack(sa_tree a, int n) + { + int p = a->stack_ptr; +@@ -61,7 +60,6 @@ static void skip_in_stack(sa_tree a, int n) + } + } + +-@ @c + sa_tree_item get_sa_item(const sa_tree head, const int n) + { + if (head->tree != NULL) { +@@ -76,7 +74,6 @@ sa_tree_item get_sa_item(const sa_tree head, const int 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); +@@ -103,13 +100,11 @@ void set_sa_item(sa_tree head, int n, sa_tree_item v, int 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); +@@ -117,7 +112,6 @@ void clear_sa_stack(sa_tree a) + a->stack_size = a->stack_step; + } + +-@ @c + void destroy_sa_tree(sa_tree a) + { + if (a == NULL) +@@ -138,7 +132,6 @@ void destroy_sa_tree(sa_tree a) + xfree(a); + } + +-@ @c + sa_tree copy_sa_tree(sa_tree b) + { + sa_tree a = (sa_tree) Mxmalloc_array(sa_tree_head, 1); +@@ -168,15 +161,17 @@ sa_tree copy_sa_tree(sa_tree b) + 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. ++/*tes + +-Allocating those here immediately improves the chance of the structure +-|a->tree[0][0][x]| being close together in actual memory locations ++ 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. + +-@c ++ 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 */ ++ We could save less for type 0 stacks. ++ ++*/ + + sa_tree new_sa_tree(int size, int type, sa_tree_item dflt) + { +@@ -190,11 +185,9 @@ sa_tree new_sa_tree(int size, int type, sa_tree_item dflt) + 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; +@@ -209,7 +202,6 @@ void restore_sa_stack(sa_tree head, int gl) + } + } + +-@ @c + void dump_sa_tree(sa_tree a, const char * name) + { + boolean f; +@@ -219,10 +211,10 @@ void dump_sa_tree(sa_tree a, const char * name) + x = a->dflt.int_value; + dump_int(x); + if (a->tree != NULL) { +- dump_int(1); /* marker */ ++ /*tex A marker: */ ++ dump_int(1); + 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; +@@ -253,11 +245,11 @@ void dump_sa_tree(sa_tree a, const char * name) + } + } + } else { +- dump_int(0); /* marker */ ++ /*tex A marker: */ ++ dump_int(0); + } + } + +-@ @c + sa_tree undump_sa_tree(const char * name) + { + int x, n; +@@ -272,13 +264,13 @@ sa_tree undump_sa_tree(const char * name) + a->stack = Mxmalloc_array(sa_stack_item, a->stack_size); + a->stack_ptr = 0; + a->tree = NULL; +- undump_int(x); /* marker */ ++ /*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; +- /* printf("undumping sa tree %s with type %d\n",name,n); */ + for (h = 0; h < HIGHPART; h++) { + undump_qqqq(f); + if (f > 0) { +diff --git a/texk/web2c/luatexdir/utils/unistring.w b/texk/web2c/luatexdir/utils/unistring.c +similarity index 75% +rename from texk/web2c/luatexdir/utils/unistring.w +rename to texk/web2c/luatexdir/utils/unistring.c +index a5b365a5f..6c95ee31e 100644 +--- a/texk/web2c/luatexdir/utils/unistring.w ++++ b/texk/web2c/luatexdir/utils/unistring.c +@@ -1,34 +1,29 @@ +-% unistring.w +-% +-% Copyright 2013 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +- +-@ @c ++/* ++ ++Copyright 2013 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ + #include "ptexlib.h" + #include + +-@ @c + static void utf_error(void) + { +- const char *hlp[] = +- { "A funny symbol that I can't read has just been (re)read.", ++ 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 + }; +@@ -37,7 +32,6 @@ static void utf_error(void) + deletions_allowed = true; + } + +-@ @c + unsigned str2uni(const unsigned char *k) + { + register int ch; +@@ -45,7 +39,8 @@ unsigned str2uni(const unsigned char *k) + const unsigned char *text = k; + if ((ch = *text++) < 0x80) { + val = (unsigned) ch; +- } else if (ch <= 0xbf) { /* error */ ++ } 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)); +@@ -64,9 +59,11 @@ unsigned str2uni(const unsigned char *k) + *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 ++ /*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) +@@ -74,8 +71,11 @@ unsigned str2uni(const unsigned char *k) + return (val); + } + +-@ This is a very basic helper +-@c ++/*tex ++ ++ A real basic helper. ++*/ ++ + unsigned char *uni2str(unsigned unic) + { + unsigned char *buf = xmalloc(5); +@@ -106,26 +106,30 @@ unsigned char *uni2str(unsigned unic) + 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. ++/*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. ++ ++*/ + +-@c + int buffer_to_unichar(int k) + { + return str2uni((const unsigned char *)(buffer+k)); + } + ++/*tex ++ ++ These came from texlang.c: ++ ++*/ + +-@ These came from texlang.w +-@c + char *uni2string(char *utf8_text, unsigned ch) + { +- /* Increment and deposit character */ ++ /*tex Increment and deposit character: */ + if (ch >= 17 * 65536) + return (utf8_text); +- + if (ch <= 127) + *utf8_text++ = (char) ch; + else if (ch <= 0x7ff) { +@@ -155,15 +159,12 @@ unsigned u_length(register unsigned int *str) + 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); ++ 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++; +@@ -171,9 +172,7 @@ void utf2uni_strcpy(unsigned int *ubuf, const char *utf8buf) + *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)); ++ *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; +@@ -187,15 +186,11 @@ void utf2uni_strcpy(unsigned int *ubuf, const char *utf8buf) + *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 { +@@ -206,5 +201,3 @@ char *utf16be_str(long code) + } + return buf; + } +- +- +diff --git a/texk/web2c/luatexdir/utils/utils.w b/texk/web2c/luatexdir/utils/utils.c +similarity index 63% +rename from texk/web2c/luatexdir/utils/utils.w +rename to texk/web2c/luatexdir/utils/utils.c +index 420f5cfe4..55ff1cb3e 100644 +--- a/texk/web2c/luatexdir/utils/utils.w ++++ b/texk/web2c/luatexdir/utils/utils.c +@@ -1,51 +1,52 @@ +-% utils.w +-% +-% Copyright 1996-2006 Han The Thanh +-% Copyright 2006-2012 Taco Hoekwater +-% +-% This file is part of LuaTeX. +-% +-% LuaTeX is free software; you can redistribute it and/or modify it under +-% the terms of the GNU General Public License as published by the Free +-% Software Foundation; either version 2 of the License, or (at your +-% option) any later version. +-% +-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +-% License for more details. +-% +-% You should have received a copy of the GNU General Public License along +-% with LuaTeX; if not, see . +- +-@ @c +- +- +-@ @c ++/* ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2012 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under the terms ++of the GNU General Public License as published by the Free Software Foundation; ++either version 2 of the License, or (at your option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY ++WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. ++ ++You should have received a copy of the GNU General Public License along with ++LuaTeX; if not, see . ++ ++*/ ++ + #include "ptexlib.h" + +-#include /* this is a trick to load mingw32's io.h early, +- using a macro redefinition of |eof()|. */ ++/*tex ++ ++ This is a trick to load mingw32's io.h early, using a macro redefinition of ++ |eof()|. ++ ++*/ ++ ++#include + #include "sys/types.h" + #include + #include + #include + #include +-#include /* for |DBL_EPSILON| */ ++ ++/*tex For |DBL_EPSILON|: */ ++ ++#include ++ + #include "zlib.h" + #include "md5.h" + +-#include "lua/luatex-api.h" /* for luatex_banner */ ++#include "lua/luatex-api.h" + #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__); +@@ -55,12 +56,11 @@ 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]; +@@ -117,7 +117,6 @@ void make_subset_tag(fd_entry * fd) + formatted_warning("subsets","subset-tag collision, resolved in round %d",j); + } + +-@ @c + __attribute__ ((format(printf, 1, 2))) + void tex_printf(const char *fmt, ...) + { +@@ -129,7 +128,6 @@ void tex_printf(const char *fmt, ...) + va_end(args); + } + +-@ @c + size_t xfwrite(void *ptr, size_t size, size_t nmemb, FILE * stream) + { + if (fwrite(ptr, size, nmemb, stream) != nmemb) +@@ -137,7 +135,6 @@ size_t xfwrite(void *ptr, size_t size, size_t nmemb, FILE * stream) + return nmemb; + } + +-@ @c + int xfflush(FILE * stream) + { + if (fflush(stream) != 0) +@@ -145,7 +142,6 @@ int xfflush(FILE * stream) + return 0; + } + +-@ @c + int xgetc(FILE * stream) + { + int c = getc(stream); +@@ -154,7 +150,6 @@ int xgetc(FILE * stream) + return c; + } + +-@ @c + int xputc(int c, FILE * stream) + { + int i = putc(c, stream); +@@ -163,7 +158,6 @@ int xputc(int c, FILE * stream) + return i; + } + +-@ @c + scaled ext_xn_over_d(scaled x, scaled n, scaled d) + { + double r = (((double) x) * ((double) n)) / ((double) d); +@@ -176,9 +170,13 @@ scaled ext_xn_over_d(scaled x, scaled n, scaled d) + return (scaled) r; + } + +-@ function strips trailing zeros in string with numbers; +-leading zeros are not stripped (as in real life) +-@c ++/*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) + { +@@ -248,9 +246,9 @@ char *stripzeros(char *a) + } + #endif + +-@ @c + void initversionstring(char **versions) + { ++ + #ifdef LuajitTeX + #define LUA_VER_STRING LUAJIT_VERSION + #else +@@ -258,35 +256,37 @@ void initversionstring(char **versions) + #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"; ++ "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(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. */ ++ + 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(),POPPLER_VERSION, ++ mp_metapost_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) { +@@ -294,16 +294,18 @@ void check_buffer_overflow(int wsize) + if (nsize < wsize) { + nsize = wsize + 5; + } +- buffer = +- (unsigned char *) xreallocarray(buffer, char, (unsigned) nsize); ++ 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. ++/*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. ++ ++*/ + +-@c + #define max_integer 0x7FFFFFFF + + scaled divide_scaled(scaled s, scaled m, int dd) +@@ -331,7 +333,7 @@ scaled divide_scaled(scaled s, scaled m, int dd) + q = 10 * q + (10 * r) / m; + r = (10 * r) % m; + } +- /* rounding */ ++ /*tex Rounding: */ + if (2 * r >= m) { + q++; + } +@@ -343,8 +345,12 @@ scaled divide_scaled(scaled s, scaled m, int dd) + #define floor win32_floor + #endif + +-@ Same function, but using doubles instead of integers (faster) +-@c ++/*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; +@@ -356,11 +362,9 @@ scaled divide_scaled_n(double sd, double md, double n) + return (scaled) di; + } + +-@ @c + int do_zround(double r) + { + int i; +- + if (r > 2147483647.0) + i = 2147483647; + else if (r < -2147483647.0) +@@ -369,16 +373,20 @@ int do_zround(double r) + i = (int) (r + 0.5); + else + i = (int) (r - 0.5); +- + return i; + } + + +-@ Old MSVC doesn't have |rint|. +-@c ++/*tex ++ ++ Old MSVC doesn't have |rint|. ++ ++*/ ++ + #if defined(_MSC_VER) && _MSC_VER <= 1600 + + # include ++ + double rint(double x) + { + return floor(x+0.5); +@@ -386,57 +394,66 @@ double rint(double x) + + #endif + +-@ replace tmpfile() on Windows +-@c ++/*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 +- * Adrian Johnson +- */ ++ ++/* ++ ++ _cairo_win_tmpfile (void) - replace tmpfile() on Windows ++ extracted from cairo-misc.c in cairo - a vector graphics library ++ with display and print output ++ ++ the functiion name is changed from _cairo_win32_tmpfile (void) to ++ _cairo_win_tmpfile (void) ++ ++ Copyright 2002 University of Southern California ++ Copyright 2005 Red Hat, Inc. ++ Copyright 2007 Adrian Johnson ++ ++ This library is free software; you can redistribute it and/or modify it ++ either under the terms of the GNU Lesser General Public License version 2.1 ++ as published by the Free Software Foundation (the "LGPL") or, at your option, ++ under the terms of the Mozilla Public License Version 1.1 (the "MPL"). If you ++ do not alter this notice, a recipient may use your version of this file under ++ either the MPL or the LGPL. ++ ++ You should have received a copy of the LGPL along with this library in the ++ file COPYING-LGPL-2.1; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA You should have ++ received a copy of the MPL along with this library in the file ++ COPYING-MPL-1.1 ++ ++ The contents of this file are subject to the Mozilla Public License Version ++ 1.1 (the "License"); you may not use this file except in compliance with the ++ License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ ++ ++ This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY ++ KIND, either express or implied. See the LGPL or the MPL for the specific ++ language governing rights and limitations. ++ ++ The Original Code is the cairo graphics library. The Initial Developer of the ++ Original Code is University of Southern California. Contributor(s): ++ ++ Carl D. Worth ++ Adrian Johnson ++ ++*/ + + #include + #define WIN32_LEAN_AND_MEAN +-/* We require Windows 2000 features such as ETO_PDY */ ++ ++/*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 +@@ -447,14 +464,15 @@ double rint(double x) + #include + #include + +-/* tmpfile() replacement for Windows. +- * +- * On Windows tmpfile() creates the file in the root directory. This +- * may fail due to unsufficient privileges. However, this isn't a +- * problem on Windows CE so we don't use it there. +- */ +-FILE * +-_cairo_win_tmpfile (void) ++/*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]; +@@ -462,14 +480,11 @@ _cairo_win_tmpfile (void) + HANDLE handle; + int fd; + FILE *fp; +- + path_len = GetTempPathW (MAX_PATH, path_name); + if (path_len <= 0 || path_len >= MAX_PATH) +- return NULL; +- ++ return NULL; + if (GetTempFileNameW (path_name, L"ps_", 0, file_name) == 0) +- return NULL; +- ++ return NULL; + handle = CreateFileW (file_name, + GENERIC_READ | GENERIC_WRITE, + 0, +@@ -478,22 +493,20 @@ _cairo_win_tmpfile (void) + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, + NULL); + if (handle == INVALID_HANDLE_VALUE) { +- DeleteFileW (file_name); +- return NULL; ++ DeleteFileW (file_name); ++ return NULL; + } +- + fd = _open_osfhandle((intptr_t) handle, 0); + if (fd < 0) { +- CloseHandle (handle); +- return NULL; ++ CloseHandle (handle); ++ return NULL; + } +- + fp = _fdopen(fd, "w+b"); + if (fp == NULL) { +- _close(fd); +- return NULL; ++ _close(fd); ++ return NULL; + } +- + return fp; + } ++ + #endif +--- texlive-source/texk/web2c/luatexdir/font/luafont.c.me 1970-01-01 01:00:00.000000000 +0100 ++++ texlive-source/texk/web2c/luatexdir/font/luafont.c 2020-11-07 15:50:28.079336523 +0100 +@@ -0,0 +1,2420 @@ ++/* ++ ++Copyright 2006-2011 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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 | | */ ++ 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 | | */ ++ } ++ 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-source/texk/web2c/luatexdir/font/writecff.c.me 1970-01-01 01:00:00.000000000 +0100 ++++ texlive-source/texk/web2c/luatexdir/font/writecff.c 2020-11-07 15:50:28.081338255 +0100 +@@ -0,0 +1,3156 @@ ++/* ++ ++Copyright 2006-2010 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#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 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; ++} +--- texlive-source/texk/web2c/luatexdir/font/writet1.c.me 1970-01-01 01:00:00.000000000 +0100 ++++ texlive-source/texk/web2c/luatexdir/font/writet1.c 2020-11-07 15:50:28.081338255 +0100 +@@ -0,0 +1,1693 @@ ++/* ++ ++Copyright 1996-2006 Han The Thanh ++Copyright 2006-2009 Taco Hoekwater ++ ++This file is part of LuaTeX. ++ ++LuaTeX is free software; you can redistribute it and/or modify it under ++the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your ++option) any later version. ++ ++LuaTeX is distributed in the hope that it will be useful, but WITHOUT ++ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ++License for more details. ++ ++You should have received a copy of the GNU General Public License along ++with LuaTeX; if not, see . ++ ++*/ ++ ++#include "ptexlib.h" ++#include ++ ++#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 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 exch 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 getinterval 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(); ++ strcat(t1_buf_array, t1_line_array); ++ 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); ++} diff --git a/SPECS/texlive.spec b/SPECS/texlive.spec index 811fdc2..46de398 100644 --- a/SPECS/texlive.spec +++ b/SPECS/texlive.spec @@ -15,7 +15,7 @@ Name: texlive Version: %{source_date} -Release: 19%{?dist} +Release: 20%{?dist} Epoch: %{tl_epoch} Summary: TeX formatting system Group: Applications/Publishing @@ -864,8 +864,6 @@ Patch7: texlive-20180414-new-poppler.patch Patch8: texlive-20180414-xml.patch # python3 support and mangling shebang Patch9: texlive-2017-python3.patch -# fix for poppler-0.63 -Patch10: texlive-20180414-poppler-0.63.patch # annocheck distro flag failures Patch11: texlive-20180414-annocheck.patch # CVE-2018-17407 @@ -875,6 +873,8 @@ Patch13: texlive-20180414-covscan.patch # Since we need to include tlmgr.pl for texconfig # lets try to keep people from shooting themselves with it Patch14: texlive-20180414-tlmgr-ignore-warning.patch +# fix for poppler-20.11.0 +Patch15: texlive-20180414-poppler-20.11.0-luatex.patch %description The TeX Live software distribution offers a complete TeX system for a @@ -23782,11 +23782,13 @@ xz -dc %{SOURCE0} | tar x %patch5 -p0 %if 0%{?fedora} >= 28 || 0%{?rhel} >= 8 %patch7 -p1 -b .newpoppler -%patch10 -p1 -b .poppler-0.63.0 %endif %patch11 -p1 -b .annocheck %patch12 -p1 %patch13 -p1 +pushd source +%patch15 -p1 +popd # Setup copies of the licenses for l in `unxz -c %{SOURCE2} | tar t`; do ln -s %{_texdir}/licenses/$l $l @@ -23799,7 +23801,9 @@ export CXXFLAGS="$RPM_OPT_FLAGS -std=c++11 -fno-strict-aliasing -Werror=format-s %if 0%{?fedora} >= 28 || 0%{?rhel} >= 8 export LDFLAGS="%{build_ldflags}" %endif -cd source +cd source/texk/web2c/ +autoreconf -vfi +cd ../.. PREF=`pwd`/inst mkdir -p work cd work @@ -24602,10 +24606,9 @@ chmod 644 %{buildroot}%{_texdir}/texmf-dist/doc/xelatex/xepersian/*.py # fix mangling shebang in scrips pushd %{buildroot}%{_texdir}/texmf-dist/ sed -i '1d' scripts/latex2man/latex2man -for f in oberdiek/pdfatfi.pl luaotfload/mkimport \ - luaotfload/mktests luaotfload/luaotfload-tool.lua \ - luaotfload/mkstatus luaotfload/mkglyphlist \ - luaotfload/mkcharacters tex4ht/mk4ht.pl epstopdf/epstopdf.pl \ +for f in oberdiek/pdfatfi.pl \ + luaotfload/luaotfload-tool.lua \ + tex4ht/mk4ht.pl epstopdf/epstopdf.pl \ texlive/mktexlsr.pl texlive/rungs.tlu \ texlive/updmap.pl texlive/test-tlpdb.tlu texlive/texconf.tlu \ texlive/fmtutil.pl ; do @@ -27185,6 +27188,9 @@ fi %doc %{_texdir}/texmf-dist/doc/context/ %changelog +* Mon Nov 09 2020 Than Ngo - 7:20180414-20 +- Resolves: #1889802, adapted texlive for poppler-20.10.0 + * Thu Jul 23 2020 Than Ngo - 7:20180414-19 - Related: #1829136, add missing mtxrun for tlmgr