マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

texmf.cnf の検索パスを理解したい話

……流石にそれはあり得ないだろうが、では texmf.cnf の検索対象のパスはどのように決まっているのだろう。チョット調べみた。

前提

調査対象は最新の TeX Live 2015(Windows)とする。現在の Kpathsearch では texmf.cnf は複数読込になっていて、実際に読み込まれた texmf.cnf のセットは次のコマンドで知ることができるはずである。

kpsewhich -all texmf.cnf

この調査では、この結果は正しいものと仮定する。

例えば、TeX Live 2015(Windows)の既定の状態では次のようになっている。

c:/texlive/2015/texmf.cnf
c:/texlive/2015/texmf-dist/web2c/texmf.cnf

この前者のファイルはユーザのカスタム設定用のもので既定では(実質的に)空になっている。後者が TeX Live の既定の設定が書かれた“既定の texmf.cnf”である。

予備実験

TeX Live の既定の状態で以下のコマンドを実行して、変数 TEXMFCNF の値を調べてみる。

kpsewhich -var-value=TEXMFCNF

結果は以下のようになる。(実際には改行無しで一行で出力される。)

{
C:/texlive/2015/bin/win32,
C:/texlive/2015/bin/win32/share/texmf-local/web2c,
C:/texlive/2015/bin/win32/share/texmf-dist/web2c,
C:/texlive/2015/bin/win32/share/texmf/web2c,
C:/texlive/2015/bin/win32/texmf-local/web2c,
C:/texlive/2015/bin/win32/texmf-dist/web2c,
C:/texlive/2015/bin/win32/texmf/web2c,
C:/texlive/2015/bin,
C:/texlive/2015/bin/share/texmf-local/web2c,
C:/texlive/2015/bin/share/texmf-dist/web2c,
C:/texlive/2015/bin/share/texmf/web2c,
C:/texlive/2015/bin/texmf-local/web2c,
C:/texlive/2015/bin/texmf-dist/web2c,
C:/texlive/2015/bin/texmf/web2c,
C:/texlive/texmf-local/web2c,
C:/texlive/2015,
C:/texlive/2015/share/texmf-local/web2c,
C:/texlive/2015/share/texmf-dist/web2c,
C:/texlive/2015/share/texmf/web2c,
C:/texlive/2015/texmf-local/web2c,
C:/texlive/2015/texmf-dist/web2c,
C:/texlive/2015/texmf/web2c
}

“既定の texmf.cnf”(C:/texlive/2015/texmf-dist/web2c/texmf.cnf)を見ると、変数 TEXMFCNF の値が次のように書かれていて、これは上の結果と一致している。

TEXMFCNF = {\
$SELFAUTOLOC,\
$SELFAUTOLOC/share/texmf-local/web2c,\
$SELFAUTOLOC/share/texmf-dist/web2c,\
$SELFAUTOLOC/share/texmf/web2c,\
$SELFAUTOLOC/texmf-local/web2c,\
$SELFAUTOLOC/texmf-dist/web2c,\
$SELFAUTOLOC/texmf/web2c,\
\
$SELFAUTODIR,\
$SELFAUTODIR/share/texmf-local/web2c,\
$SELFAUTODIR/share/texmf-dist/web2c,\
$SELFAUTODIR/share/texmf/web2c,\
$SELFAUTODIR/texmf-local/web2c,\
$SELFAUTODIR/texmf-dist/web2c,\
$SELFAUTODIR/texmf/web2c,\
\
$SELFAUTOGRANDPARENT/texmf-local/web2c,\
$SELFAUTOPARENT,\
\
$SELFAUTOPARENT/share/texmf-local/web2c,\
$SELFAUTOPARENT/share/texmf-dist/web2c,\
$SELFAUTOPARENT/share/texmf/web2c,\
$SELFAUTOPARENT/texmf-local/web2c,\
$SELFAUTOPARENT/texmf-dist/web2c,\
$SELFAUTOPARENT/texmf/web2c\
}

ここで、$SELFAUTOLOC は実行ファイル(今の場合は kpsewhich.exe)があるディレクトリであり、$SELFAUTODIR・$SELFAUTOPARENT・$SELFAUTOGRANDPARENT は $SELFAUTOLOC の 1・2・3 階層上のディレクトリである。これらの変数の値は暗黙的に決められる。

さて、既に述べたように、この“既定の texmf.cnf”の TEXMFCNF の内容は実際には(少なくとも texmf.cnf を探索する目的では)参照されていないことが予想される。

一方で、環境変数の TEXMFCNF は実際に参照されている。例えば、以下の記事にある手順はその性質を利用している。

  • 管理者権限(sudo)無しで extractbb の自動起動を有効化する (Qiita/zr-tex8r)
  • 実験①
    • 以下のパスに「foo=1」とだけ書いたファイルを置く。
      • C:/tmp/aa/texmf.cnf
      • C:/tmp/bb/texmf.cnf
      • C:/tmp/cc/texmf.cnf
    • “既定の texmf.cnf”の TEXMFCNF のリストの先頭に C:/tmp/aa を追加する。
      TEXMFCNF = {\
      C:/tmp/aa,\
      $SELFAUTOLOC,\
      ……(略)
    • この状態で変数 TEXMFCNF の値を調べる。
      kpsewhich -var-value=TEXMFCNF
      結果は以下のようになり、C:/tmp/aa がきちんと追加されている。
      {
      C:/tmp/aa,
      C:/texlive/2015/bin/win32,
      ……(略)
    • さらに、「実際に読まれる texmf.cnf」を調べてみる。
      kpsewhich -all texmf.cnf
      結果は次のようになる。
      c:/texlive/2015/texmf.cnf
      c:/texlive/2015/texmf-dist/web2c/texmf.cnf
    • すなわち、“既定の texmf.cnf”の中の TEXMFCNF への変更は、変数 TEXMFCNF の値としては有効だが、実際の texmf.cnf の検索パスとしては無効であると推測される。
    • 次に、環境変数 TEXMFCNF を以下のように設定する。
      set TEXMFCNF=C:/tmp/bb;C:/tmp/cc
    • この状態で TEXMFCNF の値を調べる(-var-value)と以下のようになる。
      C:/tmp/bb;C:/tmp/cc
      さらに、「実際の texmf.cnf」(-all)を調べる。
      c:/tmp/bb/texmf.cnf
      c:/tmp/cc/texmf.cnf
    • つまり、環境変数での TEXMFCNF の変更は、texmf.cnf の探索パスとして有効であるようだ。
    • 次に、環境変数 TEXMFCNF を以下のように設定する。
      set TEXMFCNF=C:/tmp/bb;;C:/tmp/cc
      なお、おの値の中の 2 つ連続する「;;」は、この間に「上位ファイルで指定された値」、つまり“既定の texmf.cnf”の TEXMFCNF の値が割り込むことを意味する。
    • この状態で TEXMFCNF の値を調べる。
      C:/tmp/bb;;C:/tmp/cc
      どうやら、「上位割り込み」は(少なくとも kpsewhich で示される((-expand-var を用いても同様の結果となる。)))TEXMFCNF の値としては反映されないようだ。
      「実際の texmf.cnf」を調べる。
      c:/tmp/bb/texmf.cnf
      c:/texlive/2015/texmf.cnf
      c:/texlive/2015/texmf-dist/web2c/texmf.cnf
      c:/tmp/cc/texmf.cnf
    • この場合でも、環境変数の TEXMFCNF は検索パスとして有効で、ファイルに書いた TEXMFCNF は無効であるようだ。

    次の実験に移る前に、環境を復元しておく。

    実験②
    • 次のような内容のファイルを作る。
      • C:/tmp/aa/texmf.cnf、内容は「TEXMFCNF=C:/tmp/bb;
      • C:/tmp/bb/texmf.cnf、内容は「TEXMFCNF=C:/tmp/dd;
      • C:/tmp/cc/texmf.cnf、内容は「TEXMFCNF=C:/tmp/dd;
      • C:/tmp/dd/texmf.cnf、内容は「TEXMFCNF=C:/tmp/dd;
      先と同様に、末尾が「;」である場合は「上位ファイルで指定された値」が割り込む、という規則になっている。
    • “既定の texmf.cnf”の TEXMFCNF のリストの先頭に C:/tmp/cc を追加する。
      TEXMFCNF = {\
      C:/tmp/cc,\
      $SELFAUTOLOC,\
      ……(略)
    • 環境変数 TEXMFCNF の内容を「C:/tmp/aa;」とする。
    • この状態で、変数 TEXMFCNF の値を調べる。やはり割り込み部分は表示されない。
      C/tmp/aa;
      さらに、「実際の texmf.cnf」を調べる。結果は次の通り。
      c:/tmp/aa/texmf.cnf
      c:/texlive/2015/texmf.cnf
      c:/texlive/2015/texmf-dist/web2c/texmf.cnf
    • 環境変数 TEXMFCNF を削除する。
    • C:/texlive/2015/texmf.cnf に以下の行を書き加える。
      TEXMFCNF=C:/tmp/bb;C:/tmp/cc
    • 変数 TEXMFCNF の値を調べる。*1
      C:/tmp/bb;C:/tmp/cc
      「実際の texmf.cnf」を調べる。先の TEXMFCNF の値と全く食い違う結果になっている。
      c:/texlive/2015/texmf.cnf
      c:/texlive/2015/texmf-dist/web2c/texmf.cnf
    まとめ

    以下のようであると推測される。

    • texmf.cnf ファイルに書いた TEXMFCNF は実際の texmf.cnf の検索パスとしては無効。
    • 一方、環境変数の TEXMFCNF は実際の texmf.cnf の検索パスとして有効。
    *  *  *

    [2015-09-19 追記] おっと、一番大事なことを書いてなかった……

    アレはどこから来た?

    「実際の検索パスとしての TEXMFCNF」は texmf.cnf では指定できない、ということを述べたが、ではソレはどうやって決まっているのか? つまり、「実際に読まれる texmf.cnf」は既定では

    c:/texlive/2015/texmf.cnf
    c:/texlive/2015/texmf-dist/web2c/texmf.cnf

    となるのは何故か?

    その答えはこの記事へのコメントで kakuto さんが答えて下さっている。要するに、

    決め打ち

    なのである。ただしその「決め打ちの値」は TeX 環境によって異なる。そして、TeX Live でのその値は何かというと、実は、先に示した「“既定の texmf.cnf”に書かれた TEXMFCNF の値」({$SELFAUTOLOC,……})と等しい。要するに、Kpathsearch は実際に「書かれた値」を参照しているのではないが、混乱が起こらないように、「正しい値を“既定の texmf.cnf”に書いておく」という“約束”になっていると考えられる。つまり、どこかの texmf.cnf において TEXMFCNF の値が変更・追加されることが無い限りには、kpsewhich が(-var-value 等で)示した TEXMFCNF の値は“正しい”のである。

    改めてまとめ

    「実際の検索パスとしての TEXMFCNF」は以下のように決まる。前にあるものほど優先される。

    1. 環境変数 TEXMFCNF の値。
    2. “決め打ち”した既定値。

    比較のために示すと、一般の Kpathsearch 変数(HOGEHOGE とする)の値は以下のように決まる。「kpsewhich で表示される TEXMFCNF」もこの手順に従う。

    1. 環境変数 HOGEHOGE の値。
    2. 「実際に読まれる texmf.cnf」(複数あり優先度順に並べられる*2)に書かれた HOGEHOGE の値。
    3. “決め打ち”した HOGEHOGE の既定値。

    基本的に、(あらゆる)texmf.cnf にある TEXMFCNF の値は既定(最初にインストールした状態)から変更してはいけない。これが遵守されている限りにおいて、「実際の検索パスとしての TEXMFCNF」と「kpsewhich で表示される TEXMFCNF」は一致する。つまり、「TEXMFCNF」は“例外扱い”ではなくなる。

    *1:この値には「余分の ;」がないので「上位の値の割り込み」は本来起こらないはずである。

    *2:そして、このリストを決定しているのは「実際の検索パスとしての TEXMFCNF」である。