前の記事では「新しい LuaTeX で \write18 できない話」をした。新しい(0.85 版以降)LuaTeX で「\write18
でシェル実行を行う」という文法が廃止されたのにはそれなりの理由がある。
なぜ \write18 できないのか
それは、「出力ストリーム(\openout
)の個数を従来の 16 から 128 に増長した」ことである。つまり新しい LuaTeX では次のコードが通るようになる。
\openout18=hoge.txt
そうすると、これを実行した後に次のコードを実行するとどうなるか。
\immediate\write18{rm thesis-slide.tex}
当然、「rm thesis-slide.tex
」という文字列がファイルに hoge.txt に書き出されるべきであろう。18 が有効なストリーム番号である以上、18 を特別扱いできないのである。
LaTeX の \newwrite
ところで、これも既に述べたように、現在の LaTeX カーネルでは「必要な範囲で拡張エンジンの仕様に合わせる」という方針になっている。従って、LuaTeX で出力ストリームの個数が増えたのに応じて、LaTeX カーネルの \newwrite
命令の定義も変更されている。
\ifx\directlua\@undefined % LuaTeX以外 \def\newwrite {\e@alloc\write \chardef{\count17}\m@ne\sixt@@n} \else % LuaTeXの場合 \def\newwrite {\e@alloc\write {\ifnum\allocationnumber=18 \allocationnumber19\fi %(*) \global\chardef}% {\count17}% \m@ne {128}}% ←これが上限値 \fi
詳しい説明は省くが、「128」が出てきていることは判るだろう。さらに、(*)
の行をみると「18 をスキップしている」ということも推測できる。前の記事で述べたように、shellesc では「\write18
がこれまで通り動く」仕掛けを施しているため、18 が実際に使われると不都合である。それを回避しているのであろう。
\write16 がヤバい
これを見ると、“\write18
の問題”については対策が済んでいることがわかる。ところが、新しい LuaTeX の出力ストリームの仕様に関して、対策ができていない(ように見える)問題がある。それは \write16 である。
一般の TeX 言語者にとっては既知だと思われるが、TeX 言語において「端末に文字列を表示する」ためには \write16
を使うのが通例である。詳しくいうと、次の通りである。TeX 言語の仕様では、「“無効または非オープン”の非負の値の出力ストリーム」は端末(標準出力)とログの両方を対象としている。そして出力ストリームが 0〜15 しかない従来のエンジンでは 16 以上は常に無効である。従って、慣習的に 16 が「端末(とログ)への出力」として用いられているのである。
ところが新しい LuaTeX で 0〜127 が有効な番号になってしまうと、先程 \write18
についての議論と同様に、\write16
が「端末に出力される」ことも保証されない。といっても、非オープンのストリームは結局端末として扱われるので、番号 16 が実際にオープンされるまでは従来の動作と変わらない。しかし、先の LaTeX の \newwrite
のコードでは 18 はスキップしているのに 16 はスキップしていない。だから、結局 LuaLaTeX では \write16
が「端末に出力される」ことは保証されないのである。
LaTeX 自体は問題なし、だけど…
果たしてこれらの事実はどう解釈すればいいのだろうか? LaTeX で端末に出力したい場合に \write16
を使うのはマチガイなのだろうか?
これを考えるにあたってまず気になるのが「LaTeX カーネル自体はどうしているのか」である。LaTeX のカーネルは LaTeX での文書コンパイル中に端末にメッセージを表示していて、それには当然 \write16
を使っているはず……。
LaTeX で「端末に表示」するためのユーザ命令 \typeout
の定義を調べてみよう。((これは通常の「文書コンパイル時の \typeout
の定義」である。フォーマット作成中は別の定義が使われる場合もある。))
\def\typeout#1{\begingroup\set@display@protect \immediate\write\@unused{#1}\endgroup}
この他、警告を出すための \@latex@warning
や \@latex@warning@no@line
は処理を \GenericWarning
に移譲しているが、それの定義は次のようである。
\DeclareRobustCommand{\GenericWarning}[2]{% \begingroup \def\MessageBreak{^^J#1}% \set@display@protect \immediate\write\@unused{^^J#2\on@line.^^J}% \endgroup }
これらを見ると、出力先のストリーム番号は「16」ではなく「\@unused
」となっている。そしてこの番号は普通に \newwrite
で割り当てられている。
\newwrite\@unused
\@unused
という名前が示すように、この番号(具体的な値は 0 である)のストリームに対しては(少なくとも LaTeX カーネルでは)決してオープンをしていない。そのため端末に出るという仕組である。要するに、LaTeX が行っている端末出力は \write16
ではなく \write\@unused
である((なお、LaTeX のフォーマット作成時(まだ\newwrite
を定義する前の段階)では端末出力のための番号には 16、および(何故か)17 が使われている。))ため、たとえ 16 が有効な番号になっても影響を受けないのである。
つまり、\newwrite
が 16 をスキップしないのは、“LaTeX としては”正当化できる。LaTeX で端末に出力したい場合は、\typeout
などの LaTeX が用意した命令を使うか、そうしないなら、\write16
ではなく \write\@unused
としなければいけなかった、ということになる。
……とはいっても、TeX 言語では「端末出力は \write16
」が常識であるし、LaTeX でもそうするのが寧ろ普通であっただろう。現実に、TeX Live にあるパッケージでも \write16
を使っている例は多数見つかる。
\toks0\expandafter{\@temp@write@buffer}% \immediate\write16{\the\toks0}% \let\@temp@write@buffer\@save@write@buffer}
果たして、TeX on LaTeX な開発者は、\write18
だけでなくて \write16
も修正しなければいけないのだろうか……?