「graphicx パッケージ + dvipdfmx ドライバの環境において、空白入りのファイル名をもつ画像ファイルを扱えるようにする」という課題について、少し真剣に考えてみる。最大の難点は「extractbb の自動実行」の部分であろうから、まずそれについて考えることにする。
さっそく結論…?
えーと、extractbb は空白文字入りファイル名に対応しているので
[コマンドプロンプトの場合] extractbb "to ra.png" [bashシェルの場合] extractbb 'to ra.png' extractbb "to ra.png" extractbb to\ ra.png
でオッケー。めでたしめでたし。
……じゃない。これを TeX の中で実行したいのである。それも制限付シェルエスケープ(restricted shell escape)有効の時に。
無制限なシェルエスケープな場合
TeX の外部コマンド実行は必ずシェルを経由して行われる。((恐らく system()
システムコールを単純に読んでいるんだと思う。))特に、シェルエスケープが無制限(-shell-escape 指定)である場合は、\write18
の引数の文字列*1がそのままシェルのコマンド行となる。だから、例えば Linux では“標準のシェル”が bash であるため、先に示した 3 つのコマンド行は全て shell escape で使用できる。((なお制御綴「\␣
」は展開不能だから脱トークン化した結果はそのまま「\␣
」という文字列になる。))
\immediate\write18{extractbb 'to ra.png'} \immediate\write18{extractbb "to ra.png"} \immediate\write18{extractbb to\ ra.png}
制限なシェルエスケープな場合
ところが、制限付(restricted)のシェルエスケープ有効(-shell-restricted 指定*2)の場合は、この仕様では都合が悪い。制限付シェルエスケープの目的は「特定のコマンドだけ実行を許可する」ことであり、このためにコマンド行の先頭の語(コマンド名)を検査している。しかし、“シェルのコマンド行の文法”が全て使用可能になってしまうと、それを利用して結局任意のコマンドが実行できてしまうのである。これでは“制限”の意味が成さなくなる。
% "そのまま"実行されると, touch も起動してしまう! \immediate\write18{extractbb 'to ra.png' & touch you_are_an_idiot}
このため、制限付モードの場合は、コマンド行をサニタイジング(無害化)してからシェルに渡す、という動作になっている。*3具体的なサニタイジングの手順は、環境により(シェルの種類により)異なるが、実効的には、制限付シェルエスケープ有効の場合のコマンド行の「仕様」は以下のようになる。要するに「特殊文字がほとんど無い低機能なシェル」であるかのように動作するのである。
- 空白文字、〈
'
〉、〈"
〉以外を“通常の文字”と呼ぶことにする。 - 原則は:
- コマンド行には普通の文字と空白文字のみを含むことができる。
- 行を空白(の列)で区切って“語”の列にする。先頭の語がコマンド名で、後続する語が引数となる。
- コマンド名は、予め定められた“許可リスト”(Kpathsea 変数の shell_escape_commands)に含まれるものでなければならない。
- ただし:
- 1 つ以上の語の列の両端を〈
"
〉で囲うことで、囲った部分(〈"
〉自体は含まない)を 1 つの引数と認識させることが可能。 - コマンド名は〈
"
〉で囲うことができない。 - 語の途中に〈
"
〉を入れることは不可。また、〈'
〉は全く使えない。
- 1 つ以上の語の列の両端を〈
この規則に従うと、例えば、次のコマンド行は〈'
〉を含むので不正と見なされ失敗する(何も実行されない)。
\immediate\write18{extractbb 'to ra.png' & touch you_are_an_idiot}
次のコマンド行は、もし extractbb が“許可リスト”に含まれているならば、extractbb コマンドが「to ra.png
」「&
」「touch
」「you_Are_an_idiot
」の 4 つの引数を伴って実行される。
\immediate\write18{extractbb "to ra.png" & touch you_are_an_idiot}
※なお、Linux の場合、実際のサニタイジングの結果は以下のようになる(はず)。
extractbb 'to ra.png' '&' 'touch' 'you_are_an_idiot'
さて、今やりたいことは、「extractbb を空白入りのファイル名を伴って起動する」ことであった。先に述べた規則によると、実はこれは容易であり、次のようにすればよいことが判る。
\immediate\write18{extractbb "to ra.png"}
なお、ここで指定したコマンド行は、無制限シェルエスケープ有効の場合でも、“標準のシェル”がコマンドプロンプトか sh か bash であればそのまま通用する。