% -*- coding: utf-8 -*-
% This is part of the book TeX for the Impatient.
% Copyright (C) 2003 Paul W. Abrahams, Kathryn A. Hargreaves, Karl Berry.
% See file fdl.tex for copying conditions.

\input macros
%\chapter{A compendium \linebreak of useful macros}
\chapter{实用宏集概述}

\chapterdef{eplain}

%This section describes |eplain.tex|, a collection of macros and other
%definitions that extend  plain \TeX.
%\bix^^|eplain.tex|
%The descriptions of the various macros explain their purposes,
%but usually do not explain how they
%work or provide explicit details on
%how to use them.  That information is contained in the source files for
%|eplain.tex| and in the documentation that comes with it.
%See \headcit{Resources}{resources} for how to obtain |eplain.tex|.
这一章描述 |eplain.tex|，一组用于扩展 plain \TeX\ 的宏和其他定义。
\bix^^|eplain.tex|
对各种宏的描述解释了它们的目的，但通常不解释它们的工作原理或给出如何使用它们的详情。
那些信息包含在 |eplain.tex| 源码文件以及它的文档中。
在\headcit{资源}{resources}中介绍了获取 |eplain.tex| 的方法。


%\section{Preliminaries}
\section{预备定义}

%We start with some macros for changing category codes and convenient
%definitions for two of the commonly used ones.
%^^{category codes//useful definitions for}
我们先介绍几个修改类别码及定义两种常用类别码的宏。
^^{类别码//有用的类别码定义}

%\pix^^|\makeactive|
%\pix^^|\letter|
%\pix^^|\other|
%\pix^^|\uncatcodespecials|
\pix^^|\makeactive|
\pix^^|\letter|
\pix^^|\other|
\pix^^|\uncatcodespecials|

\csdisplay
\def\makeactive#1{\catcode`#1 = \active \ignorespaces}%
\chardef\letter = 11 \chardef\other = 12
\def\uncatcodespecials{%
   \def\do##1{\catcode`##1 = \other}%
   \dospecials}% Defined in plain.
|

%In order to define `|^^M|' as an active character, you need to encase
%the definition in a group and invoke some extra machinery.
%\ttidxref{^^M}
%The \pix^|\letreturn| macro lets you define `|^^M|' without that
%extra machinery (which you can see in the definition below).
为了将 `|^^M|' 定义为活动字符，你需要将定义放在编组中，并借助某些额外机制。
\ttidxref{^^M}
\pix^|\letreturn| 宏让你无需额外机制就可以定义 `|^^M|'（它的定义在下面可以看到）。

\csdisplay
{\makeactive\^^M \long\gdef\letreturn#1{\let^^M = #1}}%
|

%These macros consume one, two, or three arguments.
这些宏用于吸收一个、两个或三个参量。

%\pix^^|\gobble|
%\pix^^|\gobbletwo|
%\pix^^|\gobblethree|
%\csdisplay
%\def\gobble#1{}\def\gobbletwo#1#2{}%
%\def\gobblethree#1#2#3{}%
%|
\pix^^|\gobble|
\pix^^|\gobbletwo|
\pix^^|\gobblethree|
\csdisplay
\def\gobble#1{}\def\gobbletwo#1#2{}%
\def\gobblethree#1#2#3{}%
|

%Now we establish some conventions for reading the rest of the file.
%Within the file we allow ``private'' control sequences that contain
%`|@|' in their names.
%These control sequences aren't accessible outside of this file (unless
%you change the category code of `|@|' again).
%\xrdef{eplainconv}
现在我们建立一些惯例，用于读取这个文件的其他部分。
在这个文件中我们允许名称带有 `|@|' 的``私有''控制序列。
这些控制序列在这个文件之外无法使用（除非你再次修改 `|@|' 的类别码）。
\xrdef{eplainconv}

\csdisplay
\catcode`@ = \letter    % Allow control sequences with @.
\let\@plainwlog = \wlog % Don't log register allocations.
\let\wlog = \gobble
\newlinechar = `^^J
|

%The next two macros provide convenient forms of diagnostic output.
%\pix^|\loggingall|  turns on all tracing, but causes the trace
%output to appear only in the log file and not at your terminal.
%\pix^|\tracingboxes| causes boxes to be displayed completely when
%they're traced.  (\TeX\ normally shows only three levels of boxing
%and five items within each box.)
接下来两个宏提供了诊断输出的便捷方式。
\pix^|\loggingall| 打开所有追踪，
但让追踪输出仅写在日志文件中，而不在终端上显示。
\pix^|\tracingboxes| 使得盒子被追踪时其内容完全显示。%
（\TeX\ 通常只显示嵌套到三层的盒子，每个盒子显示五个项目。）

\csdisplay
\def\loggingall{\tracingcommands\tw@\tracingstats\tw@
   \tracingpages\@ne\tracingoutput\@ne
   \tracinglostchars\@ne\tracingmacros\tw@
   \tracingparagraphs\@ne\tracingrestores\@ne
   \showboxbreadth\maxdimen\showboxdepth\maxdimen}%
\def\tracingboxes{\showboxbreadth = \maxdimen
   \showboxdepth = \maxdimen}%
|

%The default thickness of rules is $0.4$\pt.
%You can produce rules of any default thickness you choose by redefining
%|\vruledefaultwidth|, |\hruledefaultheight|, and |\hruledefaultdepth|
%and then using
%\pix^^|\ehrule|
%|\eh!-rule|
%and \pix^|\evrule|
%instead of |\hrule| and |\vrule|. (The `e'  stands for
%``eplain''.)
%^^{rules//thickness of}
%If you give an explicit
%dimension (e.g., |\ehrule height 16pt|), \TeX\ will use it.
标线的默认厚度为 $0.4$\pt 。
要生成默认厚度为任何值的标线，
你可以重新定义 |\vruledefaultwidth|、|\hruledefaultheight| 及 |\hrule!-defaultdepth|，
并将 |\hrule| 和 |\vrule| 换为 \pix^|\ehrule| 和 \pix^|\evrule|。%
（名称中的 `e' 代表 ``eplain''。）%
^^{标线//标线的厚度}
如果你显式给出尺寸（比如 |\ehrule height 16pt|），\TeX\ 将使用它。

\csdisplay
\newdimen\hruledefaultheight  \hruledefaultheight = 0.4pt
\newdimen\hruledefaultdepth   \hruledefaultdepth = 0.0pt
\newdimen\vruledefaultwidth   \vruledefaultwidth = 0.4pt
\def\ehrule{\hrule height\hruledefaultheight
   depth\hruledefaultdepth}%
\def\evrule{\vrule width\vruledefaultwidth}%
|

%The |\%| convention for writing a `|%|' character doesn't work when you
%want to include that character in the token list of |\write|.
%^^|\write//writing \b\tt\%\e\ with|
%^^|%//writing out|
%You can use ^|\percentchar| to achieve this.
%We also redefine ^|^^L| to be nonouter so that you can use it in a
%macro definition or argument.
|\%| 通常用于写出 `|%|' 字符，但它无法包含在 |\write| 的记号列中。
^^|\write//用它写出 \b\tt\%\e\|
%^^|%//写出该字符|
你可以用 ^|\percentchar| 达到此目的。
我们还将 ^|^^L| 重新定义为非外部宏，因此你可以在宏定义或者参量中使用它。

\csdisplay
{\catcode`\% = \other \gdef\percentchar{%}}%
 \def^^L{\par
}%
|

%\pix^|\tokstostring| converts its argument into a list of ^{character
%tokens}.
%It uses only expansions that are handled in \TeX's gullet.
%This property is necessary
%for it to work with |\edef|.  It is used by the cross-referencing
%macros (\xref{xrefs}).
\pix^|\tokstostring| 将它的参量转换为一列^{字符记号}。
它仅用到在 \TeX\ 的食道中处理的展开。
此特性是和 |\edef| 一起使用时所必需的。它用于宏的交叉引用（\xref{xrefs}）。

%In order to split the argument up at spaces, we have to use two
%subsidiary macros. |\@ttsA| finds the spaces, and |\@ttsB| handles a
%token sequence without any spaces.  Each space is replaced by the
%expansion of \pix^|\spacesub|.
为了将参量在空格处分开，我们要用到两个子宏。
|\@ttsA| 用于寻找空格，而 |\@ttsB| 用于处理不带空格的控制序列。
每个空格都被替换为 \pix^|\spacesub| 的展开。

\csdisplay
\def\tokstostring#1{\@ttsA#1 \ttsmarkA}%
\def\@ttsA#1 #2\ttsmarkA{\ifempty{#1}\else
      \@ttsB #1\@ttsmarkB
      \ifempty{#2}\else
         \spacesub\@ttsA#2\ttsmarkA\fi\fi}%
\def\@ttsB#1{\ifx #1\@ttsmarkB\else
      \string #1%
      \expandafter\@ttsB\fi}%
\def\@ttsmarkB{\@ttsmarkB}% should never be expanded
\def\spacesub{+}%
|

%\noindent
%\pix^|\ifempty| tests if its argument is empty.
\noindent
\pix^|\ifempty| 检测它的参量是否为空。

\csdisplay
\def\ifempty#1{\@ifempty #1\@emptymarkA\@emptymarkB}%
\def\@ifempty#1#2\@emptymarkB{\ifx #1\@emptymarkA}%
\def\@emptymarkA{\@emptymarkA}%
|

%The ^|\for|  macro implements a \TeX\ version of the ^{for loop} in
%traditional programming languages.  These macros come directly from
%\LaTeX.
^|\for| 宏实现了其他传统编程语言中的^{for循环}的 \TeX\ 版本。
这些宏直接取自 \LaTeX 。

\csdisplay
\def\for#1:=#2\do#3{\edef\@fortmp{#2}%
   \ifx\@fortmp\empty \else
      \expandafter\@forloop#2,\@nil,\@nil\@@#1{#3}\fi}%
\def\@nnil{\@nil}%
\def\@fornoop#1\@@#2#3{}%
\def\@forloop#1,#2,#3\@@#4#5{\def#4{#1}\ifx #4\@nnil
   \else #5\def#4{#2} ifx #4\@nnil \else
      #5\@iforloop #3\@@#4{#5}\fi\fi}%
\def\@iforloop#1,#2\@@#3#4{\def#3{#1}\ifx #3\@nnil
   \let\@nextwhile=\@fornoop \else #4\relax
      \let\@nextwhile=\@iforloop\fi
         \@nextwhile#2\@@#3{#4}}%
|

%\pix^|\obeywhitespace| is useful for reproducing line breaks, blank lines,
%and spaces in your input. It combines the effects of |\obey!-lines| and
%|\obey!-spaces|,
%^^|\obeylines| ^^|\obeyspaces|
%and also causes spaces at the start of a line to
%be printed. Tab characters are not affected by this; they still produce
%normal glue.
%\xrdef{ewhitesp}
%^^{space characters}
%^^|\fontdimen|
%^^|\font|
%^^|\letreturn|
\pix^|\obeywhitespace| 用于重新生成你的输入中的换行、空行和空格。
它结合了|\obey!-lines| 和 |\obey!-spaces| 的效果，
^^|\obeylines| ^^|\obeyspaces|
并把行首的空格也打印出来。制表符不受它影响；它们仍然生成正常的粘连。
\xrdef{ewhitesp}
^^{空格符}
^^|\fontdimen|
^^|\font|
^^|\letreturn|

\csdisplay
\def\alwaysspace{\hglue\fontdimen2\the\font \relax}%
{\makeactive\^^M \makeactive\ %
\gdef\obeywhitespace{%
\makeactive\^^M\def^^M{\par\indent}%
\aftergroup\@removebox% Kill extra paragraph at end.
\makeactive\ \let =\alwaysspace}}%
\def\@removebox{\setbox0=\lastbox}
|

%\pix^|\frac| is a good way to print fractions in text when you don't
%want to use |\over| and
%``1/2'' just doesn't look right.  This macro is the answer to
%\knuth{Exercise~11.6}.^^{fractions//slashed form}
当你不想用 |\over|，而 ``1/2'' 看来又不够好时，
你最好用 \pix^|\frac| 在正文中打印分式。
这个宏是\knuth{练习~11.6}的答案。^^{分式//斜线形式}

\csdisplay
\def\frac#1/#2{\leavevmode
   \kern.1em \raise .5ex \hbox{\the\scriptfont0 #1}%
   \kern-.1em $/$%
   \kern-.15em \lower .25ex \hbox{\the\scriptfont0 #2}}%
|

%The following macros produce logos that are useful in the \TeX\ world.
%The \AMSTeX\ logo is from \knuth{page~420}.  The \LaTeX\ logo is
%slightly modified from the one in |latex.tex| (we use a different font
%for the `A'); similarly, the \BibTeX\ logo uses |\sevenrm| instead of a
%true caps-and-small-caps font. The |.mf| source file for the \MF\ logo
%is given in the \MF\ manual:
%\smallskip
%{\narrower\noindent
%^{Knuth, Donald E.},{\sl The {\logosl METAFONT}book}.
%  Reading, Mass.: Addison-Wesley, 1986.\par}
%\smallskip
%\pix^^|\LaTeX|
%\pix^^|\AMSTeX|
%\pix^^|\BibTeX|
%\pix^^|\MF|
下面这些宏生成 \TeX\ 圈中常用的标识。
\AMSTeX\ 标识取自 \knuth{第~420~页}。
\LaTeX\ 标识取自 |latex.tex| 并稍作修改（我们修改了 `A' 的字体）；
类似地，\BibTeX\ 标识用 |\sevenrm| 字体代替真正的小型大写字体。
\MF\ 标识的 |.mf| 源文件出现在 \MF\ 手册中：
\smallskip
{\narrower\noindent
^{Knuth, Donald E.},{\sl The {\logosl METAFONT}book}.
  Reading, Mass.: Addison-Wesley, 1986.\par}
\smallskip
\pix^^|\LaTeX|
\pix^^|\AMSTeX|
\pix^^|\BibTeX|
\pix^^|\MF|

\csdisplay
\def\LaTeX{L\kern-.26em \raise.6ex\hbox{\fiverm A}%
   \kern-.15em TeX}%
\def\AMSTeX{$\cal A\kern-.1667em \lower.5ex\hbox{$\cal M$}%
   \kern-.125em S$-\TeX}%
\def\BibTeX{{\rm B\kern-.05em {\sevenrm I\kern-.025em B}%
   \kern-.08em T\kern-.1667em \lower.7ex\hbox{E}%
   \kern-.125emX}}%
\font\mflogo = logo10
\def\MF{{\mflogo META}{\tenrm \-}{\mflogo FONT}}%
|

%The next two macros produce boxes.  \pix^|\blackbox| produces a ``square
%bullet'', used in the list macros (\xref{listmacs}).
%\pix^|\makeblankbox| (from \knuth{page~311}) produces an unfilled
%rectangle, with the thickness of the border rules given by the
%arguments.
接下来的两个宏用于生成盒子。
\pix^|\blackbox| 生成一个方块，它用在列表宏中（\xref{listmacs}）。
\pix^|\makeblankbox|（取自\knuth{第~311~页}）生成一个空心矩形，
它的边框厚度由参量指定。

\csdisplay
\def\blackbox{\vrule height .8ex width .6ex depth -.2ex}%
\def\makeblankbox#1#2{%
   \hbox{\lower\dp0\vbox{\hidehrule{#1}{#2}%
      \kern -#1% overlap rules
      \hbox to \wd0{\hidevrule{#1}{#2}%
         \raise\ht0\vbox to #1{}% vrule height
         \lower\dp0\vtop to #1{}% vrule depth
         \hfil\hidevrule{#2}{#1}}%
      \kern-#1\hidehrule{#2}{#1}}}}%
\def\hidehrule#1#2{\kern-#1\hrule height#1 depth#2
   \kern-#2}%
\def\hidevrule#1#2{\kern-#1{\dimen0 = #1
   \advance\dimen0 by #2 \vrule width\dimen0}\kern-#2}%
|

%\pix^|\numbername| produces the written-out form of a number.  (If the
%number is greater than ten, the macro just reproduces the numerals
%of its argument.)
\pix^|\numbername| 从数字生成其书面名称%
（若数字大于十，这个宏仅重新生成参量的数值。）

\csdisplay
\def\numbername#1{\ifcase#1%
   zero\or one\or two\or three\or four\or five%
   \or six\or seven\or eight\or nine\or ten\or #1\fi}%
|

%\pix^|\testfileexistence| determines whether a file |\jobname.#1| is
%non\-empty and sets |\if!-fileexists| appropriately.
%^^{files//testing for existence of}
%The file name in the argument need not end in a space token since
%the macro provides the space token.
\pix^|\testfileexistence| 判定文件 |\jobname.#1| 是否非空，
并相应地设定 |\if!-fileexists| 的值。
^^{文件//检测文件是否存在}
参量中的文件名无需以空格记号结尾，因为这个宏已经提供了空格记号。

\csdisplay
\newif\iffileexists
\def\testfileexistence#1{\begingroup
      \immediate\openin0 = \jobname.#1\space
      \ifeof 0\global\fileexistsfalse
      \else \global\fileexiststrue\fi
      \immediate\closein0
   \endgroup}%
|


%\section Displays
\section 陈列公式

%By default, \TeX\ centers displayed material (the material between
%|$$|'s).  ^^{displays, formatting} \pix^|\leftdisplays| causes displays
%to be left-justified by default.  You can return to centered displays
%with \pix^|\centereddisplays|.
\TeX\ 默认将陈列公式（放在 |$$| 之间的公式）居中显示。
^^{陈列公式//显示形式}\pix^|\leftdisplays| 让陈列公式默认为左对齐显示。
你也可以用 \pix^|\centereddisplays| 命令恢复居中显示。

%The macros here are more general than they need to be just for doing
%left-justified displays.  For every display,
%\pix^|\ifeqno| will be true if an |\eqno| occurred in the display.
%\pix^|\ifleqno| will be true if an |\leqno| occurred.  If either
%kind of equation number occurred, \pix^|\eqn| produces the text of the
%equation number.  \pix^|\eq|  always produces the text of the
%equation itself.
这些宏提供让陈列公式左对齐显示之外的更多功能。
对每个陈列公式，若 |\eqno| 存在则 \pix^|\ifeqno| 为真，
若 |\leqno| 存在则 \pix^|\ifleqno| 为真。
如果其中一种公式编号存在，\pix^|\eqn| 将排印公式编号的内容。
而 \pix^|\eq| 始终排印公式本身的内容。

%These macros are based on the code on \knuth{page~376}.
这些宏基于\knuth{第~376~页}中的代码。

\csdisplay
\newif\ifeqno \newif\ifleqno
\newtoks\@eqtoks \newtoks\@eqnotoks
\def\eq{\the\@eqtoks}\def\eqn{\the\@eqnotoks}%
\def\displaysetup#1$${%
   \@displaytest#1\eqno\eqno\@displaytest}%
\def\@displaytest#1\eqno#2\eqno#3\@displaytest{%
   \if !#3!% No \eqno, check for \leqno:
      \@ldisplaytest#1\leqno\leqno\@ldisplaytest
   \else
      \eqnotrue \leqnofalse % Have \eqno, not \leqno.
      \@eqnotoks = {#2}\@eqtoks = {#1}%
   \fi
   \generaldisplay$$}%
\def\@ldisplaytest#1\leqno#2\leqno#3\@ldisplaytest{%
   \@eqtoks = {#1}%
   \if !#3!%
      \eqnofalse % No \leqno; we're done.
   \else
      \eqnotrue \leqnotrue % Have \leqno.
      \@eqnotoks = {#2}%
   \fi}%
|

%You can format displays differently by defining your own macro,
%analogous to \pix^|\leftdisplays|.
%The macro definition must
%place a call on |\display!-setup| in |\every!-display| ^^|\everydisplay|
%so as to ensure
%that |\dis!-play!-setup| ^^|\displaysetup| is called at the start of
%every display. The macro definition must also include a definition of
%^|\generaldisplay|.
通过自己定义类似 \pix^|\leftdisplays| 的宏，你可以给陈列公式设定不同的形式。
这个宏定义中必须在 |\every!-display| ^^|\everydisplay|中调用 |\display!-setup|，
以确保 |\dis!-play!-setup| ^^|\displaysetup|在每个陈列公式开始都被调用。
这个宏定义必须也包含 ^|\generaldisplay| 的定义。

\csdisplay
\newtoks\previouseverydisplay
\def\leftdisplays{%
   \previouseverydisplay = \everydisplay
   \everydisplay =
     {\the\previouseverydisplay \displaysetup}%
   \def\generaldisplay{%
      \leftline{%
         \strut \indent \hskip\leftskip
         \dimen0 = \parindent
         \advance\dimen0 by \leftskip
         \advance\displaywidth by -\dimen0
         \@redefinealignmentdisplays
         \ifeqno \ifleqno
            \kern-\dimen0
            \rlap{$\displaystyle\eqn$}%
            \kern\dimen0
         \fi\fi
         $\displaystyle{\eq}$%
         \ifeqno \ifleqno\else
            \hfill $\displaystyle{\eqn}$%
         \fi\fi}}}%
\def\centereddisplays{\let\displaysetup = \relax}%
|

%\filbreak
%\pix^|\leftdisplays| must go to some pains to make sure that
%|\dis!-play!-lines|, ^^|\displaylines|
%^|\eqalignno|,  and ^|\leqalignno| still work properly.
%|\eq| is typeset in math mode,
%and |\halign| is illegal in math mode.
%^^|\halign//illegal in math mode|
%We use ^|\vcenter| to change the context so that  |\halign| becomes
%legal again. We also remove the |\hfil| commands at the left of the template
%to obtain the flush left formatting. Other than those changes, the macros
%are the same as in |plain.tex|.
%\filbreak
\pix^|\leftdisplays| 必须历经辛苦地确保 |\dis!-play!-lines|^^|\displaylines|、
^|\eqalignno| 和 ^|\leqalignno| 还能正常使用。
|\eq| 在数学模式中排版，而 |\halign| 在数学模式中是不合法的。
^^|\halign//在数学模式中不合法|
我们用 ^|\vcenter| 改变环境，以让 |\halign| 重新变成合法的。
我们还去掉模板左边的 |\hfil| 命令以得到左对齐格式。
除这些改变之外，这些宏与 |plain.tex| 中的相同。

\csdisplay
\def\@redefinealignmentdisplays{%
   \def\displaylines##1{\displ@y
      \vcenter{\halign{\hbox to\displaywidth{$\@lign
            \displaystyle####\hfil$}\crcr##1\crcr}}}%
   \def\eqalignno##1{\displ@y
      \vcenter{\halign to\displaywidth{%
             $\@lign\displaystyle{####}$\tabskip\z@skip
            &$\@lign\displaystyle{{}####}$
               \hfil\tabskip\centering
            &\llap{$\@lign####$}\tabskip\z@skip\crcr
            ##1\crcr}}}%
   \def\leqalignno##1{\displ@y
      \vcenter{\halign to\displaywidth{%
             $\@lign\displaystyle{####}$\tabskip\z@skip
            &$\@lign\displaystyle{{}####}
               $\hfil\tabskip\centering
            &\kern-\displaywidth
             \rlap{\kern-\parindent\kern-\leftskip$
                \@lign####$}%
             \tabskip\displaywidth\crcr
            ##1\crcr}}}}%
|


%\section Time of day
\section 日期时间

%When \TeX\ starts up, it sets the values of the
%^|\time|, ^|\day|, ^|\month|, and ^|\year| parameters.
%^^{time of day}^^{date}
%\pix^|\monthname|
%produces the name of the month, abbreviated to three letters.
%\pix^|\timestring| produces the current time, as in ``1:14\thinspace
%p.m.''. \pix^|\timestamp| produces the text of the complete date, as
%in ``23 Apr 1964\quad 1:14\thinspace p.m.''.
\TeX\ 开始运行时设定了 ^|\time|、^|\day|、^|\month| 和 ^|\year| 参数的值。
^^{日期时间}^^{日期}
\pix^|\monthname| 排印月份名称的三字母缩写。
\pix^|\timestring| 排印类似当前时间，类似于 ``1:14\thinspace p.m.''。
\pix^|\timestamp| 排印完整日期文本，类似于 ``23 Apr 1964\quad 1:14\thinspace p.m.''。

\csdisplay
\def\monthname{%
   \ifcase\month
      \or Jan\or Feb\or Mar\or Apr\or May\or Jun%
      \or Jul\or Aug\or Sep\or Oct\or Nov\or Dec%
   \fi}%
\def\timestring{\begingroup
   \count0 = \time \divide\count0 by 60
   \count2 = \count0   % The hour.
   \count4 = \time \multiply\count0 by 60
   \advance\count4 by -\count0   % The minute.
   \ifnum\count4<10 \toks1 = {0}% Get a leading zero.
   \else            \toks1 = {}%
   \fi
   \ifnum\count2<12 \toks0 = {a.m.}%
   \else            \toks0 = {p.m.}%
      \advance\count2 by -12
   \fi
   \ifnum\count2=0 \count2 = 12 \fi % Make midnight `12'.
   \number\count2:\the\toks1 \number\count4
   \thinspace \the\toks0
\endgroup}%
\def\timestamp{\number\day\space\monthname\space
   \number\year\quad\timestring}%
|


%\section Lists
\section 列表

%\null
%\xrdef{listmacs}
%\bix^^{itemized lists}
%\bix^^{enumerations}
%\pix^|\numberedlist| produces numbered lists; |\endnumberedlist| ends
%them.
%\pix^|\unorderedlist| is analogous.
%For either of these, items inside the lists begin
%with \pix^|\li| (``list item'').  You can put \pix^|\listcompact|
%at the beginning of a list if
%you don't want any additional space between the items of that list.
%Lists can be nested arbitrarily.
\null
\xrdef{listmacs}
\bix^^{编号列表}
\bix^^{列举}
\pix^|\numberedlist| 排印编号列表；|\endnumberedlist| 结束该列表。
类似地，\pix^|\unorderedlist| 排印无序列表。
这两种列表中的项目以 \pix^|\li|（``list item''）开头。
如果不需要列表项目间的额外间隔，你可以在列表开始处加上 \pix^|\listcompact|。
列表可以任意嵌套。

%You can control the spacing between the items more generally by
%assigning values to the registers listed below.  If the items in
%your lists tend to be long, you might want to make |\interitemskip|
%nonzero.
%The left indentation of each list item is given by |\par!-indent| plus
%|\list!-left!-indent|; the right indentation of each list item
%is given by |\list!-right!-indent|.
更一般地，你可以通过改变下列寄存器的值控制列表项目间的间隔。
如果列表项目经常比较长，你也许希望设定非零的 |\interitemskip|。
各列表项目的左缩进量等于 |\par!-indent| 加 |\list!-left!-indent|；
各列表项目的右缩进量等于 |\list!-right!-indent|。

\csdisplay
\newskip\abovelistskip  \abovelistskip = .5\baselineskip
\newskip\interitemskip  \interitemskip = 0pt
\newskip\belowlistskip  \belowlistskip = .5\baselineskip
\newdimen\listleftindent  \listleftindent = \parindent
\newdimen\listrightindent \listrightindent = 0pt
\def\listcompact{\interitemskip = 0pt \relax}%
|

%Both numbered and unnumbered lists use the macros that follow.
%We don't change |\parindent|, since many existing macros, e.g.,
%|\footnote|, depend on |\parindent|.
%We must account for the
%possibility that items are more than one paragraph long. In this case, all
%paragraphs after the first will be indented.
%We use
%|\leftskip| and |\rightskip| to indent the list items.
%Indentation of displays is accounted for by changes to |\every!-dis!-play|.
%^^|\everydisplay|
编号列表和无序列表都使用下面这些宏。
我们并不改变 |\parindent| 的值，
因为很多现有的宏比如 |\footnote| 依赖于 |\parindent|。
我们必须考虑到项目包含不止一个段落的可能性。
在这种情形中，除第一个之外的所有段落都将被缩进。
我们用 |\leftskip| 和 |\rightskip| 缩进列表项目。
我们通过改变 |\every!-dis!-play| 设定陈列公式的缩进。
^^|\everydisplay|

\csdisplay
\newdimen\@listindent
\def\beginlist{%
   \@listindent = \parindent
   \advance\@listindent by \listleftindent
   \everydisplay = \expandafter{\the\everydisplay
      % Don't lose user's \everydisplay:
      \advance\displayindent by \@listindent
      \advance\displaywidth by -\@listindent
      \advance\displaywidth by -\listrightindent}%
   \nobreak\vskip\abovelistskip
   \parskip = 0pt
   % \leftskip shifts nested lists to the right on the page.
   \advance\leftskip by \@listindent
   \advance\rightskip by \listrightindent}%
\def\printitem{\par\noindent
   \llap{\hskip-\listleftindent \marker \enspace}}%
\def\endlist{\vskip\belowlistskip}%
|

%\noindent
%You can change the way the item labels are typeset by
%redefining the |\numbered!-marker| macro.
%\pix^^|\numberedmarker|
\noindent
通常重新定义 |\numbered!-marker| 宏，你可以改变项目标签的样式。
\pix^^|\numberedmarker|

\csdisplay
\newcount\numberedlistdepth \newcount\itemnumber
\newcount\itemletter
\def\numberedmarker{%
   \ifcase\numberedlistdepth
       (impossible)%
   \or \itemnumberout)%
   \or \itemletterout)%
   \else *%
   \fi}%
|

%\noindent Here are the definitions of |\numberedlist| and
%|\unorderedlist|.
%Both definitions have the same structure.
\noindent 这里是 |\numberedlist| 和 |\unorderedlist| 的定义。
这两个定义的结构是相同的。

\csdisplay
\def\numberedlist{\environment{@numbered-list}%
   \advance\numberedlistdepth by 1
   \itemnumber = 1 \itemletter = `a
   \beginlist \let\marker = \numberedmarker
   \def\li{%
      \ifnum\itemnumber=1\else \vskip\interitemskip \fi
      \printitem
      \advance\itemnumber by 1 \advance\itemletter by 1
   }}%
\def\itemnumberout{\number\itemnumber}%
\def\itemletterout{\char\itemletter}%
\def\endnumberedlist{\par
   \endenvironment{@numbered-list}\endlist}%
!bigskip
\newcount\unorderedlistdepth
\def\unorderedmarker{%
   \ifcase\unorderedlistdepth
       (impossible)%
   \or \blackbox
   \or ---%
   \else *%
   \fi}%
\def\unorderedlist{\environment{@unordered-list}%
   \advance\unorderedlistdepth by 1
   \beginlist \itemnumber = 1
   \let\marker = \unorderedmarker
   \def\li{%
      \ifnum\itemnumber=1\else \vskip\interitemskip \fi
      \printitem \advance\itemnumber by 1
   }}%
\def\endunorderedlist{\par
   \endenvironment{@unordered-list}\endlist}%
|

%\eix^^{itemized lists}
%\eix^^{enumerations}
\eix^^{编号列表}
\eix^^{列举}


%\section Verbatim listing
\section 原文呈现

%The \pix^|\listing| macro produces a verbatim listing of a specified
%file in the |\tt| font.
%It is based on the code on \knuth{page~380}.
%Tabs produce a fixed amount of space, and form
%feeds produce a page break.  Other control characters produce whatever
%happens to be at that font position, which is generally not very useful.
%By redefining |\setup!-listing!-hook|,
%\pix^^|\setuplistinghook|
%you can take additional actions that are appropriate for your particular
%fonts and\slash or environment before the file is read in.
\pix^|\listing| 宏用 |\tt| 字体排印指定文件的原文呈现。
它基于\knuth{第~380~页}中的代码。
制表符生成固定大小的间隔，而换页符生成一个分页。
其他控制字符生成该字体所在位置的字符，这通常不太有用。
通过重新定义 |\setup!-listing!-hook|\pix^^|\setuplistinghook|，
你可以在读入文件前针对特别字体和\slash 或使用环境作额外定制。

\csdisplay
\def\listing#1{%
   \par \begingroup \@setuplisting \setuplistinghook
   \input #1 \endgroup}%
\let\setuplistinghook = \empty
\def\@setuplisting{%
   \uncatcodespecials
   \obeywhitespace \makeactive\` \makeactive\^^I
   \def^^L{\vfill\eject}\tt}%
{\makeactive\` \gdef`{\relax\lq}}% Defeat ligatures.
{\makeactive\^^I\gdef^^I{\hskip8\fontdimen2\tt \relax}}%
|


%\section Tables of contents
\section 目录

%\null ^^{table of contents}
%The macro \pix^|\writetocentry| writes a macro call to the file
%|\jobname.toc|.  The first argument of |\writetocentry|, e.g.,
%``chapter'', is used to compose the name of the called macro. The second
%argument is the text to appear in the table of contents entry.
%|\writetocentry| appends the page number to the macro call.  For
%example:
\null ^^{目录}
\pix^|\writetocentry| 宏在 |\jobname.toc| 文件中写入一个宏调用。
|\write!-tocentry| 的第一个参量，比如``chapter''，用于构成所调用的宏的名称。
第二个参量是显示在目录项中的文本。
|\writetocentry| 添加页码到宏调用中。例如：
\csdisplay
\writetocentry{chapter}{Introduction}
|
%\noindent
%will produce the line:
\noindent
将生成下列这行：
\csdisplay
\tocchapterentry{Introduction}{2}
|
%\noindent in the |.toc| file, indicating
%that `Introduction' started on page 2.
\noindent 到 |.toc| 文件中，表示 `Introduction' 从第2页开始。

%You can use |\writenumberedtocentry| to provide a third parameter, such
%as a chapter number. For example:
利用 |\writenumberedtocentry| 你还可以提供第三个参数，比如章编号。例如：
\csdisplay
\writenumberedtocentry{chapter}{The second chapter}{2}
|
%\noindent will write a line:
\noindent 将写入这一行：
\csdisplay
\tocchapterentry{The second chapter}{2}{14}
|
%\noindent
%You can also |\write| to |\tocfile| yourself.
\noindent
你也可以自己用 |\write| 命令写入 |\tocfile|。%
\footnote{译注：原文的代码中遗漏 |\writetocentry| 的定义，
而将 |\writenumberedtocentry| 的定义重复写了两遍，译文已修正此问题。
实际上，在本书所用的 |eplain.tex| 1.9 中，
|\writetocentry| 调用 |\writenumberedtocentry|，
并在后者中根据第三个参数是否非空写入不同的目录项。
但在 |\writenumberedtocentry| 的定义中有问题，
导致写入 |tocfile| 文件的目录项始终有三个参数。
在翻译时译者也同时修正 |eplain.tex| 中的这个错误。}

\csdisplay
\newwrite\tocfile \newif\iftocfileopened
\def\opentocfile{\iftocfileopened\else
      \tocfileopenedtrue
      \immediate\openout\tocfile = \jobname.toc
\fi}%
\def\writetocentry#1#2{\ifrewritetocfile
   \opentocfile
   \write\tocfile{%
      \expandafter\noexpand \csname toc#1entry\endcsname
      {#2}{\folio}}%
\ignorespaces\fi}%
\def\writenumberedtocentry#1#2#3{\ifrewritetocfile
   \opentocfile
   \write\tocfile{%
      \expandafter\noexpand \csname toc#1entry\endcsname
      {#2}{#3}{\folio}}%
\ignorespaces\fi}%
|

%To produce a table of contents, read the |.toc| file with
%\pix^|\readtocfile|. You should call |\read!-tocfile| before the first
%|\write!-toc!-entry|.  When you're processing the table of contents
%without regenerating it, you should not rewrite
%the |.toc| file---if you do, its contents will be lost.
%The command
%|\rewrite!-tocfile!-false| will prevent the rewrite.
要排印出目录，只需用 \pix^|\readtocfile| 读取 |.toc| 文件。
你应该在首次使用 |\write!-toc!-entry| 之前调用 |\read!-tocfile|。
在处理目录且不想重新生成它时，
务必不要改写 |.toc| 文件——如果你这样做，文件内容将会丢失。
命令 |\rewrite!-tocfile!-false| 将禁止这种改写。

\csdisplay
\newif\ifrewritetocfile \rewritetocfiletrue
\def\readtocfile{\testfileexistence{toc}%
   \iffileexists
      \input \jobname.toc
      \ifrewritetocfile \opentocfile \fi
   \fi}%
|

%Here are some definitions of possible |\toc|\dots|entry| macros.  These
%definitions are meant only as examples---running leaders across the line
%is usually not the best way to typeset a table of contents.
这里给出可能出现的 |\toc|\dots|entry| 宏的一些定义。
这些定义只是作为例子而已——在目录中使用指引线通常不是最好的做法。

\csdisplay
\def\tocchapterentry#1#2{\line{\bf #1 \dotfill\ #2}}%
\def\tocsectionentry#1#2{%
   \line{\quad\sl #1 \dotfill\ \rm #2}}%
\def\tocsubsectionentry#1#2{%
   \line{\qquad\rm #1 \dotfill\ #2}}%
|


%\section Cross-references
\section 交叉引用

%\null ^^{cross-references}
%\xrdef{xrefs}
%The macros that follow provide symbolic
%cross-referencing, so that you can refer to something in another part of
%a document by name instead of by its actual page number.
%\pix^|\xrdef||{foo}| defines a label |foo| to be the current page
%number, and \pix^|\xrefn||{foo}| produces that page number, e.g., $77$.
%More often you'll want to say something like ``see p.\thinspace77'', so
%\pix^|\xref||{foo}| produces ``p.\thinspace 77''.  If |foo| is not
%defined, a warning message will be given.  |\xrefwarningfalse|
%suppresses the warning.
\null ^^{交叉引用}
\xrdef{xrefs}
接下来的宏提供了符号式的交叉引用，
让你可以在文档中通过名称而非实际页码提及其它部分的东西。
\pix^|\xrdef||{foo}| 定义标签 |foo| 为当前页码，
而 \pix^|\xrefn||{foo}| 生成该页码，比如 $77$。
你更常用的是类似 ``see p.\thinspace77'' 的写法，
因此 \pix^|\xref||{foo}| 生成 ``p.\thinspace 77''。
如果 |foo| 未定义，就会得到一个警告信息。
|\xrefwarningfalse| 取消这种警告。

%These macros provide no protection against duplicate definitions.  You can
%check for duplicate definitions by sorting the cross-reference file and
%checking, either mechanically or by eye, for adjacent definitions of
%the same symbol.
这些宏没有提供对重复定义的保护。要检查重复定义，
你可以将交叉引用文件排序，并用工具或肉眼检查同一个符号的相邻定义。

\csdisplay
\newif\ifxrefwarning  \xrefwarningtrue
\def\xrdef#1{\begingroup
   \xrlabel{#1}%
   \edef\@wr{\@writexrdef{\the\@xrlabeltoks}}%
   \@wr
   \endgroup \ignorespaces}%
\def\@writexrdef#1{\write\reffile{%
     \string\gdef
        \expandafter\string\csname#1\endcsname
        {\noexpand\folio}\percentchar}}%
\def\xrefnumber#1{%
   \xrlabel{#1}%
   % \@xrlabeltoks now has the control sequence name.
   \toks0 =
      \expandafter{\csname\the\@xrlabeltoks\endcsname}%
   \expandafter \ifx\the\toks0\relax
      \ifxrefwarning \message{Undefined label
         `\tokstostring{#1}'.}\fi
      {\let\spacesub = \space
       \expandafter\xdef\the\toks0
          {`{\tt \tokstostring{#1}}'}}\fi
   \the\toks0}%
\def\xref#1{p.\thinspace\xrefnumber{#1}}%
\def\xrefn#1{\xrefnumber{#1}}%
|

%This macro turns a label into a list of character tokens in the token
%register |\labeltoks|.  A label can include blanks and control sequences in
%it as well as normal characters, but it can't include braces.
这个宏将一个标签转换为一列字符记号，并放在寄存器 |\labeltoks| 中。
除了普通字符之外，标签还可以包含空格和控制序列，但不能包含花括号。

\csdisplay
\newtoks\@xrlabeltoks
\def\xrlabel#1{\begingroup
      \escapechar = `\_ \edef\tts{\tokstostring{#1_}}%
      \global\@xrlabeltoks = \expandafter{\tts}%
   \endgroup}%
|

%It takes two passes to get the cross-references right, since the
%definitions are written out to the auxiliary file |\jobname.aux|.
%\pix^|\readreffile| reads them back in. If you don't issue this command
%before the first definition, you'll lose all the definitions from the
%previous run.
需要运行两遍才能得到正确的交叉引用，
因为这些定义是写出到辅助文件|\jobname.aux| 里的。
\pix^|\readreffile| 命令用于将它们读取回来。
如果你不在第一个定义之前执行此命令，你将丢失上一遍运行得到的定义。

\csdisplay
\newwrite\reffile \newif\ifreffileopened
\def\openreffile{\ifreffileopened\else
      \reffileopenedtrue
      \immediate\openout\reffile = \jobname.aux
   \fi}%
\def\readreffile{%
   \testfileexistence{aux}%
   \iffileexists
      \begingroup
         \@setletters
         \input \jobname.aux
      \endgroup
   \else
      \message{No cross-reference file; I won't give you
         warnings about undefined labels.}%
      \xrefwarningfalse
   \fi
   \openreffile}%
\def\@setletters{%
   \catcode`_ = \letter \catcode`+ = \letter
   \catcode`- = \letter \catcode`@ = \letter
   \catcode`0 = \letter \catcode`1 = \letter
   \catcode`2 = \letter \catcode`3 = \letter
   \catcode`4 = \letter \catcode`5 = \letter
   \catcode`6 = \letter \catcode`7 = \letter
   \catcode`8 = \letter \catcode`9 = \letter
   \catcode`( = \letter \catcode`) = \letter}%
|

%^^{equations, labelling}
%You can give symbolic names to equations in a similar way, using
%\pix^|\eqdef| and \pix^|\eqref|.  |\eqdef| inserts its own |\eqno|
%command, so it must be invoked in a place where |\eqno| is legal.
^^{公式编号}
按同样的方式，你可以用 \pix^|\eqdef| 和 \pix^|\eqref| 给出公式的符号式名称。
|\eqdef| 插入自己的 |\eqno| 命令，因此它必须用在能用 |\eqno| 的地方。

\csdisplay
\newcount\eqnumber
\def\eqdef#1{\global\advance\eqnumber by 1
   \expandafter\xdef
      \csname#1eqref\endcsname{\the\eqnumber}%
   \immediate\write\reffile{\string\def
      \expandafter\string\csname#1eqref\endcsname
         {\the\eqnumber}}%
   \eqno
   \eqprint{\the\eqnumber}}%
|

%|\eqref| produces ``(equation number)''.  You can handle fancier
%formatting by redefining \pix^|\eqprint|. For example, you could redefine
%it so that the equation numbers include
%the chapter number.
|\eqref| 生成``(公式编号)''。
通过重新定义 \pix^|\eqprint|，你可以实现更加复杂的格式。
举个例子，你可以重新定义它，使得公式编号中包含章编号。

\csdisplay
\def\eqref#1{%
   \expandafter \ifx \csname#1eqref\endcsname \relax
      \ifxrefwarning \message{Undefined equation label
         `#1'.}\fi
      \expandafter\def\csname#1eqref\endcsname{00}%
   \else \eqprint{\csname#1eqref\endcsname}%
   \fi}%
\def\eqprint#1{(#1)}%
|


%\section Environments
\section 环境

%\null ^^{environments} These macros let you define your own named groups
%(environments) for parts of your manuscript.  Like \TeX\ groups, these
%groups can be nested, and in fact their nesting can be intertwined with
%the nesting of \TeX\ groups.  If the names at the beginning and end of
%an environment don't match, you'll get an error message. The macros are
%designed so that the message you get when such an error occurs will give
%you a good chance of localizing the cause of the error easily.
\null ^^{环境}这些宏让你可以将手稿的一部分定义为命名编组（环境）。
类似 \TeX\ 的编组，这些编组也可以嵌套，
而且实际上它们的嵌套可以与 \TeX\ 编组的嵌套相互交织。
如果开始的名称和结束的名称不匹配，你将得到一个错误信息。
这些宏如此设计，使得当你得到这种错误信息时，你有机会方便地定位错误的来源。

%You begin an environment with
%\pix^^|\environment|
%|\envi!-ron!-ment| |{foo}| and end it with |\endenvironment{foo}|, where
%|foo| is the name of the environment.  Our macros slightly improve on
%the answer to Exercise~5.7 of \texbook, by doing some checks on
%|\begingroup| and |\endgroup| pairs, as well as making sure
%|\environment| and |\endenvironment| pairs match.
用 \pix^^|\environment||\envi!-ron!-ment| |{foo}| 开始一个环境，
用 |\endenvironment{foo}| 结束该环境，其中 |foo| 是该环境的名称。
我们的宏稍微改进了 \texbook\ 练习~5.7，
添加对 |\begingroup| 和 |\endgroup| 是否配对的检查，
并确保 |\environment| 和 |\endenvironment| 能够匹配。

\csdisplay
\def\environment#1{\ifx\@groupname\undefined\else
      \errhelp = \@unnamedendgrouphelp
      \errmessage{`\@groupname' was not closed by
         \string\endenvironment}\fi
   \def\@groupname{#1}%
   \begingroup
      \let\@groupname = \undefined \ignorespaces}%
\def\endenvironment#1{\endgroup
   \def\@thearg{#1}%
   \ifx\@groupname\@thearg
   \else
      \ifx\@groupname\undefined
         \errhelp = \@isolatedendenvironmenthelp
         \errmessage{Isolated
            \string\endenvironment\space for `#1'}%
      \else
         \errhelp = \@mismatchedenvironmenthelp
         \errmessage{Environment `#1' ended,
            but `\@groupname' started}%
         \endgroup % Probably a typo in the names.
      \fi
   \fi
   \let\@groupname = \undefined \ignorespaces}%
|

%We also define help messages for each of the errors above.
%^^|\newhelp|
你也可以给上述这些错误定义它们各自的帮助信息。
^^|\newhelp|

\csdisplay
\newhelp\@unnamedendgrouphelp{%
   Most likely, you just forgot an^^J%
   \string\endenvironment.
   Maybe you should try inserting another^^J%
   \string\endgroup to recover.}%
\newhelp\@isolatedendenvironmenthelp{%
   You ended an environment X, but^^J%
   no \string\environment\space to start it
   is anywhere in sight.^^J%
   You might also be at an
   \string\endenvironment\space that would match^^J%
   a \string\begingroup, i.e., you forgot an
   \string\endgroup.}%
\newhelp\@mismatchedenvironmenthelp{%
   You started an environment X, but^^J%
   you ended it with Y.  Maybe you made a typo
   in one or the other^^J%
   of the names.}%
|

%Some environments should not be allowed to occur within
%another environment.  Let's call these environments
%``outer environments''. |\check!-env| checks that no outer environment
%is currently in effect and complains if one is. To use |\check!-env|, you
%must issue the command |\environment!-true| at the beginning of every
%outer environment.
某些环境应当不允许出现在其他环境内部。我们称这些环境为``外部环境''。
|\check!-env| 检测是否不存在当前有效的外部环境，若存在的话给出警告信息。
要使用 |\checkenv|，你必须在每个外部环境开头处执行 |\environment!-true| 命令。

\csdisplay
\newif\ifenvironment
\def\checkenv{%
   \ifenvironment
      \errhelp = \@interwovenenvhelp
      \errmessage{Interwoven environments}%
      \endgroup
   \fi}%
\newhelp\@interwovenenvhelp{%
   Perhaps you forgot to end the previous^^J%
   environment? I'm finishing off the current group,^^J%
   hoping that will fix it.}%
|


%\section Justification
\section 对齐

%\bix^^{flush left}
%\bix^^{flush right}
%\bix^^{centered text}
%The three macros \pix^|\flushleft|, \pix^|\flushright|, and
%\xrdef{eplaincenter}
%\pix^|\center| justify  the text on the following lines in the indicated way.
%The command should appear on a line by itself.
%Both the command and the text should be enclosed in a group---%
%the end of the group indicates the end of the text.
%The entire group is set as a single paragraph, with lines filled out
%on one side or another as appropriate.  Blank lines are reproduced.
\bix^^{居左对齐}
\bix^^{居右对齐}
\bix^^{居中文本}
这三个宏 \pix^|\flushleft|、\pix^|\flushright| 和
\xrdef{eplaincenter}\pix^|\center| 将后面各行文本以指定方式对齐。
这种命令必须单独写在一行中。
命令和文本应该包含在一个编组中——编组的结束就表示文本的结束。
整个编组作为一个段落排版，各行视情况在一边或两边用空白填充。
空行依原样显示。

\csdisplay
\begingroup
   \catcode `\^^M = \active
   \globaldefs = 1 %
   \def\flushleft{\beforejustify %
      \aftergroup\@endflushleft %
      \def^^M{\null\hfil\break}%
      \def\@eateol^^M{}\@eateol}%
   \def\flushright{\beforejustify %
      \aftergroup\@endflushright %
      \def^^M{\break\null\hfil}%
      \def\@eateol^^M{\hfil\null}\@eateol}%
   \def\center {\beforejustify %
      \aftergroup\@endcenter %
      \def^^M{\hfil\break\null\hfil}%
      \def\@eateol^^M{\hfil\null}\@eateol}%
\endgroup
|

%The following commands are called as a result of the |\after!-group|
%^^|\aftergroup|
%in the definitions of |\flush!-left|, |\flush!-right|, and |\center|.
%They perform the necessary cleanup operations.
在 |\flush!-left|、|\flush!-right| 和 |\center| 的定义中，
下述这些命令出现在 ^|\aftergroup| 的后面；
它们在编组结束后被调用，以执行一些必需的清理工作。

\csdisplay
\def\@endflushleft{\unpenalty
   {\parfillskip = 0pt plus 1 fil\par}%
   \ignorespaces}%
\def\@endflushright{%
   % Remove the \hfil\null\break we just put on.
   \unskip \setbox0=\lastbox \unpenalty
   % We have fil glue at the left of the line;
   % \parfillskip shouldn't affect that.
   {\parfillskip = 0pt \par}\ignorespaces}%
\def\@endcenter{%
   % Remove the \hfil\null\break we just put on.
   \unskip \setbox0=\lastbox \unpenalty
   % We have fil glue at the left of the line;
   % \parfillskip must balance it.
   {\parfillskip = 0pt plus 1fil \par}\ignorespaces}%
\def\beforejustify{%
   \par\noindent
   \catcode`\^^M = \active
   \checkenv \environmenttrue}%
|

%\eix^^{flush left}
%\eix^^{flush right}
%\eix^^{centered text}
\eix^^{居左对齐}
\eix^^{居右对齐}
\eix^^{居中文本}


%\section Tables
\section 表格

%The \pix^|\makecolumns| macro enables you to give all the entries in a
%table without having to worry about where the columns break.  For
%example, if you're typing a long alphabetical list that will be
%formatted in several columns, you usually won't know in advance where
%one column ends and the next begins.  Moreover, if another item gets
%added, the column breaks will change.
\pix^|\makecolumns| 宏允许你给出表格的所有元素，而无需担心如何分栏。
例如，在输入一个冗长的按字母排序的名单，并分多栏显示时，
你通常不知道在何处结束一栏开始下一栏。
另外，如果添加了另一项，各栏的划分将会改变。

%|\makecolumns| takes two (delimited) arguments: the total number of
%entries in the table and the number of columns in the table.  For
%example, `|\makecolumns 37/3:|' specifies a three-column table whose
%entries are the next $37$ lines.  You can adjust the positioning of the
%table on the page by changing |\parindent|, which determines the space
%to the left, and |\hsize|, which determines the space from the left
%margin of the page to the right of the block.  You can allow a page
%break above the |\valign|
%^^|\valign//used in {\tt\\makecolumns}|
%by changing
%\pix^|\abovecolumnspenalty|.
|\makecolumns| 有两个（定界）参量：表格元素的总数以及表格的栏数。
例如，`|\makecolumns 37/3:|' 指定一个三栏表格，其元素为接下来的 $37$ 行。
要调整表格在页面上的位置，你可以修改 |\parindent| 以确定表格左侧的空白，
以及 |\hsize| 以确定从页面左边缘到表格右侧的距离。
要允许在 |\valign| ^^|\valign//用于 {\tt\\makecolumns}| 之前分页，
你可以修改 \pix^|\abovecolumnspenalty|。

\csdisplay
\newcount\abovecolumnspenalty
\abovecolumnspenalty = 10000
\newcount\@linestogo      % Lines remaining to process.
\newcount\@linestogoincolumn % Lines remaining in column.
\newcount\@columndepth    % Number of lines in a column.
\newdimen\@columnwidth    % Width of each column.
\newtoks\crtok  \crtok = {\cr}%
\def\makecolumns#1/#2: {\par \begingroup
   \@columndepth = #1 \advance\@columndepth by #2
   \advance\@columndepth by -1
   \divide \@columndepth by #2
   \@linestogoincolumn = \@columndepth \@linestogo = #1
   \def\@endcolumnactions{%
      \ifnum \@linestogo<2
         \the\crtok \egroup \endgroup \par
            % End \valign and \makecolumns.
      \else
         \global\advance\@linestogo by -1
         \ifnum\@linestogoincolumn<2
            \global\@linestogoincolumn = \@columndepth
            \the\crtok
         \else &\global\advance\@linestogoincolumn by -1
         \fi
      \fi}%
   \makeactive\^^M\letreturn\@endcolumnactions
   \@columnwidth = \hsize
   \advance\@columnwidth by -\parindent
   \divide\@columnwidth by #2
   \penalty\abovecolumnspenalty
   \noindent % It's not a paragraph (usually).
   \valign\bgroup
      &\hbox to \@columnwidth{\strut ##\hfil}\cr
}% The next end-of-line starts everything going.
|


%\section Footnotes
\section 脚注

%\null ^^{footnotes}
%Footnotes are most commonly typeset by using a raised number as the
%reference mark.  We define the \pix^|\numberedfootnote| macro to do
%this.  It also redefines ^|\vfootnote| to allow slightly more general
%formatting of footnotes than \plainTeX\ does. The dimension register
%\pix^^|\footnotemarkseparation| |\foot!-note!-mark!-sepa!-ra!-tion|
%controls the space between the footnote mark (e.g., the number) and the
%beginning of the text. The \pix^|\everyfootnote| tokens are inserted
%before producing the footnote.
\null ^^{脚注}
脚注通常用升高的数字作为参考符号。
我们定义的 \pix^|\numberedfootnote| 宏就是这样的。
它还重新定义了 ^|\vfootnote|，以支持比 \plainTeX\ 更一般的脚注格式。
尺寸寄存器 \pix^^|\footnotemarkseparation| |\foot!-note!-mark!-sepa!-ra!-tion|
控制脚注符号（比如数字）与脚注文本之间的间隔。
\pix^|\everyfootnote| 记号在每个脚注之前插入。

%The \plainTeX\ definitions of |\footnote| and |\vfootnote| are
%preserved in |\@plain!-footnote| and |\@plain!-vfootnote| in case you should
%need them.
\PlainTeX\ 中的 |\footnote| 和 |\vfootnote| 定义保留在
|\@plain!-footnote| 和 |\@plain!-vfootnote| 中，以备不时之需。

\csdisplay
\newcount\footnotenumber \newtoks\everyfootnote
\newdimen\footnotemarkseparation
\footnotemarkseparation = .5em
\let\@plainfootnote = \footnote
\let\@plainvfootnote = \vfootnote
\def\vfootnote#1{\insert\footins\bgroup
  \interlinepenalty\interfootnotelinepenalty
  \splittopskip\ht\strutbox \splitmaxdepth\dp\strutbox
  \floatingpenalty\@MM
  \leftskip\z@skip \rightskip\z@skip \spaceskip\z@skip
  \xspaceskip\z@skip
  \everypar = {}%
  \the\everyfootnote
  \indent\llap{#1\kern\footnotemarkseparation}\footstrut
  \futurelet\next\fo@t}%
\def\numberedfootnote{\global\advance\footnotenumber by 1
  \@plainfootnote{$^{\number\footnotenumber}$}}%
|


%\section Double columns
\section 双栏

%\null ^^{double columns} The |\doublecolumns| command begins
%double-column output, while the |\single!-column|
%\pix^^|\singlecolumn|
%command restores
%single-column output.  You can switch back and forth between them
%on a single page.
%The glue specified by |\above!-double!-column!-skip| and
%|\below!-double!-column!-skip| is inserted before and after the
%double-column material.
\null ^^{双栏} |\doublecolumns| 命令开始双栏输出，
而 |\single!-column| \pix^^|\singlecolumn| 命令恢复单栏输出。
两者可以在同个页面上相互切换。
用 |\above!-double!-column!-skip| 和 |\below!-double!-column!-skip|
指定的粘连插入到双栏素材的前面和后面。

%The approach is derived from \knuth{page~417}.
它们的实现方式来源于\knuth{第~417~页}。

\csdisplay
\newskip\abovedoublecolumnskip
\newskip\belowdoublecolumnskip
\abovedoublecolumnskip = \bigskipamount
\belowdoublecolumnskip = \bigskipamount
\newdimen\gutter            \gutter = 2pc
\newdimen\doublecolumnhsize \doublecolumnhsize = \hsize
\newbox\@partialpage        \newdimen\singlecolumnhsize
\newdimen\singlecolumnvsize \newtoks\previousoutput
\def\doublecolumns{\par % Don't start in horizontal mode.
   \previousoutput = \expandafter{\the\output}
   \advance\doublecolumnhsize by -\gutter
   \divide\doublecolumnhsize by 2
   \output = {\global\setbox\@partialpage =
         \vbox{\unvbox255\vskip\abovedoublecolumnskip}}%
   \pagegoal = \pagetotal \break % Expands \output above.
   \output = {\doublecolumnoutput}%
   \singlecolumnhsize = \hsize
   \singlecolumnvsize = \vsize
   \hsize = \doublecolumnhsize \vsize = 2\vsize}%
|

%The |\@double!-column!-split| macro does the actual splitting.
%Insertions are assumed to be single-column material.  If you don't want
%this to be the case, you'll have to modify the output routine.  After
%|\@double!-column!-split| has done its work, |\box255| will have the
%double-column material.  The double-column material will be preceded by
%any single-column material that was typeset before |\doublecolumns| was
%invoked. |\box4| will have the material that didn't fit on the page.
|\@double!-column!-split| 宏执行实际的分栏。插入项被当作单栏素材；
如果这不是你想要的，你需要修改输出例行程序。
在 |\@double!-column!-split| 完成之后，|\box255| 将包含双栏素材。
双栏素材前面是调用 |\doublecolumns| 前的单栏素材。
|\box4| 将包含无法放入该页面的素材。

\csdisplay
\def\@doublecolumnsplit{%
   \splittopskip = \topskip \splitmaxdepth = \maxdepth
   \dimen0 = \singlecolumnvsize
      \advance\dimen0 by -\ht\@partialpage
      \advance\dimen0 by -\ht\footins
      \advance\dimen0 by -\skip\footins
      \advance\dimen0 by -\ht\topins
   \begingroup
      \vbadness = 10000
      \global\setbox1=\vsplit255 to \dimen0 \wd1=\hsize
      \global\setbox3=\vsplit255 to \dimen0 \wd3=\hsize
   \endgroup
   \global\setbox4=\vbox{\unvbox255
      \penalty\outputpenalty}%
   \global\setbox255=\vbox{\unvbox\@partialpage
      \hbox to \singlecolumnhsize{\box1\hfil\box3}%
      \vfill}}%
|
%\needspace{.5in}
%|\double!-column!-out!-put| is the real output routine.  We call the old
%|\output| to do the work of actually shipping out the box.
\needspace{.5in}
|\double!-column!-out!-put| 是真正的输出例行程序。
我们调用 |\output| 执行实际的盒子送出工作。

\csdisplay
\def\doublecolumnoutput{\@doublecolumnsplit
   \hsize = \singlecolumnhsize \vsize = \singlecolumnvsize
   \previousoutput \unvbox4}%
|

%|\singlecolumn| resumes typesetting in one column.  It assumes that
%|\doublecolumns| has been called.
|\singlecolumn| 恢复单栏排版。它假定已调用了 |\doublecolumns|。

\csdisplay
\def\singlecolumn{\par % Don't start in horizontal mode.
   \output = {\global\setbox1 =
      \vbox{\unvbox255\vskip\abovedoublecolumnskip}}%
   \pagegoal = \pagetotal \break \setbox255 = \box1
   {\singlecolumnvsize = \ht255
      \divide\singlecolumnvsize by 2
      \advance\singlecolumnvsize by +\ht\@partialpage
      \advance\singlecolumnvsize by +\ht\footins
      \advance\singlecolumnvsize by +\skip\footins
      \advance\singlecolumnvsize by +\ht\topins
   \@doublecolumnsplit}%
   \hsize = \singlecolumnhsize
   \vsize = \singlecolumnvsize
   \output = \expandafter{\the\previousoutput}%
   \unvbox255}%
|

%\margin{`Sensible paragraph skips' section deleted}
\margin{`Sensible paragraph skips' section deleted}


%\section Finishing up
\section 收尾

%We now must undo the changes that we made when we started (see
%\xref{eplainconv}).  We also give a version identification, which is
%subsequently available in |\fmt!-name| and |\fmt!-version|.
现在我们必须撤销开始的修改（见\xref{eplainconv}）。
我们还给出版本标识，它可以在 |\fmt!-name| 和 |\fmt!-version| 中得到。

\csdisplay
\let\wlog = \@plainwlog \catcode`@ = \other
\def\fmtname{eplain}%
{\edef\plainversion{\fmtversion}%
 \xdef\fmtversion{1.0: 15 May 1990
   (and plain \plainversion)}%
}%
|

%\eix^^|eplain.tex|
\eix^^|eplain.tex|

\ifoldeplain\else\ifcompletebook\else
\vskip4em{\sectionfonts\leftline{本章索引}}
\readindexfile{i}
\fi\fi

\endchapter
\byebye
