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); +}