Acetaminophen’s diary

化学に関すること,TeXに関すること,ゆきだるまに関すること。

LaTeX パッケージのオプションには空白を含めないほうがよい話

この記事は、サブブログの 2016-09-17 投稿記事を移転してきたものです。

LaTeX team からいただいた情報によると、「パッケージ読込時に “,” 区切り周囲にスペースを入れると Option clash する例」が最近発覚したらしい。以下の例示ソースで再現可能*1

\documentclass{article}
\usepackage[a, b, c]{foo}
\usepackage[a ]{foo}
\stop
! LaTeX Error: Option clash for package foo.

同じパッケージが 2 回以上使われそうになった場合、本来ならば「2回目のオプションたちが1回目のオプションたちの部分集合であればスルー、もし増えていたら Option clash エラー」となるべきである。ところが、上記ソースでは空白がおかしな作用をして、部分集合になっているのに clash している。これも次回リリース(2017-01-01)で直すつもりらしい。

\def\@if@pti@ns#1#2{%
  \let\reserved@a\@firstoftwo
  \edef\reserved@b{\zap@space#2 \@empty}%       <- new
 \@for\reserved@b:=\reserved@b\do{%             <- changed
   \ifx\reserved@b\@empty
   \else
     \expandafter\in@\expandafter{\expandafter,\reserved@b,}{,#1,}%
     \ifin@
     \else
       \let\reserved@a\@secondoftwo
     \fi
   \fi
 }%
 \reserved@a}

ついついやってしまいがちな「空白」。特にオプションを多数指定する場合、コード上は改行して書くことも多いだろう:

\usepackage[dvipdfmx,%
 bookmarks=true,%
 bookmarksnumbered=true,%
 colorlinks=true,%
 setpagesize=false,%
 pdftitle={LaTeX研修課程},%
 pdfauthor={ななしのごんべぇ},%
 pdfsubject={hyperref入門・演習},%
 pdfkeywords={TeX; dvipdfmx; hyperref; color;}]{hyperref}

最後の % を書く習慣は付けておくと無難、ということであろう。

 

LaTeX パッケージ作者もオプションには空白を含めないほうがよい話

こんなタレコミも。

\begin{filecontents}{test3.sty}
\DeclareOption{foo}{\typeout{*** FOO OPTION ***}}
\DeclareOption{bar}{\typeout{*** BAR OPTION ***}}
\DeclareOption{baz}{\typeout{*** BAZ OPTION ***}}
\DeclareOption{qux}{\typeout{*** QUX OPTION ***}}
\ExecuteOptions{foo, bar}
\ProcessOptions\relax
\end{filecontents}

\documentclass{article}
\usepackage[baz, qux]{test3}
\begin{document}
a
\end{document}

パッケージが既定で foo と bar を実行し(パッケージ内で作者が \ExecuteOptions で指定)、さらにユーザが baz と qux を指定する(\usepackage のオプション)ので、全部で四つのメッセージが出そうである。ところが

*** FOO OPTION ***
*** BAZ OPTION ***
*** QUX OPTION ***

確かに、本来は出るはずの BAR が表示されなかった!「\usepackage はコンマの周りに空白があってもよいのに、\ExecuteOptions はダメ」というトラップがあったようだ。この件を報告したところ、LaTeX team の方も「20年経って今頃なんでこんなに…?」と戸惑っていた…。

 

追記:対処されたようです (2016-10-04)

LaTeX2e public svn をみると、2016/10/02 付けで「\ExecuteOptions に空白があるとオプションを無視する話」と「\@if@pti@ns で空白があると Option clash する話」について対処された (r1227) 。いちばん難しい「\ProcessOptions の干渉」はまだで、バグ懸念で慎重になっているようだ(2018-12-01 現在も未対応の模様)。

*1:これは一般化した記述であるが、たとえば foo → graphicx、a → dvipdfmx、b → dvips、c → dvipsone 等と置き換えてみるとよいだろう。

存在するはずの LaTeX パッケージのオプションがなぜか未定義?

この記事は、サブブログの 2016-09-15 投稿記事を移転してきたものです。

早速ですが…

さて、問題です

以下の LaTeX ソースを処理してみる*1

\begin{filecontents}{test.sty}
\DeclareOption{TEST}{\typeout{*** TEST OPTION ***}}
\ProcessOptions
\AtEndOfPackage{\RequirePackage{xspace}} % !!!
\end{filecontents}
\documentclass{article}
\usepackage[TEST]{test}
\begin{document}
\end{document}

この !!! を付けた行で呼び出すパッケージによって、これがエラーになったりエラーにならなかったりする。エラーというのは

! LaTeX Error: Unknown option `TEST' for package `test'.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.8 \begin
          {document}
? 

というもの。LaTeX の required に含まれるパッケージ群 (graphics, tools) の中で、適当に試してみると、以下は OK なもの:

  • longtable
  • multicol
  • varioref
  • color
  • graphicx

以下は NG なもの:

  • alltt
  • array
  • enumerate
  • ftnright
  • hhline
  • somedefs
  • theorem
  • xspace

この違いはなんでしょう?

 

*1:簡略化のため filecontents 環境を使っているが、これは「環境内の命令を test.sty というファイルに保存して同じディレクトリに置く」というのと同じである。

続きを読む

新しい pLaTeX の話(2016年9月版)

今回も「コミュニティ版 pLaTeX」2016/09/03 版の紹介です。

正式なリリース告知は、TeX Forum (forum:2015) に出しました。

特に大きな変更は、脚注番号に関するものです。

f:id:acetaminophen:20160904200143p:plain

f:id:acetaminophen:20160904200130p:plain

過去3回分の記事はこちらから:

目次

 

続きを読む

「いまプリアンブルなのか、それとも本文中なのか」判定

この記事は、サブブログの 2016-08-11 投稿記事を移転してきたものです。

LaTeX パッケージを作るときに、「この命令がプリアンブルで実行されたら…、本文中で実行されたら…」のような分岐が必要な場合に役立つ判定法のメモ。なお、「プリアンブル」とは \documentclass … \begin{document} で挟まれた部分のこと*1

 

最初に「答え」

  1. プリアンブルより前の場合
    • これは「以下の 2. から 4. のいずれでもない場合」。
  2. プリアンブルの場合
    • \documentclass の定義=\@twoclasseserror」かつ「4. でない」。
  3. 本文中の場合
    • \documentclass の定義=\@notprerr」。
  4. プリアンブルの中で、特に \AtBeginDocument の中の場合
    • \AtBeginDocument の定義=\@firstofone」。

これで判定可能なことは、以下に掲げる latex.ltx のソースを読めばわかる(以下の行番号は LaTeX2e 2016/03/31 Patch level 3 の source2e.pdf に対応している)。

ltdefns.dtx

 43 \def\@preamblecmds{}
 44 \def\@onlypreamble#1{%
 45   \expandafter\gdef\expandafter\@preamblecmds\expandafter{%
 46        \@preamblecmds\do#1}}
 47 \@onlypreamble\@onlypreamble
 48 \@onlypreamble\@preamblecmds

ltfiles.dtx

 11 \def\document{\endgroup
    … 中略 …
 47   \let\AtBeginDocument\@firstofone
 48   \@begindocumenthook
    … 中略 …
 56   \gdef\do##1{\global\let ##1\@notprerr}%
 57   \@preamblecmds
    … 中略 …
 60   \ignorespaces}
 61 \@onlypreamble\document

ltclass.dtx

206 \def\documentclass{%
207   \let\documentclass\@twoclasseserror
208   \if@compatibility\else\let\usepackage\RequirePackage\fi
209   \@fileswithoptions\@clsextension}
210 \@onlypreamble\documentclass
385 \def\AtBeginDocument{\g@addto@macro\@begindocumenthook}
386 \def\AtEndDocument{\g@addto@macro\@enddocumenthook}
387 \@onlypreamble\AtBeginDocument

以下で簡単に説明しておく。

 

「プリアンブルより前」と「プリアンブル」の区別

ltclass.dtx の206-209行目の \documentclass の定義を読むと

一度 \documentclass が実行された(=プリアンブルに入った)時点で \documentclass\@twoclasseserror に再定義される

ことがわかる。したがって、素直にこれを利用してしまおう。ちなみに \@twoclasseserror とは LaTeX Error: Two \documentclass or \documentstyle commands. というエラーのこと。

 

「プリアンブル」と「本文中」の区別

ltclass.dtx の210行目が意味するのは

\begin{document} が実行された(=本文開始した)時点で \documentclass\@notprerr に再定義する

ということである(ちなみに \@notprerr とは LaTeX Error: Can be used only in preamble. というエラーのこと)。これだけでは難しいのでもう少し詳しくみていく。

まず、ltclass.dtx の210行目に登場する \@onlypremable は「引数にとった命令に \do を付けて \@preamblecmds という“リスト”に次々と追加していく」という命令である(ltdefns.dtx 44-46行目)。すぐ上の43行目をみるとわかるとおり、リストは「空」に初期化されていて、以降でたとえば \@onlypremable\ナントカ が施されると、“リスト”末尾に \do\ナントカ が追加される。この命令は LaTeX カーネルや各種パッケージで大量に登場する*2ので、最終的に \@preamblecmds\do\ホゲホゲ \do\フガフガ \do\ピヨピヨ … のような長大なリストになる。

出来上がったリスト \@preamblecmds は、\begin{document}(=実体は \document という命令)の一部として実行される(ltfiles.dtx 57行目)。この実行直前の56行目で \do が「引数にとった命令を \@notprerr へと再定義する」という意味に定義されるので、結果としてリストに追加されていたすべての \ナントカ が一気に \@notprerr に再定義されることになる。最初に述べたとおり、このリストの中には \documentclass も含まれているため、\documentclass がこのタイミングで \@notprerr に再定義されるのである。

 

特殊なケース:「\AtBeginDocument の中」の扱い

そもそも \AtBeginDocument とは「プリアンブル中に命令を書いておくが、その実行を \begin{document} する時点まで遅延させる」場合に用いる命令である。すなわち、実行のタイミングは “2. と 3. の境目” であり、ほとんどの場合は 2. に含めてしまって問題ない。しかし、特別扱いが必要なごく稀なケースが存在することは確かである。ここで役立つのが

\AtBeginDocument の中身が実行されるときには \AtBeginDocument\@firstofone に再定義されている

という事実である。

改めて \AtBeginDocument の定義を確認すると、引数にとったモノを \@begindocumenthook という“リスト”に追加していく命令であることがわかる(ltclass.dtx 385行目)。このリスト \@begindocumenthook\begin{document}(=\document)の一部としてようやく実行される(ltfiles.dtx 48行目)のだが、いったんこれを実行する段階に達したらもうこれ以上遅延させる必要はないことに気づくだろう。

そこで、たとえリストの中身(あるいはそれ以降)に\AtBeginDocument が使われていたとしても、もうこれ以上遅延せずその場で実行するのが妥当である。そこで、\@begindocumenthook の実行直前にあたる47行目で \AtBeginDocument\@firstofone に再定義されているのである。なお、ltclass.dtx の387行目により、\AtBeginDocument はすぐ後の ltfiles.dtx 57行目の時点で \@notprerr に再定義されてしまうので、\AtBeginDocument の意味が \@firstofone であるような範囲はごく限られていることも保障されている。

ここまでで見てきた判定は、例えば以前の記事で紹介した scsnowman パッケージ\makedocumentsnowman で使われている。

 

おまけ:LaTeX で書いてはいけないコード

コレは無限ループするので、書いてはダメ。考えてみれば当然である。

\documentclass{article}
\AtBeginDocument{\begin{document}}
\begin{document}
$ latex test
This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2017/dev) (preloaded format=latex)
 restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2016/03/31> patch level 3
Babel <3.9r> and hyphenation patterns for 83 language(s) loaded.
(/usr/local/texlive/2016dev/texmf-dist/tex/latex/base/article.cls
Document Class: article 2014/09/29 v1.4h Standard LaTeX document class
(/usr/local/texlive/2016dev/texmf-dist/tex/latex/base/size10.clo))
No file test.aux.
(./test.aux) (./test.aux) (./test.aux) (./test.aux) (./test.aux) (./test.aux)
… 中略 …
(./test.aux) (./test.aux) (./test.aux) (./test.aux) (./test.aux) (./test.aux)
! TeX capacity exceeded, sorry [input stack size=5000].
<write> 
        LaTeX Font Info: \space \space \space Checking defaults for OML/cmm/...
l.3 \begin{document}

test.aux を 4,992 回読み込んでいた…*3

*1:つまり \documentclass より前はプリアンブルではない。これは、\usepackage を \documentclass より前に書くと出てくるエラーのヘルプメッセージ \usepackage may only appear in the document preamble, i.e., between \documentclass and \begin{document}. が示唆している。なお、本記事に登場する \@onlypreamble という命令は、名前の上からは「(ある命令を)プリアンブル専用(にする)」だが、実際にはその命令をプリアンブルより前で使うこともできる。

*2:これは、プリアンブル以外で使っても何の意味を持たない命令や有害な命令だけでなく、あるいは使う頻度が格段に下がる命令を「エラー」に再定義することで、メモリ使用量を節約するという効果があるとされている。

*3:某 scsnowman パッケージでは、上記のコードで無限ループしないように ad hoc に対処している。

夏といえば、やっぱり「ゆきだるま」!

暑い日々が続いていますが、いかがお過ごしでしょうか。私は暑くて融けてしまいそうなのですが、今日は8月8日です。待ちに待ったナントカの日ですね!

というわけで、今日は、暑い日々にもめげずに必死で融けずにがんばっているゆきだるま☃を見て、涼むことにしましょう。

f:id:acetaminophen:20160808031038p:plain

☃ ⛄ ⛇ ☃ ⛄ ⛇ ☃ ⛄ ⛇ ☃ ⛄ ⛇ ☃ ⛄ ⛇ ☃ ⛄ ⛇ ☃ ⛄ ⛇ ☃

続きを読む