10. システム関数

10.1 ファイル名

10.1.1 file, dir

$(file sequence) : File Sequence
   sequence : Sequence
$(dir sequence) : Dir Sequence
   sequence : Sequence

filedir 関数はomakeが動く場所に依存しない、ファイルとディレクトリの位置を定義します。omakeでは、ターゲットをビルドするコマンドはターゲット上のディレクトリにて実行されます。プロジェクト中には多くのディレクトリが存在するため、omakeではあるディレクトリ上のファイルを確実に指定する方法が存在します。これにより、たとえ別のディレクトリ上に移動したとしても、明示的にパスを修正せずに特定のファイルを指定することができます。 file , dir 関数は以下のように用いることで、ディレクトリやファイルのパスが関連付けられます。

例えば、カレントディレクトリ中でファイル foo を示す変数を作ったとしましょう。

FOO = $(file foo)
.SUBDIRS: bar

FOO 変数が bar サブディレクトリ中で展開された場合、これは ../foo と展開されます。

これらのコマンドはトップレベルのディレクトリを確実に指定するために、トップレベルのOMakefileにてしばしば用いられます。よって、ビルドコマンドはこれらのディレクトリを、まるで絶対パスで表しているのように扱うことができます。

ROOT = $(dir .)
LIB  = $(dir lib)
BIN  = $(dir bin)

これらの変数はいったん定義されたらサブディレクトリ中でもビルドコマンドに使うことができるので、下の $(BIN) はコマンドが実行されたディレクトリに関連付けられた bin ディレクトリで展開されます。

install: hello
  cp hello $(BIN)

10.1.2 tmpfile

$(tmpfile prefix) : File
$(tmpfile prefix, suffix) : File
    prefix : String
    suffix : String

tmpfile 関数は一時的なディレクトリ中にある、一時的な空のファイル名を返します。

10.1.3 in

$(in dir, exp) : String Array
   dir : Dir
   exp : expression

in 関数は dirfile 関数と密接に関係しています。ディレクトリとファイル名を指定することで、ディレクトリ上からのファイルの位置を返します。例えば、ファイルをインストールするためによく使われる方法の一つとしてシンボリックリンクを定義することで、その値は特定のディレクトリに関連付けられているものとします。

以下のコマンドでは $(LIB) ディレクトリ中でリンクを生成しています。

FOO = $(file foo)
install:
   ln -s $(in $(LIB), $(FOO)) $(LIB)/foo

ノート

in 関数は Node ( FileDir )が与えられた場合のみに評価を行います。

10.1.4 basename

$(basename files) : String Sequence
   files : String Sequence

basename 関数は引数に指定されたファイルのリストから元のファイル名のみを返します。任意のディレクトリパスは除去されます。

例えば、式 $(basename dir1/dir2/a.out /etc/modules.conf /foo.ml)a.out modules.conf foo.ml と評価されます。

10.1.5 dirname

$(dirname files) : String Sequence
   files : String Sequence

dirname 関数は引数に指定されたファイルのリストからディレクトリ名を返します。任意のファイル名は除去されます。ディレクトリ部分が指定されていない場合は、”.”が返されます。

例えば、式 $(dirname dir1\dir2\a.out /etc/modules.conf /foo.ml bar.ml)dir1/dir2 /etc / . と評価されます。

ノート

この関数は dirof 関数と異なります。 dirname 関数は単純に文字列のシーケンスを解析して返すのに対し、 dirofFile オブジェクトを解析する関数です。

10.1.6 rootname

$(rootname files) : String Sequence
   files : String Sequence

rootname 関数は引数に指定されたファイルのリストから基底の名前を返します。ここでの『基底の名前』とは拡張子が除去されたファイル名のことです。

例えば、式 $(rootname dir1/dir2/a.out /etc/a.b.c /foo.ml)dir1/dir2/a /etc/a.b /foo と評価されます。

10.1.7 dirof

$(dirof files) : Dir Sequence
   files : File Sequence

dirof 関数は引数に指定されたファイルのリストからディレクトリを返します。

例えば、式 $(dirof dir/dir2/a.out /etc/modules.conf /foo.ml)dir1/dir2 /etc / と評価されます。

10.1.8 fullname

$(fullname files) : String Sequence
   files : File Sequence

fullname 関数は指定した各々のファイルやディレクトリの、プロジェクトルートを元にしたパスを返します。

ノート

訳注:このような動きをします。

% a = foo/bar
- : "foo/bar" : Sequence
% b = $(fullname $a)
- : <data "/your-project/foo/bar"> : String

10.1.9 absname

$(absname files) : String Sequence
   files : File Sequence

absname 関数は指定した各々のファイルやディレクトリの絶対パスを返します。

10.1.10 homename

$(homename files) : String Sequence
   files : File Sequence

homename 関数は可能であればチルダ ~ を用いてパス名を返します。展開できない形のパスは遅延的に計算されます。具体的にいうと、 homename 関数は通常、始めてチルダ型のパスとして展開できるようになるまでは、絶対パスとして評価します。

10.1.11 suffix

$(suffix files) : String Sequence
   files : StringSequence

suffix 関数はファイルのリストから拡張子を返します。もし拡張子が存在しなかった場合、空の文字列が返されます。

例えば、式 $(suffix dir1/dir2/a.out /etc/a /foo.ml).out .ml と評価されます。

10.2 パスによる検索

10.2.1 which

$(which files) : File Sequence
   files : String Sequence

which 関数は現在のコマンド検索パスから実行可能なものを検索し、引数に指定されたコマンドのファイルパスを返します。コマンドが見つからない場合にはエラーが発生します。

10.2.2 where

where 関数は which 関数と似ていますが、この関数は与えられた実行コマンドのすべての位置をリストとして返します。この場合ですと echo コマンドは Shell オブジェクトによって内的に扱われるので、出力先の最初の文字列ではビルドイン関数として扱われていることを表しています。

% where echo
echo is a Shell object method (a built-in function)
/bin/echo

10.2.3 rehash

rehash()

rehash 関数はすべての検索パスをリセットします。

10.2.4 exists-in-path

$(exists-in-path files) : String
   files : String Sequence

exists-in-path 関数は引数に指定されたすべてのコマンドが、現在の検索パス上に存在しているかどうか試します。

10.2.5 digest, digest-optional

$(digest files) : String Array
   file : File Array
raises RuntimeException

$(digest-optional files) : String Array
   file : File Array

digestdigest-optional 関数はファイルのMD5を計算します。 digest 関数はファイルが存在しない場合には例外を送出します。一方で、 digest-optional はこの様な場合 false を返します。また、MD5はキャッシュされます。

10.2.6 find-in-path, find-in-path-optional

$(find-in-path path, files) : File Array
   path : Dir Array
   files : String Array
raises RuntimeException

$(find-in-path-optional path, files) : File Array

file-in-path 関数は指定されたパスから指定されたファイルを検索します。これらの関数はファイル名が一致した場合のみ処理の対象となります(訳注: 一部ではない)。 find-in-path 関数はファイルが見つからない場合には例外を送出します。一方で、 find-in-path-optional 関数はこの様な場合、例外を送出せずに結果から取り除かれます。

10.2.7 digest-in-path, digest-in-path-optional

$(digest-in-path path, files) : String/File Array
   path : Dir Array
   files : String Array
raises RuntimeException

$(digest-in-path-optional path, files) : String/File Array

diget-in-path 関数は指定されたパスからファイルを検索し、各々のファイルのファイルパスとMD5を返します。これらの関数はファイル名が一致した場合のみ処理の対象となります。 digest-in-path 関数はファイルが見つからない場合には例外を送出します。一方で、 digest-in-path-optional 関数はこの様な場合、例外を送出せずに結果から取り除かれます。

10.3 ファイル検査

10.3.1 file-exists, target-exists, target-is-proper

$(file-exists files) : String
$(target-exists files) : String
$(target-is-proper files) : String
    files : File Sequence

file-exists 関数はリストされているファイルが存在しているかどうか調べます。 target-exists 関数は file-exists と似ていますが、この関数はファイルが存在するか、現在のプロジェクトでターゲットとなっているような場合に true を返します。 target-is-proper 関数は指定されたファイルが現在のプロジェクトで生成できる場合のみ true を返します。

10.3.2 stat-reset

$(stat-reset files) : String
    files : File Sequence

OMakeでは Stat によるキャッシュを行っています。 stat-reset 関数は stat から指定されたファイルの情報をリセットし、次回にこれらの情報が要求された場合には stat 情報が強制的に再計算されます。

10.3.3 filter-exists, filter-targets, filter-proper-targets

$(filter-exists files) : File Sequence
$(filter-targets files) : File Sequence
$(filter-proper-targets) : File Sequence
   files : File Sequence

filter-exists , filter-targets , filter-proper-targets 関数は指定されたファイルのリストをフィルタリングして、特定のファイルのみを返します。

  • filter-exists : 存在しているファイルのリストのみが結果として返されます。
  • filter-targets : ファイルが存在しているか、現在のプロジェクト上でビルドできるような場合は結果として返されます。
  • filter-proper-targets : 現在のプロジェクトでビルドできるファイルのリストのみが結果として返されます。

“distclean” ターゲットを作る

プロジェクトによって生成されたファイルを消去する distclean ルールを作るような場合に、一つの簡単な方法が存在します。それは、現在のプロジェクトでビルドすることのできるすべてのファイルを消去することです。

警告

この方法を何も考えずに実行するのではなく、慎重に考えてください。このルールはプロジェクトで作られるであろう すべてのファイル を消去してしまいます。言いかえると、この方法は消去されたファイルのリビルドが成功するかどうかについては、まったく考慮していません。また、この方法では現在のプロジェクトの外にあるファイルは消去されません。

.PHONY: distclean

distclean:
    rm $(filter-proper-targets $(ls R, .))

もしあなたがCVSを用いているのであれば、CVSに知らせずにすべてのファイルを消去する distclean ルールを作るのではなく、OMakeによって作られた cvs_realclean プログラムを作りたいと思うことでしょう。例えば、もしあなたが既によく使われている clean ターゲットを作っていたのなら、そしてあなたが distclean ルールをデフォルトでインタラクティブなものにしたいのであれば、以下のように記述することができます。

if $(not $(defined FORCE_REALCLEAN))
    FORCE_REALCLEAN = false
    export

distclean: clean
    cvs_realclean $(if $(FORCE_REALCLEAN), -f) -i .omakedb -i .omakedb.lock

あなたは -i オプションを用いることで、いつでも保持していたいより多くのファイル(設定ファイルなど)を追加することができます。

同様に、Subversionを使っていた場合、OMakeでは build/svn_realclean.om スクリプトを用いて以下のように記述できます。

if $(not $(defined FORCE_REALCLEAN))
    FORCE_REALCLEAN = false
    export

open build/svn_realclean

distclean: clean
    svn_realclean $(if $(FORCE_REALCLEAN), -f) -i .omakedb -i .omakedb.lock

中間ファイルを除去する別の方法についての詳細は、 dependencies-proper 関数を参照してください。

10.3.4 find-targets-in-path, find-targets-in-path-optional

$(find-targets-in-path path files) : File Array
$(find-targets-in-path-optional path, files) : File Array
    path : Dir Array
    files : File Sequence

find-target-in-path 関数はパス上のターゲットを検索します。指定された各々の file はディレクトリ dir を順番に検索していき、ターゲット dir/file が存在していた場合はファイル dir/file が返されます。

例えば、あなたはCのプロジェクトをビルドしようとしており、そのプロジェクトはサブディレクトリ src/ を含んでおり、その中には fee.cfoo.c が含まれているものとしましょう。そのような場合、以下の式はたとえそのファイルが存在していなくても、 src/fee.o src/foo.o と評価されます。

$(find-targets-in-path lib src, fee.o foo.o)

# このように評価されます。
src/fee.o src/foo.o

find-targets-in-path 関数はファイルが見つからない場合には例外を送出します。一方で、 find-targets-in-path-optional 関数はこの様な場合、例外を送出せずに結果から取り除かれます。

$(find-targets-in-path-optional lib src, fee.o foo.o fum.o)

# このように評価されます。
src/fee.o src/foo.o

10.3.5 find-ocaml-targets-in-path-optional

find-ocaml-targets-in-path-optional 関数は find-targets-in-path-optional と似ていますが、この関数はパス上のすべての成分と名前を検索し、最初に小文字のバージョンがビルド可能であるか調べた後で、大文字のバージョンがビルド可能であるか調べる、OCamlスタイルの検索が使われます。

10.3.6 file-sort

$(file-sort order, files) : File Sequence
   order : String
   files : File Sequence

file-sort 関数はソートルールの集合を用いてビルドのソート順を指定することで、ファイル名のリストをソートします。ソートルールは .ORDER ターゲットを用いて宣言できます。 .BUILDORDER は通常のソート順が定義されています。

$(file-sort <order>, <files>)

例えば、以下のようなルールの集合を考えてみましょう。

a: b c
b: d
c: d

.DEFAULT: a b c d
   echo $(file-sort .BUILDORDER, a b c d)

このような場合、 file-sort 関数の結果は d b c a となります。これは依存関係が生成した 後で ターゲットがソートされることを表しています。この関数は依存関係がリンクされてあるファイルをソートする際(依存関係が問題となってくる言語を扱う場合)、頻繁に用いられます。

この関数は3つの重要な制約があります。

  • この関数はルールの内容だけを使ってソートします。理由としては、ソートを実行する前に すべての 依存関係を知っておかなければならないからです。
  • この関数は現在のプロジェクトでビルド可能なファイルのみをソートします。
  • この関数は依存関係が輪状(cyclic)になっている場合、失敗します。

10.3.6.1 ソートルール

ソートルールを使用することで、 file-sort 関数にさらなる制限を加えることができます。ソートルールは2つの手順を用いて宣言します。まず、ターゲットは .ORDER ターゲットに加えなければなりません。次に、ソートルールの集合が与えられていなければなりません。ソートルールには制限(pattern constraint)を定義します。

.ORDER: .MYORDER

.MYORDER: %.foo: %.bar
.MYORDER: %.bar: %.baz

.DEFAULT: a.foo b.bar c.baz d.baz
   echo $(sort .MYORDER, a.foo b.bar c.baz d.baz)

この例では、 .MYORDER ソートルールは、拡張子 .foo の任意のファイルは拡張子 .bar の任意のファイルの後に置く必要があり、さらに拡張子 .bar の任意のファイルは拡張子 .baz の任意のファイルの後に置く必要があることを指定しています。

この例では、ソート結果は d.baz c.baz b.bar a.foo となります。

10.3.7 file-check-sort

file-check-sort(files)
   files : File Sequence
raises RuntimeException

file-check-sort 関数はファイルのリストがソート順になっているかどうか調べます。もしそうならば、リストは変更されずに返されます。そうでない場合は、この関数は例外を送出します。

$(file-check-sort <order>, <files>)

10.4 ファイルの検索とリスト

OMakeのコマンドは実行される前に『glob展開』が行われます。これは、ファイル名にはディレクトリやファイル名のシーケンスに展開された パターン を含めることができることを意味しています。構文は標準的なbash(1), csh(1)や以下のルールに従っています。

  • パス名は /\ の文字によって分割された、ディレクトリやファイル名から成るシーケンスです。例えば、2つのパス名 /home/jyh/OMakefile , /home\jyh/OMakefile は同一のファイルを表しています。
  • Glob展開はパスの文字を元にして実行されます。もしパスが下記の特殊文字を含んでいた場合、パスはシステム上の実際にあるファイルに対してマッチしているものだけがリストされます。この展開によって、パターン文字はマッチしたすべてのファイルやディレクトリのシーケンスに展開されます。

以下、ディレクトリ /dir にはファイル a , -a , a.b , b.c が含まれているものとします。

  • * : 0以上の文字を持つ任意のシーケンスにマッチします。例えば、パターン /dir/a*/dir/a /dir/aa /dir/a.b に展開されます。
  • ? : 任意の一つの文字にマッチします。パターン /dir/?a/dir/-a に展開されます。
  • [...] : 大括弧の中には文字の集合や、ASCII文字の範囲を指定します。パターンには独立な文字 c や文字の範囲 c1-c2 を含めます。パターンのマッチには任意の文字や任意の範囲を指定することができます。 ^ をつけることで与えられたパターンを反転(指定した文字を含んでいないとマッチする)できます。また、パターンの中に - を含めたい場合、パターンの最初の文字に - と指定しなければなりません。

    パターン 展開
    /dir/[a-b]* /dir/a /dir/a.b /dir/b.c
    dir/[-a-b]* /dir/a /dir/-a /dir/a.b /dir/b.c
    /dir/[-a]* /dir/a /dir/-a /dir/a.b
  • {s1,...,sN} : 中括弧の中にはコンマによって分割された文字列のシーケンスを指定します。N個の文字列が与えられていた場合、結果はN個のパターンのコピーが生成されて、各々の文字列はsiとなります。

    パターン 展開
    a{b,c,d} ab ac ad
    a{b{c,d},e} abc abd ae
    a{?{[A-Z],d},*} a?[A-Z] a?d a*

    チルダ(~)はホームディレクトリを指定する際に用いられます。この値はあなたのシステムに依存して展開されます。

    パターン 展開
    ~jyh /home/jyh
    ~bob/*.c c:\Documents and Settings\users\bob

    \ 文字はパス名のセパレータとしても、文字をエスケープするのにも使われます。もし特殊blob文字の前に用いられていた場合、 \ は次の文字を一種の特殊でない文字に変形します。さもなければ、 \ はパス名のセパレータとして解釈されます。

    パターン 展開
    ~jyh/\* ~jyh/* (* は文字通り扱われる)
    /dir/\[a-z? /dir/[a-z? ( [ は文字通り、 ? はパターンとして扱われる)
    c:\Program Files\[A-z] c:\Program Files[A-z]*

    ノート

    最後の \ 文字に関するケースは非常に曖昧です。 \ はパス名のセパレータとして扱うべきで、 [ をエスケープするために用いるのではありません。もしあなたがWin32上でこのような解釈を避けたいとするならば、たとえWin32のパス名であっても / を用いるべきです( /\ に変換されて出力されます)。

    パターン 展開
    c:/Program Files/[A-z]* c:\Program Files\WindowsUpdate ...

10.4.1 glob

$(glob strings) : Node Array
   strings : String Sequence
$(glob options, strings) : Node Array
   options : String
   strings : String Sequence

glob 関数はglob展開を行います。

... エントリは常に無視されます。

オプションは以下です。

  • b

    csh(1)スタイルの大括弧展開を行いません。

  • e

    \ 文字を特殊文字にエスケープしません。

  • n

    展開が失敗した場合、無視する代わりに展開を文字通り返します。

  • i

    展開が失敗した場合、何も行いません。

  • .

    .から始まるファイルにマッチする、ワイルドカードパターンを許可します。

  • A

    .から始まるファイルを含んだ、すべてのファイルを返します。

  • F

    通常のファイルのみ(ディレクトリでない任意のファイル)にマッチします。

  • D

    ディレクトリファイルのみにマッチします。

  • C

    cvs(1)ルールに関するファイルを無視します。

  • P

    適切なサブディレクトリのみを含みます。

加えて、以下の変数が glob の動作に影響を与えるように定義されています。

  • GLOB_OPTIONS

    デフォルトのオプションを定義している文字列

  • GLOB_IGNORE

    glob が無視すべき、ファイル名のシェルパターンのリスト

  • GLOB_ALLOW

    シェルパターンのリスト。ファイルが GLOB_ALLOW のパターンにマッチしなかった場合、そのファイルは無視されます。

また、返されるファイルは名前順でソートされます。

10.4.2 ls

$(ls files) : Node Array
   files : String Sequence
$(ls options, files) : Node Array
   files : String Sequence

ls 関数はディレクトリからファイル名を返します。

... エントリは常に無視されます。パターンにはシェルライクなパターンを指定することで、 ls 関数はglob展開を行います。

オプションは glob 関数のオプションをすべて含んでいるほかに、以下のオプションが含まれています。

  • R : ディレクトリを再帰的に走査し、リストします。

GLOB_ALLOWGLOB_IGNORE 変数はglob検索の動作を制御するために定義されています。また、返り値は名前によってソートされます。

10.4.3 subdirs

$(subdirs dirs) : Dir Array
   dirs : String Sequence
$(subdirs options, dirs) : Dir Array
   options : String
   dirs : String Sequence

subdirs 関数はディレクトリのリストに存在している、すべてのサブディレクトリを再帰的に返します。

とりうることのできるオプションは以下に定義されています。

  • A : aから始まるディレクトリを返します。
  • C : .cvsignoreルールに基づいて、特定のファイルを無視します。
  • P : 適切なサブディレクトリのみを含めます。

10.5 ファイル操作

10.5.1 mkdir

mkdir(mode, node...)
   mode : Int
   node : Node
raises RuntimeException

mkdir(node...)
   node : Node
raises RuntimeException

mkdir 関数はディレクトリかディレクトリの集合を生成します。以下のオプションがサポートされています。

  • -m mode : 作られるディレクトリのパーミッションを指定します。
  • -p : もし親のディレクトリが存在しない場合は、新しく生成します。
  • : この文字の後にくる文字列を、たとえ特殊文字が含まれていても文字通りに解釈します。

10.5.2 Stat

statlstat 関数によって返される Stat オブジェクトはファイルシステムノードについての情報を提供します。このオブジェクトは以下のフィールドを含んでいます。

  • dev : デバイス番号
  • ino : inode番号
  • king : ファイルの種類を表し、以下の内の一つが指定されています: REG (通常のファイル), DIR (ディレクトリ), CHR (キャラクタデバイス), BLK (ブロックデバイス), LNK (シンボリックリンク), FIFO (名前付きパイプ), SOCK (ソケット)
  • perm : アクセス権。数値で表現されます。
  • nlink : リンクの数
  • uid : オーナーのユーザーID
  • gid : ファイルのグループのグループID
  • rdev : デバイスのマイナー番号
  • size : ファイルのバイト数
  • atime : アクセス日時。浮動小数点で表現されます。
  • mtime : 修正日時。浮動小数点で表現されます。
  • ctime : ステータス変更日時。浮動小数点で表現されます。

すべてのフィールドがすべてのOS上で意味をもつわけではない点に注意してください。

10.5.3 stat, lstat

$(stat node...) : Stat
   node : Node or Channel
$(lstat node...) : Stat
   node : Node or Channel
raises RuntimeException

stat 関数はファイル情報を返します。もしファイルがシンボリックリンクであった場合は、 stat 関数はリンク先を参照します。一方で、 lstat 関数はリンク自身を参照します。

10.5.5 rename

rename(old, new)
   old : Node
   new : Node
mv(nodes... dir)
   nodes : Node Sequence
   dir   : Dir
cp(nodes... dir)
   nodes : Node Sequence
   dir   : Dir
raises RuntimeException

rename 関数はファイルかディレクトリの名前を old から new に変更します。

mv 関数は似ていますが、もし new がディレクトリでかつ存在している場合、シーケンスによって指定されたファイルはディレクトリの中に移動されます。そうでない場合、 mv の動作は rename と全く同じです。 cp 関数は似ていますが、この関数はオリジナルのファイルを消去しません。

mvcp 関数は以下のオプションをとります。

  • -f : 上書きする前の確認を行いません。
  • -i : 上書きする前に確認します。
  • -v : どのような処理を行っているのか出力します。
  • -r : ディレクトリの内容を再帰的にコピーします。
  • - : この文字の後に来る引数は文字通りに解釈されます。

10.5.9 chmod

chmod(mode, dst...)
   mode : Int
   dst : Node or Channel
chmod(mode dst...)
   mode : String
   dst : Node Sequence
raises RuntimeException

chmod 関数は対象のパーミッションを変更します。

オプション:

  • -v : どのような処理を行っているのか説明します。
  • -r : ファイルやディレクトリを再帰的に変更します。
  • -f : エラーが生じても続けて実行します。
  • - : 残りの引数を文字通りに解釈します。

10.5.10 chown

chown(uid, gid, node...)
   uid : Int
   gid : Int
   node : Node or Channel
chown(uid, node...)
   uid : Int
   node : Node or Channel
raises RuntimeException

chown 関数はファイルのユーザやグループIDを変更します。もし gid が指定されていない場合は、値は変更されません。IDが-1であった場合でも、そのIDは変更されません。

10.5.11 truncate

truncate(length, node...)
    length : Int
    node : Node or Channel
raises RuntimeException

truncate 関数はファイルを与えられた長さに切り詰めます。

10.5.12 umask

$(umask mode) : Int
   mode : Int
raises RuntimeException

ファイル生成マスクを設定します。この関数は前回のマスクの値が返されます。この値はスコープ化されていませんので、この変更はグローバルに影響を及ぼします。

10.6 vmount

10.6.1 vmount

vmount(src, dst)
   src, dst : Dir
vmount(flags, src, dst)
   flags : String
   src, dst : Dir

src ディレクトリを dst ディレクトリに『マウント』します。これは仮想的なマウントで、 $(file ...) 関数のふるまいを変更します。 $(file str) が使われた場合、返される値はもしファイルが存在していたら src ディレクトリに関連付けられます。さもなければファイルは現在のディレクトリに関連付けられます。

vmount 関数の主な目的は、分割された設定やアーキテクチャを用いて、複数のビルドを行うことをサポートするためです。

オプションは以下の通りです。

  • l : src ディレクトリ中のファイルへのシンボリックリンクを生成します。
  • c : src ディレクトリからファイルをコピーします。

なお、マウント操作はスコープ化されています。

10.6.2 add-project-directories

add-project-directories(dirs)
   dirs : Dir Array

omakeがプロジェクトの一部とみなしているディレクトリの集合に、ディレクトリを新しく追加します。これは主に、現在のディレクトリがプロジェクトの一部でないとomakeがエラーを出すことを避けるために用いられます。

10.6.3 remove-project-directories

remove-project-directories(dirs)
   dirs : Dir Array

omakeがプロジェクトの一部とみなしているディレクトリの集合から、ディレクトリを削除します。これは主に、特定のディレクトリがコンパイルされる必要がないことが分かっているので、インクルードしているディレクトリから .SUBDIRS を取り消すような場合に用いられます。

10.7 ファイルの内容を元にした検索

10.7.1 test

test(exp) : Bool
   exp : String Sequence

評価式の文法は以下の通りです。

  • ! exp : expは真でない
  • exp1 -a exp2 : 両方のexpが真である
  • exp1 -o exp2 : 1以上のexpが真である
  • (exp) : expは真である

基本となる評価式は以下の通りです。

  • -n str : 文字列 は0でない長さである
  • -z str : 文字列 は0の長さである
  • str = str : 両方の文字列は等しい
  • str != str : 両方の文字列は等しくない
  • int1 -eq int2 : 両方の整数は等しい
  • int1 -ne int2 : 両方の整数は等しくない
  • int1 -gt int2 : int1はint2よりも大きい(int1 > int2)
  • int1 -ge int2 : int1はint2以上である(int1 >= int2)
  • int1 -gt int2 : int1はint2よりも小さい(int1 < int2)
  • int1 -le int2 : int1はint2以下である(int1 <= int2)
  • file1 -ef file2 : Unix上では、file1とfile2は同一のデバイスとinode番号である。Win32上では、file1とfile2は同一の名前である。
  • file1 -nt file2 : file1はfile2よりも新しい
  • file1 -ot file2 : file1はfile2よりも古い
  • -b file : fileはブロック型特殊ファイルである
  • -c file : fileはキャラクタ型特殊ファイルである
  • -d file : fileはディレクトリである
  • -e file : fileが実在している
  • -f file : fileは通常のファイルである
  • -g file : set-group-id bitがfileに設定されている
  • -G file : fileのグループIDが現在影響を受けているグループである
  • -h file : fileがシンボリックリンクである(-L でもよい)
  • -k file : fileにスティッキー・ビットが設定されている
  • -L file : fileがシンボリックリンクである(-h でもよい)
  • -O file : fileのオーナーが現在影響を受けているグループである
  • -p file : fileが名前付きパイプである
  • -r file : fileが読み込み可能である
  • -s file : fileは空である
  • -S file : fileはソケットである
  • -u file : set-user-id bitがfileに設定されている
  • -w file : fileは書き込み可能である
  • -x file : fileは実行可能である

str は任意の文字列で、 - 文字をつけることができます。

int は整数として解釈できる文字列です。従来の test プログラムのバージョンとは異なり、先頭の文字にはアリティを指定することもできます。接頭辞 0b は数値をバイナリで扱います。同様に、接頭辞 0o は数値を8進数で扱い、接頭辞 0x は数値を16進数で扱います。 int-l を用いることで数値を文字列として扱うことができます。これによって、数値ではなく文字列の長さが評価されます。

file はファイル名を表す文字列です。

構文は test(1) プログラムと似ています。もしあなたがUnixのシステム上にいるのならば、manを参照すればさらに詳細を説明してくれるでしょう。以下にいくつかのサンプルを挙げておきます。

# 空のファイルを作成
osh> touch foo
# ファイルは空ですか?
osh> test(-e foo)
- : true
osh> test(! -e foo)
- : false
# 別のファイルを作成
osh> touch boo
# 新しく作ったファイルは前よりも新しいですか?
osh> test(boo -nt foo)
- : true
# さらに複雑な式を示します。
# booはfooよりも新しく、さらにfooは空である。
osh> test(\( boo -nt foo \) -a -e foo)
- : true

10.7.2 find

find(exp) : Node Array
   exp : String Sequence

find 関数はディレクトリを再帰的に検索し、 exp の評価が真であるファイルを返します。

引数の exp は以下の例外を除いて、 test 関数と同じ構文を用いています。

  1. exp はディレクトリから始まります。もし特に指定していない場合はカレントディレクトリから検索します。
  2. {} 文字は現在のファイルを表す文字に拡張されています。

exp の構文は test 関数のそれと同一ですが、以下の点が加わっています。

  • -name str : 現在のファイルはglob表現にマッチしている(詳細は “10.4 ファイルの検索とリスト” を参照してください)。
  • -regex str : 現在のファイルは正規表現にマッチしている。

find 関数はすべてのサブディレクトリを再帰的にスキャンします。以下の関数呼び出しは omake のソースディレクトリのルートから実行させています。

osh> find(. -name fo* )
- : <array
        /home/jyh/.../omake/mk/.svn/format
        /home/jyh/.../omake/RPM/.svn/format
        ...
        /home/jyh/.../omake/osx_resources/installer_files/.svn/format>

別の例では、通常のファイルかシンボリックリンクのファイルのみを並べています。

osh> find(. -name fo* -a \( -f {} -o -L {} \))
- : <array
        /home/jyh/.../omake/mk/.svn/format
        /home/jyh/.../omake/RPM/.svn/format
        ...
        /home/jyh/.../omake/osx_resources/installer_files/.svn/format>

10.8 I/O 関数

10.8.1 標準出力先

以下の変数が標準出力先として定義されています。

  • stdin

    stdin : InChannel
    

    標準入力チャネルで、読み込みを担当します。

  • stdout

    stdout : OutChannel
    

    標準出力チャネルで、書き込みを担当します。

  • stderr

    stderr : OutChannel
    

    標準エラーチャネルで、書き込みを担当します。

10.8.2 open-in-string

open-in-string 関数は文字列をまるでファイルのように扱い、読み込むためのチャネルを返します。

$(open-in-string s) : Channel
    s : String

10.8.3 open-out-string, out-contents

open-out-string 関数はファイルの代わりに文字列を書き込むチャネルを作成します。文字列は out-contents 関数を用いて取得することができます。

$(open-out-string) : Channel
$(out-contents chan) : String
    chan : OutChannel

10.8.4 fopen

fopen 関数はファイルを読み書きするために、新しくファイルを開きます。

$(fopen file, mode) : Channel
   file : File
   mode : String

file は読み書きするファイル名を指定します。 mode は以下の文字を組み合わせた文字列です。

  • r : 読み込むためにファイルを開きます。もしファイルが存在していない場合はエラーが発生します。
  • w : 書き込むためにファイルを開きます。もしファイルが存在していない場合は新しくファイルを作ります。
  • a : 追記モードでファイルを開きます。もしファイルが存在していない場合は新しくファイルを作ります。
  • + : 読み書き両方するためにファイルを開きます。
  • t : ファイルをテキストモードで開きます(デフォルト)。
  • b : ファイルをバイナリモードで開きます。
  • n : ファイルをノンブロッキングモードで開きます。
  • x : ファイルが既に存在している場合は失敗します。

Unixシステム上ではバイナリモードは意味を持たず、テキストモードとバイナリモードは等価に扱われます。

10.8.5 close

$(close channel...)
   channel : Channel

close 関数は以前に fopen によって開かれたファイルを閉じる関数です。

10.8.6 read, input-line

$(read channel, amount) : String
$(input-line channel) : String
   channel : InChannel
   amount  : Int
raises RuntimeException

read 関数は amount バイトを入力チャネルから読み込み、読み込んだデータを返します。 input-line 関数はファイルから一行を読み込み、読み込んだ一行を改行コード抜きで返します。もしファイルの終わりまでたどり着いた場合、両方の関数は例外 RuntimeException を送出します。

10.8.7 write

$(write channel, buffer, offset, amount) : String
   channel : OutChannel
   buffer  : String
   offset  : Int
   amount  : Int
$(write channel, buffer) : String
   channel : OutChannel
   buffer  : String
raises RuntimeException

4つ引数をとる場合、 write 関数は出力チャネル channel に位置 offset から、バイト buffer を書き込みます。なお、上限は amount バイトです。この関数は何バイトが書き込まれたのかというバイト数を返します。

3つ引数をとる場合も同様ですが、 offset は0となります。

2つ引数をとる場合、 offset は0で、かつ amountbuffer の長さになります。

ファイルの終わりまでたどり着いた場合、この関数は例外 RuntimeException を送出します。

10.8.8 lseek

$(lseek channel, offset, whence) : Int
   channel : Channel
   offset  : Int
   whence  : String
raises RuntimeException

lseek 関数はチャネル channel のオフセット位置を whence に基づいて変更します。 whence は以下の通りです。

  • SEEK_SET : オフセットは offset に設定されます。
  • SEEK_CUR : オフセットは現在の位置から offset バイト分移動します。
  • SEEK_END : オフセットはファイルサイズ + offset バイトの位置に設定されます。

lseek 関数はファイルの新しいオフセット位置を返します。

10.8.9 rewind

rewind(channel...)
   channel : Channel

rewind 関数は現在のファイル位置をファイルが始まる位置に設定します。

10.8.10 tell

$(tell channel...) : Int...
   channel : Channel
raises RuntimeException

tell 関数は channel の現在位置を返します。

10.8.11 flush

$(flush channel...)
   channel : OutChannel

flush 関数は書き込み用途にファイルが開かれている場合のみに使われます。この関数はファイルにまだ書き込まれていないすべてのデータを消去します。

10.8.12 channel-name

$(channel-name channel...) : String
   channel : Channel

channel-name 関数はチャネルに関係している名前を返します。

10.8.13 dup

$(dup channel) : Channel
   channel : Channel
raises RuntimeException

dup 関数は引数に指定されたチャネルと同一のファイルを示している、新しいチャネルを返します。

10.8.14 dup2

dup2(channel1, channel2)
   channel1 : Channel
   channel2 : Channel
raises RuntimeException

dup2 関数は channel2channel1 と同一のファイルを示すようにします。

10.8.15 set-nonblock

set-nonblock-mode(mode, channel...)
   channel : Channel
   mode : String

set-nonblock-mode 関数は与えられたチャネルのノンブロッキングフラグを設定します。もしチャネルが入出力中で、この操作が即座に完了しないような場合、 RuntimeException が送出されます。

10.8.16 set-close-on-exec-mode

set-close-on-exec-mode 関数は与えられたチャネルのclose-on-execフラグを設定します。もしclose-on-execフラグが設定されている場合、このチャネルは子プロセスから継承されません。そうでない場合は、このチャネルは子プロセスから継承されます。

10.8.17 pipe

$(pipe) : Pipe
raises RuntimeException

pipe 関数は2つのフィールドを持つ Pipe オブジェクトを生成します。 read フィールドは読み込むためのチャネルで、 write フィールドは書き込むためのチャネルです。

10.8.18 mkfifo

mkfifo(mode, node...)
   mode : Int
   node : Node

mkfifo 関数は名前付きパイプを生成します。

10.8.19 select

$(select rfd..., wfd..., efd..., timeout) : Select
   rfd : InChannel
   wfd : OutChannel
   efd : Channel
   timeout : float
raises RuntimeException

select 関数は与えられたチャネルの集合が入出力可能であるか監視します。 rfd には読み込み可能なチャネルのシーケンスを指定します。 wfd には書き込み可能なチャネルのシーケンスを指定します。 efd にはエラー状態を監視するチャネルのシーケンスを指定します。 timeout にはイベントを待つ最大時間を指定します。

正常な戻り値の場合、 select 関数は以下のフィールドを持つ Select オブジェクトを返します。

  • read : 読み込み可能なチャネルの配列
  • write : 書き込み可能なチャネルの配列
  • error : エラーが生じたチャネルの配列

10.8.20 lockf

lockf(channel, command, len)
   channel : Channel
   command : String
   len : Int
raises RuntimeException

lockf 関数は与えられたチャネルのPOSIXロックの範囲を設定します。範囲は現在の位置から len バイトまでです。

command のとりうる値は以下の通りです。

  • F_ULOCK : 指定された範囲をアンロックします。
  • F_LOCK : 指定された範囲を書き込みロックします。既にロックされている場合はブロックされます。
  • F_TLOCK : 指定された範囲を書き込みロックします。既にロックされている場合は失敗します。
  • F_TEST : 指定された範囲に他のロックがないか試します。
  • F_RLOCK : 指定された範囲を読み込みロックします。既にロックされている場合はブロックされます。
  • F_TRLOCK : 指定された範囲を読み込みロックします。既にロックされている場合は失敗します。

10.8.21 InetAddr

InetAddr オブジェクトはインターネットアドレスを記述しています。このオブジェクトは以下のフィールドを含んでいます。

  • addr -> String : インターネットアドレス
  • port -> Int : ポート番号

10.8.22 Host

Host オブジェクトは以下のフィールドを含んでいます。

  • name -> String : ホスト名
  • aliases -> String Array : ホストの別名の配列
  • addrtype -> String : より好ましいドメイン・ソケット
  • addrs -> InetAddr Array : ホストに所属しているインターネットアドレスの配列

10.8.23 gethostbyname

$(gethostbyname host...) : Host...
   host : String
raises RuntimeException

gethostbyname 関数は指定されたホストの Host オブジェクトを返します。 host にはドメイン名かインターネットアドレスを指定します。

10.8.24 Protocol

Protocol オブジェクトはプロトコルエントリーを表現します。このオブジェクトは以下のフィールドを含んでいます。

  • name -> String : 正規のプロトコル名
  • aliases -> String Array : プロトコルのエイリアスの配列
  • proto -> Int : プロトコル番号

10.8.25 getprotobyname

$(getprotobyname name...) : Protocol...
   name : Int or String
raises RuntimeException

getprotobyname 関数は指定されたプロトコルから Protocol オブジェクトを返します。 name にはプロトコル名かプロトコル番号を指定します。

10.8.26 Service

Service オブジェクトはネットワークサービスを表現します。このオブジェクトは以下のフィールドを含んでいます。

  • name -> String : サービス名
  • aliases -> String Array : サービスのエイリアスの配列
  • port -> Int : サービスのポート番号
  • proto -> Protocol : サービスのプロトコル

10.8.27 getservbyname

$(getservbyname service...) : Service...
   service : String or Int
raises RuntimeException

getservbyname 関数はネットワークサービスの情報を取得します。 service にはサービス名か番号を指定します。

10.8.28 socket

$(socket domain, type, protocol) : Channel
   domain : String
   type : String
   protocol : String
raises RuntimeException

socket 関数は束縛されていないソケットを生成します。

引数のとりうる値は以下の通りです。

domain は以下の値をとります。

  • PF_UNIX あるいは unix : Unixシステムでのみ利用可能なUnixドメイン
  • PF_INET あるいは inet : IPv4インターネットドメイン
  • PF_INET6 あるいは inet6 : IPv6インターネットドメイン

type は以下の値をとります。

  • SOCK_STREAM あるいは stream : ストリームソケット
  • SOCK_DGRAM あるいは dgram : データグラムソケット
  • SOCK_RAW あるいは raw : 生(raw)ソケット
  • SOCK_SEQPACKET あるいは seqpacket : パケットシーケンスソケット

protocol はプロトコルのデータベースにあるプロトコルを指定した IntString 型の引数です。

10.8.29 bind

bind(socket, host, port)
   socket : InOutChannel
   host : String
   port : Int
bind(socket, file)
   socket : InOutChannel
   file : File
raise RuntimeException

bind 関数はソケットをアドレスに束縛します。

3つ引数をとる場合、 bind 関数はインターネットの接続方法について指定し、 host にはホスト名かIPアドレスを、 port にはポート番号を指定します。

2つ引数をとる形は Unix ソケットのために用意されています。 file にはファイル名のアドレスを指定します。

10.8.30 listen

listen(socket, requests)
   socket : InOutChannel
   requests : Int
raises RuntimeException

listen 関数は requests 個のまだ取得されていないリクエストを取得するように、ソケットを設定します。

10.8.31 accept

$(accept socket) : InOutChannel
   socket : InOutChannel
raises RuntimeException

accept 関数はソケットの接続を受け入れます。

10.8.32 connect

connect(socket, addr, port)
   socket : InOutChannel
   addr : String
   port : int
connect(socket, name)
   socket : InOutChannel
   name : File
raise RuntimeException

connect 関数はソケットを対象のアドレスに接続します。

3つ引数をとる場合、 connect 関数はインターネットの接続方法について指定します。 addr にはリモートホストのインターネットアドレスをドメイン名かIPアドレスの形で指定します。 port にはポート番号を指定します。

2つ引数をとる形はUnixソケットのために用意されています。 name にはソケットのファイル名を指定します。

10.8.33 getchar

$(getc) : String
$(getc file) : String
   file : InChannel or File
raises RuntimeException

getc 関数はファイルの次の文字を返します。もし引数が指定されなかった場合、 stdin が入力として用いられます。もしファイルの終わりまでたどりついた場合、この関数は false を返します。

10.8.34 gets

$(gets) : String
$(gets channel) : String
   channel : InChannel or File
raises RuntimeException

gets 関数はファイルから次の行を返します。この関数はファイルの終わりまでたどり着いていた場合、空の文字列を返します。改行コードは取り除かれます。

10.8.35 fgets

$(fgets) : String
$(fgets channel) : String
   channel : InChannel or File
raises RuntimeException

fgets 関数は fopen によって読み込みが開かれているファイルから、次の行を返します。この関数はファイルの終わりまでたどり着いていた場合、空の文字列を返します。返される文字列は文字通りのデータ(literal data)として返されます。改行コードは取り除かれません。

10.9 出力関数

printprintln 関数を用いて出力を表示します。 println 関数は表示する値に新しく改行コードを加えます。 print 関数は加えません。

fprint(<file>, <string>)
print(<string>)
eprint(<string>)
fprintln(<file>, <string>)
println(<string>)
eprintln(<string>)

fprint 関数は fopen で開かれたファイルに対して出力します。 print 関数は標準出力チャネルに対して出力しますが、 eprint 関数は標準エラーチャネルに対して出力します。

10.10 値を出力する関数

値は printvprintvln 関数を用いて表示できます。 printvln 関数は表示する値に新しく改行コードを加えます。 printv 関数は加えません。

fprintv(<file>, <string>)
printv(<string>)
eprintv(<string>)
fprintvln(<file>, <string>)
printvln(<string>)
eprintvln(<string>)

fprinv 関数は fopen で開かれたファイルに対して出力します。 printv 関数は標準出力チャネルに対して出力しますが、 eprintv 関数は標準エラーチャネルに対して出力します。

10.10.1 その他の関数

10.10.1.1 set-channel-line

set-channel-line(channel, filename, line)
    channel : Channel
    filename : File
    line : int

指定されたチャネルの行番号情報を設定します。

10.11 高レベルな I/O 関数

10.11.1 正規表現

多くの高レベルな関数では正規表現を使用しています。正規表現はawk(1)の構文とだいたい似ている文字列から成り立っています。

文字列には以下の文字定数を含めることができます。

  • \\ : バックスラッシュ文字
  • \a : アラート文字 ^G
  • \b : バックスペース文字 ^H
  • \f : 改ページ文字 ^L
  • \n : 改行文字 ^J
  • \r : キャリッジリターン(復帰,CR)文字 ^M
  • \t : タブ文字 ^I
  • \v : 垂直タブ文字
  • \xhh... : 16進数として表現される文字列 h 。すべての正しい16進数文字列はシーケンス文字列の一部として扱われます。
  • \ddd : 1〜3桁の8進数として扱われる文字列

正規表現は特殊文字 .\^$[(){}*?+ を使うことができます。

  • c : c が特殊文字でない場合は文字通りに扱います。
  • \c : c が特殊文字であっても、 c を文字通りに扱います。
  • . : 改行を含む、任意の文字にマッチします。
  • ^ : 行の始めにマッチします。
  • $ : 行の終わりにマッチします。
  • [abc...] : abc... の任意の文字にマッチします。
  • [^abc...] : abc... を除く、任意の文字にマッチします。
  • r1|r2 : r1r2 のいづれかであった場合はマッチします。
  • r1r2 : r1 の次に r2 が来ていた場合はマッチします。
  • r+ : 1以上の r の文字列にマッチします。
  • r* : 0以上の r の文字列にマッチします。
  • r? : 0もしくは1つの r にマッチします。
  • (r) : r にマッチします。括弧はグループ化のために用いられます。
  • \(r\) : グループとして扱われますが、括弧の中でマッチした式は変数 $1 , $2 , ... を通して参照することができます。
  • r{n} : r が正確に n 回続いている場合はマッチします。
  • r{n,} : rn 回以上続いている場合はマッチします。
  • r{n,m} : rn 回以上 m 回以下続いている場合はマッチします。
  • \y : 単語の始まりあるいは単語が終わった後の空文字にマッチします。
  • \B : 単語の中にある空文字にマッチします。
  • \< : 単語の始まりの空文字にマッチします。
  • \> : 単語が終わった後のから文字にマッチします。
  • \w : 単語の任意の文字にマッチします。
  • \W : 単語の中に入っていない、任意の文字にマッチします。
  • \~ : ファイルの始まりの空文字にマッチします。
  • \' : ファイルの終わりの空文字にマッチします。

上の文字クラスは文字のシーケンスを絶対的に指定するのに用いられます。これらのシーケンスはあなたの言語環境次第で変更することもできます。

  • [:alnum:] : 英数字文字
  • [:alpha:] : アルファベット文字
  • [:lower:] : アルファベット小文字
  • [:upper:] : アルファベット大文字
  • [:cntrl:] : 制御文字
  • [:digit:] : 数字
  • [:xdigit:] : 数字あるいは16進数文字
  • [:graph:] : 出力可能でかつ表示可能な文字
  • [:print:] : 出力可能で、表示可能あるいは不可能な文字
  • [:punct:] : 句読点文字
  • [:blank:] : スペースかタブ文字
  • [:space:] : ホワイトスペース文字

10.11.2 cat

cat(files) : Sequence
   files : File or InChannel Sequence

cat 関数は複数のファイルを連結し、文字列として返します。

10.11.3 grep

grep(pattern) : String  # stdin から入力されて、デフォルトのオプションが用いられる
   pattern : String
grep(pattern, files) : String  # デフォルトのオプションが用いられる
   pattern : String
   files   : File Sequence
grep(options, pattern, files) : String
  options : String
  pattern : String
  files   : File Sequence

grep 関数はファイルの集合から正規表現 pattern に適合しているものを検索し、マッチした行を表示します。これはgrep(1)の高度に簡素化されたバージョンです。

オプションは以下の通りです。

  • q : 指定した場合、 grep からの出力は表示されません。
  • h : 指定した場合、出力された行はファイル名を含めません(一つの入力ファイルだけが与えられた場合のデフォルト)。
  • n : 指定した場合、出力された行はファイル名を含めます(複数の入力ファイルが与えられた場合のデフォルト)。
  • v : 指定した場合、マッチした行の代わりにマッチしなかった行を出力します。

pattern は正規表現です。

もし成功した ( grep がマッチした行を見つけた)場合、この関数は true を返します。そうでない場合は false を返します。

10.11.4 scan

scan(input-files)
case string1
   body1
case string2
   body2
...
default
   bodyd

scan 関数はコマンドライン上からの入力機能を提供します。この関数はファイル、あるいはファイル名を引数にとります。もし何も引数が呼ばれなかった場合、 stdin から入力されます。もし引数が指定された場合、各々の引数には InChannel が指定されるか、入力としてファイル名が用いられます。なお、出力は常に stdout です。

scan 関数は入力から一行を読み込み、以下のアルゴリズムに従って処理を行います。

各々の行で、レコードはまずいくつかのフィールドに分割されて、それらのフィールドは変数 $1, $2, ... に束縛されます。変数 $0 は行全体として定義されており、 $* はすべてのフィールドの値が定義されている配列です。 $(NF) 変数はフィールドの数が定義されています。

次に case 文が実行されます。もし string_i がトークン $i にマッチした場合、 body_i が評価されます。もし case の内容が export で終わっていたのなら、現在の状態は次の宣言句へ受け継がれます。そうでない場合、この値は捨てられます。

例えば、以下の scan 関数は単純なコマンドプロセッサのように振る舞います。

calc() =
   i = 0
   scan(script.in)
   case print
      println($i)
   case inc
      i = $(add $i, 1)
      export
   case dec
      i = $(sub $i, 1)
      export
   case addconst
      i = $(add $i, $2)
      export
   default
      eprintln($"Unknown command: $1")

scan 関数はまたいくつかのオプションをサポートしています。

scan(options, files)
...
  • A : 各々の行を引数のリストとして、クオート化された状態でパースします。例えば、以下の行は3つのワード “ls” , “-l” , “Program Files” を持つことになります: ls -l "Program Files"
  • O : 各々の行を、ホワイトスペースをセパレータとしてパースします。これは通常OMakeが文字列をパースする際のアルゴリズムを使用しています。この動作はデフォルトです。
  • x : 各々の行を分割し、さらに16進数表現を用いてワードの個数を減らします。これはURLを指定する際には通常16進数表現が用いられるためです。例えば、文字列 Program Files は代わりに以下の形 Program+Files に置き換わります。

ノート

もしあなたが出力をファイルにリダイレクトしたい場合、最も簡単な方法は stdout 変数を再定義することです。 stdout 変数は他の変数と同様スコープ化されているので、この再定義は calc 関数の外にある stdout には影響を及ぼしていません。

calc() =
    stdout = $(fopen script.out, w)
    scan(script.in)
       ...
    close(stdout)

10.11.5 awk

awk(input-files)
case pattern1:
   body1
case pattern2:
   body2
...
default:
   bodyd

あるいは

awk(options, input-files)
case pattern1:
   body1
case pattern2:
   body2
...
default:
   bodyd

awk 関数はawk(1)と似た入力機能を提供します。が、この関数は制限されています。引数 input-files には値のシーケンスを指定することで、各々の値には InChannel が指定されるか、入力としてファイル名が用いられます。もしオプションとファイルの引数が何も指定されなかったら、入力は stdin が用いられます。なお、出力は常に stdout です。

変数 RSFS にはレコードとフィールドを分割するセパレータが正規表現の形で定義されています。なお、デフォルトの RS の値は正規表現 \r|\n|\r\n で、 FS[ \t]+ です。

awk 関数は入力から一つのレコードを読み込み、以下のアルゴリズムに従って処理を行います。

各々の行で、レコードはまずフィールドセパレータ FS を用いていくつかのフィールドに分割されて、それらのフィールドは変数 $1, $2, ... に束縛されます。変数 $0 は行全体として定義されており、 $* はすべてのフィールドの値が定義されている配列です。変数 $(NF) はフィールドの数が定義されています。

次に、 case が順番どおりに評価されていきます。各々の case において、もし正規表現 pattern_i がレコード $0 にマッチしていた場合は、 body_i が評価されます。もし body_iexport で終わっていたのなら、現在の状態は次の宣言句へ受け継がれます。そうでない場合、この値は捨てられます。もし正規表現が \(r\) を含んでいたのなら、フィールド $1, $2, ... はこれらの表現で書き換えられます。

例えば、以下のコードはテキストが二つのデリミタ \begin{<name>}\end{<name>} の間にあり、さらに filter 関数の引数として渡された配列の中に <name> が入っているときだけ、その間のテキストを出力しています。

filter(names) =
   print = false

   awk(Awk.in)
   case $"^\\end\{\([:alpha:]+\)\}"
      if $(mem $1, $(names))
         print = false
         export
      export
   default
      if $(print)
         println($0)
   case $"^\\begin\{\([:alpha:]+\)\}"
      print = $(mem $1, $(names))
      export

ノート

もしあなたが出力をファイルにリダイレクトしたい場合、最も簡単な方法は stdout 変数を再定義することです。 stdout 変数は他の変数と同様スコープ化されているので、この再定義は filter 関数の外にある stdout には影響を及ぼしていません。

filter(names) =
    stdout = $(fopen file.out, w)
    awk(Awk.in)
       ...
    close(stdout)

オプション :

  • b : case を評価する際にループを『中断(Break)』します。ただし、複数のマッチ文が選択されるような場合のみです。

break 関数はループを停止する際に用いられます。これを用いると awk 関数は即座に中断します。

10.11.6 fsubst

fsubst(files)
case pattern1 [options]
   body1
case pattern2 [options]
   body2
...
default
   bodyd

fsubst 関数は sed(1) のような置換機能を提供します。 awk と似ていて、もし fsubst が何の引数も指定されずに呼び出された場合、入力は stdin が用いられます。もし引数が与えられていた場合、各々の引数は InChannnel が指定されるか、入力としてファイル名が用いられます。

RS 変数はレコードのセパレータを指定する正規表現が定義されており、 RS のデフォルトの値は \r|\n|\r\n です。

fsubst 関数は1回につき1つのレコードを読み込みます。

各々のレコードで、 case 文は順番どおりに評価されます。各々の case ではマッチした pattern を、定義された文字列に置換する機構について定義しています。

現在のところ、omakeでは g オプションだけがサポートされています。指定した場合、各々の宣言句は全体の置換を行い、すべての pattern のインスタンスによって置換が行われます。そうでない場合、置換は1回だけ行われます。

出力は stdout 変数を再定義することによってリダイレクトできます。

例えば、以下のプログラムは word に適合した文字列すべてを大文字化し、置換します。

section
   stdout = $(fopen Subst.out, w)
   fsubst(Subst.in)
   case $"\<\([[:alnum:]]+\)\." g
      value $(capitalize $1).
   close(stdout)

10.11.7 lex

lex(files)
case pattern1
   body1
case pattern2
   body2
...
default
   bodyd

lex 関数はシンプルな文法解析器を提供します。入力はファイルやチャネルのシーケンスです。 case には正規表現を指定します。この関数は入力を読み込むたび、 最も長い接頭辞 にマッチした正規表現を選択し、その内容を評価します。

同じ長さで2つの case 文がマッチしてしまった場合、 後ろの case 文が実行されます。 default 文は正規表現 . にマッチするので、パターンリストの最初に設置するのが恐らく望ましいでしょう。

もし case の内容が export で終わっていたのなら、現在の状態は次のループへ受け継がれます。

例えば、以下のプログラムは入力されたファイルからすべての英数字を集めます。

collect-words($(files)) =
   words[] =
   lex($(files))
   default
      # empty
   case $"[[:alnum:]]+" g
      words[] += $0
      export

default 文が存在する場合、この文は任意の1つの文字のみにマッチします。また、もし入力がどの正規表現にもマッチしなかった場合、この関数はエラーとなります。

break 関数はループを停止する際に用いられます。

10.11.9 Lexer

Lexer オブジェクトは容易に字句解析を行えるようにするオブジェクトで、 lex(1)flex(1) プログラムと似ています。

omakeでは、字句の解析は Lexer クラスを継承することによって動的に構成することができます。字句解析器(以後レキサと呼ぶ)の定義はメソッドを呼び出すことで指示文(directive)を指定しているものの集合と、ルールとして宣言句(clause)を指定しているものの集合によって成り立っています。

例えば、以下のシンプルなデスクトップ電卓の演算の字句解析を行う、レキサの定義について考えてみましょう。

lexer1. =
   extends $(Lexer)

   other: .
      eprintln(Illegal character: $* )
      lex()

   white: $"[[:space:]]+"
      lex()

   op: $"[-+*/()]"
      switch $*
      case +
         Token.unit($(loc), plus)
      case -
         Token.unit($(loc), minus)
      case *
         Token.unit($(loc), mul)
      case /
         Token.unit($(loc), div)
      case $"("
         Token.unit($(loc), lparen)
      case $")"
         Token.unit($(loc), rparen)

   number: $"[[:digit:]]+"
      Token.pair($(loc), exp, $(int $* ))

   eof: $"\'"
      Token.unit($(loc), eof)

このプログラムは Lexer オブジェクトから字句解析の環境を定義している lexer1 を継承しています。

残りの定義では宣言句の集合の定義を行っています。コロン(:)の前にはメソッド名を指定し、コロンの後には正規表現を指定します。この場合は内容も指定しています。内容はなくても構いません。指定されなかった場合、レキサの定義で既に存在している、与えられたメソッド名が用いられます。

警告

最も長い 接頭辞にマッチした宣言句が選択されます。もし2つの宣言句が同じ長さの場合、 後ろの 宣言句が選択されます。これはほとんどの標準的なレキサとな異なっていますが、拡張性から見ればこの仕様は大きな意味を持ちます。

最初の宣言句は他の宣言句にマッチしなかった任意の入力文字列がマッチします。この場合、未知の文字のエラーメッセージが出力されます。この宣言句は他の宣言句にマッチしなかった場合のみ選択されることに注意してください。

2番目の宣言句ではホワイトスペースを無視する役割を持っています。ホワイトスペースが見つかった場合、これを無視し、再帰的にレキサを呼び出します。

3番目の宣言句では演算子の役割を持っています。ここでは Token オブジェクトを利用しています。なお、この Token オブジェクトは3つのフィールド(ソース位置を表現している loc , name , value)を定義しています。

レキサは各々のメソッドの body 部で、現在の語彙素(lexeme)の位置を表す loc 変数が定義されているので、私たちはトークンを生成するためにこの値を用いています。

Token.unit($(loc), name) メソッドは与えられた名前とデフォルトの値を用いて新しい Token オブジェクトを構成します。

number 宣言句は正の整数の定数にマッチします。 Token.pair($(loc), name, value) は与えられた名前と値でトークンを構成します。

Lexer オブジェクトは InChannel オブジェクトを操作します。 lexer1.lex-channel(channel) メソッドは与えられたチャネルから次のトークンを読み込みます。

10.11.10 レキサのマッチング

字句解析においては、最も長くマッチした宣言句が選択されます。これは、最も長い入力文字のシーケンスにマッチした宣言句が評価対象になるということです。もしどの宣言句にもマッチしなかった場合、レキサは RuntimeException を送出します。もし1つ以上の宣言句が同じ量の入力にマッチした場合、最初の1つが評価に用いられます。

10.11.11 拡張したレキサの定義

それでは前回のレキサのサンプルを、コメントを無視するように拡張してみましょう。ここで、コメントを (* から *) で終わる任意のテキストとして定義します。なお、コメントはネスト化されているものとします。

これを実現する一つの簡単な方法としては、コメントをスキップする別のレキサを定義することが挙げられます。

lex-comment. =
   extends $(Lexer)

   level = 0

   other: .
      lex()

   term: $"[*][)]"
      if $(not $(eq $(level), 0))
         level = $(sub $(level), 1)
         lex()

   next: $"[(][*]"
      level = $(add $(level), 1)
      lex()

   eof: $"\'"
      eprintln(Unterminated comment)

このレキサには、ネストレベルを記録し続けている lebel フィールドを含んでいます。 (* に遭遇すると、この変数はレベルを1増やし、 *) が来たら、0でない場合はレベルを1減らし、続けます。

次に、前回のレキサを、コメントをスキップするような形に修正してみましょう。これはちょうど前に作った lexer1 オブジェクトを拡張することで実現できます。

lexer1. +=
   comment: $"[(][*]"
      lex-comment.lex-channel($(channel))
      lex()

comment 宣言句の内容にはコメントに遭遇した場合、 lex-comment レキサを呼び出し、このレキサが返されたときに解析し続けることを指定しています。

10.11.12 lexerオブジェクトを扱いやすくする

宣言句の内容はまた指示文のエクスポートで終了します。この場合、レキサオブジェクト自身がトークンを返すものとして使われます。もしレキサオブジェクトの上で Parser オブジェクトを使うのであれば、レキサは loc , name , value フィールドを、各々の export 宣言句の中で定義すべきです。毎回 Parser オブジェクトはレキサを呼び出し、さらに前回のレキサを起動することで返されるレキサを呼び出します。

10.11.13 Parser

Parser オブジェクトは『文脈自由な文法(context-free grammars)』をベースとした、文法解析機能を提供しています。

Parser オブジェクトは指示文のシーケンスとして指定されます。また、 Parser オブジェクトはメソッドの呼び出し、生成、ルールの指定も担当しています。

例えば、前回の Lexer のサンプルを使って、デスクトップ計算機を作ってみましょう。

parser1. =
   extends $(Parser)

   #
   # 主に使うレキサを定義
   #
   lexer = $(lexer1)

   #
   # 昇順に優先順位を定義
   #
   left(plus minus)
   left(mul div)
   right(uminus)

   #
   # プログラム
   #
   start(prog)

   prog: exp eof
      return $1

   #
   # 単純な算術式定義
   #
   exp: minus exp :prec: uminus
      neg($2)

   exp: exp plus exp
      add($1, $3)

   exp: exp minus exp
      sub($1, $3)

   exp: exp mul exp
      mul($1, $3)

   exp: exp div exp
      div($1, $3)

   exp: lparen exp rparen
      return $2

パーサは Parser クラスの式として定義されています。パーサオブジェクトは lexer フィールドを持たなければなりません。 lexerLexer オブジェクトでなければならないというわけではありませんが、トークンオブジェクトを返す lexer.lex() メソッドと name , value フィールドを提供していなければなりません。例えば、今回私たちは前回の項で定義した lexer1 オブジェクトを使用しました。

次のステップでは演算子記号の優先順位を定義します。優先順位は left , right , nonassoc メソッドの順に上がっていきます( left が一番下で nonassoc が一番上)。

文法(grammar)は最低でも一つの開始記号を持たなければなりません。これは start メソッドで宣言できます。

次に、文法の中の生成部(productions)はルールに従って並べられます。生成部の名前はコロンの前に指定し、変数のシーケンスはコロンの後に指定します。内容部には、生成部が入力の一部として解釈された場合における、意味論的な行動(semantic action)を指定します。

今回の例では、デスクトップの計算機によって解析された算術式を生成しています。今回の場合、『意味論的な行動』は数値計算としてふるまいます。変数 $1, $2, ... は生成部の右から順に、各々の変数に関連付けられた値として扱われます。

10.11.14 パーサの呼び出し

パーサは $(parser1.parse-channel start, channel)$(parser1.parse-file start, file) で呼び出されます。 start 引数には開始記号を指定し、 channel あるいは file にはパーサへの入力を指定します。

10.11.15 パースの制御

パーサの生成器はLALR(1)テーブルをベースにした、『先読み後出しのオートメーション(pushdown automation)』を生成します。通常、文法が曖昧である場合には、このオートメーションは『移動してから減らすのか』あるいは『減らしてからさらに減らすのか』で衝突することになります。これらの衝突はオートメーションが生成された時点で、標準出力に表示されます。

通常は、オートメーションはパーサが始めて使われることになるまで構築されません。

build(debug) メソッドはオートメーションの構築を強制的に行います。 build(debug) メソッドを呼び出すことによって、各々のパーサが要求されなくなるまで完全に構築させるというのは賢い方法です。 debug 変数が設定されている場合、このメソッドはパーサテーブルの任意の衝突をそれぞれ出力します。

loc 変数は行動部(action bodies)の内部で定義されます。また、生成部の右側にある、すべてのトークンの入力範囲を表現します。

10.11.16 拡張したパーサ

パーサはまた継承することで拡張できます。例えば、文法を拡張することでシフト演算 <<>> を理解できるようにしてみましょう。

初めに、私たちはレキサを拡張することで、これらのトークンを理解できるようにします。今回、私たちは += 演算子を使う代わりに、既存の lexer1 を完全なものにすることを選びました。

lexer2. =
   extends $(lexer1)

   lsl: $"<<"
      Token.unit($(loc), lsl)

   asr: $">>"
      Token.unit($(loc), asr)

次に、私たちはこれらの新しい演算子を扱うように、既存のパーサを拡張しました。ビット演算子は既存の算術演算子よりも低い優先順位にするつもりです。今回は二つの引数をとる left メソッドを使うことで実現しました。

parser2. =
   extends $(parser1)

   left(plus, lsl lsr asr)

   lexer = $(lexer2)

   exp: exp lsl exp
      lsl($1, $3)

   exp: exp asr exp
      asr($1, $3)

今回の場合、私たちは新しいレキサ lexer2 を使用して、さらに新しいシフト演算子の生成部を追加しました。

10.11.17 Passwd

Passwd オブジェクトはシステムユーザのデータベース上にあるエントリを表現します。このオブジェクトは以下のフィールドを持っています。

  • pw_name : ログインネーム
  • pw_passwd : 暗号化されたパスワード
  • pw_uid : ユーザのユーザID
  • pw_gid : ユーザのグループID
  • pw_gecos : ユーザ名かコメント欄
  • pw_dir : ユーザのホームディレクトリ
  • pw_shell : ユーザが通常使うシェル

すべてのフィールドがすべてのOS上で意味をもつわけではないことに注意してください。

10.11.18 getpwnam, getpwuid

$(getpwnam name...) : Passwd
   name : String
$(getpwuid uid...) : Passwd
   uid : Int
raises RuntimeException

getpwnam 関数はユーザのログイン名からエントリを探しだします。 getpwuid 関数はユーザID(numerical id, uid)からエントリを探し出します。もしエントリが見つからなかった場合、例外が送出されます。

10.11.19 getpwents

$(getpwents) : Array

getpwents 関数は Passwd オブジェクトの配列を返します。すべてのユーザは、システムユーザのデータベースによって用意されます。この関数はOSやユーザデータベースの状況に依存し、返される配列は完全でなかったり、空である可能性もある点に注意してください。

10.11.20 Group

Group オブジェクトはシステムのユーザグループに関するデータベースのエントリを表現します。このオブジェクトは以下のフィールドを含んでいます。

  • gr_name : グループ名
  • gr_group : 暗号化されたパスワード
  • gr_gid : グループのグループID
  • gr_mem : グループメンバのユーザ名

すべてのフィールドがすべてのOSで意味を持つわけでは無い点に注意してください。

10.11.21 getgrnam, getgrgid

$(getgrnam name...) : Group
   name : String
$(getgrgid gid...) : Group
   gid : Int
raises RuntimeException

getgrnam 関数はグループ名からグループエントリを探しだし、 getgrgid 関数はグループID(gid)からエントリを探し出します。何も見つからなかった場合は例外が送出されます。

10.11.22 tgetstr

$(tgetstr id) : String
   id : String

tgetstr 関数は指定された id を用いて『端末の能力(terminal capability, termcap)』を調べます。これは、”terminfo”の調査が TERM 環境変数によって与えられることを保証しています。もし与えられた”terminal capability”が定義されていなかった場合、この関数は空の値を返します。

ノート

シェルプロンプト内部の tgetstr によって返された値を使用したい場合、あなたは prompt-invisible 関数を用いてラップする必要があります。

10.11.23 xterm-escape-begin, xterm-escape-end

$(xterm-escape-begin) : String
$(xterm-escape-end) : String

xterm-escape-beginxterm-escape-end 関数はXTermのウィンドウタイトルを設定したい場合に用いることができる、エスケープのシーケンスを返します。この機能が使えない場合、この関数は空の値を返します。

ノート

シェルプロンプト内部の値を用いるようにしたい場合、あなたは $(prompt_invisible_begin)$(xterm-escape-begin)$(xterm-escape-end)$(prompt_invisible_end) を使う必要があります。

10.11.24 xterm-escape

$(xterm-escape s) : Sequence

TERM 環境変数が『XTermのタイトルを設定する機能が利用できる』ことを表していた場合、 $(xterm-escape s)$(xterm-escape-begin)s$(xterm-escape-end) と等価です。そうでない場合、この関数は空の値を返します。

ノート

シェルプロンプト内部の xterm-escape によって返された値を用いるようにしたい場合、あなたは prompt-invisible 関数を使ってラップする必要があります。

10.11.25 prompt-invisible-begin, prompt-invisible-end

$(prompt-invisible-begin) : String
$(prompt-invisible-end) : String

prompt-invisible-beginprompt-invisible-end 関数は、シェルプロンプトに『見えない』セクション(様々なエスケープシーケンスのような)を設定するために用いる必要のある、エスケープのシーケンスを返します。

10.11.26 prompt-invisible

$(prompt-invisible s) : Sequence

prompt-invisible は指定された引数を $(prompt-invisible-begin)$(prompt-invisible-end) でラップします。シェルプロンプトに『見えない』すべてのセクション(様々なエスケープシーケンスのような)はこの方法でラップしなければなりません。

10.11.27 gettimeofday

$(gettimeofday) : Float

gettimeofday 関数は1970年1月1日から始まる、現在までの秒数を返します。

目次

前のトピックへ

9. 基本ライブラリ

次のトピックへ

11. シェルコマンド

このページ

SourceForge.JP

SourceForge.JP

このドキュメントはsourceforge.jpのサーバを利用して提供しています。