前回の記事では、主に相手がファイルの場合を扱った。今回は、 Windows のコンソールに対して MSVCRT の入出力関数を使う場合を考える。 続きを読む
月別アーカイブ: 2017年1月
C言語のワイド文字入出力 — MSVCRTの場合
C言語のワイド文字入出力
C言語にはワイド文字で入出力を行う関数が用意されている。
※ワイド文字:C言語のワイド文字 (wchar_t) とは、不憫な子である。Windows だと Unicode のコードポイント1個すら表せない16ビットだったり、Unix だとそもそも使われている気配があまりなかったり(もっぱら UTF-8 か UTF-16 が使われている印象がある)する。2010年代の後半にもなってワイド文字なんぞを真面目に扱うブログというのは時代錯誤も甚だしい。
ワイド文字で入出力と言っても、対象がファイルの場合は最終的にはバイト列を読み書きしているわけで、どこかの段階でバイト列とワイド文字列との変換が行われているはずである。この変換方法はどうやって決まっているのか。あるいは、ワイド文字の入出力関数とバイト列 (char) の入出力関数を混在させるとどうなるのか。
また、Visual C++ や glibc の場合は fopen の第2引数に ,ccs=UTF-8
みたいな文字列を設定できるという謎の仕様がある。これを指定した場合は何が UTF-8 になるのか。
こういった細かい仕様は、ワイド文字自体がオワコンなこともあり、あまり知られていないように思う。少なくとも筆者は知らなかった。というわけで、
- ワイド文字による入出力について、C言語の規格ではどう定められているか
- Visual C++ のランタイムライブラリ (MSVCRT) ではどう実装されているか
- Linux 等で使われている glibc ではどう実装されているか
- macOS 等で使われている BSD libc ではどう実装されているか
LuaTeX における外部コマンド実行とログ出力
前置き
LuaTeX 以前の TeX 処理系では \write18 によって TeX 文書中から外部コマンドを実行できた。しかし、 LuaTeX では Lua の機能 (os.execute) によって外部コマンドの実行ができるため、現在の LuaTeX からは \write18 による外部コマンド実行機能は削除されている。
詳しくは以下のブログ記事を参照されたい:
新しい LuaTeX だって \write18 したい – マクロツイーター
対策 (2) :外部コマンドが実行されない → shellesc パッケージ!
要するに、 shellesc パッケージを使えば \write の定義が書き換えられて、従来の \immediate\write18 がそのまま動く。\ShellEscape, \DelayedShellEscape というコマンドも提供されている。(\immediate なしの \write18 相当のことをするには \write18 そのままではなく \DelayedShellEscape を使う必要がある)
また、pdftexcmds パッケージで提供されている \pdf@system コマンドを使うという手もある。
というわけで、一般の利用者および LaTeX パッケージを書く人にとっては、これらは既に済んだ話である。しかし、TeX の実行を補助するツールを作る人にとっては、話はまだ終わっていない。
本題:ログファイルへの出力
従来の \write18 によるコマンド実行の場合は、ログファイルに、実行したコマンドが
runsystem(ほにゃらら)...executed.
という感じで書き出されていた。このような実行ログは、 TeX の実行を補助するツールにおいて役に立つ場合がある。
しかし、LuaTeX における os.execute はログファイルに何も出力しない。これは困る。
LuaTeX で外部コマンド実行の際にログファイルにコマンドを吐き出すようにするには、
- \write18 の代替となるコマンドで、ログを出力するようにする
- LuaTeX の os.execute を乗っ取って、ログ出力も行うようにする
という手段が考えられる。(LuaTeX 本体の os.execute の定義を書き換えるのは、ハードルが高そうなので考えないことにする)
\write18 の代替となるコマンドでの対処
pdftexcmds パッケージの \pdf@system コマンドは、既に、実行時に
system(ほにゃらら) executed.
というメッセージを出力するようになっている。微妙に書式が違うが、まあ十分だろう。
一方で、 shellesc パッケージが提供する \write18 および \ShellEscape, \DelayedShellEscape コマンドは、実行するコマンドを書き出すという処理は特に行なっていない。
以前書いたような外部コマンドを実行する LaTeX パッケージのうち、 epstopdf パッケージは pdftexcmds パッケージの \pdf@system コマンドを使うので、ログファイルを調べることで実行されたコマンドを取得することができる。
epstopdf 以外の外部コマンドを実行する LaTeX パッケージは、基本的に「shellesc パッケージを読み込みつつ、従来通り \immediate\write18 を使用」していると思われるので、実行されたコマンド文字列をログファイルから取得する手段がない。
外部コマンドを実行するパッケージが、 shellesc パッケージに頼らずに直接 \directlua していた場合も同様である。
os.execute を乗っ取る
Lua コードによって os.execute の定義を置き換えてしまえば、 shellesc パッケージが使われていようが、直接 \directlua していようが、実行されるコマンド文字列を取得できる。
というわけで、 TeX 文書を処理する際に次のような TeX コードを実行しておけば、既存の TeX エンジンと同様にログファイルに出力してくれる:
\directlua{ local e=os.execute if e then local s=status.shell_escape or e() local function h(c,r,...) if c then texio.write_nl("log","runsystem("..c..")..."..(s==0 and"disabled"or(s==1 and"executed"or(r==nil and"disabled (restricted)"or"executed safely (allowed)")))..".\noexpand\n") end return r,... end function os.execute(c) return h(c,e(c)) end end }
(LuaTeX で外部コマンドを実行する関数は、 os.execute 以外にもいくつか存在するが、見なかったことにする。どうせ使われてないよね…?)
余談:os.execute の仕様
本家 Lua の os.execute の戻り値の仕様は、Lua 5.1 と Lua 5.2 で少し変化している。具体的には、 Lua 5.1 では C の system 関数の戻り値を整数としてそのまま返していたのが、 Lua 5.2 では、(POSIX の場合に)POSIX の仕様に沿って system 関数の戻り値を解釈するようになった。
LuaTeX はと言うと、ベースとなっている Lua のバージョンは 5.2 なのだが、 os.execute 関数の戻り値の仕様は Lua 5.1 当時のままである。
LuaTeX の os.execute 関数は本家 Lua のものではなく、独自に実装されたものなので、使用する Lua を 5.2 にアップグレードする際に変更し忘れたか、あるいは既存の Lua コードの互換性を考えてそのままにしたのだと思われる。
LuaTeX における os.execute 関数の実装は loslibext.c というファイルに入っている。