Node:Preliminaries, Next:, Previous:Top, Up:Top

序論

この章ではGNU m4とは何か、その由来、 このマニュアルの読み方と使い方、m4を起動する方法、 バグを報告する方法などを説明します。 マニュアルの続きを読むにあたっての助言をもってこの章は終ります。

ここから後の章ではm4言語のすべての機能を詳しく説明します。


Node:Intro, Next:, Previous:Preliminaries, Up:Preliminaries

m4序説

マクロを展開しながら入力を出力へコピーするという意味で、 m4はマクロプロセッサだと言えます。 マクロにはm4に最初から組み込まれている組み込み(builtin)マクロと、 ユーザが自分で定義するユーザ定義(user-defined)マクロの2種類があります。 マクロは何個でも引数を取ることができます。 m4には単なるマクロの展開機能に加え、ファイルのインクルード、 UNIXコマンドの実行、整数演算、さまざまな方法でのテキスト操作、再帰、 その他のための関数がそろっています。 m4はコンパイラのフロントエンドとして、 またマクロプロセッサそのものとしても使うことができます。

m4マクロプロセッサは、ほとんどすべてのUNIXで利用することができます。 通常その存在に気づいているのは、ほんのわずかな人たちだけです。 しかし実際に気づいた人たちは往々にして熱心なユーザとなります。 GNU Autoconfでconfigureスクリプトを生成するには GNU m4が必要なため、GNU Autoconfの人気が高まったのがきっかけとなって GNU m4をインストールする人が増えました。 もっとも、そういう人が自分でm4のプログラミングをすることは無いでしょう。 GNU m4はわずかな違いを除けばSystem V, Release 3 版 とほぼ互換性があります。 詳細はSee Compatibilityを参照してください。

ユーザの中にはm4中毒になってしまった人たちもいます。 そういう人たちは最初は簡単なことにm4を使い、 徐々に複雑なm4マクロの書き方を習得しながら、 大きなこと大きなことへと挑戦していくのです。 いちど病みつきになってしまえば、簡単な問題を解くためにさえ 洗練されたm4アプリケーションを書こうとして、実際の仕事よりも 自分のm4スクリプトのデバッグに多くの時間をさくことになるのです。 熱中しやすいプログラマーはm4で健康を損なうおそれがあるので 注意しましょう。


Node:History, Next:, Previous:Intro, Up:Preliminaries

m4の由来

ここに記されている歴史に関する覚え書きはきわめて不完全なものであり、 なんら権威あるものでもありません。 事情に通じている方は、どうかこの節をふさわしいものにするのを手伝ってください。

GPMm4の重要な祖先です。 C. Stratchey: "A General Purpose Macro generator", Computer Journal 8,3 (1965), pp. 225 ff を参照してください。 GPMは、David Gries classic "Compiler Construction for Digital Computers"でも簡潔に説明されています。

GPM純粋だったのに対して、 m4は実生活にまつわる本物の複雑さを扱うことを意図したものでした。 そのため、たとえばマクロは前もって宣言しなくても認識されるようになり、 改行文字などの空白(whitespace)をスキップするのは簡単になり、 多くの構成要素が借り物ではなく内蔵されるようになりました。

もともとm4はRational FORTRANプリプロセッサ すなわちratfor(cppに相当)のエンジンでした。


Node:Invoking m4, Next:, Previous:History, Up:Preliminaries

m4の起動(オプション一覧)

m4コマンドの形式は次のようになります。

m4 [option...] [macro-definitions...] [input-file...]

オプションは、短いオプション名を使うときは-で始め、 長いオプション名を使うときは--で始めます。 長いオプション名は、 そのオプションだと明確に特定できる先頭部分を書くだけで十分です。 m4は次のオプションを受けつけます。

--version
プログラムのバージョン番号を標準出力に出力し、 input-filesはいっさい読まずに、ただちにm4の実行を終了します。
--help
ヘルプ情報の要約を標準出力へ出力し、 input-filesはいっさい読まずに、ただちにm4の実行を終了します。
-G
--traditional
System V版と比べて、この実装で拡張された機能をすべて抑制します。 これらの一覧はSee Compatibilityを参照してください。
-E
--fatal-warnings
すべての警告(warning)を致命的なものと見なし、 最初の警告が発行された時点でm4の実行を停止し終了します。
-dflags
--debug=flags
デバッグ・レベルをflagsに設定します。 デバッグ・レベルはデバッグ用関数が表示する情報の形式と量の制御に使われます。 flagsの形式と意味についての詳細はSee Debug Levelsを参照してください。
-lnum
--arglength=num
マクロをトレースすることによって生じる出力の量を制限します。 詳細はSee Debug Levelsを参照してください。
-ofile
--error-output=file
デバッグやトレースの出力を名前が指定されたファイルへ リダイレクト(redirect)します。 エラーメッセージは通常どおり標準エラー出力へ出力します。 詳細はSee Debug Outputを参照してください。
-Idir
--include=dir
インクルード指定されたファイルが現在の作業ディレクトリ(the current working directory)で見つからない場合に、m4dirを探すようにします。 詳細はSee Search Pathを参照してください。
-e
--interactive
起動時にm4を対話的な状態(interactive)にします。 これは、すべての出力をバッファリング無しで行ない、 割り込み(interrupt)を無視するということを意味します。
-s
--synclines
Cプリプロセッサやそれに類するツールで使うための同期情報を含む行を生成します。 これはm4をコンパイラのフロントエンドとして使うときなどに便利です。 ソースファイル名と行番号の情報は、#line linenum "filename"という形式の指令(directive)によって表され、 出力の途中へ必要に応じて挿入されます。 この指令(directive)は次の行が入力ファイルfilenameの第linenum行 そのものであるか、もしくはその行の展開によるものであることを意味します。 "filename"の部分は、ファイル名が前の指令(directive)と 変わらない場合はしばしば省かれます。

このような同期指令は、必ずそれ自身で完全な一行に対してのみ与えられます。 ある出力行の途中に同期情報の食い違いがあるときは、 対応する同期指令の出力は次の行へと持ち越されます。

-P
--prefix-builtins
m4の内部に登録されているすべての組み込みマクロの名前を 接頭辞m4_が付いたものに変更します。 このオプションを使ったときには、たとえばdefineの代わりに m4_define__file__の代わりにm4___file__と書かなければ なりません。
-WREGEXP
--word-regexp=REGEXP
マクロ名の字句構成規則を指定します。 このオプションは試験的なものであり、 このオプションを含まないGNU m4の実装も存在する可能性があります。 (see Changeword)
-Hn
--hashsize=n
シンボルを表引きするための内部ハッシュテーブルの項目数をnにします。 この数は素数にするべきです。デフォルトは509項目です。 極端に多くのマクロを定義しないかぎり、この値を増やす必要は無いはずです。
-Ln
--nesting-limit=n
入れ子になったマクロの呼び出しをn段階に制限し、 この制限を超えたときは、プログラムの実行を停止します。 指定がない状態では、入れ子は250段階に制限されています。

このオプションの正確な効力は、動的な再帰構造というよりは、 テキスト上での入れ子構造に対して発揮されるものだと理解したほうが より正しいでしょう。 このオプションは、機械的な方法でm4への複雑な入力を生成したときに 役に立ったという例もありますが、大部分のユーザにとっては無用の長物でしょう。 目障りだということになったときは、 このオプションは(まだ試験段階です)削除されてしまうかも知れません。

このオプションを使っても、再走査(rescanning)による無限ループを抜け出す ことはできません。その一方で、再走査ループは必ずしもメモリや スタック領域を大量に消費するわけではありません。 再走査ループをうまく使えば、複雑で時間のかかる処理をm4に やらせることができます。 この領域に制限をもうけるのは、m4の能力を弱めてしまうことになるでしょう。 異常な使い方の例はいくらでもあります。 define(`a', `a')aはもっとも単純なものの例です (しかしsee Compatibility)。 GNU m4がこのようなケースを検出するのを期待するのは、 コンパイラシステムが無限ループを検出し、診断メッセージをだすのを 期待することに似ています。つまり、決定不能ではないとしても、 一般にとてもハードな問題です。

-Q
--quiet
--silent
マクロの呼び出しで、引数が不足していたり余分にあるときの警告を抑制します。
-B
-S
-T
これらのオプションはSystem V版m4との互換性のために存在しますが、 この実装では何の効果もありません。
-Nn
--diversions=n
これらのオプションはGNU m4の以前のバージョンとの互換性のためだけに 存在し、同時に使うことができる出力切替え先(diversion)の数を制御するために 使われていました。現在は固定された制限値がなくなったので、 何の働きもしません。

-D-Uオプションを使うと、コマンドライン上でマクロを定義したり 削除したりすることができます。これらは次の形式をとります。

-Dname
-Dname=value
--define=name
--define=name=value
どの入力ファイルを読むよりも前に、nameをシンボルテーブルに 登録します。=valueが省略されたときは、値は空文字列として 解釈します。valueはどんな文字列でもよく、したがって、マクロは入力内で 定義するときと同じように引数を取るものとして定義することもできます。
-Uname
--undefine=name
nameのすでに定義された意味を削除します。 当然、この方法で削除できるのは定義済みのマクロだけです。
-tname
--trace=name
nameを未定義として、しかしトレースが行われるように、シンボルテーブルへ 登録します。 その結果、このマクロは定義された時点からトレースされるようになります。
-Ffile
--freeze-state file
実行が終了すると、凍結状態(frozen state)の内容を指定されたfile (see Frozen files)へ書き出します。
-Rfile
--reload-state file
実行の前に、指定された凍結ファイルfile (see Frozen files)から 内部状態を復元します。

コマンドライン上の残りの引数は、入力ファイルの名前として解釈します。 ファイル名の指定がないときは、標準入力から読み込みます。 ファイル名-は、標準入力を意味するものとして解釈します。

入力ファイルは、指定された順番に読み込みます。 標準入力は1度しか読むことができませんので、ファイル名-は コマンドライン上で1度しか使えません。


Node:Bugs, Next:, Previous:Invoking m4, Up:Preliminaries

問題やバグへの対処方法

GNU m4に関して問題が起こったり、バグと思われることを見つけたときは、 どうか、それを報告してください。 バグを報告する前に、実際にそれが本物のバグなのか確かめてください。 注意深くドキュメントを読み直して、あなたのしようとしたことが 実際にできると本当に書いてあるか確かめてください。 もし、ある事ができるのかどうか明確でないときは、それも報告してください。 それはドキュメントのバグです!

バグを報告したり、自分で直そうとする前に、 そのバグを発現させる、できるだけ小さな入力ファイルを作れないか試してください。 作ることができたときは、その入力ファイルとm4が出す正確な結果を 私たちに送ってください。 また、あなたはどうなるはずだと思ったのかも添えてください。 これは、問題の原因が実はドキュメントにあるものなのか判断するのに役立ちます。

問題を正確に把握したら、電子メールを(インターネット) bug-m4@gnu.orgまたは(UUCP) mit-eddie!prep.ai.mit.edu!bug-gnu-utilsへ送ってください。 あなたが使っているm4のバージョン番号も書いてください、 コマンドm4 --versionで調べられます。

バグの報告以外の提案もいつでも歓迎します。 ドキュメントの不明確なところや分かりにくい機能についての質問があれば、 それも送ってください。


Node:Manual, Previous:Bugs, Up:Preliminaries

このマニュアルの読み方

このマニュアルにはm4の入力と出力の例がたくさん含まれており、 入力、出力およびm4からのエラーメッセージを区別するための 簡単な表記法が用いられています。 これらの例は通常の文章とは間隔をあけて、 固定幅フォントで次のように表示されます。

This is an example of an example!

入力と出力を区別するため、m4からの出力にはすべて=>が、 エラーメッセージにはすべてerror-->が先頭につきます。 したがって次のようになります。

Example of input line (入力行の例)
=>Output line from m4 (m4からの出力行の例)
error-->and an error message (エラーメッセージの例)

m4に最初から定義されているマクロの説明では、 マクロの呼び出しのプロトタイプが、引数に分かりやすい名前をつけて示されます。 これは次のようになります。

regexp(string, regexp, opt replacement)

m4ではマクロの引数はすべて文字列ですが、 数字、ファイル名、正規表現としてなど特別な解釈のされかたをするものもあります。

3番目の引数の前にあるoptは、この引数が省略可能であることを 表しています。省略したときは、空文字列として解釈されます。 引数リストの最後にある省略記号(...)は、その後にいくつでも引数を 続けてよいことを示してます。


Node:Syntax, Next:, Previous:Preliminaries, Up:Top

字句・構文解析の規則

m4は入力を読み込むと、それをトークン(token)に分割します。 トークン(token)は名前(name)、クォートされた文字列(quoted string)、 それらの構成要素とならない個々の文字のどれか1つの種類に属します。 また、m4への入力にはコメントを含めることができます。


Node:Names, Next:, Previous:Syntax, Up:Syntax

入力トークン: マクロ名

名前(name)はアルファベット、数字、_(アンダースコア) を自由に並べたもののうち、先頭の文字が数字でないものです。 名前にマクロの定義が存在するときは、マクロの呼び出しとして認識され、 展開の対象となります(see Macros)。

正しい名前(name)の例を挙げるとfoo, _tmp, name01などがあります。


Node:Quoted strings, Next:, Previous:Names, Up:Syntax

入力トークン: クォートされた文字列

クォートされた文字列(quoted string)は、 引用符`'に囲まれた文字列のうち、 文字列の内部で開始引用符`と終了引用符'の数が釣り合っているものです。 クォートされた文字列(quoted string)のトークンとしての値は、 いちばん外側にある引用符を一対だけ取った文字列です。 したがって、

`'

の値は空文字列です。そして、

``quoted''

の値は次の文字列になります。

`quoted'

引用符を表す文字は、組み込みマクロchangequoteを使って、 いつでも替えることができます。 詳細はSee Changequoteを参照してください。


Node:Other tokens, Next:, Previous:Quoted strings, Up:Syntax

入力トークン: その他

名前(name)とクォートされた文字列(quoted string)の構成要素にならない 文字はすべて、それ自身で一つのトークンとなります。


Node:Comments, Previous:Other tokens, Up:Syntax

コメント

m4では通常#と改行文字で区切られた部分がコメントとなります。 これらコメントデリミタ(comment delimiters)の間にあるすべての文字は 処理の対象とならず無視されます。 しかし、コメントデリミタ(comment delimiters)を含むコメント全体は、 出力へそのまま流されて行きます。 つまりm4においてコメントは破棄されません

コメントを入れ子にすることはできません。したがって、#の後の 最初の改行文字でコメントは終りとなります。 コメント開始文字をクォートすることによって、 コメント開始文字としての働きを抑制できます。

コメントデリミタ(comment delimiters)は組み込みマクロchangecomを 使って、いつでも好きな文字列に変更できます。 詳細はSee Changecomを参照してください。


Node:Macros, Next:, Previous:Syntax, Up:Top

マクロを呼び出す方法

この章ではマクロの呼び出し、マクロの引数、マクロの展開が行われる過程 について説明します。


Node:Invocation, Next:, Previous:Macros, Up:Macros

マクロの呼び出し

マクロを呼び出すときの形式には次のものがあります。

name

これは引数を伴わないマクロの呼び出しです。

name(arg1, arg2, ..., argn)

これはn個の引数を伴うマクロの呼び出しです。 マクロはいくつでも引数を取ることができます。 すべての引数は文字列ですが、 マクロによって引数の解釈のしかたが違うことがあります。

開きカッコ(は、 スペースを入れずにname直後に書かなければいけません。 そうしないと、そのマクロは引数なしで呼び出されてしまいます。

引数なしでマクロを呼び出すためには、カッコを付けてはいけません。 たとえば、

name()

これは、空の文字列を1つだけ引数として持つマクロの呼び出しであり、 引数を伴わないマクロの呼び出しではありません。


Node:Inhibiting Invocation, Next:, Previous:Invocation, Up:Macros

マクロの呼び出しを抑制する

先行するマクロプロセッサ(StratcheyのGPMなど)に比べて、 m4言語の革新的なところは、 先頭に特別な文字をつけて書くといったことをしなくても、マクロの呼び出しを識別できる能力です。 この機能は多くの場合において便利なのですが、 ときには不必要なマクロの呼び出しの原因となることがあります。 そこで、GNU m4には名前(name)がマクロの呼び出しとして認識されるのを 抑制するいくつかの機構やテクニックがあります。

まず、多くの組み込みマクロは引数なしで呼び出しても意味がないので、 それらの名前の直後に開きカッコがないときは、組み込みマクロは呼び出されません。 これによって、includeevalがマクロとして認識されてしまう といったよくあるケースに対処できます。 後ほど、この文書に出てくる "このマクロは引数が与えられたときだけ認識されます"という文は、 この動作を意味します。

また、コマンドオプション(--prefix-builtins, または-P) を使うと、組み込みマクロを呼び出すときは、 その名前の先頭にm4_をつけなければ認識されなくなります。 たとえばm4_dnlや、さらにはm4_m4exitと 書かなければならなくなります。 ちなみに、このオプションはユーザ定義のマクロには何の効果ももちません。

changeword機能がコンパイル時に組み込まれたm4を 使用しているときは、マクロ名の認識に使われる字句構成規則をはるかに柔軟に 指定することができます。 この規則は組み込みマクロとユーザ定義マクロ両方の名前に作用します。 この試験的な機能の詳細はSee Changewordを参照してください。

もちろん、ある名前がマクロの呼び出しとして認識されるのを防ぐ、 もっとも単純な方法は、その名前をクォートする(引用符で囲む)ことです。 この節の残り部分では、クォートすることがマクロの呼び出しにどのように 影響するのか、またマクロの呼び出しを抑制するには それをどのように使えよいのかを、もうすこし詳しく見ていきます。

マクロの呼び出しを抑制したいときは名前全体をクォートするのが普通ですが、 名前の数文字をクォートするだけでも同じ効果があります。 また、空文字列をクォートするだけでもよいのですが、 この場合は名前の内部でないと効果はありません。たとえば、

`divert'
`d'ivert
di`ver't
div`'ert

これらの結果はすべて文字列divertとなりますが、

`'divert
divert`'

こちらは両方とも組み込みマクロdivertが呼ばれます。

マクロを評価して生じた出力は常に再走査(rescan)されます。 次の例では、m4substr(abcde, 3, 2)を入力として 与えたときと同様に、文字列deが生成されます。

define(`x', `substr(ab')
define(`y', `cde, 3, 2)')
x`'y

クォートされた文字列(quoted string)の両端にあるクォートされていない文字列は、 マクロ名として認識される対象となります。 次の例では、空文字列をクォートすることによって dnlマクロが認識されるようになります。

define(`macro', `di$1')
macro(v)`'dnl

もし引用符がなかったら、 文字列divdnlとそれに続く改行文字が生成されるだけでしょう。

クォートすることで、マクロ展開による文字列とその周囲の文字を連結したものが マクロの名前として認識されるのを防ぐことができます。たとえば、

define(`macro', `di$1')
macro(v)`ert'

この入力からは、文字列divertが生み出されます。 もし引用符がなければ、組み込みマクロdivertが呼びだされるでしょう。


Node:Macro Arguments, Next:, Previous:Inhibiting Invocation, Up:Macros

マクロの引数

ある名前(name)が認識され、その名前に対するマクロの定義が存在するとき、 それはマクロとして展開されます。

その名前の直後に開きカッコ(があるときは、 引数をすべて集めてから、マクロが呼び出されます。 足りない引数があるときは、空文字列が与えられたものとして解釈されます。 余分な引数は無視されます。

組み込みマクロの呼び出しで引数の数が不足しているとき、通常ならm4は 警告を発しますが、コマンドライン・オプション-Qを使えば、 この警告を抑制できます。 ユーザ定義マクロに対する引数の個数チェックはありません。

引数の収集が行われているときでも、通常と同じようにマクロの展開は行われます。 そして展開後のテキストに出現した、コンマ、引用符、 カッコなどはすべて個々の引数の定義に寄与します。 したがってfoo, b, cに展開されるとき、 次のマクロの呼び出し、

bar(a foo, d)

は、4つの引数、a , b, c, d を伴うマクロの呼び出しとなります。 なぜ最初の引数に空白(whitespace)が含まれているのか理解するには、 引数の前にある空白はすべて削除されるのに対して、引数の後ろにある空白は 削除されないことを覚えておくとよいでしょう。


Node:Quoting Arguments, Next:, Previous:Macro Arguments, Up:Macros

マクロの引数をクォートする

個々の引数の前にある、クォートされていない空白(whitespace)は削除されます。 各引数の内部では、 クォートされていないカッコはすべて対になっていなければなりません。 たとえば、fooがマクロのとき、

foo(() (`(') `(')

これはひとつの引数を伴ったマクロの呼び出しで、 その引数の値は() (() (です。

引数自体がマクロ展開の対象になって欲しいのでない限り、 マクロへの引数はすべてクォートするのが普通です。 したがって、上記のカッコを含む例の`正しい'書き方は次のようになります。

foo(`() (() (')

しかし、ときにはいくつかの引数をクォートしないでおく必要がある場合もあり、 また、そうすることに何ら問題はありません。 ただ、注意を怠ると人生がすこしばかり厳しいものになるだけです。


Node:Macro expansion, Previous:Quoting Arguments, Up:Macros

マクロの展開

マクロの呼び出しが引数を伴うときはその収集が行われたあと、 マクロは展開されます。 そして展開後のテキストは入力に(クォートされずに)戻され、 そして再び読み込まれます(再走査)。 したがって、マクロを1つ呼び出したことで得られたテキストの中に、 完全なマクロの呼び出しやその一部が含まれている場合、 そこから更に多くのマクロが呼ばれることもあるわけです。

非常に簡単な例を挙げると、foobarへ展開され、 barHello worldへ展開されるとすると、入力

foo

は最初にbarへ展開された後、再び走査が行われ Hello worldへ展開されます。


Node:Definitions, Next:, Previous:Macros, Up:Top

新たにマクロを定義する方法

マクロはいくつかの異なる方法で定義、再定義、削除することができます。 また現在の定義を失うことなく一時的にマクロを再定義しておいて、 後で元の定義に戻すこともできます。


Node:Define, Next:, Previous:Definitions, Up:Definitions

マクロの定義方法

通常はマクロを定義したり再定義するときは、 組み込みマクロdefineを使います。

define(name [, expansion])

これはnameexpansionに展開されるように定義します。 もしexpansionが与えられなかったときは、空文字列だと見なされます。

defineは展開されると消滅します。

次の例では、マクロfooHello World.に展開されるように 定義しています。

define(`foo', `Hello world.')
=>
foo
=>Hello world.

出力に空行がある理由は、マクロ定義の直後にある改行文字が 定義の一部ではなく、従って出力にそのままコピーされるためです。 これはdnlマクロを使うことで避けることができます。 詳しくはSee Dnlを参照してください。

マクロdefineは引数が与えられたときだけ認識されます。


Node:Arguments, Next:, Previous:Define, Up:Definitions

マクロの引数

マクロは引数を取ることができます。n番目の引数は$nとして 展開用テキストの中で示し、マクロが展開されるときにn番目の 実引数(actual argument)に置き換えられます。 次の例は2つの引数を取るマクロです。2つの引数の順番を単純に交換します。

define(`exch', `$2, $1')
=>
exch(arg1, arg2)
=>arg2, arg1

これはdefineへの引数の順番を逆にしたいときなどに使えます。

define(`exch', `$2, $1')
=>
define(exch(``expansion text'', ``macro''))
=>
macro
=>expansion text

二重になった引用符の説明についてはSee Quoting Argumentsを参照してください。

GNU m4では$に続く数字は複数の桁でもよいので、 マクロはいくつでも引数を取ることができます。 これと違いUNIXのm4では1桁の数字しか認識されません。

特殊なケースとして、0番目の引数$0は常に現在展開されているマクロの名前 となります。

define(`test', ``Macro name: $0'')
=>
test
=>Macro name: test

クォートされたテキストを展開後のテキストに含めたい時は、 クォートは入れ子にできることを思い出しましょう。したがって、

define(`foo', `This is macro `foo'.')
=>
foo
=>This is macro foo.

展開されたテキストに含まれるfooは、 クォートされた文字列(quoted string)であり名前(name)ではないので、 再走査によって展開はされず、 引用符がはぎ取られるだけです(see Syntax)。


Node:Pseudo Arguments, Next:, Previous:Arguments, Up:Definitions

マクロの特殊な引数

与えられた実引数(actual arguments)の個数や全ての実引数をまとめて表すための 特別な表記方法があります。

マクロを呼び出すときに与えられた実引数の個数は、 展開用テキストの中で$#として表します。 したがって与えられた実引数の個数を表示するマクロは次のようになります。

define(`nargs', `$#')
=>
nargs
=>0
nargs()
=>1
nargs(arg1, arg2, arg3)
=>3

展開用テキストの中で$*という表記をすることで、 全ての実引数を(クォートはせずに)コンマで区切ったものを表すことができます。

define(`echo', `$*')
=>
echo(arg1,    arg2, arg3 , arg4)
=>arg1,arg2,arg3 ,arg4

引数をそれぞれクォートしなければならないことがよくありますが、 そんなときは$@という表記を使います。 これは各引数がクォートされることを除けば$*と同じです。

define(`echo', `$@')
=>
echo(arg1,    arg2, arg3 , arg4)
=>arg1,arg2,arg3 ,arg4

引用符はどこに行ったのでしょうか? もちろん展開後のテキストを 再走査したときにm4が食べてしまったのです。 違いを見るために、次のようにしてみましょう。

define(`echo1', `$*')
=>
define(`echo2', `$@')
=>
define(`foo', `This is macro `foo'.')
=>
echo1(foo)
=>This is macro This is macro foo..
echo2(foo)
=>This is macro foo.

これが理解できないときはSee Traceを参照してください。

展開用テキストに記号$が存在し、それに続く部分がm4に理解できる ものでないときは、$は他のテキストと同じように マクロ展開後のテキストへ単にコピーされます。

define(`foo', `$$$ hello $$$')
=>
foo
=>$$$ hello $$$

マクロを$12などに展開させたいときは、 $の後に一組の引用符を置きます。 これによって、m4がその$記号を引数への参照だと解釈してまうのを 防ぐことができます。


Node:Undefine, Next:, Previous:Pseudo Arguments, Up:Definitions

マクロの削除

undefineを使えばマクロの定義を削除することができます。

undefine(name)

これによってマクロnameが削除されます。 展開されてしまうのを防ぐためにマクロの名前は必ずクォートしなくてはなりません。

undefineは展開されると消滅します。

foo
=>foo
define(`foo', `expansion text')
=>
foo
=>expansion text
undefine(`foo')
=>
foo
=>foo

nameがマクロとして定義されていなくても問題はありません。 その場合はundefineが何もしないだけです。

undefineは引数が与えられたときだけ認識されます。


Node:Defn, Next:, Previous:Undefine, Up:Definitions

マクロ名の変更

すでに定義済みのマクロの名前を替えることができます。 それには組み込みマクロdefnが必要となります。

defn(name)

これはname定義をクォートしたものに展開されます。 引数が定義済みのマクロでないときは展開されると消滅します。

nameがユーザ定義マクロの場合、 クォートされた定義とは単にクォートされた展開用テキストのことです。 nameが組み込みマクロの場合、 展開後のテキストは、m4の内部にある組み込みマクロの定義を指す 特殊なトークンとなります。 このトークンは、define (および pushdef) の第2引数としてのみ意味を持ち、その他の文脈では無視されます。

通常の使用方法は、次の例でundefineの名前をzapに換える方法を 見るのが一番分かりやすいでしょう。

define(`zap', defn(`undefine'))
=>
zap(`undefine')
=>
undefine(`zap')
=>undefine(zap)

このようにdefnはユーザ定義マクロの定義や 組み込みマクロの定義をコピーするために使うことができます。 たとえ元のマクロが削除されても、もう一方の名前を使って 定義にアクセスすることができます。

defnは引数が与えられたときだけ認識されます。


Node:Pushdef, Next:, Previous:Defn, Up:Definitions

マクロの一時的な再定義

あるマクロを一時的に再定義しておき、後で元の定義に戻すことができます。 それにはdefineundefineに良く似た、 組み込みマクロpushdefpopdefを使います。

pushdef(name [, expansion])
popdef(name)

これらのマクロはスタック(stack)に似た仕組みで機能します。 pushdefは、あるマクロを一時的に再定義します。 このときnameの前の定義は、新しい定義によって置き換えられる前に 保存されます。もし前の定義が存在しない場合は、 pushdefdefineとまったく同じように機能します。

あるマクロに複数の定義が存在する場合(その中の一つだけがアクセス可能です)、 popdefを使って一番上の定義を削除することができます。 前の定義が無い場合、popdefundefineのように機能します。

define(`foo', `Expansion one.')
=>
foo
=>Expansion one.
pushdef(`foo', `Expansion two.')
=>
foo
=>Expansion two.
popdef(`foo')
=>
foo
=>Expansion one.
popdef(`foo')
=>
foo
=>foo

defineによって、複数の定義を持つマクロを再定義したときは、 一番上の定義が新しい定義で置き換えられます。 undefineによって定義を削除するときは、 一番上のもの一つだけではなく、すべての定義が削除されます。

define(`foo', `Expansion one.')
=>
foo
=>Expansion one.
pushdef(`foo', `Expansion two.')
=>
foo
=>Expansion two.
define(`foo', `Second expansion two.')
=>
foo
=>Second expansion two.
undefine(`foo')
=>
foo
=>foo

pushdefdefnを使えば、組み込みマクロを一時的に再定義することが できます。

マクロpushdefpopdefは引数が与えられたときだけ認識されます。


Node:Indir, Next:, Previous:Pushdef, Up:Definitions

マクロの間接的な呼び出し

indirを使うと、どんなマクロでも間接的に呼び出すことができます。

indir(name, ...)

indirはマクロnameを残りの引数と共に呼び出します。 これを"不正な"名前を持つマクロを呼ぶのに使うことができます(define はそういう名前でも定義できます。)

define(`$$internal$macro', `Internal macro (name `$0')')
=>
$$internal$macro
=>$$internal$macro
indir(`$$internal$macro')
=>Internal macro (name $$internal$macro)

ここでの要点は、大きなマクロ・パッケージで、 間違って呼ばれてしまうことのないマクロを定義できるということです。 それらは組み込みマクロindirによってだけ呼びだすことができます。


Node:Builtin, Previous:Indir, Up:Definitions

組み込みマクロの間接的な呼び出し

builtinを使えば、組み込みマクロを間接的に呼び出すことができます。

builtin(name, ...)

これは組み込みマクロnameを、残りの引数と共に呼び出します。 たとえnameに本来の定義を隠している別の定義が与えられていても、 本来の定義を呼び出します。

マクロbuiltinは引数が与えられたときだけ認識されます。


Node:Conditionals, Next:, Previous:Definitions, Up:Top

条件分岐、ループ、再帰

単純なテキストに展開されるようなマクロだけでは、 引数を取ることができるとしても、十分ではありません。 実行時に下される判断にもとづいて、 異なる展開がおこなわれるようなマクロが必要でしょう。 たとえば、何らかの条件構文が必要です。 また、ある処理を何回も繰り返したり、条件が真の間だけ繰り返したりするために、 ある種のループ構文も必要でしょう。


Node:Ifdef, Next:, Previous:Conditionals, Up:Conditionals

マクロが定義済みかを判定する

m4には2つの異なる条件構文が組み込まれています。 その1つはifdefです。

ifdef(name, string-1, opt string-2)

これにより、あるマクロが定義されているかどうかをテストできるようになります。 nameがマクロとして定義されていれば ifdefstring-1に展開され、 そうでないときはstring-2に展開されます。 string-2が省略されたときは (通常の規則に従い)空文字列として解釈されます。

ifdef(`foo', ``foo' is defined', ``foo' is not defined')
=>foo is not defined
define(`foo', `')
=>
ifdef(`foo', ``foo' is defined', ``foo' is not defined')
=>foo is defined

マクロifdefは引数が与えられたときだけ認識されます。


Node:Ifelse, Next:, Previous:Ifdef, Up:Conditionals

文字列の比較

もう一方の条件構文ifelseはずっとパワフルです。 与える引数の個数によって長いコメントの挿入のためや、 if-else構文、多重分岐などとして使うことができます。

ifelse(comment)
ifelse(string-1, string-2, equal, opt not-equal)
ifelse(string-1, string-2, equal, ...)

ifelseに1つだけ引数を与えた場合、 それは単に捨てられて、何も出力されません。 これはdnlを何度も使わずにブロック・コメントを 挿入するために良く使われる、m4におけるイディオムです。 GNU m4ではこの特殊な使用法が認められているので、 引数が足りないことに対する警告はこのケースでは発せられません。

ifelseに3つ、または4つの引数を与えて呼び出すと、 string-1string-2が(文字毎に比べて)等しければ equalに展開されます。 等しくなければnot-equalに展開されます。

ifelse(foo, bar, `true')
=>
ifelse(foo, foo, `true')
=>true
ifelse(foo, bar, `true', `false')
=>false
ifelse(foo, foo, `true', `false')
=>true

またifelseには4つ以上の引数を与えることができます。 この場合、ifelseは伝統的なプログラミング言語におけるcase文や switch文と同じように機能します。 string-1string-2が等しければifelseequalに 展開され、等しくなければ最初の3つの引数が捨てられたあと、 まったくおなじ手続きが繰り返されます。例で示したほうがいいでしょう。

ifelse(foo, bar, `third', gnu, gnats, `sixth', `seventh')
=>seventh

もちろん、通常はこれらの例よりもうすこし高度な使い方をするでしょう。 ifelseのよくある使い方のひとつは、さまざまな種類のループ処理を 実装するマクロの中で使用する場合です。

マクロifelseは引数が与えられたときだけ認識されます。


Node:Loops, Previous:Ifelse, Up:Conditionals

ループと再帰

m4ではループ処理が直接的にはサポートされていませんが、 再帰的なマクロを定義することはできます。 使用しているハードウェアとオペレーティング・システムによるもの以外、 再帰の深さに制限はありません。

ループ処理は再帰とすでに説明した条件構文を使うことで実現できます。

マクロの実引数を反復処理するときには組み込みマクロshiftを 使うことができます。

shift(...)

このマクロは任意の個数の引数を受け取り、 最初の引数を除く残りの引数をそれぞれクォートしてから、 それらをコンマで区切ったものに展開します。

shift(bar)
=>
shift(foo, bar, baz)
=>bar,baz

shiftを使った次の例では引数の順番を逆にするマクロを定義しています。

define(`reverse', `ifelse($#, 0, , $#, 1, ``$1'',
			  `reverse(shift($@)), `$1'')')
=>
reverse
=>
reverse(foo)
=>foo
reverse(foo, bar, gnats, and gnus)
=>and gnus, gnats, bar, foo

それほど興味深いマクロではありませんが、 shiftifelseそして再帰を使えばループ処理をどんなに簡単に 実現できるか示しています。

次に挙げるのは、単純なforループを実現するマクロの例です。 単純な数え上げのためなどに使えます。

forloop(`i', 1, 8, `i ')
=>1 2 3 4 5 6 7 8

それぞれの引数は順に、反復変数(iteration variable)の名前、 開始値、終了値、そして反復するたびに展開されるテキストです。 このマクロにおいて、マクロiはループ処理の内部でだけ定義されています。 iが以前に値を持っていた場合は、ループが終ればまたその値にもどります。

forloopは次のように入れ子にすることもできます。

forloop(`i', 1, 4, `forloop(`j', 1, 8, `(i, j) ')
')
=>(1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (1, 6) (1, 7) (1, 8)
=>(2, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6) (2, 7) (2, 8)
=>(3, 1) (3, 2) (3, 3) (3, 4) (3, 5) (3, 6) (3, 7) (3, 8)
=>(4, 1) (4, 2) (4, 3) (4, 4) (4, 5) (4, 6) (4, 7) (4, 8)
=>

forloopマクロはとても簡潔に実装することができます。 forloopマクロ自体は単なるラッパー(wrapper)で、 第1引数が持つ元の定義を保存してから、内部マクロ_forloopを呼び、 再び保存しておいた第1引数の定義を再確立します。

マクロ_forloopは第4引数を一度展開し、これで反復が終りかどうか調べます。 もし終りでなければ、反復変数を(すでに定義済みのマクロincrを使って) 1増やしてから、自分自身を再帰的に呼び出します。

forloopの実際の実装は次のようになります:

define(`forloop',
       `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')
define(`_forloop',
       `$4`'ifelse($1, `$3', ,
		   `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')

注意深い引用符の使い方に注目してください。 マクロの引数でクォートされていないのは3つだけで、 それぞれに固有の理由があります。これら3つの引数がクォート されていないのはなぜか、その理由を見つけてください、 これらがクォートされていると、どうなるのかも確かめてみてください。

これら2つのマクロは便利ではありますが、 一般の使用に耐えるほど堅牢ではありません。 開始値が終了値より小さい場合や、第1引数が名前でなかった場合など、 初歩的なエラー対策さえ欠いています。 これら不備の訂正は、読者への課題として残しておきます。


Node:Debugging, Next:, Previous:Conditionals, Up:Top

マクロや入力をデバッグする方法

m4のマクロを書いていると、 (たいていのプログラミング言語の場合と同様に) 往々にしてそれらは思ったようには動いてくれないものです。 m4にはデバッグをサポートするしくみがいくつか存在します。


Node:Dumpdef, Next:, Previous:Debugging, Up:Debugging

マクロの定義を表示する

ある名前(name)が展開されるとどうなるのか調べたいときは、 組み込みマクロdumpdefを使用することができます。

dumpdef(...)

このマクロは任意の個数の引数を取ります。 引数を与ずに呼びだすと、既知の名前すべての定義を表示します。 それ以外の場合は与えた引数の定義を表示します。 出力は標準エラー出力に直接行われます。

dumpdefは展開されると消滅します。

define(`foo', `Hello world.')
=>
dumpdef(`foo')
error-->foo:	`Hello world.'
=>
dumpdef(`define')
error-->define:	<define>
=>

最後の例は組み込みマクロの定義が表示される様子です。

表示の詳細を調整するための情報についてはSee Debug Levelsを参照してください。


Node:Trace, Next:, Previous:Dumpdef, Up:Debugging

マクロの呼び出しをトレースする

組み込みマクロtraceontraceoffを使うと、マクロの呼び出しと 展開をトレース(trace)することができます。

traceon(...)
traceoff(...)

traceontraceoffを引数なしで呼ぶと、 すべての定義済みのマクロに対してトレースがそれぞれオンまたはオフとなります。 引数を指定した場合、その名前のマクロに対してだけ作用します。

traceontraceoffは展開されると消滅します。

トレース中のマクロが呼ばれるたびに、 そのマクロの呼び出しに関する情報が引数の収集が終ったあとに表示されます。 マクロの呼び出しが展開後に消滅しないときは展開の結果が表示されます。 出力は標準エラー出力へ直接行われます。

define(`foo', `Hello World.')
=>
define(`echo', `$@')
=>
traceon(`foo', `echo')
=>
foo
error-->m4trace: -1- foo -> `Hello World.'
=>Hello World.
echo(gnus, and gnats)
error-->m4trace: -1- echo(`gnus', `and gnats') -> ``gnus',`and gnats''
=>gnus,and gnats

ダッシュ(-)の間にある数字は展開の深さを表します。 たいていは最も外側のレベルでの展開を表す1となりますが、 クォートされていないマクロの呼び出しを引数が含む場合には増えていきます。

表示の詳細を調整するための情報についてはSee Debug Levelsを参照してください。


Node:Debug Levels, Next:, Previous:Trace, Up:Debugging

デバッグ出力の制御

m4-dオプションを与えることにより、 これまでの節に記載されているマクロを使ったときに表示される情報の詳しさを 制御します。

このオプションに続けて次のうち1つまたは複数のフラグを指定します。

t
今回起動するm4におけるマクロの呼び出しを全てトレースします。
a
マクロの呼び出しに伴う実引数を表示します。 tフラグと共に指定したときは全てのマクロの呼び出しが対象となりますが、 それ以外の場合はtraceonマクロによってトレースしているマクロだけが 対象となります。
e
マクロの呼び出しが展開後に消滅しない場合、展開後のテキストを表示します。 tフラグと共に指定したときは全てのマクロの呼び出しが対象となりますが、 それ以外の場合はtraceonマクロによってトレースしているマクロだけが 対象となります。
q
実引数やマクロの展開後のテキストを表示するときに現在の引用符でクォートします。
c
ひとつのマクロの呼び出しにつき複数のトレース行を表示します。 マクロが認識された時点で引数を集める前に1行表示し、 引数を集め終った後に2行目を、 マクロの呼び出しが完了したあとに3行目を表示します。
x
トレースの各出力行にマクロの呼び出し毎に異なる`識別番号(id)'を加えます。 これは上記のcフラグを使うときに便利です。
f
トレースの各出力行に、現在の入力ファイルの名前を表示します。
l
トレースの各出力行に、現在の入力行番号を表示します。
p
指定した名前のファイルをパス・サーチ機構(see Search Path)を使って 見つけたときは、実際に使われるファイル名を表示します。
i
現在の入力ファイルが替わるたびに、ファイル名と入力行番号を表示します。
V
上記すべてのフラグを表す簡略表記です。

-dオプションに何もフラグを指定しない場合、 デフォルトでaeqが使われます。 前2つの節にある例は、これらデフォルトのフラグを使うことを想定しています。

組み込みマクロdebugmodeを使うと、実行時にデバッグ出力のフォーマットを 制御できます。

debugmode(opt flags)

引数flagsは上に列挙されている文字をいくつか指定します。 特殊なケースとして引数を+で始めると、 それらのフラグが現在のデバッグ・フラグ群に追加されます。 また引数を-で始めると現在のデバッグ・フラグ群から削除されます。 引数をまったく与えない場合、デバッグ・フラグは(-dを与えない場合と同様に) すべてゼロに設定されます。 引数として空文字列を与えるとフラグはデフォルトの値にリセットされます。


Node:Debug Output, Previous:Debug Levels, Up:Debugging

デバッグ出力の保存

デバッグとトレースの出力は、m4-oオプションを与えるか 組み込みマクロdebugfileを使うことで、 ファイルにリダイレクト(redirect)することができます。

debugfile(opt filename)

このマクロにより、ここから後に発生するデバッグとトレースの出力は すべてfilenameへ送られます。 filenameが空文字列のときはデバッグとトレースの出力は捨てられます。 debugfileが引数無しで呼ばれたときは、デバッグとトレースの出力は 標準エラー出力へ送られます。


Node:Input Control, Next:, Previous:Debugging, Up:Top

入力制御

この章ではm4への入力を制御するための、さまざまな組み込みマクロを 説明します。


Node:Dnl, Next:, Previous:Input Control, Up:Input Control

入力中の空白(whitespace)を削除する

組み込みマクロdnlは最初の改行文字までにある文字をすべて読み込んでから 改行文字も含めてそれらを捨てます。

dnl

次の例のようにdefineの呼び出しの後に続く改行を取り除くために、 defineと一緒によく使います。

define(`foo', `Macro `foo'.')dnl A very simple macro, indeed.
foo
=>Macro foo.

コメントの扱かわれ方とは対照的に(see Comments)、 次の改行までの入力が改行を含めて捨てられます。

通常dnlの後ろには、行末もしくはその他の空白(whitespace)が続きます。 GNU m4dnlに続いて開きカッコがあるとき、 それを警告する診断メッセージを出します。 この場合dnlは閉じカッコを探しながら、すべての引数を集め処理します。 この引数の収集が原因となる予想可能な副作用はすべて起こります。 このときdnlは何も出力しません。 閉じカッコのあとに続く次の改行までの入力は、 それがどの行にあっても、改行を含めてやはり捨てられます。


Node:Changequote, Next:, Previous:Dnl, Up:Input Control

引用符(quote characters)を変更する

デフォルトの引用符は組み込みマクロchangequoteによって変更できます。

changequote(opt start, opt end)

startは新しい開始引用符でendは新しい終了引用符です。 もし欠けている引数があるときは、デフォルトの引用符(`') がその欠けている引数の代わりに使用されます。

changequoteは展開されると消滅します。

changequote([, ])
=>
define([foo], [Macro [foo].])
=>
foo
=>Macro foo.

適切な文字がないときはstartendを好きな長さにしてかまいません。

changequote([[, ]])
=>
define([[foo]], [[Macro [[[foo]]].]])
=>
foo
=>Macro [foo].

引用符を両方とも空文字列にすると、事実上クォート機構が無効になり、 テキストをクォートする方法が無くなります。

define(`foo', `Macro `FOO'.')
=>
changequote(, )
=>
foo
=>Macro `FOO'.
`foo'
=>`Macro `FOO'.'

changequoteを使って現在の引用符を替えない限り、 終了引用符と対になっていない開始引用符を含む文字列をクォートする 方法はm4には存在しません。

入力に含まれる名前と混同されてしまうので、 どちらの引用符も記号でない普通の文字や_ (アンダースコア)で 始めるべきではありません。そうした場合はクォート機構が無効になります。


Node:Changecom, Next:, Previous:Changequote, Up:Input Control

コメントデリミタ(comment delimiters)を変更する

デフォルトのコメントデリミタ(comment delimiters)は 組み込みマクロchangecomで変更できます。

changecom(opt start, opt end)

startが新しいコメント開始デリミタ(start-comment delimiter)で、 endが新しいコメント終了デリミタ(end-comment delimiter)です。 欠けている引数があるときは、デフォルトのコメント区切り記号 (# と 改行文字)がその欠けている引数の代わりに使用されます。 コメント区切り記号は任意の長さにすることができます。

changecomは展開されると消滅します。

define(`comment', `COMMENT')
=>
# A normal comment
=># A normal comment
changecom(`/*', `*/')
=>
# Not a comment anymore
=># Not a COMMENT anymore
But: /* this is a comment now */ while this is not a comment
=>But: /* this is a comment now */ while this is not a COMMENT

クォートされた文字列であるかのようにコメントが出力にコピーされている 様子に注目しましょう。 コメント内部のテキストが展開されるようにしたいときは、 コメント開始デリミタをクォートしてください。

引数なしでchangecomを呼ぶとコメント機構が完全に無効になります。

define(`comment', `COMMENT')
=>
changecom
=>
# Not a comment anymore
=># Not a COMMENT anymore


Node:Changeword, Next:, Previous:Changecom, Up:Input Control

単語(word)の字句構造を変更する

マクロchangewordとそれに関連する機能すべてが実験段階にあります。 GNU m4をインストールする際 configure--enable-changewordオプションを与えたときだけ この機能を使用することができます。 これから先、この機能が変更されたり、削除されてしまうことさえありえます。 したがって、この機能に依存した使い方はしないで下さい。 この機能について意見を寄せていただくときは バグを報告するときと同じ方法で行ってください。

m4によって処理されるファイルは、クォートされた文字列、 単語(潜在的なマクロ名)、そして単純なトークン(その他の単一の文字すべて) に分割されます。 初めに単語は次の正規表現によって定義されています。

[_a-zA-Z][_a-zA-Z0-9]*

changewordを使えばこの正規表現を変更できます。 たとえば数字が含まれているファイルに置換をかけたい場合など、 m4の字句構成規則を緩めると便利なときもあるでしょう。

changeword(`[_a-zA-Z0-9]+')
define(1, 0)
=>1

字句構成規則を厳しくすると 組み込みマクロのうちいくつかが使えなくなってしまうことが多いので、 緩める場合ほど便利にはなりません。 次の例のように、間違って組み込みマクロを呼んでしまうのを避けるために 使うことはできるでしょう。

define(`_indir', defn(`indir'))
changeword(`_[_a-zA-Z0-9]*')
esyscmd(foo)
_indir(`esyscmd', `ls')

m4は単語を一度に一文字ずつ構築するので、 changewordに渡すことができる正規表現には制限があります。 これは、もし指定した正規表現がfooを受理するなら ffoも受理しなければならないというものです。

changewordには、もう一つ機能があります。 指定した正規表現に角括弧でくくられた部分が1つ以上存在する場合、 最初の角括弧でくくられた部分の外側にあるテキストが、 シンボルを表引きする前に捨てられます。

changecom(`/*', `*/')
changeword(`#\([_a-zA-Z0-9]*\)')
#esyscmd(ls)

こうすると、m4はすべてのマクロの呼び出しの先頭に #記号を必要とするようになるので、 m4でシェル・スクリプトを処理するときに shiftコマンドがm4に飲み込まれてしまわないようにできます。 また種々の一般的な単語を失うことなくプレーン・テキストを 処理できるようになります。

m4のマクロ置換はテキストに基づいていますが、 TeXのものはトークンに基づいています。 changewordによって、この違いを浮き彫りにすることができます。 たとえば次の例は同じアイデアをTeXとm4で表現したものです。 初めはTeX バージョンからです。

\def\a{\message{Hello}}
\catcode`\@=0
\catcode`\\=12
=>@a
=>@bye

つぎにm4バージョンです。

define(a, `errprint(`Hello')')
changeword(`@\([_a-zA-Z0-9]*\)')
=>@a

TeXの例において、最初の行はマクロaHelloメッセージを 表示するように定義しています。 2行目は<\>の代わりに<@>をエスケープ文字として使えるように 定義しています。 3行目は<\>をエスケープ文字ではなく通常の表示可能文字として 定義しています。 4行目はマクロaを呼び出しています。 したがって、このファイルに対してTeXを走らせると Helloメッセージを表示します。

m4の例をm4に与えるとerrprint(Hello)を表示します。 この理由はTeXはマクロが定義されたときにマクロ定義の字句解析 を行うのに対し、m4は単純にテキストを保存しておき、 字句解析は実際にマクロが使われるまで後回しにするからです。

changewordを使用すると、 m4の速度が7倍ほど遅くなることに注意してください。


Node:M4wrap, Previous:Changeword, Up:Input Control

入力の保存(save)

通常の入力が終りになるまで、テキストを`保存(save)'しておくことができます。 保存されたテキストは通常の入力が終った段階でm4に再び読み込まれます。 通常この機能は一時ファイルの削除など正常終了前に行うクリーンアップ動作を 開始するために使われます。

入力テキストを保存するためには組み込みマクロm4wrapを使います。

m4wrap(string, ...)

stringと残りの引数は入力が終端に達したときに再び読み込まれるように 安全な場所に保存されます。

define(`cleanup', `This is the `cleanup' actions.
')
=>
m4wrap(`cleanup')
=>
This is the first and last normal input line.
=>This is the first and last normal input line.
^D
=>This is the cleanup actions.

保存されている入力は通常の入力が終端に達したときだけ再び読み込まれます。 m4を終了するためにm4exitが使われたときは再読み込みは行われません。

保存されたテキストの中でm4wrapを呼びだしても差し支えありませんが、 そのとき保存されたテキストが再読み込みされる順番は決まっていません。 m4wrapが再帰的に使われていない場合、 保存された各テキストはそれぞれが保存されたのと逆の順番(LIFO--last in, first out)で再読み込みが行われます。


Node:File Inclusion, Next:, Previous:Input Control, Up:Top

ファイルのインクルード

m4では入力のどこででも名前を指定してファイルをインクルード (include)することができます。


Node:Include, Next:, Previous:File Inclusion, Up:File Inclusion

名前を指定してファイルをインクルードする

m4にはファイルをインクルードするための組み込みマクロが2つあります。

include(filename)
sinclude(filename)

どちらもfilenameという名前のファイルをm4に読み込ませます。 そのファイルの終りに達すると以前の入力ファイルから入力を再開します。

したがってincludesincludeは展開後に filenameの内容となります。

includeに指定したファイルが存在しないとエラーとなります。 ファイルが存在しない事についてのエラー・メッセージを避けたいときは sincludeを使います。 sincludeは、もしファイルが存在すればそのファイルをインクルードし、 存在しなければ消滅します。

include(`no-such-file')
=>
error-->30.include:2: m4: Cannot open no-such-file: No such file or directory
sinclude(`no-such-file')
=>

これ以降ファイルincl.m4の内容は仮に以下のものだとします。

Include file start
foo
Include file end

通常、ファイルのインクルードはファイルの内容を入力ストリームに挿入するために 使用されます。 インクルードされたファイルの内容はm4によって読まれ、 そのファイルに含まれるマクロの呼び出しは展開されます。

define(`foo', `FOO')
=>
include(`incl.m4')
=>Include file start
=>FOO
=>Include file end
=>

includesincludeがファイルの内容に展開されることを利用して、 ファイル全体に作用するマクロを定義することができます。 次の例ではbarincl.m4の内容に展開されるように定義しています。

define(`bar', include(`incl.m4'))
=>
This is `bar':  >>>bar<<<
=>This is bar:  >>>Include file start
=>foo
=>Include file end
=><<<

もっとも、これはincludeのごく普通の使い方ではありません。 なぜならファイルにはクォート、コンマ、括弧といった m4のパーサ(parser)が動作する方法に干渉するものが含まれている可能性が あるからです。

組み込みマクロincludesincludeは引数が与えられたときだけ認識されます。


Node:Search Path, Previous:Include, Up:File Inclusion

インクルードするファイルのサーチ

GNU m4ではインクルード対象のファイルが現在の作業ディレクトリ (current working directory)とは別のディレクトリにあっても かまいません。

ファイルが現在の作業ディレクトリで見つからずファイル名が絶対ファイル名(absolute file name)でないとき、GNU m4は指定されたサーチ・パス(search path) を使ってそのファイルを探します。 最初に-Iオプションで指定された各ディレクトリの中を コマンド・ラインに書かれている順番に探します。 次に環境変数M4PATHが設定されているときは、 それをコロン(:)で区切られたディレクトリのリストとして解釈し、 それらの中を順に探します。

インクルード・ファイルの自動サーチがトラブルの原因となったときは、 pデバッグ・フラグ(see Debug Levels)が問題の切り分けに 役立つでしょう。


Node:Diversions, Next:, Previous:File Inclusion, Up:Top

出力の切替え(divert)と逆切替え(undivert)

出力切替え(diversions)は出力を一時的に保存しておく方法です。 m4では好きなときに出力を一時ファイルへ切替え(divert)ておき、 後で再び出力ストリームへと逆切替え(undiverted)することができます。

切り替え先番号は0から数え上げます。切替え先0は通常の出力ストリームです。 同時に存在できる切替え先の数は主としてそれらを記述するために使われるメモリ によって制限されます。 これはGNU m4が切替え先の情報とそこへの出力をメモリに置いておこうとするためです。 しかし全ての切替え先に必要なメモリの総量には制限があります(現在は512Kです)。 この最大値を超えそうになったときは、一番大きな切替え先の内容を入れるために 一時ファイルが作られ、その分のメモリが他の切替え先のために開放されます。 したがって理論上は切替え先の数が利用可能なファイル・ディスクリプタの数 によって制限されることがありえます。


Node:Divert, Next:, Previous:Diversions, Up:Diversions

出力を切替える(divert)

出力はdivertを使って切替えます。

divert(opt number)

numberは使用する切替え先です。 numberを省略したときはゼロとして解釈されます。

divertは展開されると消滅します。

m4への入力がすべて処理されると、 その時点で存在するすべての切替え先が自動的に番号の順で逆切替え(undivert) されて、そこにたまっていたテキストが出力されます。

divert(1)
This text is diverted.
divert
=>
This text is not diverted.
=>This text is not diverted.
^D
=>
=>This text is diverted.

同じ引数でdivertを何回か呼び出すと、 切替え先にある以前のテキストは上書きされずに、 新しいテキストが以前のテキストの後に追加されてゆきます。

存在するはずのない切替え先へ出力を切替えるとそこから後の出力は 単に捨てられます。 よくある不要な出力の例はマクロ定義の後にある改行です。 次はそれらを避ける方法です。

divert(-1)
define(`foo', `Macro `foo'.')
define(`bar', `Macro `bar'.')
divert
=>

これはm4でのプログラミング上の一般的な慣用句のひとつです。


Node:Undivert, Next:, Previous:Divert, Up:Diversions

出力を逆切替え(undivert)する

切替え先に出力されたテキスト(diverted text)は組み込みマクロ undivertを使って明示的に逆切替え(undivert)することができます。

undivert(opt number, ...)

このマクロは引数で指定された切替え先を、指定された順に逆切替えして出力します。 引数が与えられなかったときは、すべての切替え先を番号順に逆切替えします。

undivertは展開されると消滅します。

divert(1)
This text is diverted.
divert
=>
This text is not diverted.
=>This text is not diverted.
undivert(1)
=>
=>This text is diverted.
=>

最後にある2つの空行に注目してください。 1つはundivertに続く改行によるもので、 もう一方はなんとdivertに続く改行によるものです! 切替え先のテキストはしばしばこのような空行で始まります。

切替え先のテキスト(diverted text)は逆切替え(undiverted)されると、 m4によって再走査されずに、 現在の出力(切替え先)に直接コピーされます。 したがってある切替え先(diversion)に出力中に、 逆切替え(undivert)しても問題ありません。

逆切替えをすると、その切替え先にあるテキストは破棄されるので そのテキストを取り出せるのは1回だけです。

divert(1)
This text is diverted first.
divert(0)undivert(1)dnl
=>
=>This text is diverted first.
undivert(1)
=>
divert(1)
This text is also diverted but not appended.
divert(0)undivert(1)dnl
=>
=>This text is also diverted but not appended.

現在の切替え先(current diversion)を逆切替え(undivert)しようとしても 黙殺されます。

GNU m4では名前を指定したファイルを逆切替え(undivert)することができます。 数字以外の引数を与えると、その名前をもつファイルの内容が 現在の出力(切替え先)に解釈されずにコピーされます。 これによって組み込みマクロincludeの機能が補完されます(see Include)。 次の例で違いを説明します。ファイルfooの内容はbarだとします。

define(`bar', `BAR')
=>
undivert(`foo')
=>bar
=>
include(`foo')
=>BAR
=>


Node:Divnum, Next:, Previous:Undivert, Up:Diversions

出力切替え先番号(diversion number)

組み込みマクロdivnumは現在の切替え先(current diversion)の番号に展開されます。

divnum
Initial divnum
=>Initial 0
divert(1)
Diversion one: divnum
divert(2)
Diversion two: divnum
divert
=>
^D
=>
=>Diversion one: 1
=>
=>Diversion two: 2

逆切替えされて出力されるテキスト自身が切替え先に出力されてしまうのを防ぐため に最後にある引数無しのdivertの呼び出しが必要です。 (訳者注: 誤訳の可能性あり。原文は The last call of divert without argument is necessary, since the undiverted text would otherwise be diverted itself.)


Node:Cleardiv, Previous:Divnum, Up:Diversions

出力切替え先のテキストを破棄する

出力を切替えているときは切替え先のテキストが実際に 必要となるかどうかは分からないことがよくあります。 テキストが溜っている切替え先は入力が終りに達した段階で メインの出力ストリームにすべて出力されるので、 切替え先にたまっているテキストを破棄するためのなんらかの手段が必要です。 すべての切替え先のテキストを破棄したいときは m4への入力をdivert(-1)とそれに続く明示的なundivert で終えるのが最も簡単でしょう。

divert(1)
Diversion one: divnum
divert(2)
Diversion two: divnum
divert(-1)
undivert
^D

このとき出力はいっさいありません。

特定の切替え先のテキストは次のマクロで消去できます。

define(`cleardivert',
`pushdef(`_num', divnum)divert(-1)undivert($@)divert(_num)popdef(`_num')')
=>

undivertと同じように呼び出しますが、 その効果は引数として与えられた切替え先のテキストを消去することです。 (このマクロにはひどいバグがあります! それを見つけて直せるか挑戦してみてください。)


Node:Text handling, Next:, Previous:Diversions, Up:Top

テキスト操作用の組み込みマクロ

m4には部分文字列の抽出、検索、置換など様々な方法でテキストを 操作するための組み込みマクロがあります。


Node:Len, Next:, Previous:Text handling, Up:Text handling

文字列の長さを計算する

文字列の長さはlenで計算できます。

len(string)

このマクロはstringの長さを表す10進数に展開されます。

len()
=>0
len(`abcdef')
=>6

組み込みマクロlenは引数が与えられたときだけ認識されます。


Node:Index, Next:, Previous:Len, Up:Text handling

部分文字列で検索する

部分文字列の検索はindexで行います。

index(string, substring)

このマクロはstringの中でsubstringが最初に出現する 位置のインデックスに展開されます。 stringの先頭にある文字のインデックスは0です。 substringstringに含まれないとき、 index-1に展開されます。

index(`gnus, gnats, and armadillos', `nat')
=>7
index(`gnus, gnats, and armadillos', `dag')
=>-1

組み込みマクロindexは引数が与えられたときだけ認識されます。


Node:Regexp, Next:, Previous:Index, Up:Text handling

正規表現で検索する

正規表現の検索は組み込みマクロregexpで行います。

regexp(string, regexp, opt replacement)

このマクロはregexpstringの中を検索します。 正規表現の構文はGNU Emacsのものと同じです。 See Regexps.

replacementを省略すると、regexpstringの中で regexpに最初にマッチした部分のインデックスに展開されます。 regexpstringのどこにもマッチしない場合は-1に展開されます。

regexp(`GNUs not Unix', `\<[a-z]\w+')
=>5
regexp(`GNUs not Unix', `\<Q\w*')
=>-1

replacementを与えたときは、regexpはこの引数の値に展開されます。 このときreplacementに含まれる\nregexp中の n番目のカッコでくくられた部分式にマッチしたテキストに置き換えられ、 \&は正規表現全体にマッチしたテキストに置き換えられます。

regexp(`GNUs not Unix', `\w\(\w+\)$', `*** \& *** \1 ***')
=>*** Unix *** nix ***

組み込みマクロregexpは引数が与えられたときだけ認識されます。


Node:Substr, Next:, Previous:Regexp, Up:Text handling

部分文字列を抽出する

部分文字列はsubstrを使って抽出します。

substr(string, from, opt length)

このマクロはstringのインデックスfromから始まるlength文字分 の部分文字列に展開されます。 lengthを省いたときはstringの最後までになります。 文字列の最初のインデックスは常に0です。

substr(`gnus, gnats, and armadillos', 6)
=>gnats, and armadillos
substr(`gnus, gnats, and armadillos', 6, 5)
=>gnats

組み込みマクロsubstrは引数が与えられたときだけ認識されます。


Node:Translit, Next:, Previous:Substr, Up:Text handling

文字の置換

文字の置き換えはtranslitで行います。

translit(string, chars, replacement)

stringの各文字のうちcharsに出て来る文字をそれぞれ replacement中で同じ位置にある文字に置き換えたものに展開されます。

replacementcharsより短いときは 余分な文字は展開後のテキストから削除されます。 replacementを省略すると展開後のテキストは stringからcharsに含まれる文字すべてを削除したものになります。

charsreplacementのどちらにも文字範囲を含めることができます。 たとえばa-z (すべての小文字アルファベット)や 0-9 (すべての数字)などです。 charsreplacementにダッシュ-そのものを含めるときは 最初か最後に置いてください。

範囲の最後の文字が最初の文字より`小さい'場合もエラーではありません。 そういうケースでは範囲が逆に広がります。 つまり9-0は文字列9876543210を意味します。

translit(`GNUs not Unix', `A-Z')
=>s not nix
translit(`GNUs not Unix', `a-z', `A-Z')
=>GNUS NOT UNIX
translit(`GNUs not Unix', `A-Z', `z-a')
=>tmfs not fnix

最初の例は大文字のアルファベットをすべて削除します。 2番目の例は小文字を大文字に変換します。 3番目の例は大文字すべてを小文字に変換しながら`反射'させます。 最初の2つの例のほうがはるかに一般的です。

組み込みマクロtranslitは引数が与えられたときだけ認識されます。


Node:Patsubst, Next:, Previous:Translit, Up:Text handling

正規表現でテキストの置換をする

文字列の全置換(global substitution)はpatsubstで行います。

patsubst(string, regexp, opt replacement)

string中でregexpにマッチする部分を探し、 それらをすべてreplacementに置換します。 正規表現の構文はGNU Emacsのものと同じです。

stringの中でregexpに適合するどの箇所にも含まれない部分は展開後の テキストにそのまま残ります。 マッチする箇所が見つかるたびに、 サーチはそのマッチした箇所の終りから続行されます。 したがってstring内のある文字が2回置換されることは決してありません。 regexpが長さ0の文字列にマッチしたときは、 無限ループを避けるためサーチの開始位置は1文字前に進められます。

置き換えが行われるときは、replacementに含まれる\nregexp内のカッコでくくられたn番目の部分式にマッチしたテキスト に置き換え、\&を正規表現全体にマッチしたテキストに置き換えたものが 展開後のテキストに挿入されます。

引数replacementは省略することができます。 そのときはregexpにマッチしたテキストは削除されます。

patsubst(`GNUs not Unix', `^', `OBS: ')
=>OBS: GNUs not Unix
patsubst(`GNUs not Unix', `\<', `OBS: ')
=>OBS: GNUs OBS: not OBS: Unix
patsubst(`GNUs not Unix', `\w*', `(\&)')
=>(GNUs)() (not)() (Unix)
patsubst(`GNUs not Unix', `\w+', `(\&)')
=>(GNUs) (not) (Unix)
patsubst(`GNUs not Unix', `[A-Z][a-z]+')
=>GN not

次はもうすこし現実的な例です。 文字列の中にマクロupcasedowncaseの呼び出しを挿入することで、 単独の単語または文全体をキャピタライズ(capitalize)します。

define(`upcase', `translit(`$*', `a-z', `A-Z')')dnl
define(`downcase', `translit(`$*', `A-Z', `a-z')')dnl
define(`capitalize1',
     `regexp(`$1', `^\(\w\)\(\w*\)', `upcase(`\1')`'downcase(`\2')')')dnl
define(`capitalize',
     `patsubst(`$1', `\w+', `capitalize1(`\&')')')dnl
capitalize(`GNUs not Unix')
=>Gnus Not Unix

組み込みマクロpatsubstは引数が与えられたときだけ認識されます。


Node:Format, Previous:Patsubst, Up:Text handling

書式付き出力

書式付き出力はformatを使って行うことができます。

format(format-string, ...)

このマクロはC言語の関数printfとよく似た動作をします。 最初の引数は書式指定文字列で%指定を含めることができます。 formatは展開されると書式付の文字列になります。

2, 3の例を使って説明するのが一番でしょう。

define(`foo', `The brown fox jumped over the lazy dog')
=>
format(`The string "%s" is %d characters long', foo, len(foo))
=>The string "The brown fox jumped over the lazy dog" is 38 characters long

See Loopsで定義されているforloopマクロを使って 表形式の出力をするときはformatを次のように使うことができます。

forloop(`i', 1, 10, `format(`%6d squared is %10d
', i, eval(i**2))')
=>     1 squared is	    1
=>     2 squared is	    4
=>     3 squared is	    9
=>     4 squared is	   16
=>     5 squared is	   25
=>     6 squared is	   36
=>     7 squared is	   49
=>     8 squared is	   64
=>     9 squared is	   81
=>    10 squared is	  100

組み込みマクロformatはANSI Cのprintf関数をモデルとしており、 次の標準的な%指定をサポートしています: c, s, d, o, x, X, u, e, E, f。 またフィールド幅と精度指定、モディファイア +, -,  , 0, #, h, lをサポートしています。 printfの動作について更に詳しいことは C ライブラリ・マニュアルを見てください。


Node:Arithmetic, Next:, Previous:Text handling, Up:Top

計算用の組み込みマクロ

m4にはCに似た文法の整数演算機構が組み込まれています。 単純なインクリメントとデクリメント操作のための組み込みマクロが便利な省略記法 として用意されています。


Node:Incr, Next:, Previous:Arithmetic, Up:Arithmetic

インクリメント演算子とデクリメント演算子

整数のインクリメントとデクリメントは組み込みマクロincrdecr によってサポートされています。

incr(number)
decr(number)

numberの数値を1だけ増やした値または1だけ減らした値に展開されます。

incr(4)
=>5
decr(7)
=>6

組み込みマクロincrおよびdecrは引数が与えられたときだけ認識されます。


Node:Eval, Previous:Incr, Up:Arithmetic

整数式を計算する

整数式はevalを使って計算します。

eval(expression, opt radix, opt width)

このマクロはexpressionの値に展開されます。

式(expression)には次の演算子を含めることができます。 リストは優先順位の高い順に並んでいます。

-
単項差(unary minus)
**
累乗(exponentiation)
* / %
積(multiplication)、商(division)、剰余(modulo)
+ -
和(addition)、差(subtraction)
<< >>
左シフト(shift left)、右シフト(shift right)
== != > >= < <=
関係演算子(relational operator)
!
論理否定(logical negation)
~
ビットごとの論理否定(bitwise negation)
&
ビットごとの論理積(bitwise and)
^
ビットごとの排他的論理和(bitwise exclusive-or)
|
ビットごとの論理和(bitwise or)
&&
論理積(logical and)
||
論理和(logical or)

累乗(exponentiation)を除いたすべての演算子は左結合(left associative)をします。

m4の実装には^を累乗(exponentiation)演算子の代用として使うもの が多くありますが、^をビットごとの排他的論理和(bitwise exclusive-or) に使っている実装も多くあります。 GNU m4はこの点について動作の変更を行いました。 かつて^は累乗を行う演算子でしたが、 現在はビットごとの排他的論理和を行う演算子となっています。

特別な接頭辞(prefix)がついていない数字は10進数となります。 0のみの接頭辞は8進数の始まりを表します。 0xは16進数の始まりを表します。 0bは2進数の始まりを表します。 0rは1から36までの任意の基数で表現した数字の始まりを表します。 0rの後には10進数で表現した基数、コロン(:)、 および数値を表す数字列を続けなくてはなりません。 いずれの基数で表すにしても数字としては0, 1, 2, ...を使い、9から上はa, b ... zまでを 数字として使います。 アルファベットの大文字と小文字は基数を表す接頭辞および数値を表す数字列のなかで 区別なく使用することができます。

部分式をグループ化するために必要なときはカッコを使うことができます。 関係演算子は関係が真のときは1を返し、偽のときは0を返します。

evalの使用例をいくつか次に挙げます。

eval(-3 * 5)
=>-15
eval(index(`Hello world', `llo') >= 0)
=>1
define(`square', `eval(($1)**2)')
=>
square(9)
=>81
square(square(5)+1)
=>676
define(`foo', `666')
=>
eval(`foo'/6)
error-->51.eval:14: m4: Bad expression in eval: foo/6
=>
eval(foo/6)
=>111

最後から2番目の例が示しているようにevalがマクロ名を勝手に展開することはありません。 例えそれらが有効な式(もしくは有効な式の一部)に展開されるにしてもです。 したがって全てのマクロはevalに渡される前に展開済である必要があります。

radixを指定すると展開後のテキストではその基数が使われます。 デフォルトの基数は10です。 evalの結果は常に符号付き(signed)であると解釈されます。 引数widthは最低限の出力幅を指定します。 展開後のテキストが要求された幅になるように 結果には0が埋め草(zero-padded)として付加されます。

eval(666, 10)
=>666
eval(666, 11)
=>556
eval(666, 6)
=>3030
eval(666, 6, 10)
=>0000003030
eval(-666, 6, 10)
=>-000003030

radixは36より大きくてはいけないことに注意してください。

組み込みマクロevalは引数が与えられたときだけ認識されます。


Node:UNIX commands, Next:, Previous:Arithmetic, Up:Top

UNIXコマンド実行用の組み込みマクロ

UNIXコマンドをm4内部から呼び出すための組み込みマクロが m4にはいくつか存在します。


Node:Syscmd, Next:, Previous:UNIX commands, Up:UNIX commands

単一のコマンドを実行する

syscmdを使えば任意のシェル・コマンドを実行することができます。

syscmd(shell-command)

シェル・コマンドとしてshell-commandを実行します。

syscmdは展開後、shell-commandからの出力にはならずに 消滅します。shell-commandからの出力やエラー・メッセージは m4には読み込まれません。 コマンドの出力を処理する必要があるときはSee Esyscmdを参照してください。

コマンドの実行に先立ち、m4は自分の出力バッファをフラッシュします。 shell-commandにおけるデフォルトの標準入力、標準出力、 および標準エラー出力はm4のものと同じです。

syscmdは引数が与えられたときだけ認識されます。


Node:Esyscmd, Next:, Previous:Syscmd, Up:UNIX commands

コマンドの出力を読む

UNIXコマンドの出力をm4に読み込ませたいときは esyscmdを使ってください。

esyscmd(shell-command)

シェル・コマンドshell-commandの標準出力の内容に展開されます。

m4はコマンドの実行に先立ち自分の出力バッファをフラッシュします。 shell-commandのデフォルトの標準入力および標準エラー出力は m4のものと同じになります。 shell-commandのエラー出力は展開テキストの一部にはなりません ― m4のエラー出力と一緒に出てくるでしょう。

次の例ではGNU m4ディストリビューションのchecksディレクトリ にいると仮定します。

define(`vice', `esyscmd(grep Vice ../COPYING)')
=>
vice
=>  Ty Coon, President of Vice
=>

esyscmdの展開によるテキストの後ろに改行がついている様子に 注意してください。

esyscmdは引数が与えられたときだけ認識されます。


Node:Sysval, Next:, Previous:Esyscmd, Up:UNIX commands

終了コード

シェル・コマンドの実行が成功したか調べるときは sysvalを使ってください。

sysval

このマクロはsyscmdesyscmdで実行した最後のシェル・コマンドの 終了ステータス(exit status)に展開されます。

syscmd(`false')
=>
ifelse(sysval, 0, zero, non-zero)
=>non-zero
syscmd(`true')
=>
sysval
=>0


Node:Maketemp, Previous:Sysval, Up:UNIX commands

一時ファイル用の名前を生成

syscmdesyscmdに指定されたコマンドが出力やその他の目的で 一時ファイルを必要とすることもあるでしょう。 一時ファイルの名前を生成するために組み込みマクロmaketempが用意されています。

maketemp(template)

このマクロはtemplateを元に作られた現時点で存在しないファイルの名前に 展開されます。 templateは文字列XXXXXXで終らなければなりません。 この6つのXはファイル名をユニークにするためにm4のプロセスidを含む何らかの文字列によって置き換えられます。

maketemp(`/tmp/fooXXXXXX')
=>/tmp/fooa07346
maketemp(`/tmp/fooXXXXXX')
=>/tmp/fooa07346

例にあるようにmaketempを複数回呼び出すと同じ文字列に展開されることが あります。これは選択の基準がファイルが存在するかどうかだからです。 maketempを次に呼び出すより前にファイルが作成されていないときは、 2つのmaketempマクロの呼び出しは同じ名前に展開される可能性があります。

maketempは引数が与えられたときだけ認識されます。


Node:Miscellaneous, Next:, Previous:UNIX commands, Up:Top

その他の組み込みマクロ

この章では、これまでのどの章にも分類できない様々な組み込みマクロを説明します。


Node:Errprint, Next:, Previous:Miscellaneous, Up:Miscellaneous

エラーメッセージを表示する

エラー・メッセージはerrprintを使って表示することができます。

errprint(message, ...)

このマクロはmessageと残りの引数の標準エラー出力への表示だけを行います。

errprintは展開されると消滅します。

errprint(`Illegal arguments to forloop
')
error-->Illegal arguments to forloop
=>

末尾の改行は自動的に印字されません。したがって例にあるように 引数の一部として与えなければなりません。 (BSDフレーバーのm4errprint呼び出しごとに 改行を1つ末尾に付加します。)

エラーの場所を特定するための組み込みユーティリティ・マクロが2つあります。

__file__
__line__

これらは現在の入力ファイルの名前をクォートしたものと そのファイル内での現在の入力行番号に展開されます。

errprint(`m4:'__file__:__line__: `Input error
')
error-->m4:56.errprint:2: Input error
=>


Node:M4exit, Previous:Errprint, Up:Miscellaneous

m4を終了させる

すべての入力を読み込んでしまう前にm4を終了したいときは m4exitを使うことができます。

m4exit(opt code)

このマクロはcodeを終了コード(exit code)としてm4を終了させます。 codeを省略したときは終了コードは0になります。

define(`fatal_error', `errprint(`m4: '__file__: __line__`: fatal error: $*
')m4exit(1)')
=>
fatal_error(`This is a BAD one, buster')
error-->m4: 57.m4exit: 5: fatal error: This is a BAD one, buster

この例ではfatal_errorマクロが呼び出された後、 m4は終了コード1で終了します。 このマクロはエラー終了を行うためだけのものです、なぜなら通常の終了手続き、 例えば出力切替え先のテキスト(diverted text)の 逆切替え(undivert)や保存されているテキスト(see M4wrap)の再読み込みなど が行われないからです。


Node:Frozen files, Next:, Previous:Miscellaneous, Up:Top

凍結状態(frozen state)の高速ロード

何百もの定義や手間がかかるその他の初期化を含んだ共通の基盤を土台として、 複数のさらに大きなm4アプリケーションを構築することができます。 通常はその共通の基盤を1つ以上のファイルに格納しておいて m4を起動するたびにユーザの入力ファイル名の前に それらのファイル名を羅列するか、ユーザの入力ファイルからincludeします。

巨大なアプリケーションの共通基盤を何度も何度も繰り返し読み込むのは 時間がかかることでしょう。 m4には大きな共通基盤を使うアプリケーションの開始時間をスピードアップ するための機構が用意されています。 ユーザが次のコマンドラインを繰り返し使うとします。

m4 base.m4 input.m4

ここでinput.m4は起動のたびにいろいろな内容を持ち、 base.m4はかなり固定的な内容を持っているとします。 こういうときは、次のようにした方がいいでしょう。

m4 -F base.m4f base.m4

このように一度実行しておき、必要になるたびに次のように実行します。

m4 -R base.m4f input.m4

最初の-Fオプションを含んでいる呼び出しは base.m4を読んで実行することで、 さまざまなアプリケーション・マクロの定義やその他の初期化を行います。 入力ファイルbase.m4の処理が完全に終ってから、 GNU m4は凍結(frozen)ファイルをbase.m4fとして生成します。 このファイルはm4の内部状態のある種のスナップ・ショットとなっています。

後者の-Rオプションを含んでいる呼び出しでは、 どの入力ファイルが読み込まれるよりもに、 base.m4fからm4のメモリの内部状態をリロード(reload)しておく ことができます。 このようにして、まっさらな状態のm4から始めるかわりに 前に起動したときの結果を効率的に回復したあとで入力を読み込みます。 この例ではbase.m4を新たに読んだときと効果は同じですが、 それをずっと速く行うことができます。

m4の1回の起動で作ったり読み込んだりできる凍結ファイルはそれぞれ 1つだけです。 一度に2つの凍結ファイルの内容を回復することはできません。 しかし-R-Fオプションを同時につかうことで、 凍結ファイルをすこしずつ更新していくことはできます。

m4 file1.m4 file2.m4 file3.m4 file4.m4

これは多少の注意を払えば同じ出力を段々と蓄積していく 次の一連のコマンドに分けることができるでしょう。

m4 -F file1.m4f file1.m4
m4 -R file1.m4f -F file2.m4f file2.m4
m4 -R file2.m4f -F file3.m4f file3.m4
m4 -R file3.m4f file4.m4

多少の注意を払う必要があるというのは、 これがどんな場合にもうまく動くようにするための あらゆる対策がなされているわけではないからです。 とりわけ、マクロのトレース属性には対応していませんし changewordの現在の設定に関してもそうです。 m4のいくつかのオプションが1度目で使用されて、次は使用されなかった 場合にどのように作用するかも完全には検討されていません。 一方でpushdefされた定義のスタックが正しく扱われることは 保証されています。 またundefineされた定義、組み込みマクロの名前変更、 引用符やコメント記号の変更についても同様です。

m4の実行が凍結されるとき、 実行終了時に起こる自動的な出力の逆切替え(undiversion)は抑制されます。 そのかわり全ての正の番号をもつ出力切替え先の内容は凍結ファイルに 保存されます。使用中の出力切替え先の番号も伝えられます。

リロード(reload)しようとしている凍結ファイルが カレント・ディレクトリにある必要はありません。 凍結ファイルを探す方法はインクルード・ファイル(include)の場合と同じです。 (see Search Path)

凍結ファイルは複数のアーキテクチャで共有することができます。 1つのマシンで凍結ファイルを作り、それを他のマシンで使うときは 2番目のマシンで同じか新しいバージョンのGNU m4を使っているなら確実です。 これらのファイルは単純な(編集可能な)テキストファイルで、 アルファベット大文字で始まり改行文字(<NL>)で終る指令から成り立っています。 指令があるはずの場所に#があるときはコメント行の始まりとなり、 空行とあわせて無視されます。 次の説明ではlengthは常に対応するstringを参照します。 数字は常に10進数で表されます。 指令の一覧です。

V number <NL>
凍結ファイルのフォーマットを確認します。 numberは1にします。
C length1 , length2 <NL> string1 string2 <NL>
string1string2を開始コメントと終了コメント文字列として使います。
Q length1 , length2 <NL> string1 string2 <NL>
string1string2を開始クォートと終了クォート文字列として使います。
F length1 , length2 <NL> string1 string2 <NL>
pushdefを使い、string1が 組み込みマクロとして名前string2をもつ関数に展開されるように定義します。
T length1 , length2 <NL> string1 string2 <NL>
pushdefを使い、string1がテキストstring2に展開される ように定義します。
D number, length <NL> string <NL>
番号numberの出力切替え先を選択し、 それを現在の出力切替え先(current diversion)にして、 stringを現在の出力切替え先にコピーします。 numberは存在しない出力切替え先を表す負数にすることができます。 現在の出力切替え先の選択だけをしたいときは、 stringを空文字列にして、このコマンドを使います。 出力切替え先番号numberとして0を使うと、 リロード時にstringが標準出力へ出力されるでしょう、 しかしm4内部からこのようにこのコマンドが生成されることはありません。


Node:Compatibility, Next:, Previous:Frozen files, Up:Top

他の版のm4との互換性

この章ではm4の本実装とUNIXとりわけSystem V, Release 3における実装 との相違点を説明します。


Node:Extensions, Next:, Previous:Compatibility, Up:Compatibility

GNU m4で拡張された機能

本バージョンのm4にはSystem V m4に存在しない機能が いくつかあります。 これらの追加された機能はコマンドライン・オプション-Gを使うことで、 他のコマンドライン・オプションによって無効にされない限り、すべて抑制されます。

上記の拡張に加えGNU m4には次のコマンドライン・オプションが 実装されています ― -F, -G, -I, -L, -R, -V, -W, -d, -l, -o, -t。 これらオプションの説明はSee Invoking m4を参照してください。

またGNU m4のデバッグとトレース機構は 他バージョンのm4にあるものより遥かに大規模です。


Node:Incompatibilities, Next:, Previous:Extensions, Up:Compatibility

System V m4 にあってGNU m4にない機能

System Vバージョンのm4にはGNU m4にまだ実装されていない 機能がいくつかあります。


Node:Other Incompat, Previous:Incompatibilities, Up:Compatibility

その他の非互換性

System Vバージョンのm4と本実装との間には他にいくつか非互換な 部分があります。


Node:Concept index, Next:, Previous:Compatibility, Up:Top

Concept index


Node:Macro index, Previous:Concept index, Up:Top

Macro index

参照先は組み込みマクロが最初に紹介されている場所だけです。 索引では始めや終りに__のある名前はそれらが取り除かれています。

Table of Contents