TeX 系统的构建 (2) – 恐怖的 web2c

自从第一部分贴出以来,受到了很多朋友的鼓励,令我感到非常高兴: 看来尽管我拙于表达,对于 TeX 系统的构建感兴趣的朋友还是不少的。不过有一点需要说明:撰写此文的主要目的并非是“构建一个最小的 TeX 系统”,只不过正巧 web2c 系统符合我们讲解的需要而已,我所希望证明的是,TeX 的构建看似复杂,其实也不过是有许多细小的“砖石”一块块搭成,并无什么神秘的地方,尽管大家的兴趣多数是在 TeX 的基础上撰写宏包,但其实参与到 TeX 系统的开发中去也并非那么困难。

web2c 只是用 YACC 写的一套简单的分析程序,由于无法作复杂的分析,所以类似 autotools 的那套“由 A 生成 B,B 生成 C,C 再生成 D”这样化学反应链一般的恐怖过程便被设计出来。

一开始,因为没法直接从 Pascal 代码中分析出其中的变量与函数,所以不得不在一些称为 .defines 的文件中专门说明这些,比如我们如果要用 web2c 转换 etex.p,就得把 common.defines, texmf .defines 和 etexdir/etex.defines 再加上 etex.p 四个文件连接起来送给 web2c 这个程序的标准输入。

然后编写 web2c 的人发现,就算这样还是不好直接通过 web2c 生成的方式搞定所有 C 程序里需要的定义,还是另外把一些内容写在 .h 头文件里,让生成的 .c 程序 #include 它就好了。于是就有一个叫做 texmf.h 的头文件被 C 程序包含,后来这个包含继续复杂化,变成现在这样,C 程序包含 texd.h, texd.h 除了自己的一部分自动生成的内容之外包含 texmfmp.h 和 texcoerce.h,而 texcoerce.h 的内容则是把 web2c 生成的一部分内容再加上一个通用的 coerce.h 连接起来得到的……实在太麻烦也太恶心了,好在我们不需要细究这个,毕竟我们只希望知道,如果要给 TeX 增加代码,对 web 文件的改动,如果增加了变量、函数、过程,则应该在 .defines 文件里增加内容,如果有额外的 C 程序需要链接在一起,则应该找一个被 texd.h 包含的 .h 文件,往里面添加增加全局变量或者函数声明。

分析 web2c/convert 这个 shell 脚本我们可以发现,这个脚本完成了完整的从 Pascal 转换到 C 的过程,一个 tex.p 被分成了三个部分:

texd.h (被其他 .c 文件包含的头文件),事实上,这个文件主要是从对应 .defines 文件里得来,比如 web2c/texmf.defines 就定义了全局变量和函数原型 (prototypes)。对此的分析可以让我们得出结论:如果我们希望把部分模块单独用 C 语言写好,最后再和原来的 TeX 程序链接起来,是可以做到的,只需要在对应的 .h 文件中增加相应的函数声明,并在 Makefile.in 文件中提供正确的链接选项即可。事实上,虽然 eTeX, pdfTeX, Omega 这些 TeX 的扩展都还是用纯 web 语言编写的,但较新的一些扩展,如 XeTeX 和 LuaTeX,都体现了尽量把代码提取出来用 C/C++ 实现的思路。

texini.c: 这是原来的程序中标记了 INITEX 那一部分的代码, 读过 TeXBook 的朋友知道,TeX 可以采用一种特殊的方式把大量的宏定义转换成一种便于以后快速载入的文件 (很像现在的 C 编译器提供的预编译头文件 PCH 的功能),这种文件就称为 format 文件,而用于生成——在这里叫做 dump——这种 format 文件的程序就是initex。以前 Knuth 的设计是,如果我们把标记了 INITEX 的那部分代码编译进去,生成的程序就叫做 initex,专门用于 dump format,如果不编译进去,就是我们日常运行的 tex,只能载入 format 而不能 \dump。熟悉 C 语言的朋友可以想象成,web2c 里把 #ifdef INITEX#endif 之间的那部分代码提取出来,形成了 texini.c。

顺便说一下,现在的 tex 程序往往都把这个 texini.c 默认就编译进去,不过一定要指定命令行参数 -initialize (-ini) 才会启用这部分功能,事实上平时我们用于生成 format 文件的 fmtutil 这个脚本就是调用 tex -ini 来创建格式的。如果用 tetex/TeXLive,可以打开 kpsewhich fmtutil.cnf 文件查看不同的格式对应的源文件 (如果用 MikTeX,你可以使用 MikTeX Options->Formats 查看),比如我这里 Plain TeX 格式对应的源文件的内容就是:

\input plain
\dump
\endinput

你同样可以用 etex -ini 启动 TeX 系统,输入上面的内容,手动地生成格式。

tex0.c, tex1.c, tex2.c: 在前面我们提到过,原来的 Pascal 代码足足有 3 万多行,自然,转换出的 C 代码也有 3 万多行,设计 web2c 的时候生怕当时的 C 编译器无法一口啃下这么长的代码,就专门开发了一个叫做 splitup 的程序,把完整的程序切分为三个差不多长度的文件——嗯,叫人很汗的想法。

得到这些文件之后就简单了,只要用 C 编译器编译并链接起来就得到我们平时运行的 TeX 程序。

最后是一幅图,把前边叙述的内容作一小结。下次我们将讨论 TeX 系统中的文件搜索与支持它的核心:Kpathsea。

TeX 系统的构建

Author: jjgod

A software engineer from China, working on text rendering for a fruit company. Interested in typography and science fiction.

2 thoughts on “TeX 系统的构建 (2) – 恐怖的 web2c”

  1. 辛苦辛苦~~~这么复杂的源流你都考察出来了~~~~

    问个相关的问题,我之前看你的文章,上面有写到这个字体方面的事。这免费的中文字体现在我能找到的最全的似乎就是Adobe公司出的AdobeMingStd了吧,这otf的字体,有win32下,被LaTeX嵌入PDF的可能么?

Leave a Reply

Your email address will not be published. Required fields are marked *